TAPRIO(8)
โจ NAME โจ
TAPRIO ๐ is the Time Aware Priority Shaper ๐ฆ, a qdisc that opens and closes traffic gates on a schedule. Literally iconic.
๐ SYNOPSIS ๐
tc qdisc ... dev dev parent classid [ handle major: ] taprio num_tc tcs
map P0 P1 P2 ... queues count1@offset1 count2@offset2 ...
base-time base-time clockid clockid
sched-entry <command 1> <gate mask 1> <interval 1>
sched-entry <command 2> <gate mask 2> <interval 2>
sched-entry <command 3> <gate mask 3> <interval 3>
sched-entry <command N> <gate mask N> <interval N>
[ max-sdu <queueMaxSDU[TC 0]> <queueMaxSDU[TC 1]>
<queueMaxSDU[TC N]> ]
[ fp <adminStatus[TC 0]> <adminStatus[TC 1]> <adminStatus[TC N]>
]
๐ธ DESCRIPTION ๐ธ
okay so TAPRIO is a simplified take on the scheduling state machine from IEEE 802.1Q-2018 Section 8.6.9 ๐ง. You get a sequence of gate states, and each gate state lets outgoing traffic through for a subset of traffic classes. That subset can even be empty, which is kind of iconic in its own way ๐.
How traffic gets mapped onto hardware queues works just like mqprio(8), so the map and queues parameters mean the same thing here โจ.
The other parameters describe the schedule itself, plus when it kicks off. It can also behave as if the schedule started in the past ๐ฐ๏ธ.
๐ PARAMETERS ๐
num_tcโ how many traffic classes you want. You can go up to 16 classes, so no cap ๐.mapโ the priority to traffic class map ๐บ๏ธ. It maps priorities 0..15 onto whichever traffic class you pick. Check mqprio(8) for the full tea โ.queuesโ gives the count and offset of the queue range for each traffic class, written ascount@offset๐. Ranges can't overlap, and each one has to be a contiguous run of queues. Be careful, bestie ๐.base-timeโ the exact instant, in nanoseconds, measured against the clockid, when the schedule starts โฐ. Ifbase-timeis in the past, the schedule still starts at this formula:
base-time + (N * cycle-time)
where N is the smallest integer that makes the result land later than "now". cycle-time is just the sum of all the intervals in the schedule. Math is slaying ๐งฎ.
clockidโ picks the clock that the qdisc's internal timer uses to measure time and schedule events โฑ๏ธ. You have to leave this out when you're using full-offload (flags0x2). In that case the clockid is implicitly/dev/ptpN, where N comes fromethtool -T eth0 | grep 'PTP Hardware Clock', and it isn't guaranteed to be synced with the system's CLOCK_TAI.
๐ญ With full-offload (flags0x2), do NOT passclockid. It gets chosen for you, and it might not match CLOCK_TAI at all.
sched-entryโ you can have multiplesched-entryparameters in one schedule ๐. Each one follows this format:
sched-entry <command> <gatemask> <interval>
The <command> can be one of three values, all following the IEEE 802.1Q-2018 definition (Table 8-7) ๐:
Sfor SetGateStatesHfor Set-And-Hold-MACRfor Set-And-Release-MAC
The <gate mask> is a bitmask where each bit maps to a traffic class. Bit 0 (the least significant bit) being "on" means traffic class 0 is "active" for that schedule entry ๐ฅ.
The <interval> is how long, in nanoseconds, that the state set by <command> and <gate mask> gets held before moving on to the next entry โณ.
flagsโ a bitmask that picks the modes for taprio ๐๏ธ.0x1โ turns on the txtime-assist feature ๐ฆ. In this mode, taprio sets the transmit timestamp based on the interval the packet needs to go out in. Then it uses the etf(8) qdisc to sort and send the packets at the right time. The second example further down is a good reference for setting this mode up.0x2โ turns on the full-offload feature ๐. Here taprio hands the gate control list to the NIC, which runs it cyclically in hardware. With full-offload you don't need to specify the clockid argument.
๐
โโ๏ธ The txtime-assist and full-offload features are mutually exclusive. Setting flags to 0x3 is invalid, so don't even try it.
txtime-delayโ this one is specific to txtime offload mode ๐ซ. It's the maximum time a packet might take to get from the taprio qdisc to the network card. The value should always be greater than the delta set in the etf(8) qdisc.max-sduโ an array with at most 16 elements, one per traffic class ๐. It lines up with the queueMaxSDU table from IEEE 802.1Q-2018. Each element is the maximum L2 payload size that can leave through that traffic class. Any elements you don't fill in default to0, and a0means that traffic class can send packets as big as the port's maximum MTU ๐ .fpโ picks whether each traffic class is express or preemptible ๐. See tc-mqprio(8) for the details.
๐ง EXAMPLES ๐ง
Okay this first one is a schedule with three traffic classes (num_tc 3), kept separate from each other. We're gonna call them TC 0, TC 1 and TC 2 ๐ซถ. Read the map below like this: traffic with priority 3 is classified as TC 0, priority 2 is classified as TC 1, and everything else is classified as TC 2 โจ.
The schedule starts at instant 1528743495910289987 using the reference CLOCK_TAI โฐ. It's made of three entries that each last 300us ๐.
# tc qdisc replace dev eth0 parent root handle 100 stab overhead 24 taprio \
num_tc 3 \
map 2 2 1 0 2 2 2 2 2 2 2 2 2 2 2 2 \
queues 1@0 1@1 2@2 \
base-time 1528743495910289987 \
sched-entry S 01 300000 \
sched-entry S 02 300000 \
sched-entry S 04 300000 \
clockid CLOCK_TAI
Next up is txtime offload mode in taprio. For the ETF side, check out etf(8) ๐ฆ.
# tc qdisc replace dev eth0 parent root handle 100 stab overhead 24 taprio \
num_tc 3 \
map 2 2 1 0 2 2 2 2 2 2 2 2 2 2 2 2 \
queues 1@0 1@0 1@0 \
base-time 1528743495910289987 \
sched-entry S 01 300000 \
sched-entry S 02 300000 \
sched-entry S 04 400000 \
flags 0x1 \
txtime-delay 200000 \
clockid CLOCK_TAI
# tc qdisc replace dev $IFACE parent 100:1 etf skip_skb_check \
offload delta 200000 clockid CLOCK_TAI
Now here's a full offload mode schedule ๐. The base-time is 200 ns, and the cycle-time is worked out automatically from all the sched-entry durations, so 20 us + 20 us + 60 us = 100 us. Even though the base-time is in the past, the hardware starts running the schedule at a PTP time equal to the smallest integer multiple of 100 us, plus 200 ns, that's bigger than the NIC's current PTP time โณ. On top of that, the MTU for traffic class 5 is capped at 200 octets. That keeps the interference it causes on traffic class 7 bounded while both of their gates are open ๐. The interference is figured out from the transmit time of the max SDU, plus the L2 header length, plus the L1 overhead (taken from the size table you set up with tc-stab(8)).
# tc qdisc add dev eth0 parent root stab overhead 24 taprio \
num_tc 8 \
map 0 1 2 3 4 5 6 7 \
queues 1@0 1@1 1@2 1@3 1@4 1@5 1@6 1@7 \
max-sdu 0 0 0 0 0 200 0 0 \
base-time 200 \
sched-entry S 80 20000 \
sched-entry S a0 20000 \
sched-entry S 5f 60000 \
flags 0x2
๐ SEE ALSO ๐
tc-stab(8) ๐ท
๐ AUTHORS ๐
Vinicius Costa Gomes <vinicius.gomes@intel.com> ๐
Bestie, that's the whole TAPRIO(8) page ๐ from iproute2, dated 25 Sept 2018. Go slay your schedules ๐ โจ๐ซถ
๐ฆ where this page comes from
- alpine 3.22: iproute2-doc 6.15.0-r0 ยท GPL-2.0-or-later
- alpine 3.23: iproute2-doc 6.17.0-r0 ยท GPL-2.0-or-later
- alpine 3.24: iproute2-doc 7.0.0-r0 ยท GPL-2.0-or-later
- arch rolling: iproute2 7.2.0-1
- arch rolling: iproute2 7.3.0-1 ยท GPL-2.0-or-later
- centos-stream 10: iproute-tc 7.0.0-2.el10 ยท GPL-2.0-or-later
- centos-stream 9: iproute-tc 6.17.0-3.el9 ยท GPL-2.0-or-later
- debian trixie: iproute2 6.15.0-1 ยท GPL-2.0-only
- fedora 43: iproute-tc 6.14.0-2.fc43 ยท GPL-2.0-or-later
- fedora 44: iproute-tc 6.17.0-2.fc44 ยท GPL-2.0-or-later
- nixos 26.05: iproute2 7.0.0
- nixpkgs unstable: iproute2 7.2.0
- ubuntu questing: iproute2 6.16.0-1ubuntu2
- ubuntu questing: iproute2 6.16.0-1ubuntu2.1 ยท GPL-2.0-only
- ubuntu questing: iproute2 6.16.0-1ubuntu2.2
- ubuntu resolute: iproute2 6.19.0-1ubuntu1 ยท GPL-2.0-only
- ubuntu resolute: iproute2 6.19.0-1ubuntu1.1 ยท GPL-2.0-only
- ubuntu stonking: iproute2 7.2.0-1ubuntu1 ยท GPL-2.0-only
- ๐ girlified by anthropic/claude-haiku-5.5 on : a language model's rewrite of the original, under its terms