sch_netem.c (20fea08b5fb639c4c175b5c74a2bb346c5c5bc2e) sch_netem.c (1e90474c377e92db7262a8968a45c1dd980ca9e5)
1/*
2 * net/sched/sch_netem.c Network emulator
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License.
8 *

--- 299 unchanged lines hidden (view full) ---

308 qdisc_reset(q->qdisc);
309 sch->q.qlen = 0;
310 qdisc_watchdog_cancel(&q->watchdog);
311}
312
313/* Pass size change message down to embedded FIFO */
314static int set_fifo_limit(struct Qdisc *q, int limit)
315{
1/*
2 * net/sched/sch_netem.c Network emulator
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License.
8 *

--- 299 unchanged lines hidden (view full) ---

308 qdisc_reset(q->qdisc);
309 sch->q.qlen = 0;
310 qdisc_watchdog_cancel(&q->watchdog);
311}
312
313/* Pass size change message down to embedded FIFO */
314static int set_fifo_limit(struct Qdisc *q, int limit)
315{
316 struct rtattr *rta;
316 struct nlattr *nla;
317 int ret = -ENOMEM;
318
319 /* Hack to avoid sending change message to non-FIFO */
320 if (strncmp(q->ops->id + 1, "fifo", 4) != 0)
321 return 0;
322
317 int ret = -ENOMEM;
318
319 /* Hack to avoid sending change message to non-FIFO */
320 if (strncmp(q->ops->id + 1, "fifo", 4) != 0)
321 return 0;
322
323 rta = kmalloc(RTA_LENGTH(sizeof(struct tc_fifo_qopt)), GFP_KERNEL);
324 if (rta) {
325 rta->rta_type = RTM_NEWQDISC;
326 rta->rta_len = RTA_LENGTH(sizeof(struct tc_fifo_qopt));
327 ((struct tc_fifo_qopt *)RTA_DATA(rta))->limit = limit;
323 nla = kmalloc(nla_attr_size(sizeof(struct tc_fifo_qopt)), GFP_KERNEL);
324 if (nla) {
325 nla->nla_type = RTM_NEWQDISC;
326 nla->nla_len = nla_attr_size(sizeof(struct tc_fifo_qopt));
327 ((struct tc_fifo_qopt *)nla_data(nla))->limit = limit;
328
328
329 ret = q->ops->change(q, rta);
330 kfree(rta);
329 ret = q->ops->change(q, nla);
330 kfree(nla);
331 }
332 return ret;
333}
334
335/*
336 * Distribution data is a variable size payload containing
337 * signed 16 bit values.
338 */
331 }
332 return ret;
333}
334
335/*
336 * Distribution data is a variable size payload containing
337 * signed 16 bit values.
338 */
339static int get_dist_table(struct Qdisc *sch, const struct rtattr *attr)
339static int get_dist_table(struct Qdisc *sch, const struct nlattr *attr)
340{
341 struct netem_sched_data *q = qdisc_priv(sch);
340{
341 struct netem_sched_data *q = qdisc_priv(sch);
342 unsigned long n = RTA_PAYLOAD(attr)/sizeof(__s16);
343 const __s16 *data = RTA_DATA(attr);
342 unsigned long n = nla_len(attr)/sizeof(__s16);
343 const __s16 *data = nla_data(attr);
344 struct disttable *d;
345 int i;
346
347 if (n > 65536)
348 return -EINVAL;
349
350 d = kmalloc(sizeof(*d) + n*sizeof(d->table[0]), GFP_KERNEL);
351 if (!d)

--- 6 unchanged lines hidden (view full) ---

358 spin_lock_bh(&sch->dev->queue_lock);
359 d = xchg(&q->delay_dist, d);
360 spin_unlock_bh(&sch->dev->queue_lock);
361
362 kfree(d);
363 return 0;
364}
365
344 struct disttable *d;
345 int i;
346
347 if (n > 65536)
348 return -EINVAL;
349
350 d = kmalloc(sizeof(*d) + n*sizeof(d->table[0]), GFP_KERNEL);
351 if (!d)

--- 6 unchanged lines hidden (view full) ---

358 spin_lock_bh(&sch->dev->queue_lock);
359 d = xchg(&q->delay_dist, d);
360 spin_unlock_bh(&sch->dev->queue_lock);
361
362 kfree(d);
363 return 0;
364}
365
366static int get_correlation(struct Qdisc *sch, const struct rtattr *attr)
366static int get_correlation(struct Qdisc *sch, const struct nlattr *attr)
367{
368 struct netem_sched_data *q = qdisc_priv(sch);
367{
368 struct netem_sched_data *q = qdisc_priv(sch);
369 const struct tc_netem_corr *c = RTA_DATA(attr);
369 const struct tc_netem_corr *c = nla_data(attr);
370
370
371 if (RTA_PAYLOAD(attr) != sizeof(*c))
371 if (nla_len(attr) != sizeof(*c))
372 return -EINVAL;
373
374 init_crandom(&q->delay_cor, c->delay_corr);
375 init_crandom(&q->loss_cor, c->loss_corr);
376 init_crandom(&q->dup_cor, c->dup_corr);
377 return 0;
378}
379
372 return -EINVAL;
373
374 init_crandom(&q->delay_cor, c->delay_corr);
375 init_crandom(&q->loss_cor, c->loss_corr);
376 init_crandom(&q->dup_cor, c->dup_corr);
377 return 0;
378}
379
380static int get_reorder(struct Qdisc *sch, const struct rtattr *attr)
380static int get_reorder(struct Qdisc *sch, const struct nlattr *attr)
381{
382 struct netem_sched_data *q = qdisc_priv(sch);
381{
382 struct netem_sched_data *q = qdisc_priv(sch);
383 const struct tc_netem_reorder *r = RTA_DATA(attr);
383 const struct tc_netem_reorder *r = nla_data(attr);
384
384
385 if (RTA_PAYLOAD(attr) != sizeof(*r))
385 if (nla_len(attr) != sizeof(*r))
386 return -EINVAL;
387
388 q->reorder = r->probability;
389 init_crandom(&q->reorder_cor, r->correlation);
390 return 0;
391}
392
386 return -EINVAL;
387
388 q->reorder = r->probability;
389 init_crandom(&q->reorder_cor, r->correlation);
390 return 0;
391}
392
393static int get_corrupt(struct Qdisc *sch, const struct rtattr *attr)
393static int get_corrupt(struct Qdisc *sch, const struct nlattr *attr)
394{
395 struct netem_sched_data *q = qdisc_priv(sch);
394{
395 struct netem_sched_data *q = qdisc_priv(sch);
396 const struct tc_netem_corrupt *r = RTA_DATA(attr);
396 const struct tc_netem_corrupt *r = nla_data(attr);
397
397
398 if (RTA_PAYLOAD(attr) != sizeof(*r))
398 if (nla_len(attr) != sizeof(*r))
399 return -EINVAL;
400
401 q->corrupt = r->probability;
402 init_crandom(&q->corrupt_cor, r->correlation);
403 return 0;
404}
405
406/* Parse netlink message to set options */
399 return -EINVAL;
400
401 q->corrupt = r->probability;
402 init_crandom(&q->corrupt_cor, r->correlation);
403 return 0;
404}
405
406/* Parse netlink message to set options */
407static int netem_change(struct Qdisc *sch, struct rtattr *opt)
407static int netem_change(struct Qdisc *sch, struct nlattr *opt)
408{
409 struct netem_sched_data *q = qdisc_priv(sch);
410 struct tc_netem_qopt *qopt;
411 int ret;
412
408{
409 struct netem_sched_data *q = qdisc_priv(sch);
410 struct tc_netem_qopt *qopt;
411 int ret;
412
413 if (opt == NULL || RTA_PAYLOAD(opt) < sizeof(*qopt))
413 if (opt == NULL || nla_len(opt) < sizeof(*qopt))
414 return -EINVAL;
415
414 return -EINVAL;
415
416 qopt = RTA_DATA(opt);
416 qopt = nla_data(opt);
417 ret = set_fifo_limit(q->qdisc, qopt->limit);
418 if (ret) {
419 pr_debug("netem: can't set fifo limit\n");
420 return ret;
421 }
422
423 q->latency = qopt->latency;
424 q->jitter = qopt->jitter;

--- 7 unchanged lines hidden (view full) ---

432 * if gap is set, need to assume 100% probability
433 */
434 if (q->gap)
435 q->reorder = ~0;
436
437 /* Handle nested options after initial queue options.
438 * Should have put all options in nested format but too late now.
439 */
417 ret = set_fifo_limit(q->qdisc, qopt->limit);
418 if (ret) {
419 pr_debug("netem: can't set fifo limit\n");
420 return ret;
421 }
422
423 q->latency = qopt->latency;
424 q->jitter = qopt->jitter;

--- 7 unchanged lines hidden (view full) ---

432 * if gap is set, need to assume 100% probability
433 */
434 if (q->gap)
435 q->reorder = ~0;
436
437 /* Handle nested options after initial queue options.
438 * Should have put all options in nested format but too late now.
439 */
440 if (RTA_PAYLOAD(opt) > sizeof(*qopt)) {
441 struct rtattr *tb[TCA_NETEM_MAX];
442 if (rtattr_parse(tb, TCA_NETEM_MAX,
443 RTA_DATA(opt) + sizeof(*qopt),
444 RTA_PAYLOAD(opt) - sizeof(*qopt)))
440 if (nla_len(opt) > sizeof(*qopt)) {
441 struct nlattr *tb[TCA_NETEM_MAX + 1];
442 if (nla_parse(tb, TCA_NETEM_MAX,
443 nla_data(opt) + sizeof(*qopt),
444 nla_len(opt) - sizeof(*qopt), NULL))
445 return -EINVAL;
446
445 return -EINVAL;
446
447 if (tb[TCA_NETEM_CORR-1]) {
448 ret = get_correlation(sch, tb[TCA_NETEM_CORR-1]);
447 if (tb[TCA_NETEM_CORR]) {
448 ret = get_correlation(sch, tb[TCA_NETEM_CORR]);
449 if (ret)
450 return ret;
451 }
452
449 if (ret)
450 return ret;
451 }
452
453 if (tb[TCA_NETEM_DELAY_DIST-1]) {
454 ret = get_dist_table(sch, tb[TCA_NETEM_DELAY_DIST-1]);
453 if (tb[TCA_NETEM_DELAY_DIST]) {
454 ret = get_dist_table(sch, tb[TCA_NETEM_DELAY_DIST]);
455 if (ret)
456 return ret;
457 }
458
455 if (ret)
456 return ret;
457 }
458
459 if (tb[TCA_NETEM_REORDER-1]) {
460 ret = get_reorder(sch, tb[TCA_NETEM_REORDER-1]);
459 if (tb[TCA_NETEM_REORDER]) {
460 ret = get_reorder(sch, tb[TCA_NETEM_REORDER]);
461 if (ret)
462 return ret;
463 }
464
461 if (ret)
462 return ret;
463 }
464
465 if (tb[TCA_NETEM_CORRUPT-1]) {
466 ret = get_corrupt(sch, tb[TCA_NETEM_CORRUPT-1]);
465 if (tb[TCA_NETEM_CORRUPT]) {
466 ret = get_corrupt(sch, tb[TCA_NETEM_CORRUPT]);
467 if (ret)
468 return ret;
469 }
470 }
471
472 return 0;
473}
474

--- 35 unchanged lines hidden (view full) ---

510 sch->bstats.packets++;
511
512 return NET_XMIT_SUCCESS;
513 }
514
515 return qdisc_reshape_fail(nskb, sch);
516}
517
467 if (ret)
468 return ret;
469 }
470 }
471
472 return 0;
473}
474

--- 35 unchanged lines hidden (view full) ---

510 sch->bstats.packets++;
511
512 return NET_XMIT_SUCCESS;
513 }
514
515 return qdisc_reshape_fail(nskb, sch);
516}
517
518static int tfifo_init(struct Qdisc *sch, struct rtattr *opt)
518static int tfifo_init(struct Qdisc *sch, struct nlattr *opt)
519{
520 struct fifo_sched_data *q = qdisc_priv(sch);
521
522 if (opt) {
519{
520 struct fifo_sched_data *q = qdisc_priv(sch);
521
522 if (opt) {
523 struct tc_fifo_qopt *ctl = RTA_DATA(opt);
524 if (RTA_PAYLOAD(opt) < sizeof(*ctl))
523 struct tc_fifo_qopt *ctl = nla_data(opt);
524 if (nla_len(opt) < sizeof(*ctl))
525 return -EINVAL;
526
527 q->limit = ctl->limit;
528 } else
529 q->limit = max_t(u32, sch->dev->tx_queue_len, 1);
530
531 q->oldest = PSCHED_PASTPERFECT;
532 return 0;
533}
534
535static int tfifo_dump(struct Qdisc *sch, struct sk_buff *skb)
536{
537 struct fifo_sched_data *q = qdisc_priv(sch);
538 struct tc_fifo_qopt opt = { .limit = q->limit };
539
525 return -EINVAL;
526
527 q->limit = ctl->limit;
528 } else
529 q->limit = max_t(u32, sch->dev->tx_queue_len, 1);
530
531 q->oldest = PSCHED_PASTPERFECT;
532 return 0;
533}
534
535static int tfifo_dump(struct Qdisc *sch, struct sk_buff *skb)
536{
537 struct fifo_sched_data *q = qdisc_priv(sch);
538 struct tc_fifo_qopt opt = { .limit = q->limit };
539
540 RTA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt);
540 NLA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt);
541 return skb->len;
542
541 return skb->len;
542
543rtattr_failure:
543nla_put_failure:
544 return -1;
545}
546
547static struct Qdisc_ops tfifo_qdisc_ops __read_mostly = {
548 .id = "tfifo",
549 .priv_size = sizeof(struct fifo_sched_data),
550 .enqueue = tfifo_enqueue,
551 .dequeue = qdisc_dequeue_head,
552 .requeue = qdisc_requeue,
553 .drop = qdisc_queue_drop,
554 .init = tfifo_init,
555 .reset = qdisc_reset_queue,
556 .change = tfifo_init,
557 .dump = tfifo_dump,
558};
559
544 return -1;
545}
546
547static struct Qdisc_ops tfifo_qdisc_ops __read_mostly = {
548 .id = "tfifo",
549 .priv_size = sizeof(struct fifo_sched_data),
550 .enqueue = tfifo_enqueue,
551 .dequeue = qdisc_dequeue_head,
552 .requeue = qdisc_requeue,
553 .drop = qdisc_queue_drop,
554 .init = tfifo_init,
555 .reset = qdisc_reset_queue,
556 .change = tfifo_init,
557 .dump = tfifo_dump,
558};
559
560static int netem_init(struct Qdisc *sch, struct rtattr *opt)
560static int netem_init(struct Qdisc *sch, struct nlattr *opt)
561{
562 struct netem_sched_data *q = qdisc_priv(sch);
563 int ret;
564
565 if (!opt)
566 return -EINVAL;
567
568 qdisc_watchdog_init(&q->watchdog, sch);

--- 21 unchanged lines hidden (view full) ---

590 qdisc_destroy(q->qdisc);
591 kfree(q->delay_dist);
592}
593
594static int netem_dump(struct Qdisc *sch, struct sk_buff *skb)
595{
596 const struct netem_sched_data *q = qdisc_priv(sch);
597 unsigned char *b = skb_tail_pointer(skb);
561{
562 struct netem_sched_data *q = qdisc_priv(sch);
563 int ret;
564
565 if (!opt)
566 return -EINVAL;
567
568 qdisc_watchdog_init(&q->watchdog, sch);

--- 21 unchanged lines hidden (view full) ---

590 qdisc_destroy(q->qdisc);
591 kfree(q->delay_dist);
592}
593
594static int netem_dump(struct Qdisc *sch, struct sk_buff *skb)
595{
596 const struct netem_sched_data *q = qdisc_priv(sch);
597 unsigned char *b = skb_tail_pointer(skb);
598 struct rtattr *rta = (struct rtattr *) b;
598 struct nlattr *nla = (struct nlattr *) b;
599 struct tc_netem_qopt qopt;
600 struct tc_netem_corr cor;
601 struct tc_netem_reorder reorder;
602 struct tc_netem_corrupt corrupt;
603
604 qopt.latency = q->latency;
605 qopt.jitter = q->jitter;
606 qopt.limit = q->limit;
607 qopt.loss = q->loss;
608 qopt.gap = q->gap;
609 qopt.duplicate = q->duplicate;
599 struct tc_netem_qopt qopt;
600 struct tc_netem_corr cor;
601 struct tc_netem_reorder reorder;
602 struct tc_netem_corrupt corrupt;
603
604 qopt.latency = q->latency;
605 qopt.jitter = q->jitter;
606 qopt.limit = q->limit;
607 qopt.loss = q->loss;
608 qopt.gap = q->gap;
609 qopt.duplicate = q->duplicate;
610 RTA_PUT(skb, TCA_OPTIONS, sizeof(qopt), &qopt);
610 NLA_PUT(skb, TCA_OPTIONS, sizeof(qopt), &qopt);
611
612 cor.delay_corr = q->delay_cor.rho;
613 cor.loss_corr = q->loss_cor.rho;
614 cor.dup_corr = q->dup_cor.rho;
611
612 cor.delay_corr = q->delay_cor.rho;
613 cor.loss_corr = q->loss_cor.rho;
614 cor.dup_corr = q->dup_cor.rho;
615 RTA_PUT(skb, TCA_NETEM_CORR, sizeof(cor), &cor);
615 NLA_PUT(skb, TCA_NETEM_CORR, sizeof(cor), &cor);
616
617 reorder.probability = q->reorder;
618 reorder.correlation = q->reorder_cor.rho;
616
617 reorder.probability = q->reorder;
618 reorder.correlation = q->reorder_cor.rho;
619 RTA_PUT(skb, TCA_NETEM_REORDER, sizeof(reorder), &reorder);
619 NLA_PUT(skb, TCA_NETEM_REORDER, sizeof(reorder), &reorder);
620
621 corrupt.probability = q->corrupt;
622 corrupt.correlation = q->corrupt_cor.rho;
620
621 corrupt.probability = q->corrupt;
622 corrupt.correlation = q->corrupt_cor.rho;
623 RTA_PUT(skb, TCA_NETEM_CORRUPT, sizeof(corrupt), &corrupt);
623 NLA_PUT(skb, TCA_NETEM_CORRUPT, sizeof(corrupt), &corrupt);
624
624
625 rta->rta_len = skb_tail_pointer(skb) - b;
625 nla->nla_len = skb_tail_pointer(skb) - b;
626
627 return skb->len;
628
626
627 return skb->len;
628
629rtattr_failure:
629nla_put_failure:
630 nlmsg_trim(skb, b);
631 return -1;
632}
633
634static int netem_dump_class(struct Qdisc *sch, unsigned long cl,
635 struct sk_buff *skb, struct tcmsg *tcm)
636{
637 struct netem_sched_data *q = qdisc_priv(sch);

--- 35 unchanged lines hidden (view full) ---

673 return 1;
674}
675
676static void netem_put(struct Qdisc *sch, unsigned long arg)
677{
678}
679
680static int netem_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
630 nlmsg_trim(skb, b);
631 return -1;
632}
633
634static int netem_dump_class(struct Qdisc *sch, unsigned long cl,
635 struct sk_buff *skb, struct tcmsg *tcm)
636{
637 struct netem_sched_data *q = qdisc_priv(sch);

--- 35 unchanged lines hidden (view full) ---

673 return 1;
674}
675
676static void netem_put(struct Qdisc *sch, unsigned long arg)
677{
678}
679
680static int netem_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
681 struct rtattr **tca, unsigned long *arg)
681 struct nlattr **tca, unsigned long *arg)
682{
683 return -ENOSYS;
684}
685
686static int netem_delete(struct Qdisc *sch, unsigned long arg)
687{
688 return -ENOSYS;
689}

--- 59 unchanged lines hidden ---
682{
683 return -ENOSYS;
684}
685
686static int netem_delete(struct Qdisc *sch, unsigned long arg)
687{
688 return -ENOSYS;
689}

--- 59 unchanged lines hidden ---