📄 sch_netem.c
字号:
init_crandom(&q->dup_cor, c->dup_corr); return 0;}static int get_reorder(struct Qdisc *sch, const struct rtattr *attr){ struct netem_sched_data *q = qdisc_priv(sch); const struct tc_netem_reorder *r = RTA_DATA(attr); if (RTA_PAYLOAD(attr) != sizeof(*r)) return -EINVAL; q->reorder = r->probability; init_crandom(&q->reorder_cor, r->correlation); return 0;}static int get_corrupt(struct Qdisc *sch, const struct rtattr *attr){ struct netem_sched_data *q = qdisc_priv(sch); const struct tc_netem_corrupt *r = RTA_DATA(attr); if (RTA_PAYLOAD(attr) != sizeof(*r)) return -EINVAL; q->corrupt = r->probability; init_crandom(&q->corrupt_cor, r->correlation); return 0;}/* Parse netlink message to set options */static int netem_change(struct Qdisc *sch, struct rtattr *opt){ struct netem_sched_data *q = qdisc_priv(sch); struct tc_netem_qopt *qopt; int ret; if (opt == NULL || RTA_PAYLOAD(opt) < sizeof(*qopt)) return -EINVAL; qopt = RTA_DATA(opt); ret = set_fifo_limit(q->qdisc, qopt->limit); if (ret) { pr_debug("netem: can't set fifo limit\n"); return ret; } q->latency = qopt->latency; q->jitter = qopt->jitter; q->limit = qopt->limit; q->gap = qopt->gap; q->counter = 0; q->loss = qopt->loss; q->duplicate = qopt->duplicate; /* for compatibility with earlier versions. * if gap is set, need to assume 100% probability */ if (q->gap) q->reorder = ~0; /* Handle nested options after initial queue options. * Should have put all options in nested format but too late now. */ if (RTA_PAYLOAD(opt) > sizeof(*qopt)) { struct rtattr *tb[TCA_NETEM_MAX]; if (rtattr_parse(tb, TCA_NETEM_MAX, RTA_DATA(opt) + sizeof(*qopt), RTA_PAYLOAD(opt) - sizeof(*qopt))) return -EINVAL; if (tb[TCA_NETEM_CORR-1]) { ret = get_correlation(sch, tb[TCA_NETEM_CORR-1]); if (ret) return ret; } if (tb[TCA_NETEM_DELAY_DIST-1]) { ret = get_dist_table(sch, tb[TCA_NETEM_DELAY_DIST-1]); if (ret) return ret; } if (tb[TCA_NETEM_REORDER-1]) { ret = get_reorder(sch, tb[TCA_NETEM_REORDER-1]); if (ret) return ret; } if (tb[TCA_NETEM_CORRUPT-1]) { ret = get_corrupt(sch, tb[TCA_NETEM_CORRUPT-1]); if (ret) return ret; } } return 0;}/* * Special case version of FIFO queue for use by netem. * It queues in order based on timestamps in skb's */struct fifo_sched_data { u32 limit; psched_time_t oldest;};static int tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch){ struct fifo_sched_data *q = qdisc_priv(sch); struct sk_buff_head *list = &sch->q; psched_time_t tnext = ((struct netem_skb_cb *)nskb->cb)->time_to_send; struct sk_buff *skb; if (likely(skb_queue_len(list) < q->limit)) { /* Optimize for add at tail */ if (likely(skb_queue_empty(list) || tnext >= q->oldest)) { q->oldest = tnext; return qdisc_enqueue_tail(nskb, sch); } skb_queue_reverse_walk(list, skb) { const struct netem_skb_cb *cb = (const struct netem_skb_cb *)skb->cb; if (tnext >= cb->time_to_send) break; } __skb_queue_after(list, skb, nskb); sch->qstats.backlog += nskb->len; sch->bstats.bytes += nskb->len; sch->bstats.packets++; return NET_XMIT_SUCCESS; } return qdisc_reshape_fail(nskb, sch);}static int tfifo_init(struct Qdisc *sch, struct rtattr *opt){ struct fifo_sched_data *q = qdisc_priv(sch); if (opt) { struct tc_fifo_qopt *ctl = RTA_DATA(opt); if (RTA_PAYLOAD(opt) < sizeof(*ctl)) return -EINVAL; q->limit = ctl->limit; } else q->limit = max_t(u32, sch->dev->tx_queue_len, 1); q->oldest = PSCHED_PASTPERFECT; return 0;}static int tfifo_dump(struct Qdisc *sch, struct sk_buff *skb){ struct fifo_sched_data *q = qdisc_priv(sch); struct tc_fifo_qopt opt = { .limit = q->limit }; RTA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt); return skb->len;rtattr_failure: return -1;}static struct Qdisc_ops tfifo_qdisc_ops = { .id = "tfifo", .priv_size = sizeof(struct fifo_sched_data), .enqueue = tfifo_enqueue, .dequeue = qdisc_dequeue_head, .requeue = qdisc_requeue, .drop = qdisc_queue_drop, .init = tfifo_init, .reset = qdisc_reset_queue, .change = tfifo_init, .dump = tfifo_dump,};static int netem_init(struct Qdisc *sch, struct rtattr *opt){ struct netem_sched_data *q = qdisc_priv(sch); int ret; if (!opt) return -EINVAL; qdisc_watchdog_init(&q->watchdog, sch); q->qdisc = qdisc_create_dflt(sch->dev, &tfifo_qdisc_ops, TC_H_MAKE(sch->handle, 1)); if (!q->qdisc) { pr_debug("netem: qdisc create failed\n"); return -ENOMEM; } ret = netem_change(sch, opt); if (ret) { pr_debug("netem: change failed\n"); qdisc_destroy(q->qdisc); } return ret;}static void netem_destroy(struct Qdisc *sch){ struct netem_sched_data *q = qdisc_priv(sch); qdisc_watchdog_cancel(&q->watchdog); qdisc_destroy(q->qdisc); kfree(q->delay_dist);}static int netem_dump(struct Qdisc *sch, struct sk_buff *skb){ const struct netem_sched_data *q = qdisc_priv(sch); unsigned char *b = skb_tail_pointer(skb); struct rtattr *rta = (struct rtattr *) b; struct tc_netem_qopt qopt; struct tc_netem_corr cor; struct tc_netem_reorder reorder; struct tc_netem_corrupt corrupt; qopt.latency = q->latency; qopt.jitter = q->jitter; qopt.limit = q->limit; qopt.loss = q->loss; qopt.gap = q->gap; qopt.duplicate = q->duplicate; RTA_PUT(skb, TCA_OPTIONS, sizeof(qopt), &qopt); cor.delay_corr = q->delay_cor.rho; cor.loss_corr = q->loss_cor.rho; cor.dup_corr = q->dup_cor.rho; RTA_PUT(skb, TCA_NETEM_CORR, sizeof(cor), &cor); reorder.probability = q->reorder; reorder.correlation = q->reorder_cor.rho; RTA_PUT(skb, TCA_NETEM_REORDER, sizeof(reorder), &reorder); corrupt.probability = q->corrupt; corrupt.correlation = q->corrupt_cor.rho; RTA_PUT(skb, TCA_NETEM_CORRUPT, sizeof(corrupt), &corrupt); rta->rta_len = skb_tail_pointer(skb) - b; return skb->len;rtattr_failure: nlmsg_trim(skb, b); return -1;}static int netem_dump_class(struct Qdisc *sch, unsigned long cl, struct sk_buff *skb, struct tcmsg *tcm){ struct netem_sched_data *q = qdisc_priv(sch); if (cl != 1) /* only one class */ return -ENOENT; tcm->tcm_handle |= TC_H_MIN(1); tcm->tcm_info = q->qdisc->handle; return 0;}static int netem_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, struct Qdisc **old){ struct netem_sched_data *q = qdisc_priv(sch); if (new == NULL) new = &noop_qdisc; sch_tree_lock(sch); *old = xchg(&q->qdisc, new); qdisc_tree_decrease_qlen(*old, (*old)->q.qlen); qdisc_reset(*old); sch_tree_unlock(sch); return 0;}static struct Qdisc *netem_leaf(struct Qdisc *sch, unsigned long arg){ struct netem_sched_data *q = qdisc_priv(sch); return q->qdisc;}static unsigned long netem_get(struct Qdisc *sch, u32 classid){ return 1;}static void netem_put(struct Qdisc *sch, unsigned long arg){}static int netem_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct rtattr **tca, unsigned long *arg){ return -ENOSYS;}static int netem_delete(struct Qdisc *sch, unsigned long arg){ return -ENOSYS;}static void netem_walk(struct Qdisc *sch, struct qdisc_walker *walker){ if (!walker->stop) { if (walker->count >= walker->skip) if (walker->fn(sch, 1, walker) < 0) { walker->stop = 1; return; } walker->count++; }}static struct tcf_proto **netem_find_tcf(struct Qdisc *sch, unsigned long cl){ return NULL;}static struct Qdisc_class_ops netem_class_ops = { .graft = netem_graft, .leaf = netem_leaf, .get = netem_get, .put = netem_put, .change = netem_change_class, .delete = netem_delete, .walk = netem_walk, .tcf_chain = netem_find_tcf, .dump = netem_dump_class,};static struct Qdisc_ops netem_qdisc_ops = { .id = "netem", .cl_ops = &netem_class_ops, .priv_size = sizeof(struct netem_sched_data), .enqueue = netem_enqueue, .dequeue = netem_dequeue, .requeue = netem_requeue, .drop = netem_drop, .init = netem_init, .reset = netem_reset, .destroy = netem_destroy, .change = netem_change, .dump = netem_dump, .owner = THIS_MODULE,};static int __init netem_module_init(void){ pr_info("netem: version " VERSION "\n"); return register_qdisc(&netem_qdisc_ops);}static void __exit netem_module_exit(void){ unregister_qdisc(&netem_qdisc_ops);}module_init(netem_module_init)module_exit(netem_module_exit)MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -