Tune Linux FQ Pacing Safely with tc
The Linux FQ qdisc splits traffic into flows and can honour the pacing an application or TCP stack asks for. This guide adds FQ to one interface with tc, turns on a shallow ECN marking threshold, and checks the live queue statistics. Examples use tc from Ubuntu's iproute2 6.1.0-1ubuntu6.4 package.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about 10 minutes if you already know which interface you are testing. You need a shell, the iproute2 package, and elevated privileges for the change itself. This replaces the root qdisc, so test on a disposable host or a maintenance window: a root qdisc swap can affect traffic immediately.
1. Identify the interface and current qdisc
Pick the interface carrying the traffic you want to observe. Replace IFACE below with a real name such as eth0 or ens3. Neither command changes network state.
ip -brief link
tc qdisc show dev IFACE
- Read the existing output first. A line such as
qdisc fq_codelorqdisc mqis not interchangeable with FQ. - Save it somewhere outside this guide if you would need to restore a more complicated setup exactly.
- FQ is classless. There are no child classes to configure, and it is aimed mainly at locally generated traffic; for forwarded traffic it falls back to a packet hash rather than a socket-owned flow.
2. Add FQ with a conservative test setting
Run this as root, or prefix it with sudo. ce_threshold asks FQ to mark packets with ECN Congestion Experienced once queueing passes 4 milliseconds, which suits congestion-control schemes such as DCTCP that deliberately lean on shallow ECN marking. It is not a general substitute for measuring your own workload.
sudo tc qdisc add dev IFACE root fq ce_threshold 4ms
- Success prints nothing.
RTNETLINK answers: File existsmeans the interface already has a root qdisc. Do not keep retrying: inspect it, then decide whether replacing it is safe.Operation not permittedmeans the command was not elevated, or your account lacks the capability to change network configuration.
Everything you did not name keeps its FQ default: a 10,000-packet overall limit and 100 packets per flow, a 1,024-bucket hash table, an orphan mask capping unowned traffic at 1,024 allocated flows, and pacing enabled. These are queueing controls, not bandwidth guarantees.
3. Verify the active parameters and counters
Ask tc for both statistics and detailed parameters:
sudo tc -s -d qdisc show dev IFACE
Expect a line similar to this, though your handle, interface name, packet counters and MTU-derived byte values will differ:
qdisc fq 8001: dev IFACE root refcnt 2 limit 10000p flow_limit 100p buckets 1024 orphan_mask 1023 quantum 3028b initial_quantum 15140b ce_threshold 4.0ms
Look for qdisc fq, root, limit, flow_limit and ce_threshold. The default quantum is twice the interface MTU, and a new flow starts with credit for ten MTUs so TCP's initial window is not delayed, which is why the byte values track your MTU.
Tip
The statistics cover sent bytes and packets, drops, backlog, flow counts, throttling and ECN marks, all cumulative for the life of this qdisc. A zero ce_mark does not prove traffic never queued for 4 milliseconds, only that nothing has been marked yet.
4. Adjust limits only for a measured reason
You can set a hard total queue limit, a per-flow limit, and a maximum rate per flow. This example limits each flow to 50 packets and caps a flow at 100 Mbit/s:
sudo tc qdisc replace dev IFACE root fq flow_limit 50 maxrate 100mbit
- Only use replace when swapping the root qdisc is acceptable.
- Reaching the total limit drops new packets. Lowering a limit can drop packets immediately so the queue meets the new value, and a small per-flow limit can drop bursts even while the total queue sits mostly empty.
- maxrate is a per-flow ceiling, not a link-wide allocation. Application requests through
SO_MAX_PACING_RATEare ignored once they ask for more than this ceiling. Withoutmaxrate, FQ defaults to unlimited, subject to whatever pacing the application or TCP supplies.
5. Undo the test change
Warning
Deleting the root qdisc changes live packet scheduling and may briefly disrupt traffic. Run this only when you intend to remove the qdisc, and use the output you saved in step 1 if you need to recreate the previous configuration.
sudo tc qdisc del dev IFACE root
tc qdisc show dev IFACE
After deletion, the kernel or your network manager may install another default qdisc. If a service manages the interface, make the persistent change there too; this command alone is not a permanent configuration record.
Done means
- Correct interface identified and its existing root qdisc inspected.
tc -s -d qdisc show dev IFACEreportsqdisc fqwith the settings you intended.- You know the queue and flow limits can drop packets once reached.
- Counters checked after representative traffic, including
ce_markif ECN marking was part of the test. - Qdisc left in place deliberately or removed with the right privileged command.