Merge branch 'dualpi2-patch'

Chia-Yu Chang says:

====================
DUALPI2 patch

This patch serise adds DualPI Improved with a Square (DualPI2) with
following features:
 * Supports congestion controls that comply with the Prague requirements
   in RFC9331 (e.g. TCP-Prague)
 * Coupled dual-queue that separates the L4S traffic in a low latency
   queue (L-queue), without harming remaining traffic that is scheduled
   in classic queue (C-queue) due to congestion-coupling using PI2
   as defined in RFC9332
 * Configurable overload strategies
 * Use of sojourn time to reliably estimate queue delay
 * Supports ECN L4S-identifier (IP.ECN==0b*1) to classify traffic into
   respective queues

For more details of DualPI2, please refer IETF RFC9332
(https://datatracker.ietf.org/doc/html/rfc9332).
====================

Link: https://patch.msgid.link/20250722095915.24485-1-chia-yu.chang@nokia-bell-labs.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2025-07-23 17:52:10 -07:00
commit 8aad37d16c
9 changed files with 1669 additions and 5 deletions

View file

@ -56,6 +56,23 @@ definitions:
- tundf
- tunoam
- tuncrit
-
name: dualpi2-drop-overload
type: enum
entries: [overflow, drop]
-
name: dualpi2-drop-early
type: enum
entries: [drop-dequeue, drop-enqueue]
-
name: dualpi2-ecn-mask
type: enum
value-start: 1
entries: [l4s-ect, cla-ect, any-ect]
-
name: dualpi2-split-gso
type: enum
entries: [no-split-gso, split-gso]
-
name: tc-stats
type: struct
@ -825,6 +842,58 @@ definitions:
-
name: drop-overmemory
type: u32
-
name: tc-dualpi2-xstats
type: struct
members:
-
name: prob
type: u32
doc: Current base PI probability
-
name: delay-c
type: u32
doc: Current C-queue delay in microseconds
-
name: delay-l
type: u32
doc: Current L-queue delay in microseconds
-
name: pkts-in-c
type: u32
doc: Number of packets enqueued in the C-queue
-
name: pkts-in-l
type: u32
doc: Number of packets enqueued in the L-queue
-
name: maxq
type: u32
doc: Maximum number of packets seen by the DualPI2
-
name: ecn-mark
type: u32
doc: All packets marked with ECN
-
name: step-mark
type: u32
doc: Only packets marked with ECN due to L-queue step AQM
-
name: credit
type: s32
doc: Current credit value for WRR
-
name: memory-used
type: u32
doc: Memory used in bytes by the DualPI2
-
name: max-memory-used
type: u32
doc: Maximum memory used in bytes by the DualPI2
-
name: memory-limit
type: u32
doc: Memory limit in bytes
-
name: tc-fq-pie-xstats
type: struct
@ -848,7 +917,7 @@ definitions:
-
name: ecn-mark
type: u32
doc: Packets marked with ecn
doc: Packets marked with ECN
-
name: new-flow-count
type: u32
@ -991,7 +1060,7 @@ definitions:
-
name: ecn-mark
type: u32
doc: Packets marked with ecn
doc: Packets marked with ECN
-
name: tc-red-xstats
type: struct
@ -2284,6 +2353,78 @@ attribute-sets:
-
name: quantum
type: u32
-
name: dualpi2-attrs
name-prefix: tca-dualpi2-
attributes:
-
name: limit
type: u32
doc: Limit of total number of packets in queue
-
name: memory-limit
type: u32
doc: Memory limit of total number of packets in queue
-
name: target
type: u32
doc: Classic target delay in microseconds
-
name: tupdate
type: u32
doc: Drop probability update interval time in microseconds
-
name: alpha
type: u32
doc: Integral gain factor in Hz for PI controller
-
name: beta
type: u32
doc: Proportional gain factor in Hz for PI controller
-
name: step-thresh-pkts
type: u32
doc: L4S step marking threshold in packets
-
name: step-thresh-us
type: u32
doc: L4S Step marking threshold in microseconds
-
name: min-qlen-step
type: u32
doc: Packets enqueued to the L-queue can apply the step threshold
when the queue length of L-queue is larger than this value.
(0 is recommended)
-
name: coupling
type: u8
doc: Probability coupling factor between Classic and L4S
(2 is recommended)
-
name: drop-overload
type: u8
doc: Control the overload strategy (drop to preserve latency or
let the queue overflow)
enum: dualpi2-drop-overload
-
name: drop-early
type: u8
doc: Decide where the Classic packets are PI-based dropped or marked
enum: dualpi2-drop-early
-
name: c-protection
type: u8
doc: Classic WRR weight in percentage (from 0 to 100)
-
name: ecn-mask
type: u8
doc: Configure the L-queue ECN classifier
enum: dualpi2-ecn-mask
-
name: split-gso
type: u8
doc: Split aggregated skb or not
enum: dualpi2-split-gso
-
name: ematch-attrs
name-prefix: tca-ematch-
@ -3708,6 +3849,9 @@ sub-messages:
-
value: drr
attribute-set: drr-attrs
-
value: dualpi2
attribute-set: dualpi2-attrs
-
value: etf
attribute-set: etf-attrs
@ -3875,6 +4019,9 @@ sub-messages:
-
value: codel
fixed-header: tc-codel-xstats
-
value: dualpi2
fixed-header: tc-dualpi2-xstats
-
value: fq
fixed-header: tc-fq-qd-stats

View file

@ -126,6 +126,7 @@
FN(CANFD_RX_INVALID_FRAME) \
FN(CANXL_RX_INVALID_FRAME) \
FN(PFMEMALLOC) \
FN(DUALPI2_STEP_DROP) \
FNe(MAX)
/**
@ -604,6 +605,11 @@ enum skb_drop_reason {
* reached a path or socket not eligible for use of memory reserves
*/
SKB_DROP_REASON_PFMEMALLOC,
/**
* @SKB_DROP_REASON_DUALPI2_STEP_DROP: dropped by the step drop
* threshold of DualPI2 qdisc.
*/
SKB_DROP_REASON_DUALPI2_STEP_DROP,
/**
* @SKB_DROP_REASON_MAX: the maximum of core drop reasons, which
* shouldn't be used as a real 'reason' - only for tracing code gen

View file

@ -1211,4 +1211,72 @@ enum {
#define TCA_ETS_MAX (__TCA_ETS_MAX - 1)
/* DUALPI2 */
enum tc_dualpi2_drop_overload {
TC_DUALPI2_DROP_OVERLOAD_OVERFLOW = 0,
TC_DUALPI2_DROP_OVERLOAD_DROP = 1,
__TCA_DUALPI2_DROP_OVERLOAD_MAX,
};
#define TCA_DUALPI2_DROP_OVERLOAD_MAX (__TCA_DUALPI2_DROP_OVERLOAD_MAX - 1)
enum tc_dualpi2_drop_early {
TC_DUALPI2_DROP_EARLY_DROP_DEQUEUE = 0,
TC_DUALPI2_DROP_EARLY_DROP_ENQUEUE = 1,
__TCA_DUALPI2_DROP_EARLY_MAX,
};
#define TCA_DUALPI2_DROP_EARLY_MAX (__TCA_DUALPI2_DROP_EARLY_MAX - 1)
enum tc_dualpi2_ecn_mask {
TC_DUALPI2_ECN_MASK_L4S_ECT = 1,
TC_DUALPI2_ECN_MASK_CLA_ECT = 2,
TC_DUALPI2_ECN_MASK_ANY_ECT = 3,
__TCA_DUALPI2_ECN_MASK_MAX,
};
#define TCA_DUALPI2_ECN_MASK_MAX (__TCA_DUALPI2_ECN_MASK_MAX - 1)
enum tc_dualpi2_split_gso {
TC_DUALPI2_SPLIT_GSO_NO_SPLIT_GSO = 0,
TC_DUALPI2_SPLIT_GSO_SPLIT_GSO = 1,
__TCA_DUALPI2_SPLIT_GSO_MAX,
};
#define TCA_DUALPI2_SPLIT_GSO_MAX (__TCA_DUALPI2_SPLIT_GSO_MAX - 1)
enum {
TCA_DUALPI2_UNSPEC,
TCA_DUALPI2_LIMIT, /* Packets */
TCA_DUALPI2_MEMORY_LIMIT, /* Bytes */
TCA_DUALPI2_TARGET, /* us */
TCA_DUALPI2_TUPDATE, /* us */
TCA_DUALPI2_ALPHA, /* Hz scaled up by 256 */
TCA_DUALPI2_BETA, /* Hz scaled up by 256 */
TCA_DUALPI2_STEP_THRESH_PKTS, /* Step threshold in packets */
TCA_DUALPI2_STEP_THRESH_US, /* Step threshold in microseconds */
TCA_DUALPI2_MIN_QLEN_STEP, /* Minimum qlen to apply STEP_THRESH */
TCA_DUALPI2_COUPLING, /* Coupling factor between queues */
TCA_DUALPI2_DROP_OVERLOAD, /* Whether to drop on overload */
TCA_DUALPI2_DROP_EARLY, /* Whether to drop on enqueue */
TCA_DUALPI2_C_PROTECTION, /* Percentage */
TCA_DUALPI2_ECN_MASK, /* L4S queue classification mask */
TCA_DUALPI2_SPLIT_GSO, /* Split GSO packets at enqueue */
TCA_DUALPI2_PAD,
__TCA_DUALPI2_MAX
};
#define TCA_DUALPI2_MAX (__TCA_DUALPI2_MAX - 1)
struct tc_dualpi2_xstats {
__u32 prob; /* current probability */
__u32 delay_c; /* current delay in C queue */
__u32 delay_l; /* current delay in L queue */
__u32 packets_in_c; /* number of packets enqueued in C queue */
__u32 packets_in_l; /* number of packets enqueued in L queue */
__u32 maxq; /* maximum queue size */
__u32 ecn_mark; /* packets marked with ecn*/
__u32 step_marks; /* ECN marks due to the step AQM */
__s32 credit; /* current c_protection credit */
__u32 memory_used; /* Memory used by both queues */
__u32 max_memory_used; /* Maximum used memory */
__u32 memory_limit; /* Memory limit of both queues */
};
#endif

View file

@ -415,6 +415,18 @@ config NET_SCH_BPF
If unsure, say N.
config NET_SCH_DUALPI2
tristate "Dual Queue PI Square (DUALPI2) scheduler"
help
Say Y here if you want to use the Dual Queue Proportional Integral
Controller Improved with a Square scheduling algorithm.
For more information, please see https://tools.ietf.org/html/rfc9332
To compile this driver as a module, choose M here: the module
will be called sch_dualpi2.
If unsure, say N.
menuconfig NET_SCH_DEFAULT
bool "Allow override default queue discipline"
help

View file

@ -63,6 +63,7 @@ obj-$(CONFIG_NET_SCH_CBS) += sch_cbs.o
obj-$(CONFIG_NET_SCH_ETF) += sch_etf.o
obj-$(CONFIG_NET_SCH_TAPRIO) += sch_taprio.o
obj-$(CONFIG_NET_SCH_BPF) += bpf_qdisc.o
obj-$(CONFIG_NET_SCH_DUALPI2) += sch_dualpi2.o
obj-$(CONFIG_NET_CLS_U32) += cls_u32.o
obj-$(CONFIG_NET_CLS_ROUTE4) += cls_route.o

1175
net/sched/sch_dualpi2.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -31,6 +31,7 @@ CONFIG_NET_SCH_CBS=m
CONFIG_NET_SCH_CHOKE=m
CONFIG_NET_SCH_CODEL=m
CONFIG_NET_SCH_DRR=m
CONFIG_NET_SCH_DUALPI2=m
CONFIG_NET_SCH_ETF=m
CONFIG_NET_SCH_FQ=m
CONFIG_NET_SCH_FQ_CODEL=m

View file

@ -0,0 +1,254 @@
[
{
"id": "a4c7",
"name": "Create DualPI2 with default setting",
"category": [
"qdisc",
"dualpi2"
],
"plugins": {
"requires": "nsPlugin"
},
"setup": [
],
"cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root dualpi2",
"expExitCode": "0",
"verifyCmd": "$TC qdisc show dev $DUMMY",
"matchPattern": "qdisc dualpi2 1: root refcnt [0-9]+ limit 10000p.* step_thresh 1ms min_qlen_step 0p coupling_factor 2 drop_on_overload drop_dequeue classic_protection 10% l4s_ect split_gso",
"matchCount": "1",
"teardown": [
"$TC qdisc del dev $DUMMY handle 1: root"
]
},
{
"id": "1ea4",
"name": "Create DualPI2 with memlimit",
"category": [
"qdisc",
"dualpi2"
],
"plugins": {
"requires": "nsPlugin"
},
"setup": [
],
"cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root dualpi2 memlimit 20000000",
"expExitCode": "0",
"verifyCmd": "$TC qdisc show dev $DUMMY",
"matchPattern": "qdisc dualpi2 1: root refcnt [0-9]+ limit 10000p.* memlimit 20000000B",
"matchCount": "1",
"teardown": [
"$TC qdisc del dev $DUMMY handle 1: root"
]
},
{
"id": "2130",
"name": "Create DualPI2 with typical_rtt and max_rtt",
"category": [
"qdisc",
"dualpi2"
],
"plugins": {
"requires": "nsPlugin"
},
"setup": [
],
"cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root dualpi2 typical_rtt 20ms max_rtt 200ms",
"expExitCode": "0",
"verifyCmd": "$TC qdisc show dev $DUMMY",
"matchPattern": "qdisc dualpi2 1: root refcnt [0-9]+ limit 10000p.* target 20ms tupdate 20ms alpha 0.042969 beta 1.496094",
"matchCount": "1",
"teardown": [
"$TC qdisc del dev $DUMMY handle 1: root"
]
},
{
"id": "90c1",
"name": "Create DualPI2 with max_rtt",
"category": [
"qdisc",
"dualpi2"
],
"plugins": {
"requires": "nsPlugin"
},
"setup": [
],
"cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root dualpi2 max_rtt 300ms",
"expExitCode": "0",
"verifyCmd": "$TC qdisc show dev $DUMMY",
"matchPattern": "qdisc dualpi2 1: root refcnt [0-9]+ limit 10000p.* target 50ms tupdate 50ms alpha 0.050781 beta 0.996094",
"matchCount": "1",
"teardown": [
"$TC qdisc del dev $DUMMY handle 1: root"
]
},
{
"id": "7b3c",
"name": "Create DualPI2 with any_ect option",
"category": [
"qdisc",
"dualpi2"
],
"plugins": {
"requires": "nsPlugin"
},
"setup": [
],
"cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root dualpi2 any_ect",
"expExitCode": "0",
"verifyCmd": "$TC qdisc show dev $DUMMY",
"matchPattern": "qdisc dualpi2 1: root refcnt [0-9]+ limit 10000p .* any_ect",
"matchCount": "1",
"teardown": [
"$TC qdisc del dev $DUMMY handle 1: root"
]
},
{
"id": "49a3",
"name": "Create DualPI2 with overflow option",
"category": [
"qdisc",
"dualpi2"
],
"plugins": {
"requires": "nsPlugin"
},
"setup": [
],
"cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root dualpi2 overflow",
"expExitCode": "0",
"verifyCmd": "$TC qdisc show dev $DUMMY",
"matchPattern": "qdisc dualpi2 1: root refcnt [0-9]+ limit 10000p.* overflow",
"matchCount": "1",
"teardown": [
"$TC qdisc del dev $DUMMY handle 1: root"
]
},
{
"id": "d0a1",
"name": "Create DualPI2 with drop_enqueue option",
"category": [
"qdisc",
"dualpi2"
],
"plugins": {
"requires": "nsPlugin"
},
"setup": [
],
"cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root dualpi2 drop_enqueue",
"expExitCode": "0",
"verifyCmd": "$TC qdisc show dev $DUMMY",
"matchPattern": "qdisc dualpi2 1: root refcnt [0-9]+ limit 10000p .* drop_enqueue",
"matchCount": "1",
"teardown": [
"$TC qdisc del dev $DUMMY handle 1: root"
]
},
{
"id": "f051",
"name": "Create DualPI2 with no_split_gso option",
"category": [
"qdisc",
"dualpi2"
],
"plugins": {
"requires": "nsPlugin"
},
"setup": [
],
"cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root dualpi2 no_split_gso",
"expExitCode": "0",
"verifyCmd": "$TC qdisc show dev $DUMMY",
"matchPattern": "qdisc dualpi2 1: root refcnt [0-9]+ limit 10000p .* no_split_gso",
"matchCount": "1",
"teardown": [
"$TC qdisc del dev $DUMMY handle 1: root"
]
},
{
"id": "456b",
"name": "Create DualPI2 with packet step_thresh",
"category": [
"qdisc",
"dualpi2"
],
"plugins": {
"requires": "nsPlugin"
},
"setup": [
],
"cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root dualpi2 step_thresh 3p",
"expExitCode": "0",
"verifyCmd": "$TC qdisc show dev $DUMMY",
"matchPattern": "qdisc dualpi2 1: root refcnt [0-9]+ limit 10000p .* step_thresh 3p",
"matchCount": "1",
"teardown": [
"$TC qdisc del dev $DUMMY handle 1: root"
]
},
{
"id": "610c",
"name": "Create DualPI2 with packet min_qlen_step",
"category": [
"qdisc",
"dualpi2"
],
"plugins": {
"requires": "nsPlugin"
},
"setup": [
],
"cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root dualpi2 min_qlen_step 1",
"expExitCode": "0",
"verifyCmd": "$TC qdisc show dev $DUMMY",
"matchPattern": "qdisc dualpi2 1: root refcnt [0-9]+ limit 10000p .* min_qlen_step 1p",
"matchCount": "1",
"teardown": [
"$TC qdisc del dev $DUMMY handle 1: root"
]
},
{
"id": "b4fa",
"name": "Create DualPI2 with packet coupling_factor",
"category": [
"qdisc",
"dualpi2"
],
"plugins": {
"requires": "nsPlugin"
},
"setup": [
],
"cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root dualpi2 coupling_factor 1",
"expExitCode": "0",
"verifyCmd": "$TC qdisc show dev $DUMMY",
"matchPattern": "qdisc dualpi2 1: root refcnt [0-9]+ limit 10000p .* coupling_factor 1",
"matchCount": "1",
"teardown": [
"$TC qdisc del dev $DUMMY handle 1: root"
]
},
{
"id": "37f1",
"name": "Create DualPI2 with packet classic_protection",
"category": [
"qdisc",
"dualpi2"
],
"plugins": {
"requires": "nsPlugin"
},
"setup": [
],
"cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root dualpi2 classic_protection 0",
"expExitCode": "0",
"verifyCmd": "$TC qdisc show dev $DUMMY",
"matchPattern": "qdisc dualpi2 1: root refcnt [0-9]+ limit 10000p .* classic_protection 0%",
"matchCount": "1",
"teardown": [
"$TC qdisc del dev $DUMMY handle 1: root"
]
}
]

View file

@ -4,8 +4,7 @@
# If a module is required and was not compiled
# the test that requires it will fail anyways
try_modprobe() {
modprobe -q -R "$1"
if [ $? -ne 0 ]; then
if ! modprobe -q -R "$1"; then
echo "Module $1 not found... skipping."
else
modprobe "$1"
@ -67,4 +66,5 @@ try_modprobe sch_hfsc
try_modprobe sch_hhf
try_modprobe sch_htb
try_modprobe sch_teql
./tdc.py -J`nproc`
try_modprobe sch_dualpi2
./tdc.py -J"$(nproc)"