このページでは InfiniBand の再送制御の機構と特徴について説明する。
以下は関連ページ。
更新履歴
(2014.04.10) 2013年4月8日の日記から作成。
(2014.04.17) 「3. RDMA READ と ATOMIC Operations の再送」を追加。
(2014.05.11) API にリンクを貼る
目次
1. 再送が発生する理由
InfiniBand は信頼性のある(reliable)サービス、つまり Reliable Connection(RC) サービスと Reliable Datagram(RD) サービスでのみ再送を行う。 Unreliable Connection(RC) サービスと Unreliable Datagram(UD) サービスはパケットの到着を確認しないため再送も行わない。
再送制御を考える場合、原則として送信側(requester)と受信側(responder)の二者が登場する。 そして再送が発生するのは以下の 3 つのパターンである。
- Requester が投げたパケットに対して、一定時間内に responder からの Acknowledge(ACK) または Negative-Acknowledge(NAK) が返ってこない場合。
- Requester が投げたパケットに対して、responder が Receiver-Not-Ready(RNR) NAK の応答を返した場合。
- Requester が投げたパケットに対して、responder が Out-of-sequence NAK の応答を返した場合。
1. は requester がパケット送信時に HCA が Local ACK Timer を設定する。 時間内に ACK または NAK が返ってこなければタイムアウトとなり、パケットの再送に移るという機構である。 これが発生する原因はファブリックの HCA・スイッチ・ケーブルの故障、QP ステートの遷移が送信可能または受信可能になっていない場合などがありえる。
2. は responder の QP に結び付けられた Receive Queue(RQ) または Shared Receive Queue(SRQ) の Receive WQE が枯渇していた場合である。 基本的な概念編で説明したように、responder 側の RQ または SRQ の Receive WQE がゼロの時に SEND オペレーションまたは RDMA WRITE with Immediate オペレーションを受けると、responder の QP は RNR NAK という特別な NAK を requester 側に返す。 Requester 側は RNR NAK を受信すると、規定時間後にパケットを再送する。
3. は reliable サービスは 基本的な概念編で説明したように PSN を使って順序制御を行っている。 Responder の QP 受け取るパケットに含まれる PSN が期待したものより大きい場合は、途中でパケットロスが発生したことが分かる。 この時、responder は Out-of-sequence NAK の応答を返す。 Requester の QP は Out-of-sequence NAK を受け取ると、タイマーなしで Send Queue(SQ) に残っている Send WQE を順に再送する。
2. 再送間隔と回数
InfiniBand の 2 種類の再送タイマーについて、その再送間隔と再送回数の上限について説明する。
2.1 Local ACK Timeout
Local ACK Timeout は requester 側の QP に ibv_modify_qp() で設定する timeout (Local ACK Timeout) と retry_cnt (Retry count) のパラメータが再送間隔と再送回数の上限を決定する。
QP からメッセージ送信後にタイマー時間内に ACK が返って来ない場合には再送が行われる。 タイマー時間が経過する度に retry_cnt が減算され、これがマイナスになるとタイムアウトで Send Work Request は完了エラー(IBV_WC_RETRY_EXC_ERR)となる。 retry_cnt は 3 ビットカウンターで 0〜7 が指定できる。

もし retry_cnt が 0 の場合は 1 回だけ送信し、タイマー時間だけ待って ACK が返ってこなければタイムアウトする。

タイマー時間は timeout で決まる。 これは 5 ビットのカウンタで、1回のタイマー間隔を Ttr = 4.096 us * 2timeout で指定する。 ただし仕様上、タイマー時間は 4 倍の誤差を許容しており Ttr 〜 4 * Ttr であればよい。
timeout に 0 を指定した場合はタイマーは無効(再送時間無限)になり、ファブリックの輻輳や異常が原因でパケットが落ちても再送が発生しなくなる。 タイムアウトも起きないので Send Queue の中で WQE は留まり、ibv_poll_cq() で完了が報告されることもない。
|
|
Local ACK Timeout のタイマーは仕様上の最小時間が 8.192 マイクロ秒だが、実機の分解能はこれよりもはるかに悪い。 Mellanox ConnectX-3 では設定にもよるが最小の分解能は 0.1〜1秒以程度である。
HCA の持つタイマーの分解能の自己申告値は ibv_query_device() の local_ca_ack_delay の項目で取得できる。 local_ca_ack_delay が timeout に設定できる最小の有効値が入っている。
local_ca_ack_delay は ibv_query_device() を実行しなくても、ibv_devinfo コマンドを使えば表示できる。 Mellanox ConnectX-3 を RHEL 6.4 上で動かすと、local_ca_ack_delay は 15 と表示される。 再送間隔は 134.217728 ミリ秒からその 4 倍の 536.870912 ミリ秒の間ということになる。
# ibv_devinfo -v | grep grep local_ca_ack_delay local_ca_ack_delay: 15
2.2 Receiver-Not-Ready(RNR) Negative-Acknowledge(NAK) Retry
Local ACK Timeout による再送制御をコントロールするのは requester の QP に ibv_modify_qp() で設定したパラメータであったが、RNR NAK による再送制御をコントロールするのは requester の QP のパラメータと responder の QP のパラメータに分かれている。
再送回数の上限を決めるのは requester の QP に ibv_modfiy_qp() で設定した rnr_retry である。 このパラメータは 3 ビットで 0〜7 を指定できる。 Requester が RNR NAK を受け取る度に rnr_retry が減算して行き、0 の時にさらに RNR NAK を受けると、送信の Work Request はエラー(IBV_WC_RNR_RETRY_EXC_ERR)となる。 ただし rnr_retry は 7 が特別値で、この値の場合は何回 RNR NAK を受けても無限に再送を繰り返すようになる。
一方、再送間隔は responder の QP に ibv_modify_qp() で設定された min_rnr_timer が決定する。 このタイマー間隔を Minimum RNR NAK Timer と呼ぶ。 min_rnr_timer は 5 ビットの値で、各値のタイマー時間は下記の表のように決められている。 Local ACK Timeout と異なり min_rnr_timer が 0 の場合は無効ではない。
|
|
HCA によっては Local ACK Timeout のタイマー時間はいい加減だが、minimum RNR NAK Timer のタイマー時間はそれよりは正確のようだ。
RNR NAK が返ってきた場合、Requester は最小 minimum RNR NAK Timer 時間ほど待ってから再送を行う(この際、rnr_retry を減算する)。 これは RNR NAK のパケットの中に responder の QP の min_rnr_timer が埋め込まれており、requester はこの情報を使って minimum RNR NAK Timer を算出することで実現している。

