Give a Linux Interface Fairer Queuing with tc SFQ
You will finish with a Stochastic Fairness Queueing (SFQ) discipline attached to a Linux interface, a command to inspect it, and a clear rollback path. SFQ shares transmission opportunities between flows. It does not cap the interface rate or create bandwidth that is not already available.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about fifteen minutes. You need the tc command from iproute2, a test interface or a maintenance window, and root privileges for the commands that change a qdisc. The examples use the locally installed iproute2 package version 6.1.0-1ubuntu6.4. Read the existing qdisc before changing anything: replacing a root qdisc can alter live traffic immediately.
1. Confirm the installed command
Start with read-only checks. These need no elevated privileges:
$ tc -V
tc utility, iproute2-6.1.0, libbpf 1.3.0
$ command -v tc
/usr/sbin/tc
The exact path can differ. The useful check is that tc is present and reports the expected family of iproute2. Inspect the target interface before making a change:
$ tc qdisc show dev IFACE
qdisc noqueue 0: root refcnt 2
Replace IFACE with a real device such as ppp0 or eth0. The output above is only an example from an interface with no ordinary root queue. If your interface already has a root qdisc, stop and record its complete output before proceeding. Do not use replace as a shortcut unless you deliberately intend to remove that configuration.
2. Understand what SFQ will and will not do
SFQ is classless and work-conserving. When packets are waiting, it keeps transmitting them, selecting non-empty hash buckets in round-robin order. With its internal classifier, the hash is based mainly on source and destination addresses and ports. A bucket therefore usually represents a TCP or UDP flow, although different flows can collide.
This is useful when one flow should not monopolise an interface queue. It is not traffic shaping. If the real bottleneck is a cable modem, DSL device, switch port or another queue outside Linux, SFQ cannot schedule packets that Linux does not own. For that situation, put a suitable SFQ instance inside a classful or shaping qdisc so that Linux controls the queue closest to the bottleneck.
Checkpoint: you should now know both the device name and where its effective queue lives. If either is unknown, do not attach SFQ to a production interface yet.
3. Attach a conservative SFQ instance
The basic command is a privileged, service-affecting change:
$ sudo tc qdisc add dev IFACE root sfq perturb 60
Run it only after the read-only check showed that the target has no root qdisc, or after you have explicitly planned the replacement. The perturb 60 setting asks SFQ to change its hash perturbation every 60 seconds. The manual's default is no perturbation. A very short interval can cause packet reordering or losses, so do not reduce it casually.
The other defaults are deliberate: the hash divisor is 1024, the queue limit is 127 packets, the per-flow depth is 127 packets on kernels after Linux 3.3, and the quantum defaults to the interface MTU. The quantum is the amount a flow may dequeue in one round and the MTU-sized default is the advised minimum. Leave these values alone until measurements show a reason to change them.
Verify the live state immediately:
$ sudo tc -s qdisc show dev IFACE
qdisc sfq 8001: root refcnt 2 limit 127p
Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
Handle numbers and counters vary. Look for qdisc sfq on the intended device. The statistics are cumulative for that qdisc, so they are evidence about the current instance rather than a lifetime history of the interface.
4. Tune only the constraint you understand
Use limit NUMBER to change the total packet limit. Use flows NUMBER to change the maximum number of active flows on kernels after Linux 3.3, and depth NUMBER to limit packets per flow. For example, this is a deliberately explicit configuration for a controlled test interface:
$ sudo tc qdisc add dev IFACE root sfq limit 3000 flows 512 divisor 16384 perturb 60
The divisor must be a power of two and cannot exceed 65536. Increasing it reduces the chance that unrelated flows share a bucket, at the cost of a larger hash table. If you use an external flow classifier, its divisor must match SFQ's divisor. Do not change one side independently.
headdrop changes overflow from dropping the newest packet at the tail of a flow to dropping from its head. The manual describes this as potentially giving TCP better feedback. It is not a universal improvement; test it against your workload. The redflowlimit, min, max, probability, avpkt, burst, ecn and harddrop options enable and tune RED behaviour per flow. Treat that as a separate experiment, because RED thresholds and ECN support change the packet-loss and marking behaviour.
5. Check counters and traffic ownership
Generate only the normal traffic your maintenance plan permits, then inspect the counters again:
$ sudo tc -s qdisc show dev IFACE
Compare Sent, dropped, overlimits and backlog with the earlier snapshot. A zero backlog does not prove that SFQ is shaping traffic, because SFQ does not shape. It only shows that the queue was empty when sampled. Likewise, a non-zero drop count needs investigation rather than an automatic increase to every limit.
If the link still shows unfairness, check the physical or virtual queue that actually fills. On an ordinary broadband uplink, an upstream device may be the bottleneck. In that case, move the scheduling point into a deliberate shaping hierarchy rather than adding more SFQ options to a queue that never fills.
6. Remove the test configuration
Removing a root qdisc is also privileged and can briefly change packet handling. First confirm that the root qdisc is the SFQ instance you created:
$ sudo tc qdisc show dev IFACE
qdisc sfq 8001: root refcnt 2 limit 127p
Only when that output matches your planned test, remove it:
$ sudo tc qdisc del dev IFACE root
$ sudo tc qdisc show dev IFACE
qdisc noqueue 0: root refcnt 2
The final output is an example, not a promise: another default or parent qdisc may be restored by the system or network manager. If a service or manager owns the interface configuration, use its documented configuration to restore the intended qdisc rather than repeatedly applying ad-hoc commands. Never delete a root qdisc merely because a handle number differs.
Done means
- You confirmed the installed iproute2 and identified the target interface.
- You inspected the existing root qdisc before making a state-changing command.
- SFQ is attached only where Linux owns the queue you need to schedule.
- You verified the live qdisc and its counters with
tc -s qdisc show. - You know that SFQ schedules flows but does not shape the link rate.
- You can remove the test instance without deleting an unrelated qdisc.