|
@@ -21,6 +21,7 @@
|
|
|
#include <linux/skbuff.h>
|
|
|
#include <linux/jhash.h>
|
|
|
#include <linux/slab.h>
|
|
|
+#include <linux/vmalloc.h>
|
|
|
#include <net/ip.h>
|
|
|
#include <net/netlink.h>
|
|
|
#include <net/pkt_sched.h>
|
|
@@ -76,7 +77,8 @@
|
|
|
#define SFQ_DEPTH 128 /* max number of packets per flow */
|
|
|
#define SFQ_SLOTS 128 /* max number of flows */
|
|
|
#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
|
|
|
* Scale allot by 8 (1<<3) so that no overflow occurs.
|
|
|
*/
|
|
@@ -112,7 +114,7 @@ struct sfq_sched_data {
|
|
|
int perturb_period;
|
|
|
unsigned int quantum; /* Allotment per round: MUST BE >= MTU */
|
|
|
int limit;
|
|
|
-
|
|
|
+ unsigned int divisor; /* number of slots in hash table */
|
|
|
/* Variables */
|
|
|
struct tcf_proto *filter_list;
|
|
|
struct timer_list perturb_timer;
|
|
@@ -120,7 +122,7 @@ struct sfq_sched_data {
|
|
|
sfq_index cur_depth; /* depth of longest slot */
|
|
|
unsigned short scaled_quantum; /* SFQ_ALLOT_SIZE(quantum) */
|
|
|
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_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
|
|
|
|
|
|
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)
|
|
@@ -201,7 +203,7 @@ static unsigned int sfq_classify(struct sk_buff *skb, struct Qdisc *sch,
|
|
|
|
|
|
if (TC_H_MAJ(skb->priority) == sch->handle &&
|
|
|
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);
|
|
|
|
|
|
if (!q->filter_list)
|
|
@@ -219,7 +221,7 @@ static unsigned int sfq_classify(struct sk_buff *skb, struct Qdisc *sch,
|
|
|
return 0;
|
|
|
}
|
|
|
#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 0;
|
|
@@ -496,7 +498,11 @@ static int sfq_change(struct Qdisc *sch, struct nlattr *opt)
|
|
|
q->perturb_period = ctl->perturb_period * HZ;
|
|
|
if (ctl->limit)
|
|
|
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;
|
|
|
while (sch->q.qlen > q->limit)
|
|
|
sfq_drop(sch);
|
|
@@ -514,15 +520,13 @@ static int sfq_change(struct Qdisc *sch, struct nlattr *opt)
|
|
|
static int sfq_init(struct Qdisc *sch, struct nlattr *opt)
|
|
|
{
|
|
|
struct sfq_sched_data *q = qdisc_priv(sch);
|
|
|
+ size_t sz;
|
|
|
int i;
|
|
|
|
|
|
q->perturb_timer.function = sfq_perturbation;
|
|
|
q->perturb_timer.data = (unsigned long)sch;
|
|
|
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++) {
|
|
|
q->dep[i].next = i + SFQ_SLOTS;
|
|
|
q->dep[i].prev = i + SFQ_SLOTS;
|
|
@@ -531,6 +535,7 @@ static int sfq_init(struct Qdisc *sch, struct nlattr *opt)
|
|
|
q->limit = SFQ_DEPTH - 1;
|
|
|
q->cur_depth = 0;
|
|
|
q->tail = NULL;
|
|
|
+ q->divisor = SFQ_DEFAULT_HASH_DIVISOR;
|
|
|
if (opt == NULL) {
|
|
|
q->quantum = psched_mtu(qdisc_dev(sch));
|
|
|
q->scaled_quantum = SFQ_ALLOT_SIZE(q->quantum);
|
|
@@ -542,6 +547,15 @@ static int sfq_init(struct Qdisc *sch, struct nlattr *opt)
|
|
|
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++) {
|
|
|
slot_queue_init(&q->slots[i]);
|
|
|
sfq_link(q, i);
|
|
@@ -556,6 +570,10 @@ static void sfq_destroy(struct Qdisc *sch)
|
|
|
tcf_destroy_chain(&q->filter_list);
|
|
|
q->perturb_period = 0;
|
|
|
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)
|
|
@@ -568,7 +586,7 @@ static int sfq_dump(struct Qdisc *sch, struct sk_buff *skb)
|
|
|
opt.perturb_period = q->perturb_period / HZ;
|
|
|
|
|
|
opt.limit = q->limit;
|
|
|
- opt.divisor = SFQ_HASH_DIVISOR;
|
|
|
+ opt.divisor = q->divisor;
|
|
|
opt.flows = q->limit;
|
|
|
|
|
|
NLA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt);
|
|
@@ -646,7 +664,7 @@ static void sfq_walk(struct Qdisc *sch, struct qdisc_walker *arg)
|
|
|
if (arg->stop)
|
|
|
return;
|
|
|
|
|
|
- for (i = 0; i < SFQ_HASH_DIVISOR; i++) {
|
|
|
+ for (i = 0; i < q->divisor; i++) {
|
|
|
if (q->ht[i] == SFQ_EMPTY_SLOT ||
|
|
|
arg->count < arg->skip) {
|
|
|
arg->count++;
|