Home / Alt manpages / tc-prio(8)

  • tc-prio(8)
  • Admin command
  • linux

Use tc-prio to Put Latency-Sensitive Traffic First

PRIO gives earlier service to selected traffic when a link is busy, but it never slows anything down by itself. This guide attaches a three-band PRIO queueing discipline to a Linux network interface, checks the classes and counters it creates, and removes it cleanly. Think of PRIO as a prioritisation tool, not a bandwidth shaper.

Allow about fifteen minutes for a read-only review, or thirty minutes if you need to test on a maintenance interface. You need the tc command from iproute2 and root privileges for the commands that change a live interface. The examples use iproute2 6.1.0, installed here as Ubuntu package version 6.1.0-1ubuntu6.4. The installed manual page is dated 16 December 2001, so treat the syntax and defaults below as the local contract, and check your own package before scripting across distributions.

Warning

Replacing a root qdisc changes packet scheduling immediately. On a production interface, test during a maintenance window and record the existing qdisc first. A mistaken root qdisc can affect all traffic on that link.

1. Check the command and the interface

These are ordinary, read-only commands. Neither needs sudo:

$ tc -V
tc utility, iproute2-6.1.0, libbpf 1.3.0
$ ip -br link
lo               UNKNOWN        127.0.0.1/8 ::1/128
INTERFACE        UP             192.0.2.10/24

Replace INTERFACE in later commands with the real device name from ip -br link. Then save the current root qdisc as your rollback reference:

$ tc qdisc show dev INTERFACE
qdisc fq_codel 0: dev INTERFACE root ...

The exact line varies. Keep it in your change record rather than relying on memory. If the interface already has a classful setup, do not replace it until you understand which classes and filters depend on it.

Checkpoint

You have a real interface name and a copy of its current tc qdisc show output.

2. Attach a three-band PRIO qdisc

PRIO creates its bands the moment the qdisc is attached. Three bands is the normal default, so this command spells the number out anyway and gives the qdisc handle 1::

$ sudo tc qdisc replace dev INTERFACE root handle 1: prio bands 3

This is the first state-changing command and needs elevated privileges. replace installs PRIO at the root, replacing whatever was there before. If you need to preserve the existing setup, stop here and use a test interface instead.

PRIO itself is work-conserving: it introduces no delay or rate limit while packets are waiting. It checks band 0 first, then band 1, then band 2, and only moves to a later band once the earlier one has nothing ready. That makes band 0 the highest service priority, even though the numbering looks backwards at first glance.

3. Verify the automatically created classes

PRIO classes can't be added one at a time, they're created with the qdisc. Since the qdisc handle here is 1:, the classes are addressed as 1:1, 1:2 and 1:3:

$ tc class show dev INTERFACE
class prio 1:1 parent 1:
class prio 1:2 parent 1:
class prio 1:3 parent 1:

Formatting can carry extra fields, but the check that matters is three PRIO classes under handle 1:. A qdisc handle's minor number is zero, so the first band is class 1:1, not 1:0.

Check the counters too:

$ sudo tc -s qdisc show dev INTERFACE
qdisc prio 1: root refcnt 2 bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
 Sent ... bytes ... pkt ...
 backlog 0b 0p requeues 0
 ...

Your byte and packet counts will differ. The priomap line is the part worth reading, it confirms the map attached to this qdisc and gives you a baseline before you generate test traffic.

4. Understand the default priority map

PRIO maps a packet priority to a band. The installed default has sixteen entries, for priorities 0 through 15:

1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
  • Priority 6 and 7 go to band 0, the highest service band.
  • Priority 0 goes to band 1.
  • Priorities 1, 2 and 3 go to band 2.

These are band numbers, not class minor numbers. The kernel priority behind them can come from an application's SO_PRIORITY, from the packet's Type of Service interpretation, or from a filter that directs traffic to a class.

Here's the common distraction: a bigger priority number does not mean a packet goes first. With PRIO, the map decides the band, and lower band numbers are served first. If your application needs a different policy, define all sixteen entries explicitly and keep the band count consistent:

$ sudo tc qdisc replace dev INTERFACE root handle 1: prio bands 3 priomap 0 0 0 0 0 0 0 0 1 1 1 1 2 2 2 2

This example sends priorities 0 through 7 to band 0, 8 through 11 to band 1, and 12 through 15 to band 2. It's only an example policy, not a universal recommendation, so test the priority values your own applications actually set. If you change the number of bands, update the map at the same time, a map pointing at a nonexistent band is an avoidable mistake.

5. Classify traffic deliberately

There are three ways traffic ends up in a band: an application can set SO_PRIORITY, a tc filter attached to the root qdisc can point traffic at a class, or the priority map can select a band on its own. The map is specific to PRIO, while filters and socket priorities are broader mechanisms.

Do not assume attaching PRIO automatically recognises an application's idea of urgent traffic. Confirm the priority source, then watch the counters while running a controlled test, ideally against a quiet test service or a spare interface. A packet sent to band 0 can delay packets in lower bands, and sustained load in a lower-numbered band can starve later bands entirely.

PRIO is often combined with a child shaper on a lower-priority class for exactly that reason. The child qdisc can stop bulk traffic from dominating the link, but its syntax and safe rate depend on the shaper you choose. Do not paste a guessed child configuration into a production interface: read that qdisc's own manual page first and save the complete parent and child configuration for rollback.

6. Remove PRIO and recover the old setup

Removal is also a privileged, disruptive change. If the previous root qdisc was disposable, delete PRIO with:

$ sudo tc qdisc del dev INTERFACE root
$ tc qdisc show dev INTERFACE
qdisc ... 0: dev INTERFACE root ...

Deletion returns the interface to the kernel's normal fallback qdisc, which may not be what you had before. If you need the previous policy back, use the saved command or configuration from your change record, don't try to reconstruct it from a shortened status line.

If the replacement fails before it changes anything, check the device name, privileges and whether another tool manages the interface. If packets are being delayed or starved after a successful change, remove PRIO or restore the known-good root qdisc first, then investigate classification and rates offline. Keep the old configuration until the counters and application behaviour check out.

Done means

  • Existing qdisc recorded: you noted it before making any change.
  • PRIO attached deliberately: a chosen band count and handle, not a default guess.
  • Classes verified: tc class show displays the automatically created classes.
  • Priority map understood: you can explain which priority values map to band 0, band 1 and band 2.
  • Classification confirmed: it's based on a verified socket priority, filter or map, not an assumption.
  • Rollback ready: you have a tested removal command and understand a busy lower-numbered band can starve later bands.