9.2. シンプルな、クラスレスのキューイング規則

既に述べたように、キューイング規則を用いると、 データの送信のされ方を変更できます。 クラスのない (クラスレスな) キューイング規則とは、 おおむねデータを受信し、それを再スケジュール・遅延・破棄するようなものです。

これを用いると、再分割 (subdivision) を用いずに、 インターフェース全体のトラフィックを絞れます。 クラスのある (クラスフルな) 「qdisc を含む qdisc」に進む前に、 この部分を理解しておくことはとても大切です。

これまでのところ、最も広く用いられている規則は pfifo_fast qdisc です (これがデフォルトです)。これは、 なぜこれらの先進的な機能の信頼性がこれほど高いのかも説明してくれます。 これらは「ひとつ余分のキュー」以上のものではないのです。

これらのキューには、それぞれ長所と短所があります。 これらのすべてが、完全にテストされているというわけではありません。

9.2.1. pfifo_fast

このキューは、名前からわかるとおり、「先入れ先出し (First In, First Out)」 で、つまりどの受信パケットも特殊な扱いは (少なくともそれほどは) されません。 このキューには 3 つのいわゆる「バンド」があります。 各バンドには FIFO のルールが適用されます。 しかし、バンド 0 に待ちパケットがあると、バンド 1 は処理されません。 バンド 1 と バンド 2 も同じ関係にあります。

カーネルは、パケットのいわゆる「サービスのタイプ (Type of Service)」フラグを 尊重し、「最小遅延 (minimum delay)」パケットをバンド 0 に入れるよう計らいます。

このクラスレスかつシンプルな qdisc を、 クラスフルな PRIO qdisc と混同しないこと。 両者は同じように動作しますが、pfifo_fast はクラスレスですから、 tc コマンドで他の qdisc を追加することはできません。

9.2.1.1. パラメータと使い方

pfifo_fast qdisc は出来合いのデフォルトですから、 ユーザによる設定はできません。 ここではデフォルトでどのように設定されているかを示します。

priomap

パケットの優先具合が、カーネルによって、どのバンドに対応付けられるかを決めます。 対応付けはパケットの TOS に従って決まります。TOS は以下のようなものです。

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

TOS の 4 ビット (「TOS フィールド」) は次のように定義されています。
Binary Decimcal  Meaning
-----------------------------------------
1000   8         Minimize delay (md)
0100   4         Maximize throughput (mt)
0010   2         Maximize reliability (mr)
0001   1         Minimize monetary cost (mmc)
0000   0         Normal Service

この 4 ビットの右にはもう 1 ビットありますから、 TOS フィールドの実際の値はこれらの TOS ビットから決まる値の 2 倍になります。 tcpdump -v -v としたときは、この 4 ビット列の値ではなく、 TOS フィールド全体の値を表示します。 これは、次の表の第一列にある値になります。

TOS     Bits  Means                    Linux Priority    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

たくさん数字が並んでいますね。第二列は、 これらに対応する 4 つの TOS ビット列の値です。 続いてそれらの意味する内容が示されています。 例えば 15 は、このパケットは Minimal Monetary Cost, Maximum Reliability, Maximum Throughput, Minimum Delay のすべてを期待しているパケットを意味しています。 私はこれを「強欲なパケット (Dutch Packet)」と呼びたいと思います。 [訳注: 著者の Hubert さんもオランダ人 :-)]

第 4 列は Linux カーネルによる TOS ビット列の解釈です。 どの優先度にマップされるかを示しています。

最後の列は、デフォルトの優先度マップ (priomap) の結果です。 コマンドラインでは、デフォルトの優先度マップは次のようになります。
1, 2, 2, 2, 1, 2, 0, 0 , 1, 1, 1, 1, 1, 1, 1, 1

つまり例えば優先度 4 のパケットは、バンド番号 1 へとマップされます (訳注: 優先度は 0 から始まることに注意)。 優先度マップでは高い優先度 (> 7) をリストすることもできます。 これらは TOS マッピングには対応していませんが、 他の方法によって設定できます。

この表は RFC1349 (詳細は直接こちらを) から取ったもので、 各アプリケーションがどのように TOS ビットを設定すると良いかについて示してあります。
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

このキューの長さはインターフェースの設定から決まります。 インターフェース設定は ifconfig や ip で閲覧・設定できます。 キューの長さを 10 にするには、 "ifconfig eth0 txqueuelen 10" と実行します。

このパラメータは tc では設定できません。

9.2.2. トークンバケツフィルタ (Token Bucket Filter)

トークンバケツフィルタ (Token Bucket Filter: TBF) は 単純な qdisc で、管理者が設定した速度を越えない範囲で到着パケットを通します。 ただし短い時間の突発的なものなら、この値を越えることを許す可能性があります。

TBF は非常に正確で、ネットワークとプロセッサへの負荷も軽いです。 単純にインターフェースの速度を落としたいと思ったときには、 まず最初にこの利用を考えてみるべきです。

TBF の実装はバッファ (バケツ) であり、 これは定期的に仮想的な情報の断片 (トークンと呼ばれます) によって、 一定の割合 (トークン速度) で満たされていきます。 バケツの最も重要なパラメータはサイズで、 保持できるトークンの数を意味します。

