TCP Small Queues begränsar antalet väntande byte i Linux-sändningsvägen per TCP-flöde och minskar därmed Fördröjning samt bufferbloat på ett målinriktat sätt. Jag visar hur denna mekanism fungerar i Linux-nätverk Stack visar hur jag sätter rimliga gränser och vilka interaktioner som uppstår med pacing, QDiscs och överbelastningskontroll.
Centrala punkter
- Flödesgräns: TSQ begränsar antalet väntande byte per TCP-sockel.
- Mindre bufferbloat: Kortare köer minskar RTT.
- Bakåtsträvande: Programmen skriver långsammare när gränsen träder i kraft.
- Rättvisa: Inget enskilt flöde upptar hela köerna.
- Adaptiv Styrning: Gränsvärdet följer hastigheten och segmentstorleken.
Hur TCP Small Queues fungerar
TSQ tar vid där TCP-segmenten ansluter till QDisc och drivrutiner. När jag skriver data till en socket kontrollerar kärnan före varje enqueue de redan allokerade byte för detta flöde. Om flödet når gränsen markerar logiken socketen som begränsad och stoppar ytterligare enqueue. Först när nätverkskortet frigör buffertutrymme får socketen sända igen och jag kan återigen skicka data till stacken. Denna strama återkoppling håller Köer kort och gör reaktionstiderna mer förutsägbara.
Varför långa köer förlänger svarstiden
Skapa stora drivrutins- och QDisc-köer Bufferbloat, framför allt med TSO/GSO och stora sändningsvolymer. En stor nedladdning kan fylla utgående köer, medan interaktiva flöden som SSH, API-anrop eller VoIP hamnar längst bak i kön. Den överbelastade kön dominerar då RTT istället för den faktiska länktiden. Överbelastningskontrollen reagerar trögt eftersom bekräftelserna (Ack) anländer sent, vilket leder till sämre cwnd-beslut. TSQ begränsar antalet förbuffrade byte per flöde så att små, tidskritiska paket snabbt kommer ut på linjen.
En titt under huven: vad kärnan räknar
Under ytan räknar kärnan inte „paket“, utan byte – närmare bestämt de minnesbyte som redan har placerats i kön av socketen. Det avgörande är vad stacken innehåller av skbuff-strukturer tillsammans med truesize har tilldelats och ännu inte har bearbetats av NIC. TSQ kopplar därtill en Gas/Avgas‑Sökväg: Om en socket når kreditgränsen sätter stacken en begränsningsflagga och anropar först igen efter att sändningarna har slutförts (NAPI/IRQ) write_space() så att applikationen får skicka igen. Denna återkoppling är snabbare än signaler från överbelastningskontrollen som enbart baseras på förluster och verkar före QDisc. Med TSO/GSO förblir mekanismen effektiv, eftersom gränsen vid före utgår från den bytebudget som ligger till grund för segmenteringen: Stora superramar släpps endast in i QDisc om det finns tillräckligt med ledigt utrymme, vilket begränsar burst-trafiken.
Dynamiska gränser och tempo
Jag drar nytta av TSQ eftersom gränsen inte förblir helt statisk, utan anpassas till Pris och segmentstorlek. Målet är ungefär en millisekund data i sändningsvägen per flöde, oavsett om hastigheten är 100 Mbit, 1 Gbit eller 10 Gbit. Vid snabbare anslutning ökar den tillåtna bytekrediten, vid långsammare anslutning minskar den. I kombination med TCP-Pacing förblir dataströmmarna små och bekräftelserna kommer tillbaka snabbare. På så sätt uppnår jag märkbart lägre Fördröjningstoppar, utan att onödigt begränsa genomströmningen.
Interaktion mellan enskilda socklar och appar
TSQ fungerar endast om applikationen också känner av mottrycket. Därför tar jag hänsyn till inställningar som SO_SNDBUF, TCP_NOTSENT_LOWAT och autocorking. Ett för stort sändningsbuffertfönster kan på kort sikt pressa in många byte i stacken; TSQ bromsar visserligen, men appen märker det först när send() blockeras eller returnerar EAGAIN. Med TCP_NOTSENT_LOWAT då drar jag av den „osända“ delen i användarmiljön och kompletterar därmed TSQ på kernelsidan. Autocorking (eller explicit TCP_CORK/MSG_MORE) hjälper till att samla ihop små skrivoperationer utan att orsaka latensspikar. Pacing-gränser per socket (t.ex. via SO_MAX_PACING_RATE) harmoniserar med TSQ: Frekvensen jämnar ut tidsmässigt, medan bytegränsen begränsar rumsmässigt. Viktigt: TCP_NODELAY Inaktiverar Nagle och kan öka interaktiviteten, men utan TSQ ökar risken för burst; med TSQ har jag båda under kontroll.
Praktisk vägledning: lämpliga TSQ-värden
Jag fastställer den övergripande ramen med net.ipv4.tcp_limit_output_bytes (Sysctl). Vanliga standardvärden ligger på omkring 128–262 KB per flöde. För många webb- och API-arbetsbelastningar väljer jag lägre värden för att de interaktiva svaren ska förbli snabba. För säkerhetskopiering eller replikering höjer jag gränsen något, så länge RTT förblir stabil. Den som vill fördjupa sig ytterligare i köhanteringen hittar grundläggande information om Paketköer på servern, som underlättar klassificeringen.
| Scenario | Länkningsfrekvens | Riktvärde tcp_limit_output_bytes | Mål |
|---|---|---|---|
| API/HTTP – mycket interaktivt | 100 Mbit – 1 Gbit | 64–128 KB | låg RTT, korta spikar |
| Blandad belastning: webb + nedladdningar | 1–10 Gbit | 128–256 KB | Balans ut Genomströmning och latens |
| Replikering/säkerhetskopiering | 1–10 Gbit | 256–512 KB | konstant bulkflöde, acceptabelt Fördröjning |
| WAN med hög RTT | 10–100 Mbit | 96–192 kB | mindre skurar, mer rättvisa Ledtrådar |
QDisc och överbelastningskontroll i samverkan
TSQ arbetar vid ingången till QDisc, medan algoritmer som fq_codel hanterar överbelastningen på linjen. Tillsammans minskar de köerna och ser till att fördelningen blir rättvis. Med TCP BBR dessutom drar jag nytta av detta, eftersom mer realistiska RTT-mätningar leder till bättre pacing och cwnd-styrning. CUBIC reagerar också smidigare när jag eliminerar för långa köningstider. På så sätt ökar genomströmningen organiskt, medan Svarstid förblir under kontroll.
Virtualisering och molnplattformar
I virtuella maskiner läggs flera buffertnivåer ihop: gäst-QDisc, virtio/vhost-köer, värd-QDisc och det fysiska nätverkskortet. Jag håller TSQ aktivt i gästen och väljer där en konservativ gräns så att inga stora dataströmmar når värden. På hypervisorn ser jag till att latenskedjorna blir korta genom att använda rättvisa QDiscs, måttliga TX-ringar och korrekt IRQ-pinning. SR-IOV kan minska latensen, men överför ansvaret till gästerna: utan TSQ i gästen riskerar man långa VF-köer. I containrar tillämpar TSQ per NetNS som vanligt; med hjälp av cgroup-pacing och CPU-begränsningar förhindrar jag att en högljudd granne indirekt ökar latensen. Det är också viktigt att beakta coalescing och offloads i virtio-vägen: överdriven sammanläggning förlänger bekräftelserna, medan för liten sammanläggning minskar effektiviteten – jag justerar utifrån latensmålet, inte dogmatiskt.
Wi-Fi och inbyggda system: Hantera specialfall på rätt sätt
På WLAN-länkar är det Aggregering i MAC-lagret. Om jag lämnar för få byte i sändningsvägen kan drivrutinen sammanfoga färre ramar, vilket minskar effektiviteten. I sådana konfigurationer höjer jag gränsen försiktigt och kontrollerar aggregeringsgraden. OpenWrt- och inbyggda plattformar drar dessutom nytta av smidiga vägar i drivrutinerna och färre atomära operationer. Jag testar varje justering under verklig trådlös belastning innan jag Profil rulla ut brett.
Övervakning och mätvärden som verkligen räknas
Jag observerar RTT– Fördelningen per socket och tittar på extremvärden, inte bara på medelvärden. Med ss, tc och exportörer läser jag av köernas längd, återutsändningar samt pacing_rate. eBPF-program levererar händelser till mig när sockets stryps och frigörs igen. Time-to-First-Byte och 95:e/99:e percentilen visar om TSQ har önskad effekt. Utan mätvärden förblir varje Optimering en flygning i blindo.
Meningsfulla A/B- och belastningstester
Jag mäter TSQ-effekter på ett reproducerbart sätt: först en baslinje utan ändringar, sedan isolerade parametersvep (t.ex. 64, 96, 128, 192 KB). För blandade arbetsbelastningar kör jag parallella strömmar (bulk + många korta förfrågningar) och jämför 95:e och 99:e percentilen för latenser, inte bara medianen. Om jag tydligt avbryter testkörningarna (uppvärmning, mätfönster, nedkylning) förblir artefakterna märkbara. Jag ser till att hålla konstanta variabler: samma nyttolastmönster, identisk rutt/MTU, identiska CPU-frekvenser för server och klient. På WAN-sträckor simulerar jag fördröjning/jitter/förlust med tc netem, för att kontrollera om TSQ-gränserna inte sätts för tidigt vid hög BDP. Först när percentilerna blir snävare och antalet återutsändningar/förluster förblir stabila överför jag värdena till produktionen.
Hårdvaruoptimering och drivrutinsinformation
Jag kontrollerar TSO/GSO-inställningarna, nätverkskortets ringbuffert och IRQ-styrningen, så att TSQ fungerar smidigt. För stora TX-ringar förlänger köerna vid enheten; för små minskar utnyttjandegraden. Grov sammanläggning av avbrott fördröjer bekräftelser, medan fin sammanläggning ökar CPU-belastningen. Jag anpassar genomgången efter praktiska behov och hänvisar till följande som en introduktion: Sammanslagning av avbrott. Målet är fortfarande en tillförlitlig Fördröjning med tillräcklig genomströmning.
NUMA, RSS och CPU-affinitet
Korta köer hjälper inte mycket om paket ständigt rör sig över NUMA-gränserna. Jag kopplar RX/TX-köer via RSS/irqbalance till kärnor i samma NUMA-domän som appen körs på. Med XPS/RPS styr jag vilka CPU:er som hanterar TX-arbete och undviker därmed cross-socket-hopper. Färre cache-missar och mindre lock-contention hjälper TSQ indirekt: Färdigställda uppgifter återkommer snabbare, socketen „avlastas“ tidigare och latensspikar uteblir. Vid ett mycket stort antal flöden per värd planerar jag in tillräckligt många köer och undviker att flera högintensiva flöden kolliderar på samma TX-ring.
Steg för steg: Kontrollera om TSQ är aktivt
Jag börjar med att titta på Sysctl: sysctl net.ipv4.tcp_limit_output_bytes visar den aktuella gränsen. Därefter använder jag ss -tin för enskilda socklar, tittar på send‑q och rtt och jämför belastningsfaser med och utan justering av gränsen. Med iperf3 skapar jag bakgrundsbelastning och mäter parallellt API-svarstider för att synliggöra prioriteringar. tc -s qdisc ger mig paket- och drop-siffror för utgångsdiskiplinen. Om 95:e/99:e percentilen förblir nära varandra och CPU– Last i ramen, justera valet av gräns.
Vanliga misstag och antimönster
- „Större buffert = högre prestanda“: Detta stämmer för genomströmningstester utan mål för latens, men fungerar inte för interaktiva tjänster. TSQ ersätter överdimensionerade köer med behovsanpassad kredit per flöde.
- „TSQ påverkar genomströmningen“: Om inställningen är korrekt begränsar TSQ burst-trafiken, inte den genomsnittliga hastigheten. Vid bulk-arbetsbelastningar skalar jag upp gränsvärdet måttligt och mäter percentilerna istället för enbart toppvärdet i Mbit/s.
- „Pacing räcker i sig“: Tidsutjämning är viktigt, men utan byte-begränsning hamnar stora GSO-ramar ändå i QDisc. TSQ och pacing kompletterar varandra.
- „Ett värde för alla“: Arbetsbelastningar, länkar och nätverkskort skiljer sig åt. Jag arbetar med intervallvärden och validerar per miljö.
- „Endast TCP påverkas“: Fokus ligger på TCP, men det finns ytterligare inställningsmöjligheter i systemet (t.ex. för UDP-belastning). Jag förhindrar att parallella protokoll okontrollerat överbelastar samma köer.
Slutsats: Latenstiden under kontroll
TSQ flyttar kontrollen av drivrutinsköer till Sockel och minskar därmed överbelastningar direkt vid källan. Jag begränsar antalet förbuffrade byte per flöde och säkerställer på så sätt snabba bekräftelser, lägre RTT och rättvist fördelade köer. I kombination med fq_codel och modern överbelastningskontroll förblir svarstiden pålitlig även under hög belastning. Särskilda fall inom Wi-Fi och inbyggda system hanterar jag med anpassade gränsvärden och tester under verkliga förhållanden. Den som övervakar nyckeltalen och justerar gränsvärdena stegvis håller Fördröjning konsekvent låg, utan att förlora onödig genomströmning.


