sched: allow user space to create pfifo_fast qdiscs on virtual interfaces, allow pfifo_fast qdiscs to have filters and filter actions - useful for controlling packet classification into wme classes

SVN-Revision: 16791
This commit is contained in:
Felix Fietkau 2009-07-12 00:01:22 +00:00
parent 8addc86845
commit d680184c26
3 changed files with 293 additions and 0 deletions

View file

@ -0,0 +1,9 @@
--- a/tc/q_fifo.c
+++ b/tc/q_fifo.c
@@ -94,5 +94,6 @@ struct qdisc_util pfifo_qdisc_util = {
extern int prio_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt);
struct qdisc_util pfifo_fast_qdisc_util = {
.id = "pfifo_fast",
+ .parse_qopt = fifo_parse_opt,
.print_qopt = prio_print_opt,
};

View file

@ -0,0 +1,142 @@
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -382,16 +382,50 @@ static const u8 prio2band[TC_PRIO_MAX+1]
#define PFIFO_FAST_BANDS 3
+struct pfifo_fast_sched_data {
+ struct tcf_proto *filter_list;
+ struct sk_buff_head list[PFIFO_FAST_BANDS];
+};
+
static inline struct sk_buff_head *prio2list(struct sk_buff *skb,
struct Qdisc *qdisc)
{
- struct sk_buff_head *list = qdisc_priv(qdisc);
+ struct pfifo_fast_sched_data *q = qdisc_priv(qdisc);
+ struct sk_buff_head *list = q->list;
return list + prio2band[skb->priority & TC_PRIO_MAX];
}
+static int pfifo_fast_filter(struct sk_buff *skb, struct Qdisc* qdisc)
+{
+#ifdef CONFIG_NET_CLS_ACT
+ struct pfifo_fast_sched_data *q = qdisc_priv(qdisc);
+ int result = 0, ret = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN;
+ struct tcf_result res;
+
+ if (q->filter_list != NULL)
+ result = tc_classify(skb, q->filter_list, &res);
+ if (result >= 0) {
+ switch (result) {
+ case TC_ACT_STOLEN:
+ case TC_ACT_QUEUED:
+ ret = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN;
+ case TC_ACT_SHOT:
+ kfree_skb(skb);
+ return ret;
+ }
+ }
+#endif
+ return 0;
+}
+
static int pfifo_fast_enqueue(struct sk_buff *skb, struct Qdisc* qdisc)
{
struct sk_buff_head *list = prio2list(skb, qdisc);
+ int ret;
+
+ ret = pfifo_fast_filter(skb, qdisc);
+ if (ret)
+ return ret;
if (skb_queue_len(list) < qdisc_dev(qdisc)->tx_queue_len) {
qdisc->q.qlen++;
@@ -403,8 +437,9 @@ static int pfifo_fast_enqueue(struct sk_
static struct sk_buff *pfifo_fast_dequeue(struct Qdisc* qdisc)
{
+ struct pfifo_fast_sched_data *q = qdisc_priv(qdisc);
+ struct sk_buff_head *list = q->list;
int prio;
- struct sk_buff_head *list = qdisc_priv(qdisc);
for (prio = 0; prio < PFIFO_FAST_BANDS; prio++) {
if (!skb_queue_empty(list + prio)) {
@@ -424,8 +459,9 @@ static int pfifo_fast_requeue(struct sk_
static void pfifo_fast_reset(struct Qdisc* qdisc)
{
+ struct pfifo_fast_sched_data *q = qdisc_priv(qdisc);
+ struct sk_buff_head *list = q->list;
int prio;
- struct sk_buff_head *list = qdisc_priv(qdisc);
for (prio = 0; prio < PFIFO_FAST_BANDS; prio++)
__qdisc_reset_queue(qdisc, list + prio);
@@ -448,8 +484,9 @@ nla_put_failure:
static int pfifo_fast_init(struct Qdisc *qdisc, struct nlattr *opt)
{
+ struct pfifo_fast_sched_data *q = qdisc_priv(qdisc);
+ struct sk_buff_head *list = q->list;
int prio;
- struct sk_buff_head *list = qdisc_priv(qdisc);
for (prio = 0; prio < PFIFO_FAST_BANDS; prio++)
skb_queue_head_init(list + prio);
@@ -457,9 +494,36 @@ static int pfifo_fast_init(struct Qdisc
return 0;
}
+static int pfifo_fast_change_class(struct Qdisc *qdisc, u32 classid, u32 parentid,
+ struct nlattr **tca, unsigned long *arg)
+{
+ return -EOPNOTSUPP;
+}
+
+static unsigned long pfifo_fast_get(struct Qdisc *qdisc, u32 classid)
+{
+ return 0;
+}
+
+static struct tcf_proto **pfifo_fast_find_tcf(struct Qdisc *qdisc, unsigned long cl)
+{
+ struct pfifo_fast_sched_data *q = qdisc_priv(qdisc);
+
+ if (cl)
+ return NULL;
+ return &q->filter_list;
+}
+
+static const struct Qdisc_class_ops pfifo_fast_class_ops = {
+ .get = pfifo_fast_get,
+ .change = pfifo_fast_change_class,
+ .tcf_chain = pfifo_fast_find_tcf,
+};
+
static struct Qdisc_ops pfifo_fast_ops __read_mostly = {
.id = "pfifo_fast",
- .priv_size = PFIFO_FAST_BANDS * sizeof(struct sk_buff_head),
+ .cl_ops = &pfifo_fast_class_ops,
+ .priv_size = sizeof(struct pfifo_fast_sched_data),
.enqueue = pfifo_fast_enqueue,
.dequeue = pfifo_fast_dequeue,
.requeue = pfifo_fast_requeue,
@@ -739,3 +803,16 @@ void dev_shutdown(struct net_device *dev
shutdown_scheduler_queue(dev, &dev->rx_queue, &noop_qdisc);
WARN_ON(timer_pending(&dev->watchdog_timer));
}
+
+static int __init sch_generic_init(void)
+{
+ return register_qdisc(&pfifo_fast_ops);
+}
+
+static void __exit sch_generic_exit(void)
+{
+ unregister_qdisc(&pfifo_fast_ops);
+}
+
+module_init(sch_generic_init)
+module_exit(sch_generic_exit)

View file

@ -0,0 +1,142 @@
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -371,16 +371,50 @@ static const u8 prio2band[TC_PRIO_MAX+1]
#define PFIFO_FAST_BANDS 3
+struct pfifo_fast_sched_data {
+ struct tcf_proto *filter_list;
+ struct sk_buff_head list[PFIFO_FAST_BANDS];
+};
+
static inline struct sk_buff_head *prio2list(struct sk_buff *skb,
struct Qdisc *qdisc)
{
- struct sk_buff_head *list = qdisc_priv(qdisc);
+ struct pfifo_fast_sched_data *q = qdisc_priv(qdisc);
+ struct sk_buff_head *list = q->list;
return list + prio2band[skb->priority & TC_PRIO_MAX];
}
+static int pfifo_fast_filter(struct sk_buff *skb, struct Qdisc* qdisc)
+{
+#ifdef CONFIG_NET_CLS_ACT
+ struct pfifo_fast_sched_data *q = qdisc_priv(qdisc);
+ int result = 0, ret = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN;
+ struct tcf_result res;
+
+ if (q->filter_list != NULL)
+ result = tc_classify(skb, q->filter_list, &res);
+ if (result >= 0) {
+ switch (result) {
+ case TC_ACT_STOLEN:
+ case TC_ACT_QUEUED:
+ ret = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN;
+ case TC_ACT_SHOT:
+ kfree_skb(skb);
+ return ret;
+ }
+ }
+#endif
+ return 0;
+}
+
static int pfifo_fast_enqueue(struct sk_buff *skb, struct Qdisc* qdisc)
{
struct sk_buff_head *list = prio2list(skb, qdisc);
+ int ret;
+
+ ret = pfifo_fast_filter(skb, qdisc);
+ if (ret)
+ return ret;
if (skb_queue_len(list) < qdisc_dev(qdisc)->tx_queue_len) {
qdisc->q.qlen++;
@@ -392,8 +426,9 @@ static int pfifo_fast_enqueue(struct sk_
static struct sk_buff *pfifo_fast_dequeue(struct Qdisc* qdisc)
{
+ struct pfifo_fast_sched_data *q = qdisc_priv(qdisc);
+ struct sk_buff_head *list = q->list;
int prio;
- struct sk_buff_head *list = qdisc_priv(qdisc);
for (prio = 0; prio < PFIFO_FAST_BANDS; prio++) {
if (!skb_queue_empty(list + prio)) {
@@ -420,8 +455,9 @@ static struct sk_buff *pfifo_fast_peek(s
static void pfifo_fast_reset(struct Qdisc* qdisc)
{
+ struct pfifo_fast_sched_data *q = qdisc_priv(qdisc);
+ struct sk_buff_head *list = q->list;
int prio;
- struct sk_buff_head *list = qdisc_priv(qdisc);
for (prio = 0; prio < PFIFO_FAST_BANDS; prio++)
__qdisc_reset_queue(qdisc, list + prio);
@@ -444,8 +480,9 @@ nla_put_failure:
static int pfifo_fast_init(struct Qdisc *qdisc, struct nlattr *opt)
{
+ struct pfifo_fast_sched_data *q = qdisc_priv(qdisc);
+ struct sk_buff_head *list = q->list;
int prio;
- struct sk_buff_head *list = qdisc_priv(qdisc);
for (prio = 0; prio < PFIFO_FAST_BANDS; prio++)
skb_queue_head_init(list + prio);
@@ -453,9 +490,36 @@ static int pfifo_fast_init(struct Qdisc
return 0;
}
+static int pfifo_fast_change_class(struct Qdisc *qdisc, u32 classid, u32 parentid,
+ struct nlattr **tca, unsigned long *arg)
+{
+ return -EOPNOTSUPP;
+}
+
+static unsigned long pfifo_fast_get(struct Qdisc *qdisc, u32 classid)
+{
+ return 0;
+}
+
+static struct tcf_proto **pfifo_fast_find_tcf(struct Qdisc *qdisc, unsigned long cl)
+{
+ struct pfifo_fast_sched_data *q = qdisc_priv(qdisc);
+
+ if (cl)
+ return NULL;
+ return &q->filter_list;
+}
+
+static const struct Qdisc_class_ops pfifo_fast_class_ops = {
+ .get = pfifo_fast_get,
+ .change = pfifo_fast_change_class,
+ .tcf_chain = pfifo_fast_find_tcf,
+};
+
static struct Qdisc_ops pfifo_fast_ops __read_mostly = {
.id = "pfifo_fast",
- .priv_size = PFIFO_FAST_BANDS * sizeof(struct sk_buff_head),
+ .cl_ops = &pfifo_fast_class_ops,
+ .priv_size = sizeof(struct pfifo_fast_sched_data),
.enqueue = pfifo_fast_enqueue,
.dequeue = pfifo_fast_dequeue,
.peek = pfifo_fast_peek,
@@ -735,3 +799,16 @@ void dev_shutdown(struct net_device *dev
shutdown_scheduler_queue(dev, &dev->rx_queue, &noop_qdisc);
WARN_ON(timer_pending(&dev->watchdog_timer));
}
+
+static int __init sch_generic_init(void)
+{
+ return register_qdisc(&pfifo_fast_ops);
+}
+
+static void __exit sch_generic_exit(void)
+{
+ unregister_qdisc(&pfifo_fast_ops);
+}
+
+module_init(sch_generic_init)
+module_exit(sch_generic_exit)