Commit 817fb15d authored by Eric Dumazet's avatar Eric Dumazet Committed by David S. Miller

net_sched: sfq: allow divisor to be a parameter

SFQ currently uses a 1024 slots hash table, and its internal structure
(sfq_sched_data) allocation needs order-1 page on x86_64

Allow tc command to specify a divisor value (hash table size), between 1
and 65536.
If no value is provided, assume the 1024 default size.

This allows admins to setup smaller (or bigger) SFQ for specific needs.

This also brings back sfq_sched_data allocations to order-0 ones, saving
3KB per SFQ qdisc.

Jesper uses ~55.000 SFQ in one machine, this patch should free 165 MB of
memory.
Signed-off-by: default avatarEric Dumazet <eric.dumazet@gmail.com>
CC: Patrick McHardy <kaber@trash.net>
CC: Jesper Dangaard Brouer <hawk@diku.dk>
CC: Jarek Poplawski <jarkao2@gmail.com>
CC: Jamal Hadi Salim <hadi@cyberus.ca>
CC: Stephen Hemminger <shemminger@vyatta.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 3fbd8758
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/jhash.h> #include <linux/jhash.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/vmalloc.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/netlink.h> #include <net/netlink.h>
#include <net/pkt_sched.h> #include <net/pkt_sched.h>
...@@ -76,7 +77,8 @@ ...@@ -76,7 +77,8 @@
#define SFQ_DEPTH 128 /* max number of packets per flow */ #define SFQ_DEPTH 128 /* max number of packets per flow */
#define SFQ_SLOTS 128 /* max number of flows */ #define SFQ_SLOTS 128 /* max number of flows */
#define SFQ_EMPTY_SLOT 255 #define SFQ_EMPTY_SLOT 255
#define SFQ_HASH_DIVISOR 1024 #define SFQ_DEFAULT_HASH_DIVISOR 1024
/* We use 16 bits to store allot, and want to handle packets up to 64K /* We use 16 bits to store allot, and want to handle packets up to 64K
* Scale allot by 8 (1<<3) so that no overflow occurs. * Scale allot by 8 (1<<3) so that no overflow occurs.
*/ */
...@@ -112,7 +114,7 @@ struct sfq_sched_data { ...@@ -112,7 +114,7 @@ struct sfq_sched_data {
int perturb_period; int perturb_period;
unsigned int quantum; /* Allotment per round: MUST BE >= MTU */ unsigned int quantum; /* Allotment per round: MUST BE >= MTU */
int limit; int limit;
unsigned int divisor; /* number of slots in hash table */
/* Variables */ /* Variables */
struct tcf_proto *filter_list; struct tcf_proto *filter_list;
struct timer_list perturb_timer; struct timer_list perturb_timer;
...@@ -120,7 +122,7 @@ struct sfq_sched_data { ...@@ -120,7 +122,7 @@ struct sfq_sched_data {
sfq_index cur_depth; /* depth of longest slot */ sfq_index cur_depth; /* depth of longest slot */
unsigned short scaled_quantum; /* SFQ_ALLOT_SIZE(quantum) */ unsigned short scaled_quantum; /* SFQ_ALLOT_SIZE(quantum) */
struct sfq_slot *tail; /* current slot in round */ struct sfq_slot *tail; /* current slot in round */
sfq_index ht[SFQ_HASH_DIVISOR]; /* Hash table */ sfq_index *ht; /* Hash table (divisor slots) */
struct sfq_slot slots[SFQ_SLOTS]; struct sfq_slot slots[SFQ_SLOTS];
struct sfq_head dep[SFQ_DEPTH]; /* Linked list of slots, indexed by depth */ struct sfq_head dep[SFQ_DEPTH]; /* Linked list of slots, indexed by depth */
}; };
...@@ -137,7 +139,7 @@ static inline struct sfq_head *sfq_dep_head(struct sfq_sched_data *q, sfq_index ...@@ -137,7 +139,7 @@ static inline struct sfq_head *sfq_dep_head(struct sfq_sched_data *q, sfq_index
static unsigned int sfq_fold_hash(struct sfq_sched_data *q, u32 h, u32 h1) static unsigned int sfq_fold_hash(struct sfq_sched_data *q, u32 h, u32 h1)
{ {
return jhash_2words(h, h1, q->perturbation) & (SFQ_HASH_DIVISOR - 1); return jhash_2words(h, h1, q->perturbation) & (q->divisor - 1);
} }
static unsigned int sfq_hash(struct sfq_sched_data *q, struct sk_buff *skb) static unsigned int sfq_hash(struct sfq_sched_data *q, struct sk_buff *skb)
...@@ -201,7 +203,7 @@ static unsigned int sfq_classify(struct sk_buff *skb, struct Qdisc *sch, ...@@ -201,7 +203,7 @@ static unsigned int sfq_classify(struct sk_buff *skb, struct Qdisc *sch,
if (TC_H_MAJ(skb->priority) == sch->handle && if (TC_H_MAJ(skb->priority) == sch->handle &&
TC_H_MIN(skb->priority) > 0 && TC_H_MIN(skb->priority) > 0 &&
TC_H_MIN(skb->priority) <= SFQ_HASH_DIVISOR) TC_H_MIN(skb->priority) <= q->divisor)
return TC_H_MIN(skb->priority); return TC_H_MIN(skb->priority);
if (!q->filter_list) if (!q->filter_list)
...@@ -219,7 +221,7 @@ static unsigned int sfq_classify(struct sk_buff *skb, struct Qdisc *sch, ...@@ -219,7 +221,7 @@ static unsigned int sfq_classify(struct sk_buff *skb, struct Qdisc *sch,
return 0; return 0;
} }
#endif #endif
if (TC_H_MIN(res.classid) <= SFQ_HASH_DIVISOR) if (TC_H_MIN(res.classid) <= q->divisor)
return TC_H_MIN(res.classid); return TC_H_MIN(res.classid);
} }
return 0; return 0;
...@@ -496,7 +498,11 @@ static int sfq_change(struct Qdisc *sch, struct nlattr *opt) ...@@ -496,7 +498,11 @@ static int sfq_change(struct Qdisc *sch, struct nlattr *opt)
q->perturb_period = ctl->perturb_period * HZ; q->perturb_period = ctl->perturb_period * HZ;
if (ctl->limit) if (ctl->limit)
q->limit = min_t(u32, ctl->limit, SFQ_DEPTH - 1); q->limit = min_t(u32, ctl->limit, SFQ_DEPTH - 1);
if (ctl->divisor) {
if (!is_power_of_2(ctl->divisor) || ctl->divisor > 65536)
return -EINVAL;
q->divisor = ctl->divisor;
}
qlen = sch->q.qlen; qlen = sch->q.qlen;
while (sch->q.qlen > q->limit) while (sch->q.qlen > q->limit)
sfq_drop(sch); sfq_drop(sch);
...@@ -514,15 +520,13 @@ static int sfq_change(struct Qdisc *sch, struct nlattr *opt) ...@@ -514,15 +520,13 @@ static int sfq_change(struct Qdisc *sch, struct nlattr *opt)
static int sfq_init(struct Qdisc *sch, struct nlattr *opt) static int sfq_init(struct Qdisc *sch, struct nlattr *opt)
{ {
struct sfq_sched_data *q = qdisc_priv(sch); struct sfq_sched_data *q = qdisc_priv(sch);
size_t sz;
int i; int i;
q->perturb_timer.function = sfq_perturbation; q->perturb_timer.function = sfq_perturbation;
q->perturb_timer.data = (unsigned long)sch; q->perturb_timer.data = (unsigned long)sch;
init_timer_deferrable(&q->perturb_timer); init_timer_deferrable(&q->perturb_timer);
for (i = 0; i < SFQ_HASH_DIVISOR; i++)
q->ht[i] = SFQ_EMPTY_SLOT;
for (i = 0; i < SFQ_DEPTH; i++) { for (i = 0; i < SFQ_DEPTH; i++) {
q->dep[i].next = i + SFQ_SLOTS; q->dep[i].next = i + SFQ_SLOTS;
q->dep[i].prev = i + SFQ_SLOTS; q->dep[i].prev = i + SFQ_SLOTS;
...@@ -531,6 +535,7 @@ static int sfq_init(struct Qdisc *sch, struct nlattr *opt) ...@@ -531,6 +535,7 @@ static int sfq_init(struct Qdisc *sch, struct nlattr *opt)
q->limit = SFQ_DEPTH - 1; q->limit = SFQ_DEPTH - 1;
q->cur_depth = 0; q->cur_depth = 0;
q->tail = NULL; q->tail = NULL;
q->divisor = SFQ_DEFAULT_HASH_DIVISOR;
if (opt == NULL) { if (opt == NULL) {
q->quantum = psched_mtu(qdisc_dev(sch)); q->quantum = psched_mtu(qdisc_dev(sch));
q->scaled_quantum = SFQ_ALLOT_SIZE(q->quantum); q->scaled_quantum = SFQ_ALLOT_SIZE(q->quantum);
...@@ -542,6 +547,15 @@ static int sfq_init(struct Qdisc *sch, struct nlattr *opt) ...@@ -542,6 +547,15 @@ static int sfq_init(struct Qdisc *sch, struct nlattr *opt)
return err; return err;
} }
sz = sizeof(q->ht[0]) * q->divisor;
q->ht = kmalloc(sz, GFP_KERNEL);
if (!q->ht && sz > PAGE_SIZE)
q->ht = vmalloc(sz);
if (!q->ht)
return -ENOMEM;
for (i = 0; i < q->divisor; i++)
q->ht[i] = SFQ_EMPTY_SLOT;
for (i = 0; i < SFQ_SLOTS; i++) { for (i = 0; i < SFQ_SLOTS; i++) {
slot_queue_init(&q->slots[i]); slot_queue_init(&q->slots[i]);
sfq_link(q, i); sfq_link(q, i);
...@@ -556,6 +570,10 @@ static void sfq_destroy(struct Qdisc *sch) ...@@ -556,6 +570,10 @@ static void sfq_destroy(struct Qdisc *sch)
tcf_destroy_chain(&q->filter_list); tcf_destroy_chain(&q->filter_list);
q->perturb_period = 0; q->perturb_period = 0;
del_timer_sync(&q->perturb_timer); del_timer_sync(&q->perturb_timer);
if (is_vmalloc_addr(q->ht))
vfree(q->ht);
else
kfree(q->ht);
} }
static int sfq_dump(struct Qdisc *sch, struct sk_buff *skb) static int sfq_dump(struct Qdisc *sch, struct sk_buff *skb)
...@@ -568,7 +586,7 @@ static int sfq_dump(struct Qdisc *sch, struct sk_buff *skb) ...@@ -568,7 +586,7 @@ static int sfq_dump(struct Qdisc *sch, struct sk_buff *skb)
opt.perturb_period = q->perturb_period / HZ; opt.perturb_period = q->perturb_period / HZ;
opt.limit = q->limit; opt.limit = q->limit;
opt.divisor = SFQ_HASH_DIVISOR; opt.divisor = q->divisor;
opt.flows = q->limit; opt.flows = q->limit;
NLA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt); NLA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt);
...@@ -646,7 +664,7 @@ static void sfq_walk(struct Qdisc *sch, struct qdisc_walker *arg) ...@@ -646,7 +664,7 @@ static void sfq_walk(struct Qdisc *sch, struct qdisc_walker *arg)
if (arg->stop) if (arg->stop)
return; return;
for (i = 0; i < SFQ_HASH_DIVISOR; i++) { for (i = 0; i < q->divisor; i++) {
if (q->ht[i] == SFQ_EMPTY_SLOT || if (q->ht[i] == SFQ_EMPTY_SLOT ||
arg->count < arg->skip) { arg->count < arg->skip) {
arg->count++; arg->count++;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment