diff options
Diffstat (limited to 'net/sched/sch_prio.c')
-rw-r--r-- | net/sched/sch_prio.c | 145 |
1 files changed, 114 insertions, 31 deletions
diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c index 6d7542c26e47..2d8c08493d6e 100644 --- a/net/sched/sch_prio.c +++ b/net/sched/sch_prio.c | |||
@@ -12,37 +12,23 @@ | |||
12 | */ | 12 | */ |
13 | 13 | ||
14 | #include <linux/module.h> | 14 | #include <linux/module.h> |
15 | #include <asm/uaccess.h> | ||
16 | #include <asm/system.h> | ||
17 | #include <linux/bitops.h> | ||
18 | #include <linux/types.h> | 15 | #include <linux/types.h> |
19 | #include <linux/kernel.h> | 16 | #include <linux/kernel.h> |
20 | #include <linux/string.h> | 17 | #include <linux/string.h> |
21 | #include <linux/mm.h> | ||
22 | #include <linux/socket.h> | ||
23 | #include <linux/sockios.h> | ||
24 | #include <linux/in.h> | ||
25 | #include <linux/errno.h> | 18 | #include <linux/errno.h> |
26 | #include <linux/interrupt.h> | ||
27 | #include <linux/if_ether.h> | ||
28 | #include <linux/inet.h> | ||
29 | #include <linux/netdevice.h> | ||
30 | #include <linux/etherdevice.h> | ||
31 | #include <linux/notifier.h> | ||
32 | #include <net/ip.h> | ||
33 | #include <net/route.h> | ||
34 | #include <linux/skbuff.h> | 19 | #include <linux/skbuff.h> |
35 | #include <net/netlink.h> | 20 | #include <net/netlink.h> |
36 | #include <net/sock.h> | ||
37 | #include <net/pkt_sched.h> | 21 | #include <net/pkt_sched.h> |
38 | 22 | ||
39 | 23 | ||
40 | struct prio_sched_data | 24 | struct prio_sched_data |
41 | { | 25 | { |
42 | int bands; | 26 | int bands; |
27 | int curband; /* for round-robin */ | ||
43 | struct tcf_proto *filter_list; | 28 | struct tcf_proto *filter_list; |
44 | u8 prio2band[TC_PRIO_MAX+1]; | 29 | u8 prio2band[TC_PRIO_MAX+1]; |
45 | struct Qdisc *queues[TCQ_PRIO_BANDS]; | 30 | struct Qdisc *queues[TCQ_PRIO_BANDS]; |
31 | int mq; | ||
46 | }; | 32 | }; |
47 | 33 | ||
48 | 34 | ||
@@ -70,14 +56,17 @@ prio_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr) | |||
70 | #endif | 56 | #endif |
71 | if (TC_H_MAJ(band)) | 57 | if (TC_H_MAJ(band)) |
72 | band = 0; | 58 | band = 0; |
73 | return q->queues[q->prio2band[band&TC_PRIO_MAX]]; | 59 | band = q->prio2band[band&TC_PRIO_MAX]; |
60 | goto out; | ||
74 | } | 61 | } |
75 | band = res.classid; | 62 | band = res.classid; |
76 | } | 63 | } |
77 | band = TC_H_MIN(band) - 1; | 64 | band = TC_H_MIN(band) - 1; |
78 | if (band >= q->bands) | 65 | if (band >= q->bands) |
79 | return q->queues[q->prio2band[0]]; | 66 | band = q->prio2band[0]; |
80 | 67 | out: | |
68 | if (q->mq) | ||
69 | skb_set_queue_mapping(skb, band); | ||
81 | return q->queues[band]; | 70 | return q->queues[band]; |
82 | } | 71 | } |
83 | 72 | ||
@@ -144,17 +133,58 @@ prio_dequeue(struct Qdisc* sch) | |||
144 | struct Qdisc *qdisc; | 133 | struct Qdisc *qdisc; |
145 | 134 | ||
146 | for (prio = 0; prio < q->bands; prio++) { | 135 | for (prio = 0; prio < q->bands; prio++) { |
147 | qdisc = q->queues[prio]; | 136 | /* Check if the target subqueue is available before |
148 | skb = qdisc->dequeue(qdisc); | 137 | * pulling an skb. This way we avoid excessive requeues |
149 | if (skb) { | 138 | * for slower queues. |
150 | sch->q.qlen--; | 139 | */ |
151 | return skb; | 140 | if (!netif_subqueue_stopped(sch->dev, (q->mq ? prio : 0))) { |
141 | qdisc = q->queues[prio]; | ||
142 | skb = qdisc->dequeue(qdisc); | ||
143 | if (skb) { | ||
144 | sch->q.qlen--; | ||
145 | return skb; | ||
146 | } | ||
152 | } | 147 | } |
153 | } | 148 | } |
154 | return NULL; | 149 | return NULL; |
155 | 150 | ||
156 | } | 151 | } |
157 | 152 | ||
153 | static struct sk_buff *rr_dequeue(struct Qdisc* sch) | ||
154 | { | ||
155 | struct sk_buff *skb; | ||
156 | struct prio_sched_data *q = qdisc_priv(sch); | ||
157 | struct Qdisc *qdisc; | ||
158 | int bandcount; | ||
159 | |||
160 | /* Only take one pass through the queues. If nothing is available, | ||
161 | * return nothing. | ||
162 | */ | ||
163 | for (bandcount = 0; bandcount < q->bands; bandcount++) { | ||
164 | /* Check if the target subqueue is available before | ||
165 | * pulling an skb. This way we avoid excessive requeues | ||
166 | * for slower queues. If the queue is stopped, try the | ||
167 | * next queue. | ||
168 | */ | ||
169 | if (!netif_subqueue_stopped(sch->dev, | ||
170 | (q->mq ? q->curband : 0))) { | ||
171 | qdisc = q->queues[q->curband]; | ||
172 | skb = qdisc->dequeue(qdisc); | ||
173 | if (skb) { | ||
174 | sch->q.qlen--; | ||
175 | q->curband++; | ||
176 | if (q->curband >= q->bands) | ||
177 | q->curband = 0; | ||
178 | return skb; | ||
179 | } | ||
180 | } | ||
181 | q->curband++; | ||
182 | if (q->curband >= q->bands) | ||
183 | q->curband = 0; | ||
184 | } | ||
185 | return NULL; | ||
186 | } | ||
187 | |||
158 | static unsigned int prio_drop(struct Qdisc* sch) | 188 | static unsigned int prio_drop(struct Qdisc* sch) |
159 | { | 189 | { |
160 | struct prio_sched_data *q = qdisc_priv(sch); | 190 | struct prio_sched_data *q = qdisc_priv(sch); |
@@ -198,21 +228,41 @@ prio_destroy(struct Qdisc* sch) | |||
198 | static int prio_tune(struct Qdisc *sch, struct rtattr *opt) | 228 | static int prio_tune(struct Qdisc *sch, struct rtattr *opt) |
199 | { | 229 | { |
200 | struct prio_sched_data *q = qdisc_priv(sch); | 230 | struct prio_sched_data *q = qdisc_priv(sch); |
201 | struct tc_prio_qopt *qopt = RTA_DATA(opt); | 231 | struct tc_prio_qopt *qopt; |
232 | struct rtattr *tb[TCA_PRIO_MAX]; | ||
202 | int i; | 233 | int i; |
203 | 234 | ||
204 | if (opt->rta_len < RTA_LENGTH(sizeof(*qopt))) | 235 | if (rtattr_parse_nested_compat(tb, TCA_PRIO_MAX, opt, qopt, |
236 | sizeof(*qopt))) | ||
205 | return -EINVAL; | 237 | return -EINVAL; |
206 | if (qopt->bands > TCQ_PRIO_BANDS || qopt->bands < 2) | 238 | q->bands = qopt->bands; |
239 | /* If we're multiqueue, make sure the number of incoming bands | ||
240 | * matches the number of queues on the device we're associating with. | ||
241 | * If the number of bands requested is zero, then set q->bands to | ||
242 | * dev->egress_subqueue_count. | ||
243 | */ | ||
244 | q->mq = RTA_GET_FLAG(tb[TCA_PRIO_MQ - 1]); | ||
245 | if (q->mq) { | ||
246 | if (sch->handle != TC_H_ROOT) | ||
247 | return -EINVAL; | ||
248 | if (netif_is_multiqueue(sch->dev)) { | ||
249 | if (q->bands == 0) | ||
250 | q->bands = sch->dev->egress_subqueue_count; | ||
251 | else if (q->bands != sch->dev->egress_subqueue_count) | ||
252 | return -EINVAL; | ||
253 | } else | ||
254 | return -EOPNOTSUPP; | ||
255 | } | ||
256 | |||
257 | if (q->bands > TCQ_PRIO_BANDS || q->bands < 2) | ||
207 | return -EINVAL; | 258 | return -EINVAL; |
208 | 259 | ||
209 | for (i=0; i<=TC_PRIO_MAX; i++) { | 260 | for (i=0; i<=TC_PRIO_MAX; i++) { |
210 | if (qopt->priomap[i] >= qopt->bands) | 261 | if (qopt->priomap[i] >= q->bands) |
211 | return -EINVAL; | 262 | return -EINVAL; |
212 | } | 263 | } |
213 | 264 | ||
214 | sch_tree_lock(sch); | 265 | sch_tree_lock(sch); |
215 | q->bands = qopt->bands; | ||
216 | memcpy(q->prio2band, qopt->priomap, TC_PRIO_MAX+1); | 266 | memcpy(q->prio2band, qopt->priomap, TC_PRIO_MAX+1); |
217 | 267 | ||
218 | for (i=q->bands; i<TCQ_PRIO_BANDS; i++) { | 268 | for (i=q->bands; i<TCQ_PRIO_BANDS; i++) { |
@@ -268,11 +318,17 @@ static int prio_dump(struct Qdisc *sch, struct sk_buff *skb) | |||
268 | { | 318 | { |
269 | struct prio_sched_data *q = qdisc_priv(sch); | 319 | struct prio_sched_data *q = qdisc_priv(sch); |
270 | unsigned char *b = skb_tail_pointer(skb); | 320 | unsigned char *b = skb_tail_pointer(skb); |
321 | struct rtattr *nest; | ||
271 | struct tc_prio_qopt opt; | 322 | struct tc_prio_qopt opt; |
272 | 323 | ||
273 | opt.bands = q->bands; | 324 | opt.bands = q->bands; |
274 | memcpy(&opt.priomap, q->prio2band, TC_PRIO_MAX+1); | 325 | memcpy(&opt.priomap, q->prio2band, TC_PRIO_MAX+1); |
275 | RTA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt); | 326 | |
327 | nest = RTA_NEST_COMPAT(skb, TCA_OPTIONS, sizeof(opt), &opt); | ||
328 | if (q->mq) | ||
329 | RTA_PUT_FLAG(skb, TCA_PRIO_MQ); | ||
330 | RTA_NEST_COMPAT_END(skb, nest); | ||
331 | |||
276 | return skb->len; | 332 | return skb->len; |
277 | 333 | ||
278 | rtattr_failure: | 334 | rtattr_failure: |
@@ -443,17 +499,44 @@ static struct Qdisc_ops prio_qdisc_ops = { | |||
443 | .owner = THIS_MODULE, | 499 | .owner = THIS_MODULE, |
444 | }; | 500 | }; |
445 | 501 | ||
502 | static struct Qdisc_ops rr_qdisc_ops = { | ||
503 | .next = NULL, | ||
504 | .cl_ops = &prio_class_ops, | ||
505 | .id = "rr", | ||
506 | .priv_size = sizeof(struct prio_sched_data), | ||
507 | .enqueue = prio_enqueue, | ||
508 | .dequeue = rr_dequeue, | ||
509 | .requeue = prio_requeue, | ||
510 | .drop = prio_drop, | ||
511 | .init = prio_init, | ||
512 | .reset = prio_reset, | ||
513 | .destroy = prio_destroy, | ||
514 | .change = prio_tune, | ||
515 | .dump = prio_dump, | ||
516 | .owner = THIS_MODULE, | ||
517 | }; | ||
518 | |||
446 | static int __init prio_module_init(void) | 519 | static int __init prio_module_init(void) |
447 | { | 520 | { |
448 | return register_qdisc(&prio_qdisc_ops); | 521 | int err; |
522 | |||
523 | err = register_qdisc(&prio_qdisc_ops); | ||
524 | if (err < 0) | ||
525 | return err; | ||
526 | err = register_qdisc(&rr_qdisc_ops); | ||
527 | if (err < 0) | ||
528 | unregister_qdisc(&prio_qdisc_ops); | ||
529 | return err; | ||
449 | } | 530 | } |
450 | 531 | ||
451 | static void __exit prio_module_exit(void) | 532 | static void __exit prio_module_exit(void) |
452 | { | 533 | { |
453 | unregister_qdisc(&prio_qdisc_ops); | 534 | unregister_qdisc(&prio_qdisc_ops); |
535 | unregister_qdisc(&rr_qdisc_ops); | ||
454 | } | 536 | } |
455 | 537 | ||
456 | module_init(prio_module_init) | 538 | module_init(prio_module_init) |
457 | module_exit(prio_module_exit) | 539 | module_exit(prio_module_exit) |
458 | 540 | ||
459 | MODULE_LICENSE("GPL"); | 541 | MODULE_LICENSE("GPL"); |
542 | MODULE_ALIAS("sch_rr"); | ||