blob: 1c7fc8f7693886f892196cad699b049bf377f32e [file] [log] [blame]
Kyle Swenson8d8f6542021-03-15 11:02:55 -06001/*
2 * Fair Queue CoDel discipline
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, or (at your option) any later version.
8 *
9 * Copyright (C) 2012,2015 Eric Dumazet <edumazet@google.com>
10 */
11
12#include <linux/module.h>
13#include <linux/types.h>
14#include <linux/kernel.h>
15#include <linux/jiffies.h>
16#include <linux/string.h>
17#include <linux/in.h>
18#include <linux/errno.h>
19#include <linux/init.h>
20#include <linux/skbuff.h>
21#include <linux/jhash.h>
22#include <linux/slab.h>
23#include <linux/vmalloc.h>
24#include <net/netlink.h>
25#include <net/pkt_sched.h>
26#include <net/codel.h>
27
28/* Fair Queue CoDel.
29 *
30 * Principles :
31 * Packets are classified (internal classifier or external) on flows.
32 * This is a Stochastic model (as we use a hash, several flows
33 * might be hashed on same slot)
34 * Each flow has a CoDel managed queue.
35 * Flows are linked onto two (Round Robin) lists,
36 * so that new flows have priority on old ones.
37 *
38 * For a given flow, packets are not reordered (CoDel uses a FIFO)
39 * head drops only.
40 * ECN capability is on by default.
41 * Low memory footprint (64 bytes per flow)
42 */
43
44struct fq_codel_flow {
45 struct sk_buff *head;
46 struct sk_buff *tail;
47 struct list_head flowchain;
48 int deficit;
49 u32 dropped; /* number of drops (or ECN marks) on this flow */
50 struct codel_vars cvars;
51}; /* please try to keep this structure <= 64 bytes */
52
53struct fq_codel_sched_data {
54 struct tcf_proto __rcu *filter_list; /* optional external classifier */
55 struct fq_codel_flow *flows; /* Flows table [flows_cnt] */
56 u32 *backlogs; /* backlog table [flows_cnt] */
57 u32 flows_cnt; /* number of flows */
Kyle Swenson8d8f6542021-03-15 11:02:55 -060058 u32 quantum; /* psched_mtu(qdisc_dev(sch)); */
Kyle Swensone01461f2021-03-15 11:14:57 -060059 u32 drop_batch_size;
60 u32 memory_limit;
Kyle Swenson8d8f6542021-03-15 11:02:55 -060061 struct codel_params cparams;
62 struct codel_stats cstats;
Kyle Swensone01461f2021-03-15 11:14:57 -060063 u32 memory_usage;
64 u32 drop_overmemory;
Kyle Swenson8d8f6542021-03-15 11:02:55 -060065 u32 drop_overlimit;
66 u32 new_flow_count;
67
68 struct list_head new_flows; /* list of new flows */
69 struct list_head old_flows; /* list of old flows */
70};
71
Kyle Swensone01461f2021-03-15 11:14:57 -060072#define HIGH_Q_MEM_AVAIL 512 /* MBytes */
73#define LOW_Q_MEM_AVAIL 128 /* MBytes */
74
Kyle Swenson8d8f6542021-03-15 11:02:55 -060075static unsigned int fq_codel_hash(const struct fq_codel_sched_data *q,
76 struct sk_buff *skb)
77{
Kyle Swensone01461f2021-03-15 11:14:57 -060078 return reciprocal_scale(skb_get_hash(skb), q->flows_cnt);
Kyle Swenson8d8f6542021-03-15 11:02:55 -060079}
80
81static unsigned int fq_codel_classify(struct sk_buff *skb, struct Qdisc *sch,
82 int *qerr)
83{
84 struct fq_codel_sched_data *q = qdisc_priv(sch);
85 struct tcf_proto *filter;
86 struct tcf_result res;
87 int result;
88
89 if (TC_H_MAJ(skb->priority) == sch->handle &&
90 TC_H_MIN(skb->priority) > 0 &&
91 TC_H_MIN(skb->priority) <= q->flows_cnt)
92 return TC_H_MIN(skb->priority);
93
94 filter = rcu_dereference_bh(q->filter_list);
95 if (!filter)
96 return fq_codel_hash(q, skb) + 1;
97
98 *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
99 result = tc_classify(skb, filter, &res, false);
100 if (result >= 0) {
101#ifdef CONFIG_NET_CLS_ACT
102 switch (result) {
103 case TC_ACT_STOLEN:
104 case TC_ACT_QUEUED:
105 *qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN;
106 case TC_ACT_SHOT:
107 return 0;
108 }
109#endif
110 if (TC_H_MIN(res.classid) <= q->flows_cnt)
111 return TC_H_MIN(res.classid);
112 }
113 return 0;
114}
115
116/* helper functions : might be changed when/if skb use a standard list_head */
117
118/* remove one skb from head of slot queue */
119static inline struct sk_buff *dequeue_head(struct fq_codel_flow *flow)
120{
121 struct sk_buff *skb = flow->head;
122
123 flow->head = skb->next;
124 skb->next = NULL;
125 return skb;
126}
127
128/* add skb to flow queue (tail add) */
129static inline void flow_queue_add(struct fq_codel_flow *flow,
130 struct sk_buff *skb)
131{
132 if (flow->head == NULL)
133 flow->head = skb;
134 else
135 flow->tail->next = skb;
136 flow->tail = skb;
137 skb->next = NULL;
138}
139
Kyle Swensone01461f2021-03-15 11:14:57 -0600140static unsigned int fq_codel_drop(struct Qdisc *sch, unsigned int max_packets)
Kyle Swenson8d8f6542021-03-15 11:02:55 -0600141{
142 struct fq_codel_sched_data *q = qdisc_priv(sch);
143 struct sk_buff *skb;
144 unsigned int maxbacklog = 0, idx = 0, i, len;
145 struct fq_codel_flow *flow;
Kyle Swensone01461f2021-03-15 11:14:57 -0600146 unsigned int threshold;
147 unsigned int mem = 0;
Kyle Swenson8d8f6542021-03-15 11:02:55 -0600148
Kyle Swensone01461f2021-03-15 11:14:57 -0600149 /* Queue is full! Find the fat flow and drop packet(s) from it.
Kyle Swenson8d8f6542021-03-15 11:02:55 -0600150 * This might sound expensive, but with 1024 flows, we scan
151 * 4KB of memory, and we dont need to handle a complex tree
152 * in fast path (packet queue/enqueue) with many cache misses.
Kyle Swensone01461f2021-03-15 11:14:57 -0600153 * In stress mode, we'll try to drop 64 packets from the flow,
154 * amortizing this linear lookup to one cache line per drop.
Kyle Swenson8d8f6542021-03-15 11:02:55 -0600155 */
156 for (i = 0; i < q->flows_cnt; i++) {
157 if (q->backlogs[i] > maxbacklog) {
158 maxbacklog = q->backlogs[i];
159 idx = i;
160 }
161 }
Kyle Swensone01461f2021-03-15 11:14:57 -0600162
163 /* Our goal is to drop half of this fat flow backlog */
164 threshold = maxbacklog >> 1;
165
Kyle Swenson8d8f6542021-03-15 11:02:55 -0600166 flow = &q->flows[idx];
Kyle Swensone01461f2021-03-15 11:14:57 -0600167 len = 0;
168 i = 0;
169 do {
170 skb = dequeue_head(flow);
171 len += qdisc_pkt_len(skb);
172 mem += skb->truesize;
173 kfree_skb(skb);
174 } while (++i < max_packets && len < threshold);
175
176 flow->dropped += i;
Kyle Swenson8d8f6542021-03-15 11:02:55 -0600177 q->backlogs[idx] -= len;
Kyle Swensone01461f2021-03-15 11:14:57 -0600178 q->memory_usage -= mem;
179 sch->qstats.drops += i;
180 sch->qstats.backlog -= len;
181 sch->q.qlen -= i;
Kyle Swenson8d8f6542021-03-15 11:02:55 -0600182 return idx;
183}
184
185static unsigned int fq_codel_qdisc_drop(struct Qdisc *sch)
186{
187 unsigned int prev_backlog;
188
189 prev_backlog = sch->qstats.backlog;
Kyle Swensone01461f2021-03-15 11:14:57 -0600190 fq_codel_drop(sch, 1U);
Kyle Swenson8d8f6542021-03-15 11:02:55 -0600191 return prev_backlog - sch->qstats.backlog;
192}
193
194static int fq_codel_enqueue(struct sk_buff *skb, struct Qdisc *sch)
195{
196 struct fq_codel_sched_data *q = qdisc_priv(sch);
Kyle Swensone01461f2021-03-15 11:14:57 -0600197 unsigned int idx, prev_backlog, prev_qlen;
Kyle Swenson8d8f6542021-03-15 11:02:55 -0600198 struct fq_codel_flow *flow;
199 int uninitialized_var(ret);
Kyle Swensone01461f2021-03-15 11:14:57 -0600200 unsigned int pkt_len;
201 bool memory_limited;
Kyle Swenson8d8f6542021-03-15 11:02:55 -0600202
203 idx = fq_codel_classify(skb, sch, &ret);
204 if (idx == 0) {
205 if (ret & __NET_XMIT_BYPASS)
206 qdisc_qstats_drop(sch);
207 kfree_skb(skb);
208 return ret;
209 }
210 idx--;
211
212 codel_set_enqueue_time(skb);
213 flow = &q->flows[idx];
214 flow_queue_add(flow, skb);
215 q->backlogs[idx] += qdisc_pkt_len(skb);
216 qdisc_qstats_backlog_inc(sch, skb);
217
218 if (list_empty(&flow->flowchain)) {
219 list_add_tail(&flow->flowchain, &q->new_flows);
220 q->new_flow_count++;
221 flow->deficit = q->quantum;
Kyle Swenson8d8f6542021-03-15 11:02:55 -0600222 }
Kyle Swensone01461f2021-03-15 11:14:57 -0600223 q->memory_usage += skb->truesize;
224 memory_limited = q->memory_usage > q->memory_limit;
225 if (++sch->q.qlen <= sch->limit && !memory_limited)
Kyle Swenson8d8f6542021-03-15 11:02:55 -0600226 return NET_XMIT_SUCCESS;
227
228 prev_backlog = sch->qstats.backlog;
Kyle Swensone01461f2021-03-15 11:14:57 -0600229 prev_qlen = sch->q.qlen;
Kyle Swenson8d8f6542021-03-15 11:02:55 -0600230
Kyle Swensone01461f2021-03-15 11:14:57 -0600231 /* save this packet length as it might be dropped by fq_codel_drop() */
232 pkt_len = qdisc_pkt_len(skb);
233 /* fq_codel_drop() is quite expensive, as it performs a linear search
234 * in q->backlogs[] to find a fat flow.
235 * So instead of dropping a single packet, drop half of its backlog
236 * with a 64 packets limit to not add a too big cpu spike here.
237 */
238 ret = fq_codel_drop(sch, q->drop_batch_size);
239
240 prev_qlen -= sch->q.qlen;
241 prev_backlog -= sch->qstats.backlog;
242 q->drop_overlimit += prev_qlen;
243 if (memory_limited)
244 q->drop_overmemory += prev_qlen;
245
246 /* As we dropped packet(s), better let upper stack know this.
247 * If we dropped a packet for this flow, return NET_XMIT_CN,
248 * but in this case, our parents wont increase their backlogs.
249 */
250 if (ret == idx) {
251 qdisc_tree_reduce_backlog(sch, prev_qlen - 1,
252 prev_backlog - pkt_len);
253 return NET_XMIT_CN;
254 }
255 qdisc_tree_reduce_backlog(sch, prev_qlen, prev_backlog);
Kyle Swenson8d8f6542021-03-15 11:02:55 -0600256 return NET_XMIT_SUCCESS;
257}
258
259/* This is the specific function called from codel_dequeue()
260 * to dequeue a packet from queue. Note: backlog is handled in
261 * codel, we dont need to reduce it here.
262 */
263static struct sk_buff *dequeue(struct codel_vars *vars, struct Qdisc *sch)
264{
265 struct fq_codel_sched_data *q = qdisc_priv(sch);
266 struct fq_codel_flow *flow;
267 struct sk_buff *skb = NULL;
268
269 flow = container_of(vars, struct fq_codel_flow, cvars);
270 if (flow->head) {
271 skb = dequeue_head(flow);
272 q->backlogs[flow - q->flows] -= qdisc_pkt_len(skb);
Kyle Swensone01461f2021-03-15 11:14:57 -0600273 q->memory_usage -= skb->truesize;
Kyle Swenson8d8f6542021-03-15 11:02:55 -0600274 sch->q.qlen--;
275 }
276 return skb;
277}
278
279static struct sk_buff *fq_codel_dequeue(struct Qdisc *sch)
280{
281 struct fq_codel_sched_data *q = qdisc_priv(sch);
282 struct sk_buff *skb;
283 struct fq_codel_flow *flow;
284 struct list_head *head;
285 u32 prev_drop_count, prev_ecn_mark;
286 unsigned int prev_backlog;
287
288begin:
289 head = &q->new_flows;
290 if (list_empty(head)) {
291 head = &q->old_flows;
292 if (list_empty(head))
293 return NULL;
294 }
295 flow = list_first_entry(head, struct fq_codel_flow, flowchain);
296
297 if (flow->deficit <= 0) {
298 flow->deficit += q->quantum;
299 list_move_tail(&flow->flowchain, &q->old_flows);
300 goto begin;
301 }
302
303 prev_drop_count = q->cstats.drop_count;
304 prev_ecn_mark = q->cstats.ecn_mark;
305 prev_backlog = sch->qstats.backlog;
306
307 skb = codel_dequeue(sch, &q->cparams, &flow->cvars, &q->cstats,
308 dequeue);
309
310 flow->dropped += q->cstats.drop_count - prev_drop_count;
311 flow->dropped += q->cstats.ecn_mark - prev_ecn_mark;
312
313 if (!skb) {
314 /* force a pass through old_flows to prevent starvation */
315 if ((head == &q->new_flows) && !list_empty(&q->old_flows))
316 list_move_tail(&flow->flowchain, &q->old_flows);
317 else
318 list_del_init(&flow->flowchain);
319 goto begin;
320 }
321 qdisc_bstats_update(sch, skb);
322 flow->deficit -= qdisc_pkt_len(skb);
323 /* We cant call qdisc_tree_reduce_backlog() if our qlen is 0,
324 * or HTB crashes. Defer it for next round.
325 */
326 if (q->cstats.drop_count && sch->q.qlen) {
327 qdisc_tree_reduce_backlog(sch, q->cstats.drop_count,
328 q->cstats.drop_len);
329 q->cstats.drop_count = 0;
330 q->cstats.drop_len = 0;
331 }
332 return skb;
333}
334
335static void fq_codel_reset(struct Qdisc *sch)
336{
337 struct fq_codel_sched_data *q = qdisc_priv(sch);
338 int i;
339
340 INIT_LIST_HEAD(&q->new_flows);
341 INIT_LIST_HEAD(&q->old_flows);
342 for (i = 0; i < q->flows_cnt; i++) {
343 struct fq_codel_flow *flow = q->flows + i;
344
345 while (flow->head) {
346 struct sk_buff *skb = dequeue_head(flow);
347
348 qdisc_qstats_backlog_dec(sch, skb);
349 kfree_skb(skb);
350 }
351
352 INIT_LIST_HEAD(&flow->flowchain);
353 codel_vars_init(&flow->cvars);
354 }
355 memset(q->backlogs, 0, q->flows_cnt * sizeof(u32));
356 sch->q.qlen = 0;
Kyle Swensone01461f2021-03-15 11:14:57 -0600357 q->memory_usage = 0;
Kyle Swenson8d8f6542021-03-15 11:02:55 -0600358}
359
360static const struct nla_policy fq_codel_policy[TCA_FQ_CODEL_MAX + 1] = {
361 [TCA_FQ_CODEL_TARGET] = { .type = NLA_U32 },
362 [TCA_FQ_CODEL_LIMIT] = { .type = NLA_U32 },
363 [TCA_FQ_CODEL_INTERVAL] = { .type = NLA_U32 },
364 [TCA_FQ_CODEL_ECN] = { .type = NLA_U32 },
365 [TCA_FQ_CODEL_FLOWS] = { .type = NLA_U32 },
366 [TCA_FQ_CODEL_QUANTUM] = { .type = NLA_U32 },
367 [TCA_FQ_CODEL_CE_THRESHOLD] = { .type = NLA_U32 },
Kyle Swensone01461f2021-03-15 11:14:57 -0600368 [TCA_FQ_CODEL_DROP_BATCH_SIZE] = { .type = NLA_U32 },
369 [TCA_FQ_CODEL_MEMORY_LIMIT] = { .type = NLA_U32 },
Kyle Swenson8d8f6542021-03-15 11:02:55 -0600370};
371
372static int fq_codel_change(struct Qdisc *sch, struct nlattr *opt)
373{
374 struct fq_codel_sched_data *q = qdisc_priv(sch);
375 struct nlattr *tb[TCA_FQ_CODEL_MAX + 1];
376 int err;
377
378 if (!opt)
379 return -EINVAL;
380
381 err = nla_parse_nested(tb, TCA_FQ_CODEL_MAX, opt, fq_codel_policy);
382 if (err < 0)
383 return err;
384 if (tb[TCA_FQ_CODEL_FLOWS]) {
385 if (q->flows)
386 return -EINVAL;
387 q->flows_cnt = nla_get_u32(tb[TCA_FQ_CODEL_FLOWS]);
388 if (!q->flows_cnt ||
389 q->flows_cnt > 65536)
390 return -EINVAL;
391 }
392 sch_tree_lock(sch);
393
394 if (tb[TCA_FQ_CODEL_TARGET]) {
395 u64 target = nla_get_u32(tb[TCA_FQ_CODEL_TARGET]);
396
397 q->cparams.target = (target * NSEC_PER_USEC) >> CODEL_SHIFT;
398 }
399
400 if (tb[TCA_FQ_CODEL_CE_THRESHOLD]) {
401 u64 val = nla_get_u32(tb[TCA_FQ_CODEL_CE_THRESHOLD]);
402
403 q->cparams.ce_threshold = (val * NSEC_PER_USEC) >> CODEL_SHIFT;
404 }
405
406 if (tb[TCA_FQ_CODEL_INTERVAL]) {
407 u64 interval = nla_get_u32(tb[TCA_FQ_CODEL_INTERVAL]);
408
409 q->cparams.interval = (interval * NSEC_PER_USEC) >> CODEL_SHIFT;
410 }
411
412 if (tb[TCA_FQ_CODEL_LIMIT])
413 sch->limit = nla_get_u32(tb[TCA_FQ_CODEL_LIMIT]);
414
415 if (tb[TCA_FQ_CODEL_ECN])
416 q->cparams.ecn = !!nla_get_u32(tb[TCA_FQ_CODEL_ECN]);
417
418 if (tb[TCA_FQ_CODEL_QUANTUM])
419 q->quantum = max(256U, nla_get_u32(tb[TCA_FQ_CODEL_QUANTUM]));
420
Kyle Swensone01461f2021-03-15 11:14:57 -0600421 if (tb[TCA_FQ_CODEL_DROP_BATCH_SIZE])
422 q->drop_batch_size = min(1U, nla_get_u32(tb[TCA_FQ_CODEL_DROP_BATCH_SIZE]));
423
424 if (tb[TCA_FQ_CODEL_MEMORY_LIMIT])
425 q->memory_limit = min(1U << 31, nla_get_u32(tb[TCA_FQ_CODEL_MEMORY_LIMIT]));
426
427 while (sch->q.qlen > sch->limit ||
428 q->memory_usage > q->memory_limit) {
Kyle Swenson8d8f6542021-03-15 11:02:55 -0600429 struct sk_buff *skb = fq_codel_dequeue(sch);
430
431 q->cstats.drop_len += qdisc_pkt_len(skb);
432 kfree_skb(skb);
433 q->cstats.drop_count++;
434 }
435 qdisc_tree_reduce_backlog(sch, q->cstats.drop_count, q->cstats.drop_len);
436 q->cstats.drop_count = 0;
437 q->cstats.drop_len = 0;
438
439 sch_tree_unlock(sch);
440 return 0;
441}
442
443static void *fq_codel_zalloc(size_t sz)
444{
445 void *ptr = kzalloc(sz, GFP_KERNEL | __GFP_NOWARN);
446
447 if (!ptr)
448 ptr = vzalloc(sz);
449 return ptr;
450}
451
452static void fq_codel_free(void *addr)
453{
454 kvfree(addr);
455}
456
457static void fq_codel_destroy(struct Qdisc *sch)
458{
459 struct fq_codel_sched_data *q = qdisc_priv(sch);
460
461 tcf_destroy_chain(&q->filter_list);
462 fq_codel_free(q->backlogs);
463 fq_codel_free(q->flows);
464}
465
Kyle Swensone01461f2021-03-15 11:14:57 -0600466static int fq_codel_mem_default(void)
467{
468 int mbytes;
469
470 if (totalram_pages > HIGH_Q_MEM_AVAIL * 1024 * 1024 / PAGE_SIZE)
471 mbytes = 32;
472 else if (totalram_pages > LOW_Q_MEM_AVAIL * 1024 * 1024 / PAGE_SIZE)
473 mbytes = 8;
474 else
475 mbytes = 4;
476
477 return mbytes << 20;
478}
479
Kyle Swenson8d8f6542021-03-15 11:02:55 -0600480static int fq_codel_init(struct Qdisc *sch, struct nlattr *opt)
481{
482 struct fq_codel_sched_data *q = qdisc_priv(sch);
483 int i;
484
485 sch->limit = 10*1024;
486 q->flows_cnt = 1024;
Kyle Swensone01461f2021-03-15 11:14:57 -0600487 q->memory_limit = fq_codel_mem_default();
488 q->drop_batch_size = 64;
Kyle Swenson8d8f6542021-03-15 11:02:55 -0600489 q->quantum = psched_mtu(qdisc_dev(sch));
Kyle Swenson8d8f6542021-03-15 11:02:55 -0600490 INIT_LIST_HEAD(&q->new_flows);
491 INIT_LIST_HEAD(&q->old_flows);
492 codel_params_init(&q->cparams, sch);
493 codel_stats_init(&q->cstats);
494 q->cparams.ecn = true;
495
496 if (opt) {
497 int err = fq_codel_change(sch, opt);
498 if (err)
499 return err;
500 }
501
502 if (!q->flows) {
503 q->flows = fq_codel_zalloc(q->flows_cnt *
504 sizeof(struct fq_codel_flow));
505 if (!q->flows)
506 return -ENOMEM;
507 q->backlogs = fq_codel_zalloc(q->flows_cnt * sizeof(u32));
508 if (!q->backlogs) {
509 fq_codel_free(q->flows);
510 return -ENOMEM;
511 }
512 for (i = 0; i < q->flows_cnt; i++) {
513 struct fq_codel_flow *flow = q->flows + i;
514
515 INIT_LIST_HEAD(&flow->flowchain);
516 codel_vars_init(&flow->cvars);
517 }
518 }
519 if (sch->limit >= 1)
520 sch->flags |= TCQ_F_CAN_BYPASS;
521 else
522 sch->flags &= ~TCQ_F_CAN_BYPASS;
523 return 0;
524}
525
526static int fq_codel_dump(struct Qdisc *sch, struct sk_buff *skb)
527{
528 struct fq_codel_sched_data *q = qdisc_priv(sch);
529 struct nlattr *opts;
530
531 opts = nla_nest_start(skb, TCA_OPTIONS);
532 if (opts == NULL)
533 goto nla_put_failure;
534
535 if (nla_put_u32(skb, TCA_FQ_CODEL_TARGET,
536 codel_time_to_us(q->cparams.target)) ||
537 nla_put_u32(skb, TCA_FQ_CODEL_LIMIT,
538 sch->limit) ||
539 nla_put_u32(skb, TCA_FQ_CODEL_INTERVAL,
540 codel_time_to_us(q->cparams.interval)) ||
541 nla_put_u32(skb, TCA_FQ_CODEL_ECN,
542 q->cparams.ecn) ||
543 nla_put_u32(skb, TCA_FQ_CODEL_QUANTUM,
544 q->quantum) ||
Kyle Swensone01461f2021-03-15 11:14:57 -0600545 nla_put_u32(skb, TCA_FQ_CODEL_DROP_BATCH_SIZE,
546 q->drop_batch_size) ||
547 nla_put_u32(skb, TCA_FQ_CODEL_MEMORY_LIMIT,
548 q->memory_limit) ||
Kyle Swenson8d8f6542021-03-15 11:02:55 -0600549 nla_put_u32(skb, TCA_FQ_CODEL_FLOWS,
550 q->flows_cnt))
551 goto nla_put_failure;
552
553 if (q->cparams.ce_threshold != CODEL_DISABLED_THRESHOLD &&
554 nla_put_u32(skb, TCA_FQ_CODEL_CE_THRESHOLD,
555 codel_time_to_us(q->cparams.ce_threshold)))
556 goto nla_put_failure;
557
558 return nla_nest_end(skb, opts);
559
560nla_put_failure:
561 return -1;
562}
563
564static int fq_codel_dump_stats(struct Qdisc *sch, struct gnet_dump *d)
565{
566 struct fq_codel_sched_data *q = qdisc_priv(sch);
567 struct tc_fq_codel_xstats st = {
568 .type = TCA_FQ_CODEL_XSTATS_QDISC,
569 };
570 struct list_head *pos;
571
572 st.qdisc_stats.maxpacket = q->cstats.maxpacket;
573 st.qdisc_stats.drop_overlimit = q->drop_overlimit;
574 st.qdisc_stats.ecn_mark = q->cstats.ecn_mark;
575 st.qdisc_stats.new_flow_count = q->new_flow_count;
576 st.qdisc_stats.ce_mark = q->cstats.ce_mark;
Kyle Swensone01461f2021-03-15 11:14:57 -0600577 st.qdisc_stats.memory_usage = q->memory_usage;
578 st.qdisc_stats.drop_overmemory = q->drop_overmemory;
Kyle Swenson8d8f6542021-03-15 11:02:55 -0600579
580 list_for_each(pos, &q->new_flows)
581 st.qdisc_stats.new_flows_len++;
582
583 list_for_each(pos, &q->old_flows)
584 st.qdisc_stats.old_flows_len++;
585
586 return gnet_stats_copy_app(d, &st, sizeof(st));
587}
588
589static struct Qdisc *fq_codel_leaf(struct Qdisc *sch, unsigned long arg)
590{
591 return NULL;
592}
593
594static unsigned long fq_codel_get(struct Qdisc *sch, u32 classid)
595{
596 return 0;
597}
598
599static unsigned long fq_codel_bind(struct Qdisc *sch, unsigned long parent,
600 u32 classid)
601{
602 /* we cannot bypass queue discipline anymore */
603 sch->flags &= ~TCQ_F_CAN_BYPASS;
604 return 0;
605}
606
607static void fq_codel_put(struct Qdisc *q, unsigned long cl)
608{
609}
610
611static struct tcf_proto __rcu **fq_codel_find_tcf(struct Qdisc *sch,
612 unsigned long cl)
613{
614 struct fq_codel_sched_data *q = qdisc_priv(sch);
615
616 if (cl)
617 return NULL;
618 return &q->filter_list;
619}
620
621static int fq_codel_dump_class(struct Qdisc *sch, unsigned long cl,
622 struct sk_buff *skb, struct tcmsg *tcm)
623{
624 tcm->tcm_handle |= TC_H_MIN(cl);
625 return 0;
626}
627
628static int fq_codel_dump_class_stats(struct Qdisc *sch, unsigned long cl,
629 struct gnet_dump *d)
630{
631 struct fq_codel_sched_data *q = qdisc_priv(sch);
632 u32 idx = cl - 1;
633 struct gnet_stats_queue qs = { 0 };
634 struct tc_fq_codel_xstats xstats;
635
636 if (idx < q->flows_cnt) {
637 const struct fq_codel_flow *flow = &q->flows[idx];
638 const struct sk_buff *skb = flow->head;
639
640 memset(&xstats, 0, sizeof(xstats));
641 xstats.type = TCA_FQ_CODEL_XSTATS_CLASS;
642 xstats.class_stats.deficit = flow->deficit;
643 xstats.class_stats.ldelay =
644 codel_time_to_us(flow->cvars.ldelay);
645 xstats.class_stats.count = flow->cvars.count;
646 xstats.class_stats.lastcount = flow->cvars.lastcount;
647 xstats.class_stats.dropping = flow->cvars.dropping;
648 if (flow->cvars.dropping) {
649 codel_tdiff_t delta = flow->cvars.drop_next -
650 codel_get_time();
651
652 xstats.class_stats.drop_next = (delta >= 0) ?
653 codel_time_to_us(delta) :
654 -codel_time_to_us(-delta);
655 }
656 while (skb) {
657 qs.qlen++;
658 skb = skb->next;
659 }
660 qs.backlog = q->backlogs[idx];
661 qs.drops = flow->dropped;
662 }
663 if (gnet_stats_copy_queue(d, NULL, &qs, 0) < 0)
664 return -1;
665 if (idx < q->flows_cnt)
666 return gnet_stats_copy_app(d, &xstats, sizeof(xstats));
667 return 0;
668}
669
670static void fq_codel_walk(struct Qdisc *sch, struct qdisc_walker *arg)
671{
672 struct fq_codel_sched_data *q = qdisc_priv(sch);
673 unsigned int i;
674
675 if (arg->stop)
676 return;
677
678 for (i = 0; i < q->flows_cnt; i++) {
679 if (list_empty(&q->flows[i].flowchain) ||
680 arg->count < arg->skip) {
681 arg->count++;
682 continue;
683 }
684 if (arg->fn(sch, i + 1, arg) < 0) {
685 arg->stop = 1;
686 break;
687 }
688 arg->count++;
689 }
690}
691
692static const struct Qdisc_class_ops fq_codel_class_ops = {
693 .leaf = fq_codel_leaf,
694 .get = fq_codel_get,
695 .put = fq_codel_put,
696 .tcf_chain = fq_codel_find_tcf,
697 .bind_tcf = fq_codel_bind,
698 .unbind_tcf = fq_codel_put,
699 .dump = fq_codel_dump_class,
700 .dump_stats = fq_codel_dump_class_stats,
701 .walk = fq_codel_walk,
702};
703
Kyle Swensone01461f2021-03-15 11:14:57 -0600704struct Qdisc_ops fq_codel_qdisc_ops __read_mostly = {
Kyle Swenson8d8f6542021-03-15 11:02:55 -0600705 .cl_ops = &fq_codel_class_ops,
706 .id = "fq_codel",
707 .priv_size = sizeof(struct fq_codel_sched_data),
708 .enqueue = fq_codel_enqueue,
709 .dequeue = fq_codel_dequeue,
710 .peek = qdisc_peek_dequeued,
711 .drop = fq_codel_qdisc_drop,
712 .init = fq_codel_init,
713 .reset = fq_codel_reset,
714 .destroy = fq_codel_destroy,
715 .change = fq_codel_change,
716 .dump = fq_codel_dump,
717 .dump_stats = fq_codel_dump_stats,
718 .owner = THIS_MODULE,
719};
Kyle Swensone01461f2021-03-15 11:14:57 -0600720EXPORT_SYMBOL(fq_codel_qdisc_ops);
Kyle Swenson8d8f6542021-03-15 11:02:55 -0600721
722static int __init fq_codel_module_init(void)
723{
724 return register_qdisc(&fq_codel_qdisc_ops);
725}
726
727static void __exit fq_codel_module_exit(void)
728{
729 unregister_qdisc(&fq_codel_qdisc_ops);
730}
731
732module_init(fq_codel_module_init)
733module_exit(fq_codel_module_exit)
734MODULE_AUTHOR("Eric Dumazet");
735MODULE_LICENSE("GPL");