Commit 7d2681a6 authored by Patrick McHardy's avatar Patrick McHardy Committed by David S. Miller

[NET_SCHED]: sch_sfq: add support for external classifiers

Add support for external classifiers to allow using different flow
hash functions similar to ESFQ. When no classifier is attached the
built-in hash is used as before.
Signed-off-by: default avatarPatrick McHardy <kaber@trash.net>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 5239008b
...@@ -95,6 +95,7 @@ struct sfq_sched_data ...@@ -95,6 +95,7 @@ struct sfq_sched_data
int limit; int limit;
/* Variables */ /* Variables */
struct tcf_proto *filter_list;
struct timer_list perturb_timer; struct timer_list perturb_timer;
u32 perturbation; u32 perturbation;
sfq_index tail; /* Index of current slot in round */ sfq_index tail; /* Index of current slot in round */
...@@ -155,6 +156,39 @@ static unsigned sfq_hash(struct sfq_sched_data *q, struct sk_buff *skb) ...@@ -155,6 +156,39 @@ static unsigned sfq_hash(struct sfq_sched_data *q, struct sk_buff *skb)
return sfq_fold_hash(q, h, h2); return sfq_fold_hash(q, h, h2);
} }
static unsigned int sfq_classify(struct sk_buff *skb, struct Qdisc *sch,
int *qerr)
{
struct sfq_sched_data *q = qdisc_priv(sch);
struct tcf_result res;
int result;
if (TC_H_MAJ(skb->priority) == sch->handle &&
TC_H_MIN(skb->priority) > 0 &&
TC_H_MIN(skb->priority) <= SFQ_HASH_DIVISOR)
return TC_H_MIN(skb->priority);
if (!q->filter_list)
return sfq_hash(q, skb) + 1;
*qerr = NET_XMIT_BYPASS;
result = tc_classify(skb, q->filter_list, &res);
if (result >= 0) {
#ifdef CONFIG_NET_CLS_ACT
switch (result) {
case TC_ACT_STOLEN:
case TC_ACT_QUEUED:
*qerr = NET_XMIT_SUCCESS;
case TC_ACT_SHOT:
return 0;
}
#endif
if (TC_H_MIN(res.classid) <= SFQ_HASH_DIVISOR)
return TC_H_MIN(res.classid);
}
return 0;
}
static inline void sfq_link(struct sfq_sched_data *q, sfq_index x) static inline void sfq_link(struct sfq_sched_data *q, sfq_index x)
{ {
sfq_index p, n; sfq_index p, n;
...@@ -245,8 +279,18 @@ static int ...@@ -245,8 +279,18 @@ static int
sfq_enqueue(struct sk_buff *skb, struct Qdisc *sch) sfq_enqueue(struct sk_buff *skb, struct Qdisc *sch)
{ {
struct sfq_sched_data *q = qdisc_priv(sch); struct sfq_sched_data *q = qdisc_priv(sch);
unsigned hash = sfq_hash(q, skb); unsigned int hash;
sfq_index x; sfq_index x;
int ret;
hash = sfq_classify(skb, sch, &ret);
if (hash == 0) {
if (ret == NET_XMIT_BYPASS)
sch->qstats.drops++;
kfree_skb(skb);
return ret;
}
hash--;
x = q->ht[hash]; x = q->ht[hash];
if (x == SFQ_DEPTH) { if (x == SFQ_DEPTH) {
...@@ -289,8 +333,18 @@ static int ...@@ -289,8 +333,18 @@ static int
sfq_requeue(struct sk_buff *skb, struct Qdisc *sch) sfq_requeue(struct sk_buff *skb, struct Qdisc *sch)
{ {
struct sfq_sched_data *q = qdisc_priv(sch); struct sfq_sched_data *q = qdisc_priv(sch);
unsigned hash = sfq_hash(q, skb); unsigned int hash;
sfq_index x; sfq_index x;
int ret;
hash = sfq_classify(skb, sch, &ret);
if (hash == 0) {
if (ret == NET_XMIT_BYPASS)
sch->qstats.drops++;
kfree_skb(skb);
return ret;
}
hash--;
x = q->ht[hash]; x = q->ht[hash];
if (x == SFQ_DEPTH) { if (x == SFQ_DEPTH) {
...@@ -465,6 +519,8 @@ static int sfq_init(struct Qdisc *sch, struct nlattr *opt) ...@@ -465,6 +519,8 @@ static int sfq_init(struct Qdisc *sch, struct nlattr *opt)
static void sfq_destroy(struct Qdisc *sch) static void sfq_destroy(struct Qdisc *sch)
{ {
struct sfq_sched_data *q = qdisc_priv(sch); struct sfq_sched_data *q = qdisc_priv(sch);
tcf_destroy_chain(q->filter_list);
del_timer(&q->perturb_timer); del_timer(&q->perturb_timer);
} }
...@@ -490,9 +546,40 @@ static int sfq_dump(struct Qdisc *sch, struct sk_buff *skb) ...@@ -490,9 +546,40 @@ static int sfq_dump(struct Qdisc *sch, struct sk_buff *skb)
return -1; return -1;
} }
static int sfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
struct nlattr **tca, unsigned long *arg)
{
return -EOPNOTSUPP;
}
static unsigned long sfq_get(struct Qdisc *sch, u32 classid)
{
return 0;
}
static struct tcf_proto **sfq_find_tcf(struct Qdisc *sch, unsigned long cl)
{
struct sfq_sched_data *q = qdisc_priv(sch);
if (cl)
return NULL;
return &q->filter_list;
}
static void sfq_walk(struct Qdisc *sch, struct qdisc_walker *arg)
{
return;
}
static const struct Qdisc_class_ops sfq_class_ops = {
.get = sfq_get,
.change = sfq_change_class,
.tcf_chain = sfq_find_tcf,
.walk = sfq_walk,
};
static struct Qdisc_ops sfq_qdisc_ops __read_mostly = { static struct Qdisc_ops sfq_qdisc_ops __read_mostly = {
.next = NULL, .cl_ops = &sfq_class_ops,
.cl_ops = NULL,
.id = "sfq", .id = "sfq",
.priv_size = sizeof(struct sfq_sched_data), .priv_size = sizeof(struct sfq_sched_data),
.enqueue = sfq_enqueue, .enqueue = sfq_enqueue,
......
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