When working with Linux Traffic Control (tc), many developers encounter an unexpected behavior when removing custom queue disciplines:
# Setup temporary rate limiting
tc qdisc add dev eth1 root tbf rate 600kbit latency 50ms burst 1540
# Later attempt to remove it
tc qdisc del dev eth1 root
Instead of reverting to the default pfifo_fast qdisc, this completely removes the queuing discipline, causing network operations to fail.
The default pfifo_fast queue discipline is special - it's automatically created by the kernel when a network interface comes up. Attempting to manually create it fails:
# This doesn't work
tc qdisc add dev eth1 root pfifo_fast
This occurs because pfifo_fast is a built-in qdisc without configurable parameters through the tc command.
The correct approach is to replace (rather than delete) the existing qdisc:
# Replace existing qdisc with default pfifo_fast
tc qdisc replace dev eth1 root pfifo
Alternatively, you can use the following equivalent command:
# Another way to restore default queuing
tc qdisc add dev eth1 root pfifo_fast 2>/dev/null || \
tc qdisc change dev eth1 root pfifo_fast
In cases where you need to temporarily use a different qdisc before returning to default, consider these patterns:
# Store original qdisc configuration
ORIG_QDISC=$(tc qdisc show dev eth1)
# Apply temporary shaping
tc qdisc add dev eth1 root tbf rate 1mbit burst 32kbit latency 400ms
# Later restore original configuration
eval "tc qdisc $ORIG_QDISC"
# Or for simple cases:
tc qdisc replace dev eth1 root pfifo
The pfifo_fast qdisc has three bands (priority queues) that handle traffic differently:
# View default pfifo_fast configuration
tc qdisc show dev eth1
Key characteristics include:
- Packets are classified by Type of Service (ToS) bits
- Band 0 is highest priority
- Each band uses FIFO queuing
- Default priomap determines classification
Here's a complete workflow for temporary rate limiting:
#!/bin/bash
# Backup current qdisc
CURRENT_QDISC=$(tc qdisc show dev eth1 | awk '{print $2,$3,$4}')
# Apply temporary rate limit
tc qdisc add dev eth1 root tbf rate 500kbit latency 50ms burst 10kb
# ... perform operations with rate limiting ...
# Restore original qdisc
if [ "$CURRENT_QDISC" = "pfifo_fast 0:" ]; then
tc qdisc replace dev eth1 root pfifo
else
tc qdisc replace dev eth1 root $CURRENT_QDISC
fi
The Linux kernel automatically creates a pfifo_fast
queue discipline (qdisc) for each network interface during initialization. This is the default queuing mechanism with three priority bands:
qdisc pfifo_fast 0: dev eth0 root refcnt 2 bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
When we remove a custom qdisc with:
# tc qdisc del dev eth1 root
The interface becomes effectively unconfigured, which breaks network functionality. This occurs because:
- The kernel doesn't automatically recreate pfifo_fast
- The network stack expects a qdisc to be present
- Packets have no queuing mechanism
The proper way to handle temporary qdisc changes is:
# tc qdisc replace dev eth1 root tbf rate 600kbit latency 50ms burst 1540
# tc qdisc replace dev eth1 root pfifo_fast
If you've already deleted the qdisc, recreate it with:
# tc qdisc add dev eth1 root pfifo_fast
Some systems may require additional parameters:
# tc qdisc add dev eth1 root handle 1: pfifo_fast
For systems where pfifo_fast recreation fails, a similar alternative is:
# tc qdisc add dev eth1 root handle 1: prio bands 3
# tc qdisc add dev eth1 parent 1:1 pfifo limit 1000
# tc qdisc add dev eth1 parent 1:2 pfifo limit 1000
# tc qdisc add dev eth1 parent 1:3 pfifo limit 1000
Always verify your qdisc configuration:
# tc qdisc show dev eth1
# tc -s qdisc show dev eth1
Check kernel messages for errors:
# dmesg | grep qdisc
To make changes persistent across reboots, add to your network configuration:
post-up /sbin/tc qdisc add dev $IFACE root pfifo_fast