High-throughput TCP connections operating across lossy networks receive continuous streams of Selective Acknowledgment (SACK) blocks detailing missing and delivered packet sequences. To prevent excessive CPU utilization caused by managing thousands of fragmented sk_buff structures in memory, the Linux kernel implements SACK compression and coalescing routines. While SACK compression dramatically reduces queue memory overhead, aggressive compression can delay immediate retransmission signaling and trigger SACK reneging stalls. Tracing SACK compression behaviour with eBPF isolates these packet loss recovery delays.
Understanding Linux Kernel SACK Compression Architecture
The Linux TCP implementation optimizes SACK block processing through two key mechanisms:
- SACK Compression (
tcp_sack_compress): When multiple SACK blocks arrive rapidly without advancing the cumulative ACK pointer (snd_una), the kernel coalesces adjacent sequence ranges into combined records to reduce retransmission queue traversal overhead. - ACK Coalescing Delays: The kernel may delay transmitting intermediate SACK updates if subsequent ACKs can merge duplicate sequence boundaries.
- SACK Reneging: If an overloaded receiver exhausts receive memory, it may discard previously SACKed out-of-order blocks. The sender must detect this reneging event and reset its recovery window.
Auditing Active SACK Compression Counters
Inspect baseline system counters governing SACK compression before instrumenting kernel probes:
# Check system-wide SACK and ACK compression statistics
netstat -s | grep -i -E "sack|compressed"
# Inspect current SACK enablement (1 = enabled)
sysctl net.ipv4.tcp_sack
Metrics such as TCPSACKDiscard, TCPAckCompressed, and TCPSackMerged provide initial insight into coalescing activity.
Tracing tcp_sack_compress Using bpftrace
The Linux kernel invokes tcp_sack_compress when merging selective acknowledgment blocks. Hook this routine to evaluate compression frequency and sequence counts:
sudo bpftrace -e '
#include <net/sock.h>
#include <linux/tcp.h>
kprobe:tcp_sack_compress
{
$sk = (struct sock *)arg0;
$tp = (struct tcp_sock *)arg0;
$sacked_out = $tp->sacked_out;
$lost_out = $tp->lost_out;
printf("SACK Compression Active: PID %-6d Comm %-12s SACKed: %-4u Lost: %-4u\n",
pid, comm, $sacked_out, $lost_out);
@sack_compress_count = count();
}'
Running this script verifies whether heavy packet loss induces excessive SACK compaction passes.
Measuring Retransmission Stalls During SACK Processing
To quantify latency penalties when the kernel collapses large SACK queues, measure the duration spent executing tcp_sacktag_write_queue:
sudo bpftrace -e '
#include <linux/skbuff.h>
kprobe:tcp_sacktag_write_queue
{
@sack_start[tid] = nsecs;
}
kretprobe:tcp_sacktag_write_queue
{
if (@sack_start[tid]) {
$elapsed_us = (nsecs - @sack_start[tid]) / 1000;
@sack_tagging_latency_us = hist($elapsed_us);
delete(@sack_start[tid]);
}
}
END
{
printf("\nSACK Tagging Processing Latency (microseconds):\n");
print(@sack_tagging_latency_us);
clear(@sack_start);
}'
Diagnosing and Remediating SACK Latency Spikes
Evaluate probe results to tune production transport stacks:
- Microsecond Latency Outliers: If
tcp_sacktag_write_queueexceeds 500 microseconds, transmission queues contain excessive fragmented skbs. Enable Fair Queueing (fq) with pacing to smooth packet spacing. - Adjust SACK Compression Limits: Recent kernels expose
net.ipv4.tcp_comp_sack_nrto regulate how many SACK blocks accumulate before compression is enforced. - Ensure Timestamp Alignment: Maintain
net.ipv4.tcp_timestamps=1to provide PAWS (Protection Against Wrapped Sequence Numbers) verification during high-throughput SACK recovery.