Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
2/* Copyright (C) 2024 Nokia
3 *
4 * Author: Koen De Schepper <koen.de_schepper@nokia-bell-labs.com>
5 * Author: Olga Albisser <olga@albisser.org>
6 * Author: Henrik Steen <henrist@henrist.net>
7 * Author: Olivier Tilmans <olivier.tilmans@nokia.com>
8 * Author: Chia-Yu Chang <chia-yu.chang@nokia-bell-labs.com>
9 *
10 * DualPI Improved with a Square (dualpi2):
11 * - Supports congestion controls that comply with the Prague requirements
12 * in RFC9331 (e.g. TCP-Prague)
13 * - Supports coupled dual-queue with PI2 as defined in RFC9332
14 * - Supports ECN L4S-identifier (IP.ECN==0b*1)
15 *
16 * note: Although DCTCP and BBRv3 can use shallow-threshold ECN marks,
17 * they do not meet the 'Prague L4S Requirements' listed in RFC 9331
18 * Section 4, so they can only be used with DualPI2 in a datacenter
19 * context.
20 *
21 * References:
22 * - RFC9332: https://datatracker.ietf.org/doc/html/rfc9332
23 * - De Schepper, Koen, et al. "PI 2: A linearized AQM for both classic and
24 * scalable TCP." in proc. ACM CoNEXT'16, 2016.
25 */
26
27#include <linux/errno.h>
28#include <linux/hrtimer.h>
29#include <linux/if_vlan.h>
30#include <linux/kernel.h>
31#include <linux/limits.h>
32#include <linux/module.h>
33#include <linux/skbuff.h>
34#include <linux/types.h>
35
36#include <net/gso.h>
37#include <net/inet_ecn.h>
38#include <net/pkt_cls.h>
39#include <net/pkt_sched.h>
40
41/* 32b enable to support flows with windows up to ~8.6 * 1e9 packets
42 * i.e., twice the maximal snd_cwnd.
43 * MAX_PROB must be consistent with the RNG in dualpi2_roll().
44 */
45#define MAX_PROB U32_MAX
46
47/* alpha/beta values exchanged over netlink are in units of 256ns */
48#define ALPHA_BETA_SHIFT 8
49
50/* Scaled values of alpha/beta must fit in 32b to avoid overflow in later
51 * computations. Consequently (see and dualpi2_scale_alpha_beta()), their
52 * netlink-provided values can use at most 31b, i.e. be at most (2^23)-1
53 * (~4MHz) as those are given in 1/256th. This enable to tune alpha/beta to
54 * control flows whose maximal RTTs can be in usec up to few secs.
55 */
56#define ALPHA_BETA_MAX ((1U << 31) - 1)
57
58/* Internal alpha/beta are in units of 64ns.
59 * This enables to use all alpha/beta values in the allowed range without loss
60 * of precision due to rounding when scaling them internally, e.g.,
61 * scale_alpha_beta(1) will not round down to 0.
62 */
63#define ALPHA_BETA_GRANULARITY 6
64
65#define ALPHA_BETA_SCALING (ALPHA_BETA_SHIFT - ALPHA_BETA_GRANULARITY)
66
67/* We express the weights (wc, wl) in %, i.e., wc + wl = 100 */
68#define MAX_WC 100
69
70struct dualpi2_sched_data {
71 struct Qdisc *l_queue; /* The L4S Low latency queue (L-queue) */
72 struct Qdisc *sch; /* The Classic queue (C-queue) */
73
74 /* Registered tc filters */
75 struct tcf_proto __rcu *tcf_filters;
76 struct tcf_block *tcf_block;
77
78 /* PI2 parameters */
79 u64 pi2_target; /* Target delay in nanoseconds */
80 u32 pi2_tupdate; /* Timer frequency in nanoseconds */
81 u32 pi2_prob; /* Base PI probability */
82 u32 pi2_alpha; /* Gain factor for the integral rate response */
83 u32 pi2_beta; /* Gain factor for the proportional response */
84 struct hrtimer pi2_timer; /* prob update timer */
85
86 /* Step AQM (L-queue only) parameters */
87 u32 step_thresh; /* Step threshold */
88 bool step_in_packets; /* Step thresh in packets (1) or time (0) */
89
90 /* C-queue starvation protection */
91 s32 c_protection_credit; /* Credit (sign indicates which queue) */
92 s32 c_protection_init; /* Reset value of the credit */
93 u8 c_protection_wc; /* C-queue weight (between 0 and MAX_WC) */
94 u8 c_protection_wl; /* L-queue weight (MAX_WC - wc) */
95
96 /* General dualQ parameters */
97 u32 memory_limit; /* Memory limit of both queues */
98 u8 coupling_factor;/* Coupling factor (k) between both queues */
99 u8 ecn_mask; /* Mask to match packets into L-queue */
100 u32 min_qlen_step; /* Minimum queue length to apply step thresh */
101 bool drop_early; /* Drop at enqueue (1) instead of dequeue (0) */
102 bool drop_overload; /* Drop (1) on overload, or overflow (0) */
103 bool split_gso; /* Split aggregated skb (1) or leave as is (0) */
104
105 /* Statistics */
106 u64 c_head_ts; /* Enqueue timestamp of the C-queue head */
107 u64 l_head_ts; /* Enqueue timestamp of the L-queue head */
108 u64 last_qdelay; /* Q delay val at the last probability update */
109 u32 packets_in_c; /* Enqueue packet counter of the C-queue */
110 u32 packets_in_l; /* Enqueue packet counter of the L-queue */
111 u32 maxq; /* Maximum queue size of the C-queue */
112 u32 ecn_mark; /* ECN mark pkt counter due to PI probability */
113 u32 step_marks; /* ECN mark pkt counter due to step AQM */
114 u32 memory_used; /* Memory used of both queues */
115 u32 max_memory_used;/* Maximum used memory */
116
117 /* Deferred drop statistics */
118 u32 deferred_drops_cnt; /* Packets dropped */
119 u32 deferred_drops_len; /* Bytes dropped */
120};
121
122struct dualpi2_skb_cb {
123 u64 ts; /* Timestamp at enqueue */
124 u8 apply_step:1, /* Can we apply the step threshold */
125 classified:2, /* Packet classification results */
126 ect:2; /* Packet ECT codepoint */
127};
128
129enum dualpi2_classification_results {
130 DUALPI2_C_CLASSIC = 0, /* C-queue */
131 DUALPI2_C_L4S = 1, /* L-queue (scale mark/classic drop) */
132 DUALPI2_C_LLLL = 2, /* L-queue (no drops/marks) */
133 __DUALPI2_C_MAX /* Keep last*/
134};
135
136static struct dualpi2_skb_cb *dualpi2_skb_cb(struct sk_buff *skb)
137{
138 qdisc_cb_private_validate(skb, sizeof(struct dualpi2_skb_cb));
139 return (struct dualpi2_skb_cb *)qdisc_skb_cb(skb)->data;
140}
141
142static u64 dualpi2_sojourn_time(struct sk_buff *skb, u64 reference)
143{
144 return reference - dualpi2_skb_cb(skb)->ts;
145}
146
147static u64 head_enqueue_time(struct Qdisc *q)
148{
149 struct sk_buff *skb = qdisc_peek_head(q);
150
151 return skb ? dualpi2_skb_cb(skb)->ts : 0;
152}
153
154static u32 dualpi2_scale_alpha_beta(u32 param)
155{
156 u64 tmp = ((u64)param * MAX_PROB >> ALPHA_BETA_SCALING);
157
158 do_div(tmp, NSEC_PER_SEC);
159 return tmp;
160}
161
162static u32 dualpi2_unscale_alpha_beta(u32 param)
163{
164 u64 tmp = ((u64)param * NSEC_PER_SEC << ALPHA_BETA_SCALING);
165
166 do_div(tmp, MAX_PROB);
167 return tmp;
168}
169
170static ktime_t next_pi2_timeout(struct dualpi2_sched_data *q)
171{
172 return ktime_add_ns(ktime_get_ns(), q->pi2_tupdate);
173}
174
175static bool skb_is_l4s(struct sk_buff *skb)
176{
177 return dualpi2_skb_cb(skb)->classified == DUALPI2_C_L4S;
178}
179
180static bool skb_in_l_queue(struct sk_buff *skb)
181{
182 return dualpi2_skb_cb(skb)->classified != DUALPI2_C_CLASSIC;
183}
184
185static bool skb_apply_step(struct sk_buff *skb, struct dualpi2_sched_data *q)
186{
187 return skb_is_l4s(skb) && qdisc_qlen(q->l_queue) >= q->min_qlen_step;
188}
189
190static bool dualpi2_mark(struct dualpi2_sched_data *q, struct sk_buff *skb)
191{
192 if (INET_ECN_set_ce(skb)) {
193 q->ecn_mark++;
194 return true;
195 }
196 return false;
197}
198
199static void dualpi2_reset_c_protection(struct dualpi2_sched_data *q)
200{
201 q->c_protection_credit = q->c_protection_init;
202}
203
204/* This computes the initial credit value and WRR weight for the L queue (wl)
205 * from the weight of the C queue (wc).
206 * If wl > wc, the scheduler will start with the L queue when reset.
207 */
208static void dualpi2_calculate_c_protection(struct Qdisc *sch,
209 struct dualpi2_sched_data *q, u32 wc)
210{
211 q->c_protection_wc = wc;
212 q->c_protection_wl = MAX_WC - wc;
213 q->c_protection_init = (s32)psched_mtu(qdisc_dev(sch)) *
214 ((int)q->c_protection_wc - (int)q->c_protection_wl);
215 dualpi2_reset_c_protection(q);
216}
217
218static bool dualpi2_roll(u32 prob)
219{
220 return get_random_u32() <= prob;
221}
222
223/* Packets in the C-queue are subject to a marking probability pC, which is the
224 * square of the internal PI probability (i.e., have an overall lower mark/drop
225 * probability). If the qdisc is overloaded, ignore ECT values and only drop.
226 *
227 * Note that this marking scheme is also applied to L4S packets during overload.
228 * Return true if packet dropping is required in C queue
229 */
230static bool dualpi2_classic_marking(struct dualpi2_sched_data *q,
231 struct sk_buff *skb, u32 prob,
232 bool overload)
233{
234 if (dualpi2_roll(prob) && dualpi2_roll(prob)) {
235 if (overload || dualpi2_skb_cb(skb)->ect == INET_ECN_NOT_ECT)
236 return true;
237 dualpi2_mark(q, skb);
238 }
239 return false;
240}
241
242/* Packets in the L-queue are subject to a marking probability pL given by the
243 * internal PI probability scaled by the coupling factor.
244 *
245 * On overload (i.e., @local_l_prob is >= 100%):
246 * - if the qdisc is configured to trade losses to preserve latency (i.e.,
247 * @q->drop_overload), apply classic drops first before marking.
248 * - otherwise, preserve the "no loss" property of ECN at the cost of queueing
249 * delay, eventually resulting in taildrop behavior once sch->limit is
250 * reached.
251 * Return true if packet dropping is required in L queue
252 */
253static bool dualpi2_scalable_marking(struct dualpi2_sched_data *q,
254 struct sk_buff *skb,
255 u64 local_l_prob, u32 prob,
256 bool overload)
257{
258 if (overload) {
259 /* Apply classic drop */
260 if (!q->drop_overload ||
261 !(dualpi2_roll(prob) && dualpi2_roll(prob)))
262 goto mark;
263 return true;
264 }
265
266 /* We can safely cut the upper 32b as overload==false */
267 if (dualpi2_roll(local_l_prob)) {
268 /* Non-ECT packets could have classified as L4S by filters. */
269 if (dualpi2_skb_cb(skb)->ect == INET_ECN_NOT_ECT)
270 return true;
271mark:
272 dualpi2_mark(q, skb);
273 }
274 return false;
275}
276
277/* Decide whether a given packet must be dropped (or marked if ECT), according
278 * to the PI2 probability.
279 *
280 * Never mark/drop if we have a standing queue of less than 2 MTUs.
281 */
282static bool must_drop(struct Qdisc *sch, struct dualpi2_sched_data *q,
283 struct sk_buff *skb)
284{
285 u64 local_l_prob;
286 bool overload;
287 u32 prob;
288
289 if (sch->qstats.backlog < 2 * psched_mtu(qdisc_dev(sch)))
290 return false;
291
292 prob = READ_ONCE(q->pi2_prob);
293 local_l_prob = (u64)prob * q->coupling_factor;
294 overload = local_l_prob > MAX_PROB;
295
296 switch (dualpi2_skb_cb(skb)->classified) {
297 case DUALPI2_C_CLASSIC:
298 return dualpi2_classic_marking(q, skb, prob, overload);
299 case DUALPI2_C_L4S:
300 return dualpi2_scalable_marking(q, skb, local_l_prob, prob,
301 overload);
302 default: /* DUALPI2_C_LLLL */
303 return false;
304 }
305}
306
307static void dualpi2_read_ect(struct sk_buff *skb)
308{
309 struct dualpi2_skb_cb *cb = dualpi2_skb_cb(skb);
310 int wlen = skb_network_offset(skb);
311
312 switch (skb_protocol(skb, true)) {
313 case htons(ETH_P_IP):
314 wlen += sizeof(struct iphdr);
315 if (!pskb_may_pull(skb, wlen) ||
316 skb_try_make_writable(skb, wlen))
317 goto not_ecn;
318
319 cb->ect = ipv4_get_dsfield(ip_hdr(skb)) & INET_ECN_MASK;
320 break;
321 case htons(ETH_P_IPV6):
322 wlen += sizeof(struct ipv6hdr);
323 if (!pskb_may_pull(skb, wlen) ||
324 skb_try_make_writable(skb, wlen))
325 goto not_ecn;
326
327 cb->ect = ipv6_get_dsfield(ipv6_hdr(skb)) & INET_ECN_MASK;
328 break;
329 default:
330 goto not_ecn;
331 }
332 return;
333
334not_ecn:
335 /* Non pullable/writable packets can only be dropped hence are
336 * classified as not ECT.
337 */
338 cb->ect = INET_ECN_NOT_ECT;
339}
340
341static int dualpi2_skb_classify(struct dualpi2_sched_data *q,
342 struct sk_buff *skb)
343{
344 struct dualpi2_skb_cb *cb = dualpi2_skb_cb(skb);
345 struct tcf_result res;
346 struct tcf_proto *fl;
347 int result;
348
349 dualpi2_read_ect(skb);
350 if (cb->ect & q->ecn_mask) {
351 cb->classified = DUALPI2_C_L4S;
352 return NET_XMIT_SUCCESS;
353 }
354
355 if (TC_H_MAJ(skb->priority) == q->sch->handle &&
356 TC_H_MIN(skb->priority) < __DUALPI2_C_MAX) {
357 cb->classified = TC_H_MIN(skb->priority);
358 return NET_XMIT_SUCCESS;
359 }
360
361 fl = rcu_dereference_bh(q->tcf_filters);
362 if (!fl) {
363 cb->classified = DUALPI2_C_CLASSIC;
364 return NET_XMIT_SUCCESS;
365 }
366
367 result = tcf_classify(skb, NULL, fl, &res, false);
368 if (result >= 0) {
369#ifdef CONFIG_NET_CLS_ACT
370 switch (result) {
371 case TC_ACT_STOLEN:
372 case TC_ACT_QUEUED:
373 case TC_ACT_TRAP:
374 return NET_XMIT_SUCCESS | __NET_XMIT_STOLEN;
375 case TC_ACT_SHOT:
376 return NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
377 }
378#endif
379 cb->classified = TC_H_MIN(res.classid) < __DUALPI2_C_MAX ?
380 TC_H_MIN(res.classid) : DUALPI2_C_CLASSIC;
381 }
382 return NET_XMIT_SUCCESS;
383}
384
385static int dualpi2_enqueue_skb(struct sk_buff *skb, struct Qdisc *sch,
386 struct sk_buff **to_free)
387{
388 struct dualpi2_sched_data *q = qdisc_priv(sch);
389 struct dualpi2_skb_cb *cb;
390
391 if (unlikely(qdisc_qlen(sch) >= sch->limit) ||
392 unlikely((u64)q->memory_used + skb->truesize > q->memory_limit)) {
393 qdisc_qstats_overlimit(sch);
394 if (skb_in_l_queue(skb))
395 qdisc_qstats_overlimit(q->l_queue);
396 return qdisc_drop_reason(skb, sch, to_free,
397 SKB_DROP_REASON_QDISC_OVERLIMIT);
398 }
399
400 if (q->drop_early && must_drop(sch, q, skb)) {
401 qdisc_drop_reason(skb, sch, to_free,
402 SKB_DROP_REASON_QDISC_CONGESTED);
403 return NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
404 }
405
406 cb = dualpi2_skb_cb(skb);
407 cb->ts = ktime_get_ns();
408 q->memory_used += skb->truesize;
409 if (q->memory_used > q->max_memory_used)
410 q->max_memory_used = q->memory_used;
411
412 if (qdisc_qlen(sch) > q->maxq)
413 q->maxq = qdisc_qlen(sch);
414
415 if (skb_in_l_queue(skb)) {
416 /* Apply step thresh if skb is L4S && L-queue len >= min_qlen */
417 dualpi2_skb_cb(skb)->apply_step = skb_apply_step(skb, q);
418
419 /* Keep the overall qdisc stats consistent */
420 ++sch->q.qlen;
421 qdisc_qstats_backlog_inc(sch, skb);
422 ++q->packets_in_l;
423 if (!q->l_head_ts)
424 q->l_head_ts = cb->ts;
425 return qdisc_enqueue_tail(skb, q->l_queue);
426 }
427 ++q->packets_in_c;
428 if (!q->c_head_ts)
429 q->c_head_ts = cb->ts;
430 return qdisc_enqueue_tail(skb, sch);
431}
432
433/* By default, dualpi2 will split GSO skbs into independent skbs and enqueue
434 * each of those individually. This yields the following benefits, at the
435 * expense of CPU usage:
436 * - Finer-grained AQM actions as the sub-packets of a burst no longer share the
437 * same fate (e.g., the random mark/drop probability is applied individually)
438 * - Improved precision of the starvation protection/WRR scheduler at dequeue,
439 * as the size of the dequeued packets will be smaller.
440 */
441static int dualpi2_qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch,
442 struct sk_buff **to_free)
443{
444 struct dualpi2_sched_data *q = qdisc_priv(sch);
445 int err;
446
447 err = dualpi2_skb_classify(q, skb);
448 if (err != NET_XMIT_SUCCESS) {
449 if (err & __NET_XMIT_BYPASS)
450 qdisc_qstats_drop(sch);
451 __qdisc_drop(skb, to_free);
452 return err;
453 }
454
455 if (q->split_gso && skb_is_gso(skb)) {
456 netdev_features_t features;
457 struct sk_buff *nskb, *next;
458 int cnt, byte_len, orig_len;
459 int err;
460
461 features = netif_skb_features(skb);
462 nskb = skb_gso_segment(skb, features & ~NETIF_F_GSO_MASK);
463 if (IS_ERR_OR_NULL(nskb))
464 return qdisc_drop(skb, sch, to_free);
465
466 cnt = 1;
467 byte_len = 0;
468 orig_len = qdisc_pkt_len(skb);
469 skb_list_walk_safe(nskb, nskb, next) {
470 skb_mark_not_on_list(nskb);
471
472 /* Iterate through GSO fragments of an skb:
473 * (1) Set pkt_len from the single GSO fragments
474 * (2) Copy classified and ect values of an skb
475 * (3) Enqueue fragment & set ts in dualpi2_enqueue_skb
476 */
477 qdisc_skb_cb(nskb)->pkt_len = nskb->len;
478 qdisc_skb_cb(nskb)->pkt_segs = 1;
479 dualpi2_skb_cb(nskb)->classified =
480 dualpi2_skb_cb(skb)->classified;
481 dualpi2_skb_cb(nskb)->ect = dualpi2_skb_cb(skb)->ect;
482 err = dualpi2_enqueue_skb(nskb, sch, to_free);
483
484 if (err == NET_XMIT_SUCCESS) {
485 /* Compute the backlog adjustment that needs
486 * to be propagated in the qdisc tree to reflect
487 * all new skbs successfully enqueued.
488 */
489 ++cnt;
490 byte_len += nskb->len;
491 }
492 }
493 if (cnt > 1) {
494 /* The caller will add the original skb stats to its
495 * backlog, compensate this if any nskb is enqueued.
496 */
497 --cnt;
498 byte_len -= orig_len;
499 }
500 qdisc_tree_reduce_backlog(sch, -cnt, -byte_len);
501 consume_skb(skb);
502 return err;
503 }
504 return dualpi2_enqueue_skb(skb, sch, to_free);
505}
506
507/* Select the queue from which the next packet can be dequeued, ensuring that
508 * neither queue can starve the other with a WRR scheduler.
509 *
510 * The sign of the WRR credit determines the next queue, while the size of
511 * the dequeued packet determines the magnitude of the WRR credit change. If
512 * either queue is empty, the WRR credit is kept unchanged.
513 *
514 * As the dequeued packet can be dropped later, the caller has to perform the
515 * qdisc_bstats_update() calls.
516 */
517static struct sk_buff *dequeue_packet(struct Qdisc *sch,
518 struct dualpi2_sched_data *q,
519 int *credit_change,
520 u64 now)
521{
522 struct sk_buff *skb = NULL;
523 int c_len;
524
525 *credit_change = 0;
526 c_len = qdisc_qlen(sch) - qdisc_qlen(q->l_queue);
527 if (qdisc_qlen(q->l_queue) && (!c_len || q->c_protection_credit <= 0)) {
528 skb = __qdisc_dequeue_head(&q->l_queue->q);
529 WRITE_ONCE(q->l_head_ts, head_enqueue_time(q->l_queue));
530 if (c_len)
531 *credit_change = q->c_protection_wc;
532 qdisc_qstats_backlog_dec(q->l_queue, skb);
533
534 /* Keep the global queue size consistent */
535 --sch->q.qlen;
536 q->memory_used -= skb->truesize;
537 } else if (c_len) {
538 skb = __qdisc_dequeue_head(&sch->q);
539 WRITE_ONCE(q->c_head_ts, head_enqueue_time(sch));
540 if (qdisc_qlen(q->l_queue))
541 *credit_change = ~((s32)q->c_protection_wl) + 1;
542 q->memory_used -= skb->truesize;
543 } else {
544 dualpi2_reset_c_protection(q);
545 return NULL;
546 }
547 *credit_change *= qdisc_pkt_len(skb);
548 qdisc_qstats_backlog_dec(sch, skb);
549 return skb;
550}
551
552static int do_step_aqm(struct dualpi2_sched_data *q, struct sk_buff *skb,
553 u64 now)
554{
555 u64 qdelay = 0;
556
557 if (q->step_in_packets)
558 qdelay = qdisc_qlen(q->l_queue);
559 else
560 qdelay = dualpi2_sojourn_time(skb, now);
561
562 if (dualpi2_skb_cb(skb)->apply_step && qdelay > q->step_thresh) {
563 if (!dualpi2_skb_cb(skb)->ect) {
564 /* Drop this non-ECT packet */
565 return 1;
566 }
567
568 if (dualpi2_mark(q, skb))
569 ++q->step_marks;
570 }
571 qdisc_bstats_update(q->l_queue, skb);
572 return 0;
573}
574
575static void drop_and_retry(struct dualpi2_sched_data *q, struct sk_buff *skb,
576 struct Qdisc *sch, enum skb_drop_reason reason)
577{
578 ++q->deferred_drops_cnt;
579 q->deferred_drops_len += qdisc_pkt_len(skb);
580 kfree_skb_reason(skb, reason);
581 qdisc_qstats_drop(sch);
582}
583
584static struct sk_buff *dualpi2_qdisc_dequeue(struct Qdisc *sch)
585{
586 struct dualpi2_sched_data *q = qdisc_priv(sch);
587 struct sk_buff *skb;
588 int credit_change;
589 u64 now;
590
591 now = ktime_get_ns();
592
593 while ((skb = dequeue_packet(sch, q, &credit_change, now))) {
594 if (!q->drop_early && must_drop(sch, q, skb)) {
595 drop_and_retry(q, skb, sch,
596 SKB_DROP_REASON_QDISC_CONGESTED);
597 continue;
598 }
599
600 if (skb_in_l_queue(skb) && do_step_aqm(q, skb, now)) {
601 qdisc_qstats_drop(q->l_queue);
602 drop_and_retry(q, skb, sch,
603 SKB_DROP_REASON_DUALPI2_STEP_DROP);
604 continue;
605 }
606
607 q->c_protection_credit += credit_change;
608 qdisc_bstats_update(sch, skb);
609 break;
610 }
611
612 if (q->deferred_drops_cnt) {
613 qdisc_tree_reduce_backlog(sch, q->deferred_drops_cnt,
614 q->deferred_drops_len);
615 q->deferred_drops_cnt = 0;
616 q->deferred_drops_len = 0;
617 }
618 return skb;
619}
620
621static s64 __scale_delta(u64 diff)
622{
623 do_div(diff, 1 << ALPHA_BETA_GRANULARITY);
624 return diff;
625}
626
627static void get_queue_delays(struct dualpi2_sched_data *q, u64 *qdelay_c,
628 u64 *qdelay_l)
629{
630 u64 now, qc, ql;
631
632 now = ktime_get_ns();
633 qc = READ_ONCE(q->c_head_ts);
634 ql = READ_ONCE(q->l_head_ts);
635
636 *qdelay_c = qc ? now - qc : 0;
637 *qdelay_l = ql ? now - ql : 0;
638}
639
640static u32 calculate_probability(struct Qdisc *sch)
641{
642 struct dualpi2_sched_data *q = qdisc_priv(sch);
643 u32 new_prob;
644 u64 qdelay_c;
645 u64 qdelay_l;
646 u64 qdelay;
647 s64 delta;
648
649 get_queue_delays(q, &qdelay_c, &qdelay_l);
650 qdelay = max(qdelay_l, qdelay_c);
651
652 /* Alpha and beta take at most 32b, i.e, the delay difference would
653 * overflow for queuing delay differences > ~4.2sec.
654 */
655 delta = ((s64)qdelay - (s64)q->pi2_target) * q->pi2_alpha;
656 delta += ((s64)qdelay - (s64)q->last_qdelay) * q->pi2_beta;
657 q->last_qdelay = qdelay;
658
659 /* Bound new_prob between 0 and MAX_PROB */
660 if (delta > 0) {
661 new_prob = __scale_delta(delta) + q->pi2_prob;
662 if (new_prob < q->pi2_prob)
663 new_prob = MAX_PROB;
664 } else {
665 new_prob = q->pi2_prob - __scale_delta(~delta + 1);
666 if (new_prob > q->pi2_prob)
667 new_prob = 0;
668 }
669
670 /* If we do not drop on overload, ensure we cap the L4S probability to
671 * 100% to keep window fairness when overflowing.
672 */
673 if (!q->drop_overload)
674 return min_t(u32, new_prob, MAX_PROB / q->coupling_factor);
675 return new_prob;
676}
677
678static u32 get_memory_limit(struct Qdisc *sch, u32 limit)
679{
680 /* Apply rule of thumb, i.e., doubling the packet length,
681 * to further include per packet overhead in memory_limit.
682 */
683 u64 memlim = mul_u32_u32(limit, 2 * psched_mtu(qdisc_dev(sch)));
684
685 if (upper_32_bits(memlim))
686 return U32_MAX;
687 else
688 return lower_32_bits(memlim);
689}
690
691static u32 convert_us_to_nsec(u32 us)
692{
693 u64 ns = mul_u32_u32(us, NSEC_PER_USEC);
694
695 if (upper_32_bits(ns))
696 return U32_MAX;
697
698 return lower_32_bits(ns);
699}
700
701static u32 convert_ns_to_usec(u64 ns)
702{
703 do_div(ns, NSEC_PER_USEC);
704 if (upper_32_bits(ns))
705 return U32_MAX;
706
707 return lower_32_bits(ns);
708}
709
710static enum hrtimer_restart dualpi2_timer(struct hrtimer *timer)
711{
712 struct dualpi2_sched_data *q = timer_container_of(q, timer, pi2_timer);
713 struct Qdisc *sch = q->sch;
714 spinlock_t *root_lock; /* to lock qdisc for probability calculations */
715
716 rcu_read_lock();
717 root_lock = qdisc_lock(qdisc_root_sleeping(sch));
718 spin_lock(root_lock);
719
720 WRITE_ONCE(q->pi2_prob, calculate_probability(sch));
721 hrtimer_set_expires(&q->pi2_timer, next_pi2_timeout(q));
722
723 spin_unlock(root_lock);
724 rcu_read_unlock();
725 return HRTIMER_RESTART;
726}
727
728static struct netlink_range_validation dualpi2_alpha_beta_range = {
729 .min = 1,
730 .max = ALPHA_BETA_MAX,
731};
732
733static const struct nla_policy dualpi2_policy[TCA_DUALPI2_MAX + 1] = {
734 [TCA_DUALPI2_LIMIT] = NLA_POLICY_MIN(NLA_U32, 1),
735 [TCA_DUALPI2_MEMORY_LIMIT] = NLA_POLICY_MIN(NLA_U32, 1),
736 [TCA_DUALPI2_TARGET] = { .type = NLA_U32 },
737 [TCA_DUALPI2_TUPDATE] = NLA_POLICY_MIN(NLA_U32, 1),
738 [TCA_DUALPI2_ALPHA] =
739 NLA_POLICY_FULL_RANGE(NLA_U32, &dualpi2_alpha_beta_range),
740 [TCA_DUALPI2_BETA] =
741 NLA_POLICY_FULL_RANGE(NLA_U32, &dualpi2_alpha_beta_range),
742 [TCA_DUALPI2_STEP_THRESH_PKTS] = { .type = NLA_U32 },
743 [TCA_DUALPI2_STEP_THRESH_US] = { .type = NLA_U32 },
744 [TCA_DUALPI2_MIN_QLEN_STEP] = { .type = NLA_U32 },
745 [TCA_DUALPI2_COUPLING] = NLA_POLICY_MIN(NLA_U8, 1),
746 [TCA_DUALPI2_DROP_OVERLOAD] =
747 NLA_POLICY_MAX(NLA_U8, TCA_DUALPI2_DROP_OVERLOAD_MAX),
748 [TCA_DUALPI2_DROP_EARLY] =
749 NLA_POLICY_MAX(NLA_U8, TCA_DUALPI2_DROP_EARLY_MAX),
750 [TCA_DUALPI2_C_PROTECTION] =
751 NLA_POLICY_RANGE(NLA_U8, 0, MAX_WC),
752 [TCA_DUALPI2_ECN_MASK] =
753 NLA_POLICY_RANGE(NLA_U8, TC_DUALPI2_ECN_MASK_L4S_ECT,
754 TCA_DUALPI2_ECN_MASK_MAX),
755 [TCA_DUALPI2_SPLIT_GSO] =
756 NLA_POLICY_MAX(NLA_U8, TCA_DUALPI2_SPLIT_GSO_MAX),
757};
758
759static int dualpi2_change(struct Qdisc *sch, struct nlattr *opt,
760 struct netlink_ext_ack *extack)
761{
762 struct nlattr *tb[TCA_DUALPI2_MAX + 1];
763 struct dualpi2_sched_data *q;
764 int old_backlog;
765 int old_qlen;
766 int err;
767
768 if (!opt || !nla_len(opt)) {
769 NL_SET_ERR_MSG_MOD(extack, "Dualpi2 options are required");
770 return -EINVAL;
771 }
772 err = nla_parse_nested(tb, TCA_DUALPI2_MAX, opt, dualpi2_policy,
773 extack);
774 if (err < 0)
775 return err;
776 if (tb[TCA_DUALPI2_STEP_THRESH_PKTS] && tb[TCA_DUALPI2_STEP_THRESH_US]) {
777 NL_SET_ERR_MSG_MOD(extack, "multiple step thresh attributes");
778 return -EINVAL;
779 }
780
781 q = qdisc_priv(sch);
782 sch_tree_lock(sch);
783
784 if (tb[TCA_DUALPI2_LIMIT]) {
785 u32 limit = nla_get_u32(tb[TCA_DUALPI2_LIMIT]);
786
787 WRITE_ONCE(sch->limit, limit);
788 WRITE_ONCE(q->memory_limit, get_memory_limit(sch, limit));
789 }
790
791 if (tb[TCA_DUALPI2_MEMORY_LIMIT])
792 WRITE_ONCE(q->memory_limit,
793 nla_get_u32(tb[TCA_DUALPI2_MEMORY_LIMIT]));
794
795 if (tb[TCA_DUALPI2_TARGET]) {
796 u64 target = nla_get_u32(tb[TCA_DUALPI2_TARGET]);
797
798 WRITE_ONCE(q->pi2_target, target * NSEC_PER_USEC);
799 }
800
801 if (tb[TCA_DUALPI2_TUPDATE]) {
802 u64 tupdate = nla_get_u32(tb[TCA_DUALPI2_TUPDATE]);
803
804 WRITE_ONCE(q->pi2_tupdate, convert_us_to_nsec(tupdate));
805 }
806
807 if (tb[TCA_DUALPI2_ALPHA]) {
808 u32 alpha = nla_get_u32(tb[TCA_DUALPI2_ALPHA]);
809
810 WRITE_ONCE(q->pi2_alpha, dualpi2_scale_alpha_beta(alpha));
811 }
812
813 if (tb[TCA_DUALPI2_BETA]) {
814 u32 beta = nla_get_u32(tb[TCA_DUALPI2_BETA]);
815
816 WRITE_ONCE(q->pi2_beta, dualpi2_scale_alpha_beta(beta));
817 }
818
819 if (tb[TCA_DUALPI2_STEP_THRESH_PKTS]) {
820 u32 step_th = nla_get_u32(tb[TCA_DUALPI2_STEP_THRESH_PKTS]);
821
822 WRITE_ONCE(q->step_in_packets, true);
823 WRITE_ONCE(q->step_thresh, step_th);
824 } else if (tb[TCA_DUALPI2_STEP_THRESH_US]) {
825 u32 step_th = nla_get_u32(tb[TCA_DUALPI2_STEP_THRESH_US]);
826
827 WRITE_ONCE(q->step_in_packets, false);
828 WRITE_ONCE(q->step_thresh, convert_us_to_nsec(step_th));
829 }
830
831 if (tb[TCA_DUALPI2_MIN_QLEN_STEP])
832 WRITE_ONCE(q->min_qlen_step,
833 nla_get_u32(tb[TCA_DUALPI2_MIN_QLEN_STEP]));
834
835 if (tb[TCA_DUALPI2_COUPLING]) {
836 u8 coupling = nla_get_u8(tb[TCA_DUALPI2_COUPLING]);
837
838 WRITE_ONCE(q->coupling_factor, coupling);
839 }
840
841 if (tb[TCA_DUALPI2_DROP_OVERLOAD]) {
842 u8 drop_overload = nla_get_u8(tb[TCA_DUALPI2_DROP_OVERLOAD]);
843
844 WRITE_ONCE(q->drop_overload, (bool)drop_overload);
845 }
846
847 if (tb[TCA_DUALPI2_DROP_EARLY]) {
848 u8 drop_early = nla_get_u8(tb[TCA_DUALPI2_DROP_EARLY]);
849
850 WRITE_ONCE(q->drop_early, (bool)drop_early);
851 }
852
853 if (tb[TCA_DUALPI2_C_PROTECTION]) {
854 u8 wc = nla_get_u8(tb[TCA_DUALPI2_C_PROTECTION]);
855
856 dualpi2_calculate_c_protection(sch, q, wc);
857 }
858
859 if (tb[TCA_DUALPI2_ECN_MASK]) {
860 u8 ecn_mask = nla_get_u8(tb[TCA_DUALPI2_ECN_MASK]);
861
862 WRITE_ONCE(q->ecn_mask, ecn_mask);
863 }
864
865 if (tb[TCA_DUALPI2_SPLIT_GSO]) {
866 u8 split_gso = nla_get_u8(tb[TCA_DUALPI2_SPLIT_GSO]);
867
868 WRITE_ONCE(q->split_gso, (bool)split_gso);
869 }
870
871 old_qlen = qdisc_qlen(sch);
872 old_backlog = sch->qstats.backlog;
873 while (qdisc_qlen(sch) > sch->limit ||
874 q->memory_used > q->memory_limit) {
875 struct sk_buff *skb = qdisc_dequeue_internal(sch, true);
876
877 q->memory_used -= skb->truesize;
878 qdisc_qstats_backlog_dec(sch, skb);
879 rtnl_qdisc_drop(skb, sch);
880 }
881 qdisc_tree_reduce_backlog(sch, old_qlen - qdisc_qlen(sch),
882 old_backlog - sch->qstats.backlog);
883
884 sch_tree_unlock(sch);
885 return 0;
886}
887
888/* Default alpha/beta values give a 10dB stability margin with max_rtt=100ms. */
889static void dualpi2_reset_default(struct Qdisc *sch)
890{
891 struct dualpi2_sched_data *q = qdisc_priv(sch);
892
893 q->sch->limit = 10000; /* Max 125ms at 1Gbps */
894 q->memory_limit = get_memory_limit(sch, q->sch->limit);
895
896 q->pi2_target = 15 * NSEC_PER_MSEC;
897 q->pi2_tupdate = 16 * NSEC_PER_MSEC;
898 q->pi2_alpha = dualpi2_scale_alpha_beta(41); /* ~0.16 Hz * 256 */
899 q->pi2_beta = dualpi2_scale_alpha_beta(819); /* ~3.20 Hz * 256 */
900
901 q->step_thresh = 1 * NSEC_PER_MSEC;
902 q->step_in_packets = false;
903
904 dualpi2_calculate_c_protection(q->sch, q, 10); /* wc=10%, wl=90% */
905
906 q->ecn_mask = TC_DUALPI2_ECN_MASK_L4S_ECT; /* INET_ECN_ECT_1 */
907 q->min_qlen_step = 0; /* Always apply step mark in L-queue */
908 q->coupling_factor = 2; /* window fairness for equal RTTs */
909 q->drop_overload = TC_DUALPI2_DROP_OVERLOAD_DROP; /* Drop overload */
910 q->drop_early = TC_DUALPI2_DROP_EARLY_DROP_DEQUEUE; /* Drop dequeue */
911 q->split_gso = TC_DUALPI2_SPLIT_GSO_SPLIT_GSO; /* Split GSO */
912}
913
914static int dualpi2_init(struct Qdisc *sch, struct nlattr *opt,
915 struct netlink_ext_ack *extack)
916{
917 struct dualpi2_sched_data *q = qdisc_priv(sch);
918 int err;
919
920 q->l_queue = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops,
921 TC_H_MAKE(sch->handle, 1), extack);
922 if (!q->l_queue)
923 return -ENOMEM;
924
925 err = tcf_block_get(&q->tcf_block, &q->tcf_filters, sch, extack);
926 if (err)
927 return err;
928
929 q->sch = sch;
930 dualpi2_reset_default(sch);
931 hrtimer_setup(&q->pi2_timer, dualpi2_timer, CLOCK_MONOTONIC,
932 HRTIMER_MODE_ABS_PINNED_SOFT);
933
934 if (opt && nla_len(opt)) {
935 err = dualpi2_change(sch, opt, extack);
936
937 if (err)
938 return err;
939 }
940
941 hrtimer_start(&q->pi2_timer, next_pi2_timeout(q),
942 HRTIMER_MODE_ABS_PINNED_SOFT);
943 return 0;
944}
945
946static int dualpi2_dump(struct Qdisc *sch, struct sk_buff *skb)
947{
948 struct dualpi2_sched_data *q = qdisc_priv(sch);
949 struct nlattr *opts;
950 bool step_in_pkts;
951 u32 step_th;
952
953 step_in_pkts = READ_ONCE(q->step_in_packets);
954 step_th = READ_ONCE(q->step_thresh);
955
956 opts = nla_nest_start_noflag(skb, TCA_OPTIONS);
957 if (!opts)
958 goto nla_put_failure;
959
960 if (step_in_pkts &&
961 (nla_put_u32(skb, TCA_DUALPI2_LIMIT, READ_ONCE(sch->limit)) ||
962 nla_put_u32(skb, TCA_DUALPI2_MEMORY_LIMIT,
963 READ_ONCE(q->memory_limit)) ||
964 nla_put_u32(skb, TCA_DUALPI2_TARGET,
965 convert_ns_to_usec(READ_ONCE(q->pi2_target))) ||
966 nla_put_u32(skb, TCA_DUALPI2_TUPDATE,
967 convert_ns_to_usec(READ_ONCE(q->pi2_tupdate))) ||
968 nla_put_u32(skb, TCA_DUALPI2_ALPHA,
969 dualpi2_unscale_alpha_beta(READ_ONCE(q->pi2_alpha))) ||
970 nla_put_u32(skb, TCA_DUALPI2_BETA,
971 dualpi2_unscale_alpha_beta(READ_ONCE(q->pi2_beta))) ||
972 nla_put_u32(skb, TCA_DUALPI2_STEP_THRESH_PKTS, step_th) ||
973 nla_put_u32(skb, TCA_DUALPI2_MIN_QLEN_STEP,
974 READ_ONCE(q->min_qlen_step)) ||
975 nla_put_u8(skb, TCA_DUALPI2_COUPLING,
976 READ_ONCE(q->coupling_factor)) ||
977 nla_put_u8(skb, TCA_DUALPI2_DROP_OVERLOAD,
978 READ_ONCE(q->drop_overload)) ||
979 nla_put_u8(skb, TCA_DUALPI2_DROP_EARLY,
980 READ_ONCE(q->drop_early)) ||
981 nla_put_u8(skb, TCA_DUALPI2_C_PROTECTION,
982 READ_ONCE(q->c_protection_wc)) ||
983 nla_put_u8(skb, TCA_DUALPI2_ECN_MASK, READ_ONCE(q->ecn_mask)) ||
984 nla_put_u8(skb, TCA_DUALPI2_SPLIT_GSO, READ_ONCE(q->split_gso))))
985 goto nla_put_failure;
986
987 if (!step_in_pkts &&
988 (nla_put_u32(skb, TCA_DUALPI2_LIMIT, READ_ONCE(sch->limit)) ||
989 nla_put_u32(skb, TCA_DUALPI2_MEMORY_LIMIT,
990 READ_ONCE(q->memory_limit)) ||
991 nla_put_u32(skb, TCA_DUALPI2_TARGET,
992 convert_ns_to_usec(READ_ONCE(q->pi2_target))) ||
993 nla_put_u32(skb, TCA_DUALPI2_TUPDATE,
994 convert_ns_to_usec(READ_ONCE(q->pi2_tupdate))) ||
995 nla_put_u32(skb, TCA_DUALPI2_ALPHA,
996 dualpi2_unscale_alpha_beta(READ_ONCE(q->pi2_alpha))) ||
997 nla_put_u32(skb, TCA_DUALPI2_BETA,
998 dualpi2_unscale_alpha_beta(READ_ONCE(q->pi2_beta))) ||
999 nla_put_u32(skb, TCA_DUALPI2_STEP_THRESH_US,
1000 convert_ns_to_usec(step_th)) ||
1001 nla_put_u32(skb, TCA_DUALPI2_MIN_QLEN_STEP,
1002 READ_ONCE(q->min_qlen_step)) ||
1003 nla_put_u8(skb, TCA_DUALPI2_COUPLING,
1004 READ_ONCE(q->coupling_factor)) ||
1005 nla_put_u8(skb, TCA_DUALPI2_DROP_OVERLOAD,
1006 READ_ONCE(q->drop_overload)) ||
1007 nla_put_u8(skb, TCA_DUALPI2_DROP_EARLY,
1008 READ_ONCE(q->drop_early)) ||
1009 nla_put_u8(skb, TCA_DUALPI2_C_PROTECTION,
1010 READ_ONCE(q->c_protection_wc)) ||
1011 nla_put_u8(skb, TCA_DUALPI2_ECN_MASK, READ_ONCE(q->ecn_mask)) ||
1012 nla_put_u8(skb, TCA_DUALPI2_SPLIT_GSO, READ_ONCE(q->split_gso))))
1013 goto nla_put_failure;
1014
1015 return nla_nest_end(skb, opts);
1016
1017nla_put_failure:
1018 nla_nest_cancel(skb, opts);
1019 return -1;
1020}
1021
1022static int dualpi2_dump_stats(struct Qdisc *sch, struct gnet_dump *d)
1023{
1024 struct dualpi2_sched_data *q = qdisc_priv(sch);
1025 struct tc_dualpi2_xstats st = {
1026 .prob = READ_ONCE(q->pi2_prob),
1027 .packets_in_c = q->packets_in_c,
1028 .packets_in_l = q->packets_in_l,
1029 .maxq = q->maxq,
1030 .ecn_mark = q->ecn_mark,
1031 .credit = q->c_protection_credit,
1032 .step_marks = q->step_marks,
1033 .memory_used = q->memory_used,
1034 .max_memory_used = q->max_memory_used,
1035 .memory_limit = q->memory_limit,
1036 };
1037 u64 qc, ql;
1038
1039 get_queue_delays(q, &qc, &ql);
1040 st.delay_l = convert_ns_to_usec(ql);
1041 st.delay_c = convert_ns_to_usec(qc);
1042 return gnet_stats_copy_app(d, &st, sizeof(st));
1043}
1044
1045/* Reset both L-queue and C-queue, internal packet counters, PI probability,
1046 * C-queue protection credit, and timestamps, while preserving current
1047 * configuration of DUALPI2.
1048 */
1049static void dualpi2_reset(struct Qdisc *sch)
1050{
1051 struct dualpi2_sched_data *q = qdisc_priv(sch);
1052
1053 qdisc_reset_queue(sch);
1054 qdisc_reset_queue(q->l_queue);
1055 q->c_head_ts = 0;
1056 q->l_head_ts = 0;
1057 q->pi2_prob = 0;
1058 q->packets_in_c = 0;
1059 q->packets_in_l = 0;
1060 q->maxq = 0;
1061 q->ecn_mark = 0;
1062 q->step_marks = 0;
1063 q->memory_used = 0;
1064 q->max_memory_used = 0;
1065 dualpi2_reset_c_protection(q);
1066}
1067
1068static void dualpi2_destroy(struct Qdisc *sch)
1069{
1070 struct dualpi2_sched_data *q = qdisc_priv(sch);
1071
1072 q->pi2_tupdate = 0;
1073 hrtimer_cancel(&q->pi2_timer);
1074 if (q->l_queue)
1075 qdisc_put(q->l_queue);
1076 tcf_block_put(q->tcf_block);
1077}
1078
1079static struct Qdisc *dualpi2_leaf(struct Qdisc *sch, unsigned long arg)
1080{
1081 return NULL;
1082}
1083
1084static unsigned long dualpi2_find(struct Qdisc *sch, u32 classid)
1085{
1086 return 0;
1087}
1088
1089static unsigned long dualpi2_bind(struct Qdisc *sch, unsigned long parent,
1090 u32 classid)
1091{
1092 return 0;
1093}
1094
1095static void dualpi2_unbind(struct Qdisc *q, unsigned long cl)
1096{
1097}
1098
1099static struct tcf_block *dualpi2_tcf_block(struct Qdisc *sch, unsigned long cl,
1100 struct netlink_ext_ack *extack)
1101{
1102 struct dualpi2_sched_data *q = qdisc_priv(sch);
1103
1104 if (cl)
1105 return NULL;
1106 return q->tcf_block;
1107}
1108
1109static void dualpi2_walk(struct Qdisc *sch, struct qdisc_walker *arg)
1110{
1111 unsigned int i;
1112
1113 if (arg->stop)
1114 return;
1115
1116 /* We statically define only 2 queues */
1117 for (i = 0; i < 2; i++) {
1118 if (arg->count < arg->skip) {
1119 arg->count++;
1120 continue;
1121 }
1122 if (arg->fn(sch, i + 1, arg) < 0) {
1123 arg->stop = 1;
1124 break;
1125 }
1126 arg->count++;
1127 }
1128}
1129
1130/* Minimal class support to handle tc filters */
1131static const struct Qdisc_class_ops dualpi2_class_ops = {
1132 .leaf = dualpi2_leaf,
1133 .find = dualpi2_find,
1134 .tcf_block = dualpi2_tcf_block,
1135 .bind_tcf = dualpi2_bind,
1136 .unbind_tcf = dualpi2_unbind,
1137 .walk = dualpi2_walk,
1138};
1139
1140static struct Qdisc_ops dualpi2_qdisc_ops __read_mostly = {
1141 .id = "dualpi2",
1142 .cl_ops = &dualpi2_class_ops,
1143 .priv_size = sizeof(struct dualpi2_sched_data),
1144 .enqueue = dualpi2_qdisc_enqueue,
1145 .dequeue = dualpi2_qdisc_dequeue,
1146 .peek = qdisc_peek_dequeued,
1147 .init = dualpi2_init,
1148 .destroy = dualpi2_destroy,
1149 .reset = dualpi2_reset,
1150 .change = dualpi2_change,
1151 .dump = dualpi2_dump,
1152 .dump_stats = dualpi2_dump_stats,
1153 .owner = THIS_MODULE,
1154};
1155
1156static int __init dualpi2_module_init(void)
1157{
1158 return register_qdisc(&dualpi2_qdisc_ops);
1159}
1160
1161static void __exit dualpi2_module_exit(void)
1162{
1163 unregister_qdisc(&dualpi2_qdisc_ops);
1164}
1165
1166module_init(dualpi2_module_init);
1167module_exit(dualpi2_module_exit);
1168
1169MODULE_DESCRIPTION("Dual Queue with Proportional Integral controller Improved with a Square (dualpi2) scheduler");
1170MODULE_AUTHOR("Koen De Schepper <koen.de_schepper@nokia-bell-labs.com>");
1171MODULE_AUTHOR("Chia-Yu Chang <chia-yu.chang@nokia-bell-labs.com>");
1172MODULE_AUTHOR("Olga Albisser <olga@albisser.org>");
1173MODULE_AUTHOR("Henrik Steen <henrist@henrist.net>");
1174MODULE_AUTHOR("Olivier Tilmans <olivier.tilmans@nokia.com>");
1175
1176MODULE_LICENSE("Dual BSD/GPL");
1177MODULE_VERSION("1.0");