バケツに入った各トークンは、 それぞれひとつの受信データパケットをデータキューから拾い、 そしてバケツからは削除されます。 この 2 つの流れ (トークンとデータ) からなるアルゴリズムには、 3 つのシナリオが考えられます。

後ろの 2 シナリオがとても重要です。 これらは、このフィルタを通過するデータのバンド幅を、 管理者が制御できることを意味しているからです。

トークンが溜まっていくと、制限を越えたデータバーストも、 短いものならロスなしに通過できますが、 過負荷状態が続くとパケットはだんだん遅延していき、ついには破棄されます。

ただし実際の実装では、トークンが対応しているのはバイトであり、 パケットではありません。

9.2.2.1. パラメータと使い方

ほとんど変更の必要はないでしょうが、 TBF にも調整つまみはついています。 まず、つねに指定できるパラメータです:

バケツにトークンが入っていて、これを空にすることが許されるとき、 デフォルトではこの作業は無限の速度で行われます。 これがまずい場合には、以下のパラメータを使って下さい。

peakrate

トークンが使える状態でパケットが到着すると、 デフォルトではそのパケットはすぐさま、いうならば「光速」で送り出されます。 これは、特に大きなバケツを使っている場合には、 望ましいことではないかもしれません。

peakrate は、バケツを空ける際に許される速さを決めるために用います。 すべてが本に書いてある通りなら、 この動作は「パケットを送って、十分長い間待って、そして次のパケットを送る」 ことによって実現されています。 待ち時間は、ちょうど peakrate で送られるように計算してあります。

しかしながら、Unix でのデフォルトの時間単位は 10ms なので、 パケットサイズの平均を 10.000 ビットとすると、 peakrate は 1mbit/s にしかできません!

mtu/minburst

1mbit/s の peakrate は、これが通常の速度より遅ければ、 あまり便利とは言えません。 時間単位ごとに送れるパケットの数を増やせば、 より peakrate は大きくできます。 これは実質的には 2 番目のバケツを作るのと同じことです。

この 2 番目のバケツは、デフォルトでは 1 つのパケットでしかなく、 つまり実際にはバケツではありません。

peakrate の最大許容値を計算するには、 mtu の設定値に 100 (あるいは正確には HZ。これは Intel では 100, Alpha では 1024) をかけてください。

9.2.2.2. 設定例

シンプルですが、とっても便利な設定を示します。
# tc qdisc add dev ppp0 root tbf rate 220kbit latency 50ms burst 1540

はい、なぜ便利なのか説明しましょう。 DSL モデムやケーブルモデムのように、 キューの大きなネットワークデバイスがあるとして、 そのデバイスには (イーサネットインターフェースのような) 高速なデバイスで通信しているとしましょう。 するとアップロードの際に、対話通信が完全にダメになってしまいます。

この理由は、アップロードがモデムのキューを一杯にしてしまうからです。 このキューはおそらく非常に大きく、 実際これによってアップロードのスループットが向上しています。 しかしこれは望ましいことではないでしょう。 キューをあまり大きくせずにおいて、 データの送信時にも他のことができるよう、 対話通信を可能としておきたいことが多いはずです。

上記の実行例では、モデムのキューが必要ない程度にまで、 送信速度を落としています。これでキューは Linux 上にあり、 これに対して制限を課すことができます。

220kbit は、あなたの環境の「実際の」速度 (マイナス数パーセント) にしてください。手持ちのモデムが非常に高速なら、'burst' を少々増やしてください。

9.2.3. 確率的不偏キューイング (Stochastic Fairness Queueing)

確率的不偏キューイング (Stochastic Fairness Queueing: SFQ) は、不偏キューイングのアルゴリズムをシンプルに実装したものです。 これは他のものほど正確ではありませんが、 あまり計算量が必要なく、それでいて非常に公平です。

SFQ でのキーワードは「会話 (または流れ)」で、 これは TCP セッションや UDP ストリームとほとんど同じ意味です。 トラフィックはかなり多い数の FIFO キューに分割され、 各キューがそれぞれの会話に対応します。 そしてトラフィックはラウンドロビン的に送られます。 すなわちデータを送る機会が各セッションに順番に与えられます。

これは非常に公平な動作につながり、 ある特定の会話が残りをかき消してしまうようなことはなくなります。 SFQ には「確率的」と名前がついていますが、 これは SFQ が実際には各セッションごとにキューを割り当てるのではなく、 トラフィックをある限られた数のキューに、 ハッシュアルゴリズムを使って分割するからです。

このハッシュのため、複数のセッションが同じバケツに入ることもあり得ます。 各セッションがパケットを送信する機会を握っているのはこのバケツなので、 実効的な速度は半分になってしまいます。 この状況が目立つのを防ぐために、 SFQ はハッシュアルゴリズムを極めて頻繁に変更し、 あるセッション同士の衝突が、短い時間しか続かないようにしています。

これは重要なポイントですが、 SFQ が有効なのは、実際の出力インターフェースが、 本当に一杯一杯になっている場合に限られます。 そうでなければ、あなたの linux マシンのキューはほぼ空っぽで、 よって何の効果も持ちません。 SFQ を他の qdisc と組み合わせ、 「両方にいい顔をしたい」状況を実現するための方法については後ほど議論します。

特に、ケーブルモデムや DSL ルータに向かうイーサネットに対して SFQ を設定するのは、他の制限を行わないならば的外れです。