9.2. Einfach klassenlose Warteschlangenregeln (QDisc)

Wie gesagt, mit Warteschlangenregeln, ändern wir die Art, wie Daten gesendet werden. Klassenlose (classless) Warteschlangenregeln (Queueing Disciplines - QDisc) sorgen im Großen und Ganzen nur dafür, wie akzeptierte Daten verschoben, verzögert oder fallen lassen werden.

Diese können verwendet werden, um für eine komplette Schnittstelle den Datenverkehr zu formen (shaping), ohne Unterteilungen. Es ist wichtig, dass Du diesen Teil der Warteschlange (Queueing) verstehst, ehe wir zu den klassenbehafteten Warteschlangenregeln-enthält-Warteschlangenregeln (classful qdisc-containing-qdiscs) kommen!

Die mit Abstand ie am weitesten verbreitete Disziplin ist die pfifo_fast qdisc - sie ist die Standardeinstellung. Das erklärt auch, warum diese erweiterten Funktionen so robust sind. Sie sind nichts weiter als 'nur eine andere Warteschlange'.

Jede dieser Warteschlangen hat spezifische Stärken und Schwächen. Nicht alle von ihnen können getestet werden.

9.2.1. pfifo_fast

Diese Warteschlange (queue) ist, wie der Name sagt, First In, First Out, (Als Erster rein, als Erster raus) das bedeutet kein Paket erfährt eine besondere Behandlung. Zumindest nicht ganz. Diese Warteschlange hat 3 sogenannte "Bänder". Innerhalb jedes Bandes gelten FIFO Regeln. Solange Pakete in Band 0 warten, wird Band 1 nicht bearbeitet. Das Gleiche gilt für Band 1 und Band 2.

Die Kernel beachtet die sogenannten 'Type of Service-Flags' der Pakete, und kümmert sich darum 'minimale Verzögerungen' in Pakete von Band 0 einzufügen.

Verwechsel nicht diese 'classless simple qdisc' mit dem 'classful PRIO one'! Obwohl sie sich ähnlich verhalten, ist pfifo_fast klassenlosen und du kannst nicht andere QDiscs mit dem Befehl tc hinzufügen.

9.2.1.1. Parameter & Verwendung

Du kannst pfifo_fast QDisc nicht konfigurieren, es ist der festverdrahtete Standard. So ist sie standardmäßig konfiguriert:

priomap

Festlegung der Paketprioritäten, vom Kernel zugewiesen, als Karte der Bänder. Das Mapping erfolgt auf der Grundlage des TOS-Oktetts vom Paket, es sieht wie folgt aus:

   0     1     2     3     4     5     6     7
+-----+-----+-----+-----+-----+-----+-----+-----+
|                 |                       |     |
|   PRECEDENCE    |          TOS          | MBZ |
|                 |                       |     |
+-----+-----+-----+-----+-----+-----+-----+-----+

Die vier TOS-bis (das TOS-Feld) ist folgendermassen definiert:

Binär Dezimal  Bedeutung                      deutsche Übersetzung
-----------------------------------------------------------------------
1000   8         Minimize delay (md)          minimale Verzögerung
0100   4         Maximize throughput (mt)     maximaler Durchsatz 
0010   2         Maximize reliability (mr)    maximale Zuverlässigkeit
0001   1         Minimize monetary cost (mmc) minimale finanzielle Kosten 
0000   0         Normal Service               normaler Service

Da Bit 1 ist das Rechte von diesen vier Bits ist, ist der tatsächliche Wert des TOS-Feldes der doppelte Wert des TOS-Bits. Tcpdump -v -v zeigt den Wert des gesamten TOS-Feldes und nicht nur die vier Bits. Zu sehen in der ersten Spalte der Tabelle:

TOS     Bits  Bedeutung                Linux Priorität   Band
------------------------------------------------------------
0x0     0     Normal Service           0 Best Effort     1
0x2     1     Minimize Monetary Cost   1 Filler          2
0x4     2     Maximize Reliability     0 Best Effort     1
0x6     3     mmc+mr                   0 Best Effort     1
0x8     4     Maximize Throughput      2 Bulk            2
0xa     5     mmc+mt                   2 Bulk            2
0xc     6     mr+mt                    2 Bulk            2
0xe     7     mmc+mr+mt                2 Bulk            2
0x10    8     Minimize Delay           6 Interactive     0
0x12    9     mmc+md                   6 Interactive     0
0x14    10    mr+md                    6 Interactive     0
0x16    11    mmc+mr+md                6 Interactive     0
0x18    12    mt+md                    4 Int. Bulk       1
0x1a    13    mmc+mt+md                4 Int. Bulk       1
0x1c    14    mr+mt+md                 4 Int. Bulk       1
0x1e    15    mmc+mr+mt+md             4 Int. Bulk       1

Viele Zahlen. Die zweite Spalte beinhaltet den Wert der betreffenden vier TOS-Bits, gefolgt von ihren Bedeutung. Zum Beispiel, 15 steht für ein Paket welches minimale finanzielle Kosten, maximale Zuverlässigkeit, maximalen Durchsatz UND minimale Verzögerung haben möchte. Ich würde dies ein "holländisches Paket" nennen.

Die vierte Spalte zeigt in welcher Art und Weise der Linux-Kernel die TOS-Bits interpretiert, indem sie auf die Priorität abgebildet werden.

Die letzte Spalte zeigt das Ergebnis der Standard priomap. Auf der Kommandozeile sieht die Standard-priomap wie folgt aus:

1, 2, 2, 2, 1, 2, 0, 0 , 1, 1, 1, 1, 1, 1, 1, 1

Dies bedeutet Priorität 4 wird zum Beispiel zu Band Nummer 1 verschoben wird. In der priomap können auch höhere Prioritäten (> 7) gelistet sein, sie entsprechen zwar nicht den TOS-Zuordnungen, können aber mit anderen Mitteln eingestellt werden.

Aus der Tabelle von RFC 1349 (für weitere Details lies bitte dort) erfährst du, wie Anwendungen sehr wohl ihre TOS-Bits setzen könnten:

TELNET                   1000           (minimize delay)
FTP
	Control          1000           (minimize delay)
        Data             0100           (maximize throughput)

TFTP                     1000           (minimize delay)

SMTP 
	Command phase    1000           (minimize delay)
        DATA phase       0100           (maximize throughput)

Domain Name Service
	UDP Query        1000           (minimize delay)
	TCP Query        0000
	Zone Transfer    0100           (maximize throughput)

NNTP                     0001           (minimize monetary cost)

ICMP
	Errors           0000
	Requests         0000 (mostly)
	Responses        <same as request> (mostly)

txqueuelen

Die Länge dieses Queue kommt von der Schnittstellenkonfiguration, welche du mit ifconfig und ip sehen und setzen kannst. Um die Länge eines Queue auf 10 zu setzen, lautet die Eingabe: ifconfig eth0 txqueuelen 10

Diesen Parameter kannst du nicht mit tc setzen!

9.2.2. Token Bucket Filter

Der Token Bucket Filter (TBF) ist eine einfache QDisc, sie funktioniert nur bei Paketen mit einer Rate, die etwas über dem administrativ gesetzten Wert liegen, aber mit der Möglichkeit kurze Ausbrüche über dieser Rate zu erlauben.

TBF ist sehr präzise sowie Netzwerk- und Prozessorfreundlich. Es sollte die erste Wahl sein, wenn du einfach eine Schnittstelle verlangsamen willst!

Die TBF Implementierung besteht aus einem Puffer (Bucket), der ständig mit einigen virtuellen Informationsstücke, Tokens genannt, gefüllt wird. Das geschied mit einer bestimmten Rate (Token-Rate). Der wichtigste Parameter des Buckets ist seine Größe, das ist die Anzahl der Tokens die gespeichern werden können.

Jeder ankommende Token speichert ein ankommendes Datenpaket von dem Queue und wird dann aus dem Bucket gelöscht. Verbinde diesen Algorithmus mit den beiden Strömungen -- Token und Daten, daraus ergeben sich uns drei mögliche Szenarien:

Das letzte Szenario ist sehr wichtig, denn es ermöglicht die verfügbare Bandbreite für durch den Filter gehende Daten administrativ zu gestalten.

Die Anhäufung von Token ermöglicht einen kurzen Ausbruch um Daten über dem Limit noch verlustfrei übergeben zu können, aber jede dauerhafte Überlastung wird dazu führen, dass dauerhaft Pakete verzögert und dann fallen gelassen werden.

