diff options
Diffstat (limited to 'net/sched/sch_netem.c')
-rw-r--r-- | net/sched/sch_netem.c | 122 |
1 files changed, 109 insertions, 13 deletions
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index bb9bf8d5003c..cdc8d283791c 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c | |||
@@ -25,6 +25,8 @@ | |||
25 | 25 | ||
26 | #include <net/pkt_sched.h> | 26 | #include <net/pkt_sched.h> |
27 | 27 | ||
28 | #define VERSION "1.1" | ||
29 | |||
28 | /* Network Emulation Queuing algorithm. | 30 | /* Network Emulation Queuing algorithm. |
29 | ==================================== | 31 | ==================================== |
30 | 32 | ||
@@ -185,10 +187,13 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch) | |||
185 | || q->counter < q->gap /* inside last reordering gap */ | 187 | || q->counter < q->gap /* inside last reordering gap */ |
186 | || q->reorder < get_crandom(&q->reorder_cor)) { | 188 | || q->reorder < get_crandom(&q->reorder_cor)) { |
187 | psched_time_t now; | 189 | psched_time_t now; |
190 | psched_tdiff_t delay; | ||
191 | |||
192 | delay = tabledist(q->latency, q->jitter, | ||
193 | &q->delay_cor, q->delay_dist); | ||
194 | |||
188 | PSCHED_GET_TIME(now); | 195 | PSCHED_GET_TIME(now); |
189 | PSCHED_TADD2(now, tabledist(q->latency, q->jitter, | 196 | PSCHED_TADD2(now, delay, cb->time_to_send); |
190 | &q->delay_cor, q->delay_dist), | ||
191 | cb->time_to_send); | ||
192 | ++q->counter; | 197 | ++q->counter; |
193 | ret = q->qdisc->enqueue(skb, q->qdisc); | 198 | ret = q->qdisc->enqueue(skb, q->qdisc); |
194 | } else { | 199 | } else { |
@@ -248,24 +253,31 @@ static struct sk_buff *netem_dequeue(struct Qdisc *sch) | |||
248 | const struct netem_skb_cb *cb | 253 | const struct netem_skb_cb *cb |
249 | = (const struct netem_skb_cb *)skb->cb; | 254 | = (const struct netem_skb_cb *)skb->cb; |
250 | psched_time_t now; | 255 | psched_time_t now; |
251 | long delay; | ||
252 | 256 | ||
253 | /* if more time remaining? */ | 257 | /* if more time remaining? */ |
254 | PSCHED_GET_TIME(now); | 258 | PSCHED_GET_TIME(now); |
255 | delay = PSCHED_US2JIFFIE(PSCHED_TDIFF(cb->time_to_send, now)); | 259 | |
256 | pr_debug("netem_run: skb=%p delay=%ld\n", skb, delay); | 260 | if (PSCHED_TLESS(cb->time_to_send, now)) { |
257 | if (delay <= 0) { | ||
258 | pr_debug("netem_dequeue: return skb=%p\n", skb); | 261 | pr_debug("netem_dequeue: return skb=%p\n", skb); |
259 | sch->q.qlen--; | 262 | sch->q.qlen--; |
260 | sch->flags &= ~TCQ_F_THROTTLED; | 263 | sch->flags &= ~TCQ_F_THROTTLED; |
261 | return skb; | 264 | return skb; |
262 | } | 265 | } else { |
266 | psched_tdiff_t delay = PSCHED_TDIFF(cb->time_to_send, now); | ||
267 | |||
268 | if (q->qdisc->ops->requeue(skb, q->qdisc) != NET_XMIT_SUCCESS) { | ||
269 | sch->qstats.drops++; | ||
263 | 270 | ||
264 | mod_timer(&q->timer, jiffies + delay); | 271 | /* After this qlen is confused */ |
265 | sch->flags |= TCQ_F_THROTTLED; | 272 | printk(KERN_ERR "netem: queue discpline %s could not requeue\n", |
273 | q->qdisc->ops->id); | ||
266 | 274 | ||
267 | if (q->qdisc->ops->requeue(skb, q->qdisc) != 0) | 275 | sch->q.qlen--; |
268 | sch->qstats.drops++; | 276 | } |
277 | |||
278 | mod_timer(&q->timer, jiffies + PSCHED_US2JIFFIE(delay)); | ||
279 | sch->flags |= TCQ_F_THROTTLED; | ||
280 | } | ||
269 | } | 281 | } |
270 | 282 | ||
271 | return NULL; | 283 | return NULL; |
@@ -290,11 +302,16 @@ static void netem_reset(struct Qdisc *sch) | |||
290 | del_timer_sync(&q->timer); | 302 | del_timer_sync(&q->timer); |
291 | } | 303 | } |
292 | 304 | ||
305 | /* Pass size change message down to embedded FIFO */ | ||
293 | static int set_fifo_limit(struct Qdisc *q, int limit) | 306 | static int set_fifo_limit(struct Qdisc *q, int limit) |
294 | { | 307 | { |
295 | struct rtattr *rta; | 308 | struct rtattr *rta; |
296 | int ret = -ENOMEM; | 309 | int ret = -ENOMEM; |
297 | 310 | ||
311 | /* Hack to avoid sending change message to non-FIFO */ | ||
312 | if (strncmp(q->ops->id + 1, "fifo", 4) != 0) | ||
313 | return 0; | ||
314 | |||
298 | rta = kmalloc(RTA_LENGTH(sizeof(struct tc_fifo_qopt)), GFP_KERNEL); | 315 | rta = kmalloc(RTA_LENGTH(sizeof(struct tc_fifo_qopt)), GFP_KERNEL); |
299 | if (rta) { | 316 | if (rta) { |
300 | rta->rta_type = RTM_NEWQDISC; | 317 | rta->rta_type = RTM_NEWQDISC; |
@@ -426,6 +443,84 @@ static int netem_change(struct Qdisc *sch, struct rtattr *opt) | |||
426 | return 0; | 443 | return 0; |
427 | } | 444 | } |
428 | 445 | ||
446 | /* | ||
447 | * Special case version of FIFO queue for use by netem. | ||
448 | * It queues in order based on timestamps in skb's | ||
449 | */ | ||
450 | struct fifo_sched_data { | ||
451 | u32 limit; | ||
452 | }; | ||
453 | |||
454 | static int tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch) | ||
455 | { | ||
456 | struct fifo_sched_data *q = qdisc_priv(sch); | ||
457 | struct sk_buff_head *list = &sch->q; | ||
458 | const struct netem_skb_cb *ncb | ||
459 | = (const struct netem_skb_cb *)nskb->cb; | ||
460 | struct sk_buff *skb; | ||
461 | |||
462 | if (likely(skb_queue_len(list) < q->limit)) { | ||
463 | skb_queue_reverse_walk(list, skb) { | ||
464 | const struct netem_skb_cb *cb | ||
465 | = (const struct netem_skb_cb *)skb->cb; | ||
466 | |||
467 | if (PSCHED_TLESS(cb->time_to_send, ncb->time_to_send)) | ||
468 | break; | ||
469 | } | ||
470 | |||
471 | __skb_queue_after(list, skb, nskb); | ||
472 | |||
473 | sch->qstats.backlog += nskb->len; | ||
474 | sch->bstats.bytes += nskb->len; | ||
475 | sch->bstats.packets++; | ||
476 | |||
477 | return NET_XMIT_SUCCESS; | ||
478 | } | ||
479 | |||
480 | return qdisc_drop(nskb, sch); | ||
481 | } | ||
482 | |||
483 | static int tfifo_init(struct Qdisc *sch, struct rtattr *opt) | ||
484 | { | ||
485 | struct fifo_sched_data *q = qdisc_priv(sch); | ||
486 | |||
487 | if (opt) { | ||
488 | struct tc_fifo_qopt *ctl = RTA_DATA(opt); | ||
489 | if (RTA_PAYLOAD(opt) < sizeof(*ctl)) | ||
490 | return -EINVAL; | ||
491 | |||
492 | q->limit = ctl->limit; | ||
493 | } else | ||
494 | q->limit = max_t(u32, sch->dev->tx_queue_len, 1); | ||
495 | |||
496 | return 0; | ||
497 | } | ||
498 | |||
499 | static int tfifo_dump(struct Qdisc *sch, struct sk_buff *skb) | ||
500 | { | ||
501 | struct fifo_sched_data *q = qdisc_priv(sch); | ||
502 | struct tc_fifo_qopt opt = { .limit = q->limit }; | ||
503 | |||
504 | RTA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt); | ||
505 | return skb->len; | ||
506 | |||
507 | rtattr_failure: | ||
508 | return -1; | ||
509 | } | ||
510 | |||
511 | static struct Qdisc_ops tfifo_qdisc_ops = { | ||
512 | .id = "tfifo", | ||
513 | .priv_size = sizeof(struct fifo_sched_data), | ||
514 | .enqueue = tfifo_enqueue, | ||
515 | .dequeue = qdisc_dequeue_head, | ||
516 | .requeue = qdisc_requeue, | ||
517 | .drop = qdisc_queue_drop, | ||
518 | .init = tfifo_init, | ||
519 | .reset = qdisc_reset_queue, | ||
520 | .change = tfifo_init, | ||
521 | .dump = tfifo_dump, | ||
522 | }; | ||
523 | |||
429 | static int netem_init(struct Qdisc *sch, struct rtattr *opt) | 524 | static int netem_init(struct Qdisc *sch, struct rtattr *opt) |
430 | { | 525 | { |
431 | struct netem_sched_data *q = qdisc_priv(sch); | 526 | struct netem_sched_data *q = qdisc_priv(sch); |
@@ -438,7 +533,7 @@ static int netem_init(struct Qdisc *sch, struct rtattr *opt) | |||
438 | q->timer.function = netem_watchdog; | 533 | q->timer.function = netem_watchdog; |
439 | q->timer.data = (unsigned long) sch; | 534 | q->timer.data = (unsigned long) sch; |
440 | 535 | ||
441 | q->qdisc = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops); | 536 | q->qdisc = qdisc_create_dflt(sch->dev, &tfifo_qdisc_ops); |
442 | if (!q->qdisc) { | 537 | if (!q->qdisc) { |
443 | pr_debug("netem: qdisc create failed\n"); | 538 | pr_debug("netem: qdisc create failed\n"); |
444 | return -ENOMEM; | 539 | return -ENOMEM; |
@@ -601,6 +696,7 @@ static struct Qdisc_ops netem_qdisc_ops = { | |||
601 | 696 | ||
602 | static int __init netem_module_init(void) | 697 | static int __init netem_module_init(void) |
603 | { | 698 | { |
699 | pr_info("netem: version " VERSION "\n"); | ||
604 | return register_qdisc(&netem_qdisc_ops); | 700 | return register_qdisc(&netem_qdisc_ops); |
605 | } | 701 | } |
606 | static void __exit netem_module_exit(void) | 702 | static void __exit netem_module_exit(void) |