0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043 #include <linux/module.h>
0044 #include <linux/slab.h>
0045 #include <linux/types.h>
0046 #include <linux/kernel.h>
0047 #include <linux/errno.h>
0048 #include <linux/skbuff.h>
0049 #include <linux/prefetch.h>
0050 #include <net/pkt_sched.h>
0051 #include <net/codel.h>
0052 #include <net/codel_impl.h>
0053 #include <net/codel_qdisc.h>
0054
0055
0056 #define DEFAULT_CODEL_LIMIT 1000
0057
0058 struct codel_sched_data {
0059 struct codel_params params;
0060 struct codel_vars vars;
0061 struct codel_stats stats;
0062 u32 drop_overlimit;
0063 };
0064
0065
0066
0067
0068
0069 static struct sk_buff *dequeue_func(struct codel_vars *vars, void *ctx)
0070 {
0071 struct Qdisc *sch = ctx;
0072 struct sk_buff *skb = __qdisc_dequeue_head(&sch->q);
0073
0074 if (skb) {
0075 sch->qstats.backlog -= qdisc_pkt_len(skb);
0076 prefetch(&skb->end);
0077 }
0078 return skb;
0079 }
0080
0081 static void drop_func(struct sk_buff *skb, void *ctx)
0082 {
0083 struct Qdisc *sch = ctx;
0084
0085 kfree_skb(skb);
0086 qdisc_qstats_drop(sch);
0087 }
0088
0089 static struct sk_buff *codel_qdisc_dequeue(struct Qdisc *sch)
0090 {
0091 struct codel_sched_data *q = qdisc_priv(sch);
0092 struct sk_buff *skb;
0093
0094 skb = codel_dequeue(sch, &sch->qstats.backlog, &q->params, &q->vars,
0095 &q->stats, qdisc_pkt_len, codel_get_enqueue_time,
0096 drop_func, dequeue_func);
0097
0098
0099
0100
0101 if (q->stats.drop_count && sch->q.qlen) {
0102 qdisc_tree_reduce_backlog(sch, q->stats.drop_count, q->stats.drop_len);
0103 q->stats.drop_count = 0;
0104 q->stats.drop_len = 0;
0105 }
0106 if (skb)
0107 qdisc_bstats_update(sch, skb);
0108 return skb;
0109 }
0110
0111 static int codel_qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch,
0112 struct sk_buff **to_free)
0113 {
0114 struct codel_sched_data *q;
0115
0116 if (likely(qdisc_qlen(sch) < sch->limit)) {
0117 codel_set_enqueue_time(skb);
0118 return qdisc_enqueue_tail(skb, sch);
0119 }
0120 q = qdisc_priv(sch);
0121 q->drop_overlimit++;
0122 return qdisc_drop(skb, sch, to_free);
0123 }
0124
0125 static const struct nla_policy codel_policy[TCA_CODEL_MAX + 1] = {
0126 [TCA_CODEL_TARGET] = { .type = NLA_U32 },
0127 [TCA_CODEL_LIMIT] = { .type = NLA_U32 },
0128 [TCA_CODEL_INTERVAL] = { .type = NLA_U32 },
0129 [TCA_CODEL_ECN] = { .type = NLA_U32 },
0130 [TCA_CODEL_CE_THRESHOLD]= { .type = NLA_U32 },
0131 };
0132
0133 static int codel_change(struct Qdisc *sch, struct nlattr *opt,
0134 struct netlink_ext_ack *extack)
0135 {
0136 struct codel_sched_data *q = qdisc_priv(sch);
0137 struct nlattr *tb[TCA_CODEL_MAX + 1];
0138 unsigned int qlen, dropped = 0;
0139 int err;
0140
0141 if (!opt)
0142 return -EINVAL;
0143
0144 err = nla_parse_nested_deprecated(tb, TCA_CODEL_MAX, opt,
0145 codel_policy, NULL);
0146 if (err < 0)
0147 return err;
0148
0149 sch_tree_lock(sch);
0150
0151 if (tb[TCA_CODEL_TARGET]) {
0152 u32 target = nla_get_u32(tb[TCA_CODEL_TARGET]);
0153
0154 q->params.target = ((u64)target * NSEC_PER_USEC) >> CODEL_SHIFT;
0155 }
0156
0157 if (tb[TCA_CODEL_CE_THRESHOLD]) {
0158 u64 val = nla_get_u32(tb[TCA_CODEL_CE_THRESHOLD]);
0159
0160 q->params.ce_threshold = (val * NSEC_PER_USEC) >> CODEL_SHIFT;
0161 }
0162
0163 if (tb[TCA_CODEL_INTERVAL]) {
0164 u32 interval = nla_get_u32(tb[TCA_CODEL_INTERVAL]);
0165
0166 q->params.interval = ((u64)interval * NSEC_PER_USEC) >> CODEL_SHIFT;
0167 }
0168
0169 if (tb[TCA_CODEL_LIMIT])
0170 sch->limit = nla_get_u32(tb[TCA_CODEL_LIMIT]);
0171
0172 if (tb[TCA_CODEL_ECN])
0173 q->params.ecn = !!nla_get_u32(tb[TCA_CODEL_ECN]);
0174
0175 qlen = sch->q.qlen;
0176 while (sch->q.qlen > sch->limit) {
0177 struct sk_buff *skb = __qdisc_dequeue_head(&sch->q);
0178
0179 dropped += qdisc_pkt_len(skb);
0180 qdisc_qstats_backlog_dec(sch, skb);
0181 rtnl_qdisc_drop(skb, sch);
0182 }
0183 qdisc_tree_reduce_backlog(sch, qlen - sch->q.qlen, dropped);
0184
0185 sch_tree_unlock(sch);
0186 return 0;
0187 }
0188
0189 static int codel_init(struct Qdisc *sch, struct nlattr *opt,
0190 struct netlink_ext_ack *extack)
0191 {
0192 struct codel_sched_data *q = qdisc_priv(sch);
0193
0194 sch->limit = DEFAULT_CODEL_LIMIT;
0195
0196 codel_params_init(&q->params);
0197 codel_vars_init(&q->vars);
0198 codel_stats_init(&q->stats);
0199 q->params.mtu = psched_mtu(qdisc_dev(sch));
0200
0201 if (opt) {
0202 int err = codel_change(sch, opt, extack);
0203
0204 if (err)
0205 return err;
0206 }
0207
0208 if (sch->limit >= 1)
0209 sch->flags |= TCQ_F_CAN_BYPASS;
0210 else
0211 sch->flags &= ~TCQ_F_CAN_BYPASS;
0212
0213 return 0;
0214 }
0215
0216 static int codel_dump(struct Qdisc *sch, struct sk_buff *skb)
0217 {
0218 struct codel_sched_data *q = qdisc_priv(sch);
0219 struct nlattr *opts;
0220
0221 opts = nla_nest_start_noflag(skb, TCA_OPTIONS);
0222 if (opts == NULL)
0223 goto nla_put_failure;
0224
0225 if (nla_put_u32(skb, TCA_CODEL_TARGET,
0226 codel_time_to_us(q->params.target)) ||
0227 nla_put_u32(skb, TCA_CODEL_LIMIT,
0228 sch->limit) ||
0229 nla_put_u32(skb, TCA_CODEL_INTERVAL,
0230 codel_time_to_us(q->params.interval)) ||
0231 nla_put_u32(skb, TCA_CODEL_ECN,
0232 q->params.ecn))
0233 goto nla_put_failure;
0234 if (q->params.ce_threshold != CODEL_DISABLED_THRESHOLD &&
0235 nla_put_u32(skb, TCA_CODEL_CE_THRESHOLD,
0236 codel_time_to_us(q->params.ce_threshold)))
0237 goto nla_put_failure;
0238 return nla_nest_end(skb, opts);
0239
0240 nla_put_failure:
0241 nla_nest_cancel(skb, opts);
0242 return -1;
0243 }
0244
0245 static int codel_dump_stats(struct Qdisc *sch, struct gnet_dump *d)
0246 {
0247 const struct codel_sched_data *q = qdisc_priv(sch);
0248 struct tc_codel_xstats st = {
0249 .maxpacket = q->stats.maxpacket,
0250 .count = q->vars.count,
0251 .lastcount = q->vars.lastcount,
0252 .drop_overlimit = q->drop_overlimit,
0253 .ldelay = codel_time_to_us(q->vars.ldelay),
0254 .dropping = q->vars.dropping,
0255 .ecn_mark = q->stats.ecn_mark,
0256 .ce_mark = q->stats.ce_mark,
0257 };
0258
0259 if (q->vars.dropping) {
0260 codel_tdiff_t delta = q->vars.drop_next - codel_get_time();
0261
0262 if (delta >= 0)
0263 st.drop_next = codel_time_to_us(delta);
0264 else
0265 st.drop_next = -codel_time_to_us(-delta);
0266 }
0267
0268 return gnet_stats_copy_app(d, &st, sizeof(st));
0269 }
0270
0271 static void codel_reset(struct Qdisc *sch)
0272 {
0273 struct codel_sched_data *q = qdisc_priv(sch);
0274
0275 qdisc_reset_queue(sch);
0276 codel_vars_init(&q->vars);
0277 }
0278
0279 static struct Qdisc_ops codel_qdisc_ops __read_mostly = {
0280 .id = "codel",
0281 .priv_size = sizeof(struct codel_sched_data),
0282
0283 .enqueue = codel_qdisc_enqueue,
0284 .dequeue = codel_qdisc_dequeue,
0285 .peek = qdisc_peek_dequeued,
0286 .init = codel_init,
0287 .reset = codel_reset,
0288 .change = codel_change,
0289 .dump = codel_dump,
0290 .dump_stats = codel_dump_stats,
0291 .owner = THIS_MODULE,
0292 };
0293
0294 static int __init codel_module_init(void)
0295 {
0296 return register_qdisc(&codel_qdisc_ops);
0297 }
0298
0299 static void __exit codel_module_exit(void)
0300 {
0301 unregister_qdisc(&codel_qdisc_ops);
0302 }
0303
0304 module_init(codel_module_init)
0305 module_exit(codel_module_exit)
0306
0307 MODULE_DESCRIPTION("Controlled Delay queue discipline");
0308 MODULE_AUTHOR("Dave Taht");
0309 MODULE_AUTHOR("Eric Dumazet");
0310 MODULE_LICENSE("Dual BSD/GPL");