Bitte beachte bei der tatsächlichen Umsetzung, die Tokens entsprechen Bytes, nicht Pakete.

9.2.2.1. Parameter & Verwendung

Auch wenn du es wahrscheinlich nicht zum ändern brauchst, stellt TBF einige Knöpfe zur Verfügung. Zuerst die Parameter die immer zur Verfügung stehen:

limit oder latency

Limit ist die Anzahl der Bytes, die in der Warteschlange auf verfügbare Tokens warten werden. Du kannst auch mit dem Parameter der Latenzzeit die maximale Zeit, die ein Paket in der TBF sitzt, angeben. Letztere Berechnung berücksichtigt die Größe des Bucket (Eimers), die Geschwindigkeit und möglicherweise die Peakrate (falls gesetzt).

burst/buffer/maxburst

Größe des Bucket in Byte. Dies ist die maximale Anzahl von Bytes die beim Token unmittelbar verfügbar ist. Eine Regel ist, größere Shaping-Raten erfordern einen größeren Puffer. Für 10Mbit/s auf Intel, benötigst du mindestens 10kByte Puffer, wenn du deine konfigurierte Rate erreichen willst!

Wenn dein Puffer zu klein ist, können Pakete fallengelassen werden, weil mehr Token pro Zyklus kommen als in deinen Bucket passen.

mpu

Ein zero-sized-Paket ist keine Null-Bandbreite. Beim Ethernet ist kein Paket kleiner als 64 Byte. Die Mindestpaketeinheit bestimmt die minimale Token-Nutzung für ein Paket.

rate

Der Speedknopf. Siehe Bemerkungen über obere Grenzen!

Enthält der Bucket Token und darf diese entleeren, so geht das standardmäßig auch bei unendlicher Geschwindigkeit. Ist das unakzeptabel, verwende die folgenden Parameter:

peakrate

Sind Tokens verfügbar und Pakete kommen an, werden sie standardmäßig sofort gesendet, sozusagen mit 'Lichtgeschwindigkeit'. Das ist nicht das was du willst, vor allem wenn du einen großen Bucket hast.

Mit der Peakrate kann festgelegt werden, wie schnell der Bucket erschöpft sein darf. Machen wir alles nach Buch, wird bei Erreichen der Freigabe eines Pakets gerade lange genug gewartet, und das Nächste losgelassen. Wir berechnen unsere Wartezeiten so schicken wir nur innerhalb der Peakrate.

Aufgrund des Standard Unix Zeittakts von 10ms, mit durchschnittlich 10.000 Bits Paketen, begrenzen wir die Peakrate auf 1 Mbit/s!

mtu/minburst

Die 1Mbit/s Peakrate ist nicht sehr sinnvoll, wenn deine regulären Rate höher ist. Eine höhere Peakrate ist möglich beim Senden von mehr Paketen pro Zeittakt, was effektiv bedeutet, wir bauen einen zweiten Bucket!

Dieser zweite Bucket ist standardmäßig ein einzelnes Paket, das ist gar kein Bucket.

Um die maximal mögliche Peakrate zu berechnen, multipliziere die konfigurierte mtu mit 100 (oder korrekter, HZ, 100 bei Intel, 1024 bei Alpha).

9.2.2.2. Beispielkonfiguration

Eine einfache, aber *sehr* nützliche Konfiguration ist:

# tc qdisc add dev ppp0 root tbf rate 220kbit latency 50ms burst 1540

Ok, warum ist das sinnvoll? Wenn du ein Netzwerkgerät mit einer langen Warteschlange hast, wie ein DSL-Modem oder ein Kabelmodem, und du mußt über eine schnelle Schnittstelle kommunizieren, wie eine Ethernet-Schnittstelle, wirst du feststellen, dass der Upload deine Interaktivität total zerstört.

Das kommt, weil der Upload die Warteschlange des Modems voll macht, sie ist wahrscheinlich *riesige* und hilft tatsächlich einen guten Datendurchsatz beim Upload zu erreichen. Aber das willst du nicht, du brauchst eine nicht so grosse Warteschlange um noch einen Rest an Interaktivität zu behalten, so kannst du noch andere Sachen machen beim Senden von Daten.

