How to Restore Default pfifo_fast Queue Discipline in Linux Traffic Control After Temporary Rate Limiting


14 views

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