プログラム的には受信側プログラムが SQ や SRQ を再充填する間隔を計算した上で、min_rnr_timer を設定することになる。
2.3 パラメータ設定の非対称性
上記のように minimum RNR NAK Timer を設定する min_rnr_timer は、それを設定した自分自身で使うのではない。 このため同じ再送制御のパラメータであっても min_rnr_timer は Init → RTR で、それ以外は RTR → RTS で設定するという一見いびつな仕様になっている。
Init→RTR | RTR→RTS |
---|---|
min_rnr_timer | timeout retry_cnt rnr_retry |
Local ACK Timeout はタイムアウト時間に「無限時間待つ」があるが「無限回数再送する」はなく、RNR NAK Retry がその逆「無限時間待つ」がなく「無限回数再送する」があるのも、この非対称性のためだと思われる。
3. 受信側が処理済みのパケットが再送されたら?
概念編で述べたように再送制御は送信側(requester) の QP の sq_psn と受信側(responder)の QP の rq_psn を使って、送信がどこまで進んでいるかを把握する。 PSN を使うことで、requester から responder へ送られる途中でパケットがロスした場合の再送制御は完全に行える。
しかし responder がいったんパケットを処理し、その応答パケットを requester を投げ返したが、途中でパケットがロストする可能性がある。 この場合でも、requester から Local ACK Timeout による再送が実施される。 この再送パケットをうまく処理するには PSN だけでは不十分の場合がある。
3.1 SEND と RDMA WRITE with/without Immediate の場合
SEND オペレーションや RDMA WRITE with/without Immediate オペレーションの場合、パケット内の PSN が QP の rq_psn よりも「古」ければ何もせずただ応答パケットを返すだけでよい。

3.2 RDMA READ の場合
RDMA READ オペレーションは requester から responder へリクエストが送られた後に、responder が応答パケットの形でデータを requester へ転送する。 このため responder 側には RDMA READ リクエストを保存する管理テーブルがあり、1 つの RDMA READ オペレーションがテーブル内の 1 つのエントリとして記録される。
応答パケットがパケットロスした場合、requester は再送を行うが、これは最初の RDMA READ リクエストのうち、すでに受信した部分を除いた縮小した RDMA READ リクエストとなる。 Responder は管理テーブルに既に登録されているエントリと送信範囲が被るので、これが再送であることを認識できる。 既存のエントリを書き換える形でテーブルを更新する(再送 RDMA READ リクエストをテーブルに新規登録すると、同じ範囲を 2 度転送することになるため)。

3.3 ATOMIC Operations の場合
ATOMIC Operation は Fetch and Add オペレーションと Compare and Swap オペレーションの 2 種類がある。 だがどちらのオペレーションでも 1 回のオペレーションに付き、メモリ操作は 1 回だけに留める必要がある。 もし再送の度にメモリ操作を実行すると期待した結果と異なる結果になる。
また ATOMIC Operation はメモリ操作前のメモリ内容(8バイト)を応答パケットに入れて requester へ送り返すが、この内容はあくまでもメモリ操作を実行したタイミングの結果である必要がある。
このため ATOMIC Operations のための ATOMIC 操作管理テーブルを設け、最初のリクエストによるメモリ操作の結果を記録する。

