TAPRIO(8)

๐Ÿ“ฆ
version 7.3.0 ยท 3 versions
, in alpine 3.22, alpine 3.23, alpine 3.24, arch rolling, centos-stream 10, centos-stream 9, debian trixie, fedora 43, fedora 44, nixos 26.05, nixpkgs unstable, ubuntu questing, ubuntu resolute, ubuntu stonking

โœจ 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 as count@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 โฐ. If base-time is 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 (flags 0x2). In that case the clockid is implicitly /dev/ptpN, where N comes from ethtool -T eth0 | grep 'PTP Hardware Clock', and it isn't guaranteed to be synced with the system's CLOCK_TAI.
๐Ÿ˜ญ With full-offload (flags 0x2), do NOT pass clockid. It gets chosen for you, and it might not match CLOCK_TAI at all.
  • sched-entry โ€” you can have multiple sched-entry parameters 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) ๐Ÿ’œ:

  • S for SetGateStates
  • H for Set-And-Hold-MAC
  • R for 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 to 0, and a 0 means 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 ๐Ÿ’…โœจ๐Ÿซถ