diff options
Diffstat (limited to 'net/sched/sch_netem.c')
-rw-r--r-- | net/sched/sch_netem.c | 159 |
1 files changed, 23 insertions, 136 deletions
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index a11959908d9a..f840d6b27c65 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c | |||
@@ -233,7 +233,11 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch) | |||
233 | */ | 233 | */ |
234 | cb->time_to_send = psched_get_time(); | 234 | cb->time_to_send = psched_get_time(); |
235 | q->counter = 0; | 235 | q->counter = 0; |
236 | ret = q->qdisc->ops->requeue(skb, q->qdisc); | 236 | |
237 | __skb_queue_head(&q->qdisc->q, skb); | ||
238 | q->qdisc->qstats.backlog += qdisc_pkt_len(skb); | ||
239 | q->qdisc->qstats.requeues++; | ||
240 | ret = NET_XMIT_SUCCESS; | ||
237 | } | 241 | } |
238 | 242 | ||
239 | if (likely(ret == NET_XMIT_SUCCESS)) { | 243 | if (likely(ret == NET_XMIT_SUCCESS)) { |
@@ -248,20 +252,6 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch) | |||
248 | return ret; | 252 | return ret; |
249 | } | 253 | } |
250 | 254 | ||
251 | /* Requeue packets but don't change time stamp */ | ||
252 | static int netem_requeue(struct sk_buff *skb, struct Qdisc *sch) | ||
253 | { | ||
254 | struct netem_sched_data *q = qdisc_priv(sch); | ||
255 | int ret; | ||
256 | |||
257 | if ((ret = q->qdisc->ops->requeue(skb, q->qdisc)) == 0) { | ||
258 | sch->q.qlen++; | ||
259 | sch->qstats.requeues++; | ||
260 | } | ||
261 | |||
262 | return ret; | ||
263 | } | ||
264 | |||
265 | static unsigned int netem_drop(struct Qdisc* sch) | 255 | static unsigned int netem_drop(struct Qdisc* sch) |
266 | { | 256 | { |
267 | struct netem_sched_data *q = qdisc_priv(sch); | 257 | struct netem_sched_data *q = qdisc_priv(sch); |
@@ -283,25 +273,22 @@ static struct sk_buff *netem_dequeue(struct Qdisc *sch) | |||
283 | if (sch->flags & TCQ_F_THROTTLED) | 273 | if (sch->flags & TCQ_F_THROTTLED) |
284 | return NULL; | 274 | return NULL; |
285 | 275 | ||
286 | skb = q->qdisc->dequeue(q->qdisc); | 276 | skb = q->qdisc->ops->peek(q->qdisc); |
287 | if (skb) { | 277 | if (skb) { |
288 | const struct netem_skb_cb *cb = netem_skb_cb(skb); | 278 | const struct netem_skb_cb *cb = netem_skb_cb(skb); |
289 | psched_time_t now = psched_get_time(); | 279 | psched_time_t now = psched_get_time(); |
290 | 280 | ||
291 | /* if more time remaining? */ | 281 | /* if more time remaining? */ |
292 | if (cb->time_to_send <= now) { | 282 | if (cb->time_to_send <= now) { |
283 | skb = qdisc_dequeue_peeked(q->qdisc); | ||
284 | if (unlikely(!skb)) | ||
285 | return NULL; | ||
286 | |||
293 | pr_debug("netem_dequeue: return skb=%p\n", skb); | 287 | pr_debug("netem_dequeue: return skb=%p\n", skb); |
294 | sch->q.qlen--; | 288 | sch->q.qlen--; |
295 | return skb; | 289 | return skb; |
296 | } | 290 | } |
297 | 291 | ||
298 | if (unlikely(q->qdisc->ops->requeue(skb, q->qdisc) != NET_XMIT_SUCCESS)) { | ||
299 | qdisc_tree_decrease_qlen(q->qdisc, 1); | ||
300 | sch->qstats.drops++; | ||
301 | printk(KERN_ERR "netem: %s could not requeue\n", | ||
302 | q->qdisc->ops->id); | ||
303 | } | ||
304 | |||
305 | qdisc_watchdog_schedule(&q->watchdog, cb->time_to_send); | 292 | qdisc_watchdog_schedule(&q->watchdog, cb->time_to_send); |
306 | } | 293 | } |
307 | 294 | ||
@@ -344,14 +331,13 @@ static int get_dist_table(struct Qdisc *sch, const struct nlattr *attr) | |||
344 | root_lock = qdisc_root_sleeping_lock(sch); | 331 | root_lock = qdisc_root_sleeping_lock(sch); |
345 | 332 | ||
346 | spin_lock_bh(root_lock); | 333 | spin_lock_bh(root_lock); |
347 | d = xchg(&q->delay_dist, d); | 334 | kfree(q->delay_dist); |
335 | q->delay_dist = d; | ||
348 | spin_unlock_bh(root_lock); | 336 | spin_unlock_bh(root_lock); |
349 | |||
350 | kfree(d); | ||
351 | return 0; | 337 | return 0; |
352 | } | 338 | } |
353 | 339 | ||
354 | static int get_correlation(struct Qdisc *sch, const struct nlattr *attr) | 340 | static void get_correlation(struct Qdisc *sch, const struct nlattr *attr) |
355 | { | 341 | { |
356 | struct netem_sched_data *q = qdisc_priv(sch); | 342 | struct netem_sched_data *q = qdisc_priv(sch); |
357 | const struct tc_netem_corr *c = nla_data(attr); | 343 | const struct tc_netem_corr *c = nla_data(attr); |
@@ -359,27 +345,24 @@ static int get_correlation(struct Qdisc *sch, const struct nlattr *attr) | |||
359 | init_crandom(&q->delay_cor, c->delay_corr); | 345 | init_crandom(&q->delay_cor, c->delay_corr); |
360 | init_crandom(&q->loss_cor, c->loss_corr); | 346 | init_crandom(&q->loss_cor, c->loss_corr); |
361 | init_crandom(&q->dup_cor, c->dup_corr); | 347 | init_crandom(&q->dup_cor, c->dup_corr); |
362 | return 0; | ||
363 | } | 348 | } |
364 | 349 | ||
365 | static int get_reorder(struct Qdisc *sch, const struct nlattr *attr) | 350 | static void get_reorder(struct Qdisc *sch, const struct nlattr *attr) |
366 | { | 351 | { |
367 | struct netem_sched_data *q = qdisc_priv(sch); | 352 | struct netem_sched_data *q = qdisc_priv(sch); |
368 | const struct tc_netem_reorder *r = nla_data(attr); | 353 | const struct tc_netem_reorder *r = nla_data(attr); |
369 | 354 | ||
370 | q->reorder = r->probability; | 355 | q->reorder = r->probability; |
371 | init_crandom(&q->reorder_cor, r->correlation); | 356 | init_crandom(&q->reorder_cor, r->correlation); |
372 | return 0; | ||
373 | } | 357 | } |
374 | 358 | ||
375 | static int get_corrupt(struct Qdisc *sch, const struct nlattr *attr) | 359 | static void get_corrupt(struct Qdisc *sch, const struct nlattr *attr) |
376 | { | 360 | { |
377 | struct netem_sched_data *q = qdisc_priv(sch); | 361 | struct netem_sched_data *q = qdisc_priv(sch); |
378 | const struct tc_netem_corrupt *r = nla_data(attr); | 362 | const struct tc_netem_corrupt *r = nla_data(attr); |
379 | 363 | ||
380 | q->corrupt = r->probability; | 364 | q->corrupt = r->probability; |
381 | init_crandom(&q->corrupt_cor, r->correlation); | 365 | init_crandom(&q->corrupt_cor, r->correlation); |
382 | return 0; | ||
383 | } | 366 | } |
384 | 367 | ||
385 | static const struct nla_policy netem_policy[TCA_NETEM_MAX + 1] = { | 368 | static const struct nla_policy netem_policy[TCA_NETEM_MAX + 1] = { |
@@ -438,11 +421,8 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt) | |||
438 | if (q->gap) | 421 | if (q->gap) |
439 | q->reorder = ~0; | 422 | q->reorder = ~0; |
440 | 423 | ||
441 | if (tb[TCA_NETEM_CORR]) { | 424 | if (tb[TCA_NETEM_CORR]) |
442 | ret = get_correlation(sch, tb[TCA_NETEM_CORR]); | 425 | get_correlation(sch, tb[TCA_NETEM_CORR]); |
443 | if (ret) | ||
444 | return ret; | ||
445 | } | ||
446 | 426 | ||
447 | if (tb[TCA_NETEM_DELAY_DIST]) { | 427 | if (tb[TCA_NETEM_DELAY_DIST]) { |
448 | ret = get_dist_table(sch, tb[TCA_NETEM_DELAY_DIST]); | 428 | ret = get_dist_table(sch, tb[TCA_NETEM_DELAY_DIST]); |
@@ -450,17 +430,11 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt) | |||
450 | return ret; | 430 | return ret; |
451 | } | 431 | } |
452 | 432 | ||
453 | if (tb[TCA_NETEM_REORDER]) { | 433 | if (tb[TCA_NETEM_REORDER]) |
454 | ret = get_reorder(sch, tb[TCA_NETEM_REORDER]); | 434 | get_reorder(sch, tb[TCA_NETEM_REORDER]); |
455 | if (ret) | ||
456 | return ret; | ||
457 | } | ||
458 | 435 | ||
459 | if (tb[TCA_NETEM_CORRUPT]) { | 436 | if (tb[TCA_NETEM_CORRUPT]) |
460 | ret = get_corrupt(sch, tb[TCA_NETEM_CORRUPT]); | 437 | get_corrupt(sch, tb[TCA_NETEM_CORRUPT]); |
461 | if (ret) | ||
462 | return ret; | ||
463 | } | ||
464 | 438 | ||
465 | return 0; | 439 | return 0; |
466 | } | 440 | } |
@@ -541,7 +515,7 @@ static struct Qdisc_ops tfifo_qdisc_ops __read_mostly = { | |||
541 | .priv_size = sizeof(struct fifo_sched_data), | 515 | .priv_size = sizeof(struct fifo_sched_data), |
542 | .enqueue = tfifo_enqueue, | 516 | .enqueue = tfifo_enqueue, |
543 | .dequeue = qdisc_dequeue_head, | 517 | .dequeue = qdisc_dequeue_head, |
544 | .requeue = qdisc_requeue, | 518 | .peek = qdisc_peek_head, |
545 | .drop = qdisc_queue_drop, | 519 | .drop = qdisc_queue_drop, |
546 | .init = tfifo_init, | 520 | .init = tfifo_init, |
547 | .reset = qdisc_reset_queue, | 521 | .reset = qdisc_reset_queue, |
@@ -624,99 +598,12 @@ nla_put_failure: | |||
624 | return -1; | 598 | return -1; |
625 | } | 599 | } |
626 | 600 | ||
627 | static int netem_dump_class(struct Qdisc *sch, unsigned long cl, | ||
628 | struct sk_buff *skb, struct tcmsg *tcm) | ||
629 | { | ||
630 | struct netem_sched_data *q = qdisc_priv(sch); | ||
631 | |||
632 | if (cl != 1) /* only one class */ | ||
633 | return -ENOENT; | ||
634 | |||
635 | tcm->tcm_handle |= TC_H_MIN(1); | ||
636 | tcm->tcm_info = q->qdisc->handle; | ||
637 | |||
638 | return 0; | ||
639 | } | ||
640 | |||
641 | static int netem_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, | ||
642 | struct Qdisc **old) | ||
643 | { | ||
644 | struct netem_sched_data *q = qdisc_priv(sch); | ||
645 | |||
646 | if (new == NULL) | ||
647 | new = &noop_qdisc; | ||
648 | |||
649 | sch_tree_lock(sch); | ||
650 | *old = xchg(&q->qdisc, new); | ||
651 | qdisc_tree_decrease_qlen(*old, (*old)->q.qlen); | ||
652 | qdisc_reset(*old); | ||
653 | sch_tree_unlock(sch); | ||
654 | |||
655 | return 0; | ||
656 | } | ||
657 | |||
658 | static struct Qdisc *netem_leaf(struct Qdisc *sch, unsigned long arg) | ||
659 | { | ||
660 | struct netem_sched_data *q = qdisc_priv(sch); | ||
661 | return q->qdisc; | ||
662 | } | ||
663 | |||
664 | static unsigned long netem_get(struct Qdisc *sch, u32 classid) | ||
665 | { | ||
666 | return 1; | ||
667 | } | ||
668 | |||
669 | static void netem_put(struct Qdisc *sch, unsigned long arg) | ||
670 | { | ||
671 | } | ||
672 | |||
673 | static int netem_change_class(struct Qdisc *sch, u32 classid, u32 parentid, | ||
674 | struct nlattr **tca, unsigned long *arg) | ||
675 | { | ||
676 | return -ENOSYS; | ||
677 | } | ||
678 | |||
679 | static int netem_delete(struct Qdisc *sch, unsigned long arg) | ||
680 | { | ||
681 | return -ENOSYS; | ||
682 | } | ||
683 | |||
684 | static void netem_walk(struct Qdisc *sch, struct qdisc_walker *walker) | ||
685 | { | ||
686 | if (!walker->stop) { | ||
687 | if (walker->count >= walker->skip) | ||
688 | if (walker->fn(sch, 1, walker) < 0) { | ||
689 | walker->stop = 1; | ||
690 | return; | ||
691 | } | ||
692 | walker->count++; | ||
693 | } | ||
694 | } | ||
695 | |||
696 | static struct tcf_proto **netem_find_tcf(struct Qdisc *sch, unsigned long cl) | ||
697 | { | ||
698 | return NULL; | ||
699 | } | ||
700 | |||
701 | static const struct Qdisc_class_ops netem_class_ops = { | ||
702 | .graft = netem_graft, | ||
703 | .leaf = netem_leaf, | ||
704 | .get = netem_get, | ||
705 | .put = netem_put, | ||
706 | .change = netem_change_class, | ||
707 | .delete = netem_delete, | ||
708 | .walk = netem_walk, | ||
709 | .tcf_chain = netem_find_tcf, | ||
710 | .dump = netem_dump_class, | ||
711 | }; | ||
712 | |||
713 | static struct Qdisc_ops netem_qdisc_ops __read_mostly = { | 601 | static struct Qdisc_ops netem_qdisc_ops __read_mostly = { |
714 | .id = "netem", | 602 | .id = "netem", |
715 | .cl_ops = &netem_class_ops, | ||
716 | .priv_size = sizeof(struct netem_sched_data), | 603 | .priv_size = sizeof(struct netem_sched_data), |
717 | .enqueue = netem_enqueue, | 604 | .enqueue = netem_enqueue, |
718 | .dequeue = netem_dequeue, | 605 | .dequeue = netem_dequeue, |
719 | .requeue = netem_requeue, | 606 | .peek = qdisc_peek_dequeued, |
720 | .drop = netem_drop, | 607 | .drop = netem_drop, |
721 | .init = netem_init, | 608 | .init = netem_init, |
722 | .reset = netem_reset, | 609 | .reset = netem_reset, |