Die Zeile oben verlangsamt das Senden auf eine Rate, die nicht zu einer Warteschlange im Modem führt, - es ist die Warteschlange von Linux mit der wir eine limitierte Größe steuern.

Ändere die 220kbit auf die *tatsächliche* Geschwindigkeit deines Uplinks, minus ein paar Prozent. Solltest du ein wirklich schnelles Modem haben, hebe den 'Burst' ein wenig an.

9.2.3. Statistisch faire Warteschlange

Statistisch faire Warteschlange / Stochastic Fairness Queueing (SFQ) ist eine einfache Umsetzung der Fair-Queueing-Algorithmen-Familie. Sie ist weniger genau als andere, aber sie erfordert auch weniger Berechnungen, und das wo sie fast perfekt fair ist.

Das Schlüsselwort bei SFQ ist Konversation (oder Fluss), diese entspricht größtenteils einer TCP-Sitzung (Session) oder einem UDP-Stream. Der Traffic wird auf eine ziemlich groöe Anzahl von FIFO-Warteschlangen unterteilt, eine für jede Konversation. Traffic wird dann in einen Rundlauf (Round-Robin) geschickt, so erhält jede Sitzung wieder die Möglichkeit Daten zu senden.

Dies führt zu einem sehr fairen Verhalten und verbietet jedem einzelnen Gespräch den Rest zu übertönen. SFQ ist 'Statistic', weil sie nicht wirklich eine Warteschlange für jede Sitzung zuordnen, es besitzt einen Algorithmus, der den Traffic auf eine begrenzte Anzahl von Queues mit einem Hash-Algorithmus verteilt.

Durch dem Hash können multiple Sessions, am Ende mit dem gleichen Bucket, die Chance zum Senden eines Pakets, dadurch wird die effektiv zur Verfügung stehende Geschwindigkeit halbieren. Um das zu verhindern ändert SFQ seiner Hash-Algorithmus recht häufig, so kollidieren zwei Sitzungen nur für wenige Sekunden.

SFQ ist nur in dem Fall wirklich sinnvoll, wenn deine ausgehende Schnittstelle wirklich voll ist! Ist das nicht so, gibt es keine Warteschlange auf Ihrem Linux-Rechner und hat somit keine Auswirkung. Später beschreiben wir, wie SFQ mit anderen QDiscs kombiniert werden, um eine 'best-of-both worlds'-Situation zu erreichen.

Genauer gesagt, die Einstellung von SFQ auf der Ethernet-Schnittstelle zu deinem Kabelmodem oder DSL-Router ist sinnlos ohne weiteres Shaping!

9.2.3.1. Parameter & Verwendung

Der SFQ ist ziemlich viel Selbstoptimierung:

perturb

Rekonfiguriere das Hashing einmal auf zu viele Sekunden. Wenn nichts gesetzt ist, wird der Hash niemals neu konfiguriert werden. Nicht zu empfehlen. 10 Sekunden sind wahrscheinlich ein guter Wert.

quantum

Anzahl der Bytes eines Streams welches aus der Warteschlange raus dürfen ehe die nächste Warteschlange neu beginnt. Der Standardwert ist 1 mal die maximale Paketgröße Paket (MTU-Größe). Nicht unter die MTU gesetzen!

limit

Die Gesamtzahl der Pakete, die in die SFQ-Warteschlange gestellt werden (danach beginnt das fallen lassen).

9.2.3.2. Konfigurationsbeispiel

Du hast ein Device, welches eine identische Verbindungsgeschwindigkeit und wirklich verfügbaren Rate hat, wie ein Telefon-Modem, wird diese Konfiguration helfen die Fairness zu fördern:

# tc qdisc add dev ppp0 root sfq perturb 10
# tc -s -d qdisc ls
qdisc sfq 800c: dev ppp0 quantum 1514b limit 128p flows 128/1024 perturb 10sec 
 Sent 4812 bytes 62 pkts (dropped 0, overlimits 0) 

Die Anzahl 800c: ist die automatisch zugewiesenen Handle-Nummer, Limit bedeutet, 128 Pakete können in diesem Queue warten. Es stehen 1024 hashbuckets für die Buchhaltung zur Verfügung, davon können 128 gleichzeitig aktiv sein (es passen nicht mehr Pakete ins Queue!) Alle 10 Sekunden werden die Hashes neu konfiguriert.