3.4 RDMA READ と ATOMIC Operations の同時実行数
3.2 と 3.3 で述べたように RDMA READ と ATOMIC Operations には管理テーブルが必要だが、InfiniBand の仕様はこれを RDMA READ と ATOMIC Operations 兼用と定義している。 そのためテーブルのサイズが RDMA READ リクエストの同時受信数と ATOMIC Operations の同時受信数の合計を決めている。 テーブルの大きさは QP の max_dest_rd_atomic 属性によって決定され、これはチュートリアル編で述べたように Init → RTR にステートを遷移させる際に設定する。
また受信側の max_dest_rd_atomic で同時受信の最大数が決まっている以上、送信側にも制限が必要である。 QP の max_rd_atomic 属性は RDMA READ オペレーションと ATOMIC Operations の同時発行数の最大を決める。 同時発行数が最大の場合に SQ の次の Send WQE が RDMA READ オペレーションか ATOMIC Operations の場合は、送信は停止されることになる。 max_rd_atomic はチュートリアル編で述べたように RTR → RTS にステートを遷移させる際に設定する。
設定を誤って送信側 QP の max_rd_atomic > 受信側 QP max_dest_rd_atomic とした場合、max_dest_rd_atomic を越えた RDMA READ & ATOMIC Operations が送られることがある。 上限を越えた場合、受信側には Local Access Violation Work Queue Error (IBV_EVENT_QP_ACCESS_ERR) の非同期エラーが発生し、送信側には Remote Invalid Request Error (IBV_WC_REM_INV_REQ_ERR) の完了エラーが発生する。
4. 再送制御の落とし穴
再送制御に関してマニュアルを読んだだけでは理解し辛い項目について補足しておく。
4.1 Local ACK Timer を無限に設定すると何が起きるか?
Local ACK Timer の時間は timeout に 0 を設定すると「無限」となる。 ACK の返ってこなかった Send WR に対して同じメッセージを再送しなくなる。 ただし、IBV_WC_RETRY_EXC_ERR の完了エラーが発生しなくなるわけではない。
確かに単発のリクエストに ACK が返ってこない場合、あるいは連続するリクエストの全てに ACK が返ってこない場合は再送が無限に停止してしまう。

しかし輻輳が原因でパケットを失った場合や、InfiniBand ファブリックの一時的な故障が原因でパケットが失われた場合、しばらくするとネットワークが回復し、後続のリクエストは responder に到着する場合がある。 この場合、reliable サービスは PSN を使って順序制御しているので、responder には前に送られるべきリクエストが失われたことを検知できる。 その結果、out-of-sequence NAK を requester に返す。 Requester は NAK を解釈して、Send Queue (SQ) に残っている最初のリクエストから再送を実行する。

上の図は out-of-sequence NAK による再送によって、通信は成功している。 しかし再送時にネットワーク異常が起こった場合、これは再送失敗とカウントされ retry_cnt は減算される。 そのためネットワーク異常が間髪的に起こるような不安定なファブリックでは、リトライカウンターが 0 になって IBV_WC_RETRY_EXC_ERR の完了エラーが発生することが考えられる。
4.2 そもそも ACK はどこに送られるのか?
再送制御は ACK や NAK が responder から requester に送られることを前提としている。 だがそもそも responder は何処に ACK/NAK を返しているのだろうか?
普通に考えれば responder が受け取ったパケットの送信元(Source) LID の送信元(Source) QP に送り返せばよいはずである。 しかし実はそうはなってない。 InfiniBand の機構的には以下の 2 つの問題があるためである。
- Responder は Init → RTR で ibv_modify_qp() を使って QP に設定する dlid のパラメータと、パケットの Destination LID が一致しなければ NAK も返さずに破棄(silently drop)してしまう。
- 信頼性のあるコネクション(Reliable Connection; RC)サービスのパケットには Source LID、Destination LID、Destination QPN は含まれているが、Source QPN が含まれていない。
特に 2. が問題である。 パケットを見ても送信元は分からない。 ではどうするかというと Init → RTR で設定した dlid、dest_qpn_num 宛てに送信する。 その結果、ibv_modify_qp() を使ったパラメータ設定を間違えると、ACK/NAK を requester ではない QP に送信してしまうこともある。

また上記の議論でお分かりだと思うが、QP が ACK/NAK を返せるのは QP ステートの中で受信可能な状態(RTR、RTS、SQD、SQE)である必要がある。 QP のエラー時には Error ステートに落ちるが、このステートでは dlid も dest_qp_num も失効しているので NAK が送れなくなっている。 そのため responder の QP がいったん Error ステートとなると、requester の QP 側は Local ACK Retry が上限に達するまで再送を繰り返してから IBV_WC_RETRY_EXC_ERR の完了エラーを受け取ることになる。
InfiniBand はその低遅延を売りにして、証券取引所のシステムやその取引所へアクセスする証券会社のシステムで使われている。 たしかに InfiniBand は正常動作時の遅延は非常に小さいのだが、異常時に異常を検出可能になるまでの遅延時間は非常に大きい。 High-Frequency Trading(HFT)の世界で、InfiniBand のこの異常時遅延の増大をどのように抑えているかはよく分からない。 知っている人がいたら教えてください。