QoS / Buffer の概念地図¶
QoS の話は語彙が多くて、どこから読めばよいかが見えづらいです。ここでは「パケットが入ってから出るまで、どこで何が決まるか」を一本道で並べ、それぞれの設定テーブルがどの段階に作用するかを示します。
QoS / Buffer 機能は何を解決するか¶
ネットワーク機器の「公平性」と「優先度」は、ASIC のバッファとキューが有限であるという物理的な事実から逃れられません。100Gbps のリンクが 4 本同じ出口に向けて 100Gbps を送り出そうとした瞬間、3 本ぶんの 300Gbps はバッファに積むしかなく、バッファが尽きればドロップします。QoS / Buffer サブシステムが解決しているのは、この限られたバッファとキューを「どのトラフィックを優遇するか」「どこで止めるか」「どこで捨てるか」というポリシで埋め、運用が意図した挙動に揃えることです。
具体的には次の問いに答えます。
- RoCE のような lossless が必要なトラフィックが詰まったとき、上流に PAUSE を送って物理的に止めたい
- 動画やストリーミング telemetry のように遅延に敏感なトラフィックを strict priority で先に出したい
- best effort トラフィックは公平に分け合いつつ、急に増えたフローだけ早めに ECN マークして TCP に減速を促したい
- バッファのピーク占有を可視化し、設計時の見積もりと実運用のギャップに早期に気付きたい
これらが全て同じ ASIC リソースを取り合っているので、設定テーブルが多層に分かれているわけです。
SONiC 内での位置¶
QoS / Buffer は データプレーンに最も深く張り付くサブシステムで、控えめに見ても以下の 3 層にまたがります。
- 設定 (control plane 寄り):
CONFIG_DBのBUFFER_*、QUEUE、SCHEDULER、WRED_PROFILE、各種 MAP テーブルをqosorch/bufferorchが読み、APPL_DB → ASIC_DB と流して syncd 経由で SAI コール(create_buffer_pool、create_queue、create_schedulerなど)に変換します。 - データプレーン: ASIC 内のキュー、PG、バッファプール、WRED ドロッパ、scheduler 木が実体です。SONiC は SAI 越しに「設定」を流し込むだけで、パケットごとの判定は ASIC 側で完結します。
- 可観測性: FlexCounter が queue / PG / pool の使用量と WRED / ECN カウンタを定期的に COUNTERS_DB に落とし、watermark は別 group として「観測区間ピーク」を保持します。
show queue counters/show priority-group watermarkなどの CLI は COUNTERS_DB / STATE_DB を読むだけです。
つまり運用者が触れる Redis テーブルと、実際にパケットが通る ASIC 構造体が 1:1 に近い距離で対応している、それが QoS / Buffer 章を読むときの前提です。
用語の定義¶
混同しやすい用語をここで一度切り分けておきます。
- TC (Traffic Class): SONiC 内部で使うクラス番号 (0〜7)。受信側で DSCP / DOT1P / EXP から 1 つに決め打ちし、以後の経路選択(PG、queue、scheduler、WRED)は TC を経由します。
- PG (Priority Group): ingress 側のバッファ計上単位。
xon/xoffを持って PFC のトリガを担います。lossless TC は通常 PG3 / PG4 に固定します。 - Queue: egress 側のバッファ計上 + 出力順制御単位。WRED ドロッパと scheduler はここに紐付きます。典型的には UC 用 8 個 + MC 用 8 個。
- Buffer Pool: ingress / egress 別に存在する共有バッファのプール。
size、mode(static / dynamic)、type(lossless / lossy) を持ちます。 - Buffer Profile: 「ある PG / queue がこの pool からどれくらい取って、どう alpha (
dynamic_th) で配分し、xon/xoffの閾値はいくつにするか」をまとめた 1 セット。同じ profile を複数の PG / queue で共有します。 - WRED / ECN: 平均キュー長がある閾値を超えたら、確率的に drop または ECN mark する仕組み。WRED テーブルは色(green / yellow / red)ごとに別閾値を持てます。
- PFC (Priority-Based Flow Control): 802.1Qbb。PG 単位で「止めて」をリンク相手の MAC 層に送り、特定 priority のフレームだけ流量を絞る仕組み。
- PFCWD: PFC を受けたまま回復しない queue / port を強制的に drop モードに落として、PFC storm を波及させない監視 daemon。
- Watermark: queue / PG / pool の使用バッファ量の「観測区間内ピーク」。SAI カウンタを SONiC が定期 snapshot します。
典型シーン: RoCE クラスタでバッファが溢れかける夕方¶
GPU クラスタの allreduce が走り始めて、ToR 1 台の特定 uplink で TC3 (lossless) のキュー深さが急上昇する場面を想像してください。設定が揃っているとき、SONiC とハードウェアの間ではこう動きます。
flowchart LR
IN["<b>ingress packet</b><br/>DSCP=26"]:::pkt
subgraph INGRESS["Ingress (受信)"]
direction TB
M1["DSCP_TO_TC_MAP<br/>→ TC=3"]:::map
M2["TC_TO_PG_MAP<br/>→ PG=3"]:::map
PGBUF["<b>PG buffer</b><br/>xoff 接近"]:::buf
PFC["<b>PFC frame</b><br/>upstream に PAUSE"]:::alert
end
subgraph EGRESS["Egress (送信)"]
direction TB
M3["TC_TO_QUEUE_MAP<br/>→ Q=3"]:::map
QBUF["<b>queue buffer</b>"]:::buf
WRED["WRED / ECN mark"]:::policy
SCH["SCHEDULER<br/>strict / DWRR"]:::policy
end
OUT["<b>出力</b>"]:::pkt
CNT[("COUNTERS_DB<br/>PFC_RX/TX")]:::obs
WM[("WATERMARK<br/>queue peak")]:::obs
IN --> M1 --> M2 --> PGBUF
PGBUF ==>|xoff 到達| PFC
PGBUF --> M3 --> QBUF --> WRED --> SCH --> OUT
PFC -.観測.-> CNT
QBUF -.観測.-> WM
classDef pkt fill:#fff3cd,stroke:#856404,stroke-width:2px,color:#000;
classDef map fill:#e2e3e5,stroke:#41464b,color:#000;
classDef buf fill:#d1ecf1,stroke:#0c5460,stroke-width:2px,color:#000;
classDef alert fill:#f8d7da,stroke:#721c24,stroke-width:2px,color:#000;
classDef policy fill:#d4edda,stroke:#155724,color:#000;
classDef obs fill:#e8d5f0,stroke:#4b0082,color:#000;
このとき運用者が見るのは大体次の 3 つです。
show priority-group watermarkで PG のピーク占有を確認し、xoff にどれだけ近付いたかshow queue countersで TC3 queue のWRED_DROPPED/ECN_MARKEDの伸びshow pfc countersで実際に PFC を送信した方向と件数
これらが揃わなければ、CLI、設定、ASIC のどこで止まっているかを切り分ける順序が立ちません。
パケットが通る順番¶
- 受信ポートで TC を決める:
DSCP_TO_TC_MAP(IP の場合)またはDOT1P_TO_TC_MAP(L2 の場合)が、ポートで適用されるPORT_QOS_MAP経由で参照されます。MPLS のラベル EXP を扱うときは MPLS TC-to-TC マップ が追加で挟まります。 - TC を ingress PG に対応付ける:
TC_TO_PG_MAPが ingress priority group (PG) を決めます。ingress 側のバッファ計上は PG 単位で行われます。 - 入力バッファに積む:
BUFFER_PGテーブルで PG ごとにBUFFER_PROFILEが割り当てられ、その profile がBUFFER_POOLを指します。lossless トラフィック(典型的には RoCE 用の TC3/TC4)はここで PFC 用 headroom (xon/xoff) を持ちます。 - 出力 queue を決める:
TC_TO_QUEUE_MAPが egress queue を選びます。 - 出力バッファに積む:
BUFFER_QUEUEが queue 単位でBUFFER_PROFILEを割り当てます。QUEUEテーブルでは queue ごとの WRED と scheduler を紐付けます。 - 混んできたら捨てるか mark する:
WRED_PROFILEで WRED / ECN 閾値を決めます。詳細は WRED and ECN statistics を参照。 - 取り出す順番を決める:
SCHEDULERが strict priority / DWRR / shaping を設定します。sonic-qos-scheduler-and-shapingHLD が背景です。詳細は SONiC QoS scheduler / shaping。
このうち「入力側で止める仕組み」が PFC、「出力側で捨てる/mark する仕組み」が WRED / ECN、「使ったバッファのピークを覚える仕組み」が watermark です。
バッファに登場する 4 つのテーブル¶
| テーブル | スコープ | 役割 |
|---|---|---|
BUFFER_POOL |
switch-wide | ingress / egress のプール総量とモード(static/dynamic) |
BUFFER_PROFILE |
共有 | 各 PG / queue が使う size / xon / xoff / dynamic_th の塊 |
BUFFER_PG |
port × PG | 入力側の PG にプロファイルを紐付ける |
BUFFER_QUEUE |
port × queue | 出力側 queue にプロファイルを紐付ける |
xon/xoff を持つ profile が ingress PG に当たって初めて「lossless」と呼べる経路になります。lossless と lossy は別 TC として DSCP_TO_TC_MAP で振り分けるのが原則です。
QoS と PFC の境界¶
PFC(Priority-Based Flow Control)は、ingress PG のバッファ占有が xoff を超えた瞬間に「この PG に対応する priority だけ止めて」とリンク相手に通知する仕組みです。
- どの priority に対して PFC を有効にするかは
PORT_QOS_MAP:pfc_enable、それを PG にどう写すかはPFC_PRIORITY_TO_PRIORITY_GROUP_MAPが決めます。 - 受信した側の lossless 動作は asymmetric にもできます。詳細は asymmetric PFC test plan。
- PFC 暴走時にキューを強制停止して回復させるのが PFCWD で、これは別 daemon(pfcwd)として動きます。
Watermark とは何を見ているのか¶
Watermark は「対象オブジェクトの使用バッファ量が、観測区間中に到達したピーク」を 1 値だけ保持する SAI カウンタです。SONiC では WATERMARK_TABLE の telemetry interval ごとに collect され、show queue watermark / show priority-group watermark で見えます。
- 観測対象は queue、PG(shared / headroom 別)、buffer pool(shared / headroom)。
- 観測の単位を「実際に設定で有効なポート / queue / PG だけ」に揃えるのが align-watermark-flow-with-port-configuration HLD です。
- 履歴を残すのは別仕組みで、PFC は PFC historical statistics として保存できます。
詳細な意味付けは watermark counters in SONiC を読むのが早いです。
この章で混同しがちな概念¶
- WRED と PFC は両立する: WRED は egress、PFC は ingress。lossless TC に対しても、最終的な egress drop 抑制のために WRED / ECN を併用するのが普通です。
- scheduler と shaping は同じテーブル:
SCHEDULERのtypeがDWRR/STRICT、meter_type+pirが shaping。 - PG と queue は別物: PG は ingress、queue は egress。
show priority-group ...とshow queue ...を取り違えないようにします。 - buffer profile の数は SAI 依存: ASIC によって最大本数が違うので、似た profile を分けすぎると SAI 側の hardware resource が先に枯渇します。
似た機能との違い¶
QoS / Buffer は他のサブシステムと領域が重なって見えがちですが、それぞれ役割が違います。
| 比較対象 | 共通点 | 違い |
|---|---|---|
| ACL | 「特定のトラフィックを選別する」点 | ACL は match → action(permit / deny / redirect / counter / mirror)。QoS の入口で TC を決めるのも実は ACL ベースで可能だが、通常は DSCP / DOT1P map を使う。 |
| Storm Control | 「ある種類のトラフィックを抑える」点 | Storm Control は BUM (broadcast/unknown unicast/multicast) フレームの rate を粗く制限する仕組み。queue 単位の輻輳制御ではない。 |
| Policer (ingress rate-limit) | 「速度を制限する」点 | Policer は ingress でトークンバケットによる drop / mark。QoS の shaper は egress queue ごとの送出速度上限で、バッファに溜める点が違う。 |
| CoPP | 「CPU 向けトラフィックを優先する」点 | CoPP は trap した制御パケット(BGP/LACP/ARP など)を CPU queue ごとに rate-limit する別系統。一般 forwarding 用の QoS とは queue が別。 |
| Telemetry (gNMI / DTel) | 「混雑状況を可視化する」点 | gNMI は COUNTERS_DB を読むだけ、DTel は ASIC が直接 INT を export。QoS 自体の輻輳判断には関与しない。 |
特に CoPP は QoS と同じ「優先制御」だが対象が CPU bound trap のみ という分離を覚えておくと、show queue counters CPU が指している場所が一気に腹落ちします。
読了後にできること¶
ここまで読めば、次の判断が自分でつけられるはずです。
- 「DSCP 26 のパケットが Q3 に乗らない」という症状を、
DSCP_TO_TC_MAP→TC_TO_QUEUE_MAP→PORT_QOS_MAPのどこで切れているか順序立てて切り分けられる - lossless で動かしたい TC を新規追加する際、
BUFFER_PROFILEを新規にするか既存共有にするかを「ASIC 側 profile 数の制約」と「xon/xoffを変えたいか」で判断できる - PFC が立たない / 暴走するときに、
PFC_PRIORITY_TO_PRIORITY_GROUP_MAPとPORT_QOS_MAP:pfc_enableの両方を確認すべきと分かる show priority-group watermarkの数値が「観測区間中のピーク」であって瞬時値ではないことを踏まえ、COUNTERS_DB:WATERMARK*の polling interval と組で読める- WRED / ECN の green / yellow / red 閾値設計が、
SCHEDULERの strict / DWRR と一緒に設計されないと意味が薄いことが分かる
このあと細部を追うなら、テーブル定義は BUFFER_PROFILE 仕様 と QUEUE 仕様、運用は setup.md と operations.md に進みます。
この章の前提知識¶
この章を読み進める前に、次の章を押さえておくと迷子になりにくい。