InfiniBand プログラムに必要な基本的な概念

作成日:2014.03.29
修正日:2014.05.11

このページでは InfiniBand Verbs プログラムをはじめるのに必要な基本的な概念を説明する。 この文書の情報は RDMA_CM API を使ったプログラムをする場合にも有効だと思われる。

以下は関連ページ。


更新履歴
(2014.03.29) 作成。
(2014.04.04) 6.2 QP番号とマルチキャストと 8 章以降を追加
(2014.04.16) 12.3 Partition を追加
(2014.05.11) API にリンクを貼る


目次

1. イーサーネットを振り返ってみる

InfiniBand を語る前に現在のネットワークの主流になっているイーサーネット(Ethernet)を振り返ってみる。

イーサネットはローカルエリアネットワークを構成するための端末とネットワーク装置の規格で、OSI参照モデルでは物理層とデータリンク層に相当する。 時代によって変遷があるが現在は端末側にネットワークインターフェイスカード(Network Interface Card; NIC)を取りつけ、それをツイステッド・ペア・ケーブルを用いて機器を接続するのが主流だ。 複数台の NIC はハブ(Hub) によって接続する。

NIC は 48ビットのMAC アドレスが割り振られている。 これは NIC の製造段階で割り振られるユニークな値であり、基本的には全ての NIC は別々の値を持っている。 イーサーネットを使った通信は自分の MAC アドレスと通信したい相手の MAC アドレスを指定して行うことになる。

古典的なイーサーネットは、電気的に繋がった NIC がサブネット(Subnet)を構成していた。 一つの NIC が出した信号は電気的にはサブネット内の全ての NIC が受信可能だった。 そのため同時に 2 つの NIC が送信すると信号が混信してしまう。 これを防ぐためにイーサーネットは CSMA/CD (Carrier Sense Multiple Access/Collision Detection) という処理を行った。

  1. 通信を開始したい NIC はまずサブネットを監視して、他の通信中の NIC がないことを確認する。
  2. 伝送路が空いているようなら、自分の通信を開始するがその先頭にプリアンブルと呼ばれる8バイトのダミー情報を付ける。このプリアンブルの送信中に他の NIC が同時に通信を開始して衝突しないかを確認する。
  3. プリアンブル中に邪魔がされなかったら伝送路は自分が占拠したと考えて、通信を送る。
  4. 自分がプリアンブルを送信している間に、他の NIC がプリアンブルを送信したら「衝突」を検出して通信を中断する。他の NIC も同じように衝突を検出するので通信を中断するはずである。
  5. 衝突検出後はランダム時間を待ってから再度プリアンブルを流すところから通信を開始する。
  6. 伝送路の占拠に成功し通信を開始した場合、そのメッセージには自分の MAC アドレスと通信したい相手の MAC アドレスを含めた通信を行う。

伝送路を共有しているのでこの通信内容は全ての NIC に伝わるが、通信中の「相手先の MAC アドレス」と自分の MAC アドレスを比較して自分の宛かどうかを判断する。 自分宛でなければ内容を破棄する。

しかし CSMA/CD はサブネットの NIC の数を増やすごとに、1 NIC が可能な通信量が減ってゆく。 そこでスイッチングハブ(Switch Hub)が生まれた。 スイッチングハブは自身を通過するパケットの宛先 MAC アドレスを学習することで、ネットワークに繋がっている NIC がどのポートに繋がっているかを特定する。

ただし MAC アドレスは 48 ビットのアドレス空間にある程度バラけて存在する。 スイッチングハブが学習できる MAC アドレス数は機種ごとに差があるが数万〜数十万程度になる。 そのためハッシュテーブルなどの複雑なデータ構造での制御が必要となり、スイッチングは数十マイクロ秒程度のレイテンシーは避けられない。

2. では InfiniBand Architecture は?

InfiniBand はイーサネットと同様のサーバ間をつなぐ高速 I/O インターコネクトである。 歴史的には紆余曲折があるが現在はInfiniBand Trade Associationがとりまとめをしている(Wikipedia)。

接続ケーブルとして銅軸や光ファイバーが使われ、接続端子は QSFP や CX4 が使われる。 ノードに挿すカードは Channel Adapter(CA) と呼ぶ。 CA の中でも通常のコンピュータホストに挿すカードは Host Channel Adapter(HCA) と呼ぶ。 他にストレージ製品に挿す Target Channel Adaptor(TCA) があるが、TCA は HCA のサブセットなので HCA だけを考えれば足りる。

InfiniBand は以下のような特徴を持っている。

高いスループット
主流の Quad Data Rate(QDR) では実行転送レートが 10 Gbits/秒、最新の Fourteen Data Rate(FDR) では 14 Gbits/秒になる。これをケーブル内で 4 本に束ねると QDR では 40 Gbits/秒、FDR なら 56 Gbits/秒が実現できる。また全二重通信なので送信と受信を合計すると QDR なら 80 Gbits/秒、FDR なら 112 Gbits/秒になる。
短いレイテンシー
スイッチのレイテンシーが短い。QDR なら HCA-HCA の直結で 100 ナノ秒程度、HCA-Switch-HCA も 1 マイクロ秒程度のレイテンシーで通信ができる。
フロー制御
InfiniBand には最低 2 種類のフロー制御が存在する。1 つはイーサネットにも存在する隣接する HCA とスイッチ間、スイッチとスイッチ間にはたらくリンクレイヤーのフロー制御である。もう 1 つは HCA 間で end-to-end ではたらくフロー制御で、両端のバッファの空き容量を相手に通知することで通信量を調整することができる。2 種類のフロー制御によって輻輳に強いネットワークを構成しており、スループット上限に近い性能を容易に引き出すことができる。
Remote Data Memory Access (RDMA)
データを送信先から受信先に送るだけではなく、リモートノードのメモリアドレスを指定してデータを書き込む RDMA WRITE や、リモートノードのメモリを読み取る RDMA READ 機能を持ってる。
ゼロコピー
InfiniBand は送信元プログラムが malloc()mmap() で確保したメモリを、受信先プログラムのメモリに直接届けることができる。この間に余分なデータコピーが発生しないため低レイテンシー化に寄与している
Software Defined Network
サブネット内にあるノードからノードへのパケット経路は Subnet Manager と呼ばれるコントローラによってソフト的に制御する。ループを構成する部分があっても正しい経路設定をすれば動作させることができ、冗長性が高く、ボトルネックの少ないトポロジーを構成できる。

3. InfiniBand Fabric

InfiniBand のネットワークは ファブリック(Fabric) と呼ばれる。 ファブリックは「布」の意味で、イーサネットのスター型のネットワークと異なりループを含んだ結線が可能になっている。

HCA とスイッチには製造時に Globally Unique Identifier(GUID) という識別子が一意になるように割り振られる。 これはイーサーネットの MAC アドレスに対応する。 MAC アドレスが 48 ビットだったのに対して、InfiniBand の GUID は 64 ビットだが大きな違いはない。 両方とも IEEE のRegistration Authority MA-L で管理されている。 GUID は HCA はポート毎に、スイッチには 1 個だけ割り振られている。

InfiniBand のローカルネットワークはサブネット(Subnet)と呼ばれ、1つのサブネットにはノード・スイッチを合わせて 49,151 台まで接続することが可能である。 複数のサブネットを「ルーター」でつなぐことも可能である。 ただし InfiniBand 仕様では「ルーティング」機能は一部未定義で、OSS ベースのルーティングソフトも提供されておらず、一般には利用されていないようだ。

3.1 LID Routed Network

イーサーネットの通信は MAC アドレスで通信相手を指定するが、InfiniBand は GUID とは別に Local Identifier(LID) という 16 ビットの識別子を割り振りこれを使って通信相手を特定する。 LID は GUID ごとに割り当てられ、HCA はポート毎に、スイッチは 1 つだけ LID が割り振られる。 割り当てられるのは 0x0001 〜 0xBFFFF のユニキャスト用の LID である。

HCA やスイッチは電源が落ちると自分に割り当てられた LID を忘れるので、電源を入れなおすと再度の割り当て直しが必要になる。

LID の範囲
値の範囲意味
0x0000未割り当て
0x0001 〜 0xBFFFユニキャスト
0xC000 〜 0xFFFEマルチキャスト
0xFFFFPermissive DLID

InfiniBand の通信が 64 ビットの GUID を使わず 16 ビットの LID を付け直すのは、通信の遅延を抑えるのに役立っている。

48 ビットの MAC アドレスや 64 ビットの GUID だとポートに出力先を記録する経路テーブルがハッシュテーブルのような多層のデータ構造にならざる得ない。

しかし 16 ビットの LID の場合、ユニキャスト LID は 49,151 個しかないので、各 1 バイトづつ持っても 48 KB の配列で済む。 マルチキャスト LID もどのポートから出力するかというビットマップをたかだか 16,383 個持てばよい。 32 ポートのスイッチであれば、1 マルチキャスト LID に 4 バイトで 64 KB の配列で済む。 データが小さいのでスイッチ内の CPU のキャッシュに楽に載る。 そのため遅延が小さくなる。

サブネットを越えて別のノードにアクセスする場合には、LID ではなく Global Identifier(GID) を用いる。 GID は 128 ビットの構成で IPv6 アドレスと同形式である。 基本的には上位 64 ビットにサブネットをあらわす ID を、下位 64 ビットに GUID を入れたものが GID になる。

ただ InfiniBand のルーティング機能は未定義な部分も多く、ソフトも対応していない。 サブネット内のノード間で LID の替わりに GID を指定しても通信はできない。

3.2 構成部品

サブネットは InfiniBand の HCA とスイッチによって構成される。

Host Channel Adaptor(HCA)

HCA は PCI express に挿すカードだが、1 ポートまたは 2 ポートの口を持っている。 ポートは 0 を除いてポート1ポート2 のように番号付けられる。

HCA

マルチポートの NIC は機能的に分かれた NIC が 1 枚のカードに載った N-in-1 だが、マルチポート HCA はそれとは異なる。 マルチポート NIC がポートごとに独立したアパートやマンションだとすると、マルチポート HCA は出口が 2 つある一戸建てである。 これは InfiniBand プログラムの詳細を解説する段で説明する。

それでもマルチポート HCA はポートごとに別々のサブネットに属することができる。 LID はサブネットごとに振られるので、ポートごとに別々のサブネットに属している場合には LID の番号が重複することはありる。

InfiniBand 仕様上、HCA は 255 ポートまで持てるが、実際には 2 ポートを越えるものは存在しない。 OpenFabric が提供するミドルウェアも 3 ポート以上の HCA があると誤動作する。

スイッチ

InfiniBand スイッチは物理的なポートに対して ポート1ポート2ポート3、… のように番号付けられる。 これ以外に物理ポートを持たない仮想ポートがあり ポート0 と呼ばれる。 ポート0はあまり意識することはないが、ibnetdiscover などのファブリックの確認ツールには出現する。

スイッチ

ポート0は管理用に使われる。 スイッチの GUID はポート0に対して割り振られており、LID もポート0につけられる。 他の物理ポートは GUID を持たず、LID の割り付けもない。

InfiniBand のスイッチは一つのサブネットに属することしかできない。 現在のイーサネットのスイッチは VLAN を切ることで同一筐体内を仮想的に複数の LAN に所属させることはできるが、InfiniBand スイッチには類似の機能がない。 ただしpartitionという機能がある。

スイッチも 255 ポートまで持てるが、販売されているスイッチには 64 ポートを越えるものは存在しない。 1つの筐体で64ポートを越えるスイッチは、内部に複数のスイッチが入っているだけである。 OpenFabric が提供するミドルウェアも 64 ポートを越えるスイッチがあると誤動作する。

3.3 Subnet Management

起動時に HCA のポートやスイッチに LID を割り振る仕事は、Subnet Manager というソフトが担う。 InfiniBand は通常のデータ通信に使うパケットとは別に、ファブリックの運用管理などを目的とした管理パケットが存在するが、その中に Directed Route Subnet Management Packet(DrSMP) が存在する。 Subnet Manager は DrSMP を使って、ファブリックのトポロジーの探索と LID の割り当てを行う。

Subnet Manager は HCA のいずれかのポートまたはスイッチ内で動作し、自分の基点と定めたポートから DrSMP を送信する。 DrSMP には InitPath と呼ばれる 64 エントリの配列がある。 この配列に基点からはじめてどのポートから出力するかを指定し、探索経路を組み立てることができる。

下の図のようなスイッチ2台、HCA 2 台の構成を考えてみる。 Subnet Manager は HCA1 のポート1 を基点とする。

まずポート1の先に何が入っているのか尋ねる。 最初に Switch 1 が見つかることになる。 実際には Switch 1 という名前ではなく GUID を返答する。

図中の DrSMP の矢印が右側を向いているのは往路で InitPath を参照する。 DrSMP の矢印が左を向いているのは復路で、来た道を逆順に辿る。

Dr SMP

次に Switch 1 のポート1 の先を探す。 これは HCA 1 に戻るのだが、DrSMP には (Subnet Manager ではなく) HCA ファームが応答して自分の GUID を返答する。 逆順に復帰して HCA1 のポート 1 に戻ると、Subnet Manager に結果が渡される。 結果、Switch 1 のポート1の先には、自分自身がつながっていたことが判明する。

Dr SMP

探索が進んで Switch 2 を見つけた後に、Switch 2 のポート 1 の先にあるものを探す。 Switch 1 が二度目に見つかることになる。 Subnet Manager は異なる経路を辿って Switch 1 が見つかったので、ファブリックのトポロジーがループを構成していることを見つけることができる。

Dr SMP

さらに探索を進めると Switch 2 のポート 4 の先に HCA2 を見つけることができた。

Dr SMP

このように DrSMP を投げることで、ファブリック内の全ての HCA とスイッチを探索して GUID を取得することができる。 ファブリック内のトポロジーを解析後、HCA の各ポートとスイッチに LID を割り当て、スイッチには経路情報(各 LID に対してどのポートから出力するか)を設定する。 以上の設定で InfiniBand ファブリックは運用可能な状態になる。

Subnet Manager は LID も割り当てた後の運用フェーズでも、ファブリックの一部の構成が変わった場合は、その場所を認識して LID を追加で割り当てるといった処理を行う。

InfiniBand スイッチには Subnet Manager 機能を内蔵しているものがあり、InfiniBandインテリジェントスイッチと呼ばれる。 インテリジェントスイッチはそうでないものに比べて少し高い。 ただ Subnet Manager には OpenSM という OSS 実装があるので、スイッチにインテリジェンスがなくても特に困らない。

4. メモリモデル:ゼロコピーと RDMA

TCP の通信モデルはソケット毎に送信用と受信用の 2 本の仮想的なリングバッファがカーネル内に用意される。 プログラムが send() で送信バッファに書き込むと NIC は自動的に通信相手にデータを送り、通信相手からの送られたデータは受信バッファに格納され、プログラムが recv() によってそれを取り出すことになる。 UDP は一対一の通信モデルではないが、それでも送信・受信バッファがありsendmsg()recvmsg() がある点では同じである。 TCP も UDP も本当に送信したいデータや、受信データを本当に格納したいデータはユーザプログラム上のメモリであり、それを一度カーネルの送信・受信バッファにコピーする必要がある。

イーサネットのメモリモデル

一方、InfiniBand の通信はゼロコピー(Zero Copy)を大きな特徴とする。 送信側はユーザプログラムが malloc()mmap() で確保したメモリを送信指定する。 受信側も同様である。

ただ実際のパケットの送受信を行うのは HCA である。 HCA は PCI express バスを介して物理メモリアドレスに対して DMA 転送を行うので、ユーザプログラムの仮想メモリアドレス空間が理解できるわけではない。 InfiniBand プログラムではデータの送受信を行う前に、自分のプログラムのうち HCA がアクセス可能な領域を Memory Region として OS に登録する。 OS に登録された memory region は仮想メモリページと物理メモリページの関係をピンして固定化する。 そのため memory region 内の仮想メモリはスワップなどによってディスクに追い出されることがなくなる。

また memory region の登録時に仮想メモリページと物理メモリページの変換表を作り、これを HCA に渡すことで HCA がユーザプログラムの仮想メモリアドレス空間の一部を認識できるようになる。 HCA は送信時も受信時もこの変換表を参照して、本当の DMA アクセスすべき物理メモリアドレスを決定できる。 そのためホストの CPU を使わないでデータ転送が可能になっている。

Dr SMP

また仮想メモリページと物理ページの変換表の機構を使うことで、InfiniBand はリモートのユーザプログラムのアドレスを直接指定してデータをコピーする Remote Data Memory Access (RDMA) を実行することができる。 RDMA はリモートノードへデータを書き込むのが RDMA WRITE で、リモートノードからデータを読み込むのが RDMA READ になる。 原則として RDMA はリモートノードの CPU を一切介さずに、その裏側で勝手に動作する(ただし RDMA WRITE はデータを書き終わったタイミングを通知する機能もある)。

5. サービスタイプ

InfiniBand は OSI 参照モデルで物理層からトランスポート層までを一括で提供している。

OSI 参照モデルでのイーサーネット & TCP/IP と InfiniBand の比較
OSI 参照モデルイーサネット & TCP or UDP/IPInfiniBand
アプリケーション  
プレゼンテーション  
セッション  
トランスポートTCP or UDPRC/RD/UD/UC サービス
ネットワークIP詳細は定義されていない
データイーサネットInfiniBand データ層
物理R45J、無線などQSFP

InfiniBand のユーザとの接点になるトランスポート層は、信頼性のある(reliable)信頼性のない(unreliable)コネクション(Connection)データグラム(Datagram)の区別で、Reliable Connection(RC)Reliable Datagram(RD)Unreliable Datagram(UD)Unreliable Connection(UC) の 4 つのサービスタイプを持っている。

5.1 基本の 4 つのサービスタイプ

4 つのサービスタイプはまとめると次の表のようになる。

サービスタイプ
 ReliableUnreliable
ConnectionRCUC
DatagramRDUD

ただし RD はユーザランドに機能が提供されておらず機能を使用することができない。 また UC もあまり使われないため、通常は RC と UD が利用される。

Service Type Reliable Connection
(RC)
Reliable Datagram
(RD)
Unreliable Datagram
(UD)
Unreliable Connection
(UC)
Verbs IBV_QPT_RC 使用できない IBV_QPT_UD IBV_QPT_UC
データロス検出 Yes No Yes
エラーリカバリ Yes 自動で再送 No 自前でやる必要あり
RDMA & ATOMIC Operation Yes No RDMA WRITE系のみ対応
RDMA READ系と ATOMIC は非対応
マルチキャスト対応 No No Yes No
トランザクション対応 Yes ※1 No No No
Shared Receive Queue Yes No Yes No
メッセージサイズ 理論上 231 バイト
実際は実機の制限あり
最大 4096 バイト 理論上 231 バイト
実際は実機の制限あり
TCP/IP で喩えると TCP 該当するものなし UDP 該当するものなし

5.2 それ以外のパケット

上記の 4 つのサービス以外にいくつか特殊なサービス・パケットが存在する。

一番重要なのは MAD(Management Datagrams) である。 これは InfiniBand のファブリックや機器の管理を行うためのパケットで、複数の利用用途が規定されている。

Subnet Manager ソフトや、infiniband-diags パッケージに含まれる各種ツールを使った時に上記の MAD を利用することになる。

MAD 以外では RC を拡張した eXtensible Reliable Connection (XRC) サービスタイプ、RAW パケットや、リンク層の制御を行うリンクパケット、輻輳制御を行う Congestion Notification Packet などがある。

6. 基本的な通信モデル

6.1 Queue Pair(QP)

TCP や UDP ではソケットが通信の端点となっていたが、InfiniBand では Queue Pair(QP) がその役割を果たす。 TCP と UDP は1つのネットワークインターフェイスの中にそれぞれポート番号(1〜65535)に割り当てられたソケットを作成することができるが、InfiniBand は理論上 HCA ごとに 最大 224 個の QP を作成することができる(実際はメモリの制限で 4〜7 万個程度しか作れない)。

ソケットが送信バッファ(send buffer)と受信バッファ(receive buffer)を持っていたように、QP は Send Queue(SQ)Receive Queue(RQ) を持っている(QP の Queue Pair という名称は、SQ と RQ の二つをペアで持っているところから来ている)。

QP

ソケットの送信バッファと受信バッファはデータそのものを格納するリングバッファであったが、RQ と SQ は送信要求と受信要求を格納する First-In First-Out (FIFO) である。 この要求は Work Request(WR) と呼ばれる。 送信要求は Send WR であり、受信要求は Receive WR である。 両方とも memory region 内のメモリ領域や、送信・受信に関する細かいパラメータを保持した構造体である。 そして Send WR は ibv_post_send() で SQ に、Receive WR は ibv_post_recv() で RQ に積む。

なお InfiniBand の通信は Work Request を一つのメッセージとして送られる。 バイトストリームが無限に続く TCP ではなく、データグラム単位で送られる UDP に近い。 ただし InfiniBand のメッセージの最大サイズは仕様上は 231 バイトになる(実機では制限があるが、それでも Mellanox の ConnectX-3 では最大 230 バイトになる)。

SQ または RQ に積まれた WR は Work Queue Element(WQE) として管理される。 WR も WQE も概念的にはほとんど差がない。 InfiniBand 仕様の設計者がそう決めたので一応名前が呼び分けられている。

HCA は SQ の WQE 情報に基づきデータの送信を開始する。 この処理はユーザープログラムの ibv_post_send() の呼び出しとは非同期で行われる。 受信したデータは RQ の WQE を先頭から取り出して、その中に指定されたメモリ領域に書き込みを行う。 この処理はもーザープログラムの ibv_post_recv() も非同期で行われる。

6.2 QP 番号(QPN)

QP の番号(QP Number; QPN)は 24 ビット幅があるため 0x0 〜 0xFFFFFF までをとりうる。 このうち 0x0 と 0x1 と 0xFFFFFF は特別な値である。 ユーザプログラムは ibv_create_qp() で QP を作成するが、0x2 〜 0xFFFFFE の範囲の番号を与えれる。 ただしこの値は HCA によって自動的に割り当てられ、自分が決定することはできない。 QP 番号はどのように与えられるかは HCA による。 ただし現在主流の Mellanox HCA の現在のファームウェアではほぼ乱数値になる。

この動作は TCP & UDP でポート番号を 0 にして bind() を呼び出し出してポート番号の自動割り当てを受けるのに似ている。 だが TCP & UDP がプログラムがポート番号を決めて、特定のポートに割り付けることもできる。 これは SSH なら TCP/22、HTTP なら TCP/80、などサービスを提供するのに役立っている。 しかしこのような機能は InfiniBand にはない

QP 番号の範囲
値の範囲意味
0x000000QP0
0x000001QP1
0x000002 〜 0xFFFFFE一般使用
0xFFFFFFマルチキャスト用

QP0 は特殊用途で、5.2 で述べた SMP をやりとりする専用の QP として使われる。 また QP1 は SMP 以外の MAD (SA と GMP)をやりとりするための専用の QP として使われる。 QP0 と QP1 は IB ドライバが起動時にカーネル内で最初に確保されるため、ユーザープログラムから直接使うことはない。

0xFFFFFF はマルチキャスト送信のための専用の QPN である。 マルチキャストメッセージを送信する場合、受信先の QP は特定できないため代理に 0xFFFFFF を入れておくことになっている。

6.3 Completion Queue(CQ)

プログラムは ibv_post_send() で積んだ Send WR の完了をどのように知ればよいのだろうか? あるいは自分がデータを受信したタイミングをどのように知ればよいのだろうか?

InfiniBand は WR の処理が終了したことを完了(completion)という概念でとらえる。 Send WR や Receive WR が成功で処理が終了した場合は成功した完了(Successful Completion)であり、処理がエラーで終了した場合には完了エラー(Completion Error)である。

完了は QP の外側に Completion Queue(CQ) と呼ばれるデータ構造に貯められる。 これも FIFO である。 送信と受信の WQE が処理された場合は、それぞれが設定された CQ に Completion Queue Entry(CQE) が積まれる。 WQE が Element で、CQE は Entry なのは間違いではない。 InfiniBand 仕様にちゃんとそう書いて在る。

原則、1つの WQE に対して CQE は 1 つ積まれる。 ユーザは CQ に対して ibv_poll_cq() を使うことで、積まれていた CQE を取り出すことができる。 これが WR を処理した結果となる。

取り出された CQE は、またもや名前が変わり、Work Completion(WC) と呼ばれる。 CQE も WC も概念的にはほとんど差がない。 名称的混乱は InfiniBand 仕様の特徴である。

全体像

プログラムが定期的に ibv_poll_cq() を実行して完了を待つ以外に、8.1 で述べる completion channel を用いて完了を割り込みとして検知する方法がある。

6.4 QP と CQ の組み合わせ

QP は作成時に CQ を設定する。

CQ は QP から独立しており、送信 CQ と受信 CQ は別々に設定できる。

CQモデル(1対1)

もちろん QP 内の送信 CQ と受信 CQ が同一のものでもよい。 QP 間で CQ を共有することもできる。

CQモデル(1対多)

WC 内には自分がどの QP からの完了なのを示す情報が入っている。 しかし WC 内には自分が送信から発生した完了か、受信から発生した完了かを、正確に区別する情報が入ってない(後述する wr_id を使う方法を別にして)。 そのため個人的には送信 CQ と受信 CQ は分けるのをお奨めする。

6.5 Work Request と Work Completion のデータ構造

SQ や RQ に積まれる Work Request をもう少し詳しく見てみる。

まず Send WR や Receive WR は以下のようなデータ構造を持つ(ただし今回の説明に必要な抜粋である)。

Work Request を表すデータ構造体(struct ibv_send_wr、struct ibv_recv_wr)の抜粋
フィールド名説明
wr_iduint64_tプログラムが任意の値を入れてよいフィールド
sg_liststruct ibv_sge*Scatter/gather リストの配列へのポインタ
num_sgeintScatter/gather リストの配列の要素数
opcodeibv_wr_opcodeSend WR のみに存在し、後述する通信メソッドの種類を指定する
imm_datauint32_tSend WR のみに存在し、後述する通信メソッドの一部で利用される

WR のバッファは sg_listnum_sge を使い Scatter/Gather リスト として指定する。 S/G リストは memory region 内の連続した領域を一つの S/G エントリとし、その S/G エントリを複数並べることができる。 そのためユーザプログラムの中で不連続なメモリ領域を一度に送信したり、逆に不連続なメモリ領域にデータを受信したりすることが、コピーなしで可能である。

Scatter/Gather list

一方、Work Completion は以下のようなデータ構造になる。

Work Completion を表すデータ構造体(struct ibv_wc)の抜粋
フィールド名説明
wr_iduint64_tWR の wr_id がコピーされる
statusenum ibv_wc_status完了が成功したかエラーか。エラーの場合はエラー種類も報告。
opcodeenum ibv_wc_opcodeWR の通信メソッドの種類を報告。ただしエラー時は信用できない。
byte_lenbyte_lenReceive WR に対する完了が成功した場合、転送したデータサイズが入る。
imm_datauint32_t後述する通信メソッドの一部で利用される。
qp_numuint32_tWR の所属していた QP を QP 番号で報告する。

Send WR または Receive WR で wr_id に入れた値は、WC の wr_id で取り出せるので、これを使って SQ、RQ に投入したバッファの管理を行うことができる。 wr_id は任意の 64 ビット幅の値を入れるので、現在主流の 64 ビットコンピュータであればポインタそのものを格納することもできるし、64 ビット幅を適当なビットに区切って意味を持たせることも自由である。

6.6 QP はいくつ必要か?

InfiniBand プログラムではプロセスごとに HCA を利用する。 そのためホスト内に複数のプロセスがあればそれぞれ個別の QP が必要となる。

UD サービスでは QP は多対多の通信が可能なので、基本的に各プロセスに 1 つ QP を作成すれば通信は可能である。

UD QP

一方で、RC サービスでは QP は一対一の通信となる。 各プロセスに通信相手プロセスの個数分の QP を作成する必要がある。

下の図では Host1 の Process1 だけが 5 個の QP を持ち他のプロセスと通信するが、各プロセスが他の全プロセスとフルメッシュでアクセスしたい場合には合計 30 個の QP が必要である。 通信相手が増えるとさらに QP は多くなる。 一般的にはノード数 N とノードあたりのプロセス数 P に対して N ×P×P の QP が必要となる。

RC QP

RC サービスを使ったことによる QP 資源の大量消費を抑えるために、RC サービスを拡張した XRC サービスが存在する。 その詳細は今後執筆する別記事で説明する。

6.7 いつ送信が完了するか?

送信の完了は reliable service か unreliable サービスかによって異なる。

Unreliable サービスである UD の場合、HCA が Send WQE を取り出してそれをファブリックに投げたら完了となる。 そのまま送信 CQ に CQE が積まれる。 データが受信側に届くかどうかは確認しない。

UDサービスの送信シーケンス

Reliable サービス である RC の場合、リモートから ACK が返り、それをローカルの HCA が受信した時点で完了となる。 ACK が届かない場合、一定のルールに基づいてローカル側は再送を繰り返す。 再送を繰り返して上限に達した場合にはタイムアウトする。 その場合は、ローカル側は完了エラーとして送信 CQ に CQE を積む。

RCサービスの送信シーケンス

RC がタイムアウトするパターンは 2 種類ある。 一つはファブリックに障害が発生して、通信不能になった場合である。 これは分かり易い。

もう一つはリモート側が RQ に積むべき WQE を切らしてしまった場合である。 この場合、リモート側は受信準備が済んでいない RNR(Receiver Not Ready) というステータスの、Negative ACK (NAK) をローカル側に返す。 RNR NAK を受け取ったローカル側は一定時間をあけて再送するが、規定回数の再送に失敗した場合にはタイムアウトするのである。

TCP の場合には受け取り側が ibv_post_recv() を実行しないため受信バッファが溢れることがあっても、ネットワーク的につながっていれば送信側はずっと待ってくれる。 しかし InfiniBand ではネットワークが完全な状態でも、リモート側がシャキシャキ動作しないと、タイムアウトエラーになることがありうる。

再送制御の詳細は「InfiniBand の再送制御を理解する」で説明する。

6.8 Shared Receive Queue

前述のように InfiniBand は RQ に油断なく Receive WR を常に投入し続けないとエラーになるかもしれないという厳しい仕様である。 多数の QP がある時に少数の QP に集中した負荷がかかると、うっかりタイムアウトする危険性があがる。

そこで Shared Receive Queue (SRQ) の登場である。 QP が SRQ を設定すると、自分の RQ が無効化され、データ受信時には SRQ から Receive WQE を取り出すようになる。 プログラムは個別の QP の RQ ではなく、共通の SRQ にのみ Receive WR と投入すればよくなる。 SRQ への Receive WR の投入は ibv_post_recv() ではなく、ibv_post_srq_recv() を使うが、それ以外はほとんど同じだ。

SRQ

QP は Queue Pair であったはずが、SRQ を使うと SQ しかないわけで名称的なおかしさを感じるが、DVD が digital versatile disc からただの DVD という 3 文字の単語になったように、今は QP というただの 2 文字の単語があると解釈すべきだろう。

7. オペレーションの種類

6. 基本的な通信モデル では InfiniBand の基本的な通信モデルを説明したが、InfiniBand の通信には SEND、RDMA WRITE、RDMA WRITE with Immediate、RDMA READ、ATOMIC Operation などの複数種類のオペレーションが存在する。

ただしサービスによっては使用できないオペレーションが存在する。 RC は全てのオペレーションが使用可能だが、UD は Send-Receive オペレーションだけとなる。

サービス毎に使用可能なオペレーション種類
オペレーション種類UDUCRC
SENDOKOKOK
RDMA WRITE OKOK
RDMA WRITE w/Imm OKOK
RDMA READ  OK
Atomic Operation  OK

7.1 SEND

SEND オペレーションはこれまで述べてきた送受信の基本モデルである。 ローカル側がリモート側にデータを送り込む。

SEND-RECV

SEND オペレーションは、転送すべきデータとは別に 32 ビットの定数値を送ることができる。 これは 6.5 Work Request と Work Completion のデータ構造 で述べたローカル側の Send WR の imm_data を使って指定し、受信 CQ の WC の中の imm_data で受け取ることができる。

なお即値を送らない SEND オペレーションをただの SEND、即値を一緒に送る SEND オペレーションを SEND with Immediate と呼び分けることもある。

7.2 RDMA WRITE

RDMA WRITE オペレーションは第二のモデルである。 これもローカル側がリモート側にデータを送り込むモデルである。

RDMA WRITE

ローカル側は scatter/gather が使えるが、リモートのメモリ領域は連続した単一のメモリ域となる。

7.3 RDMA WRITE with Immediate

RDMA WRITE with Immediate オペレーションは第三のモデルである。 これもローカル側がリモート側にデータを送り込むモデルである。

RDMA WRITE オペレーションは、リモート側にしてみれば、自分のメモリが書き換えられたのに自分では検知できない。 これだけだと不都合なので、受信完了を受け取れる RDMA WRITE として SEND with Immediate オペレーションと同様に 32 ビットの値を送ることができるようになった。

SEND オペレーションと SEND with Immediate オペレーションはほぼ同等のモデルだったが、RDMA WRITE オペレーションと RDMA WRITE with Immediate オペレーションはプログラム的にまったく異なるモデルである点に注意が必要。

RDMA WRITE with Immediate

完了があがることによって、リモート側は自分のメモリが書き換えられたことを検知できるようになる。 ではリモート側は自分のどのメモリアドレスが書き換えられたか知ることができるだろうか?

これはできない。 imm_data の値をプログラムで工夫することで、ローカルとリモートで符牒を合わせるしかない。

7.4 RDMA READ

RDMA READ オペレーションは第四のモデルである。 いままでとは反対にリモート側からローカル側にデータを送り込むモデルである。

RDMA READ

ローカル側は scatter/gather が使えるが、リモートのメモリ領域は連続した単一のメモリ域となる。

これまでのモデルは Send WR の sg_listnum_sge は送信するメモリ領域を指定していたが、RDMA READ オペレーションは Send WR で受信するメモリ領域を指定する点が重要である。 ローカル側に Receive WR を積んでいても、それは使われない。

何が Send WR で何が Receive WR なのか分からなくなるような仕様だが、名称の混乱を気にしていては InfiniBand は理解できない。

7.5 ATOMIC Operations

ATOMIC オペレーションと呼ぶものは、Fetch and Add オペレーションCompare and Swap オペレーション の二つである。 両方ともリモートのメモリに対して不可分操作を行い、その結果の変更前のリモートのメモリ内容をローカル側に戻すという一連の操作を行う。

Compare and Swap オペレーションはリモートの「メモリアドレス」「現在値」と「変更値」の三つを送る。 リモート側は「メモリアドレス」の位置の内容が「現在値」と比較して一致していれば「変更値」に不可分に変更する。

Fetch and Add オペレーションはリモートの「メモリアドレス」「増分」の二つを送る。 リモート側は「メモリアドレス」の位置の内容に不可分に「増分」を足す。

操作できるのはリモートの 8 バイト境界に沿う 8 バイトメモリが対象である。 1/2/4 バイトの操作は用意されていない。 またリモート側はそのマシンのネイティブエンディアンに従って処理をする。 Little endian の x86-64 と big endian の SPARC では結果が異なる点に注意が必要である。

ATOMIC オペレーションはリモート側の変更前のメモリ内容(8バイト)をローカル側に転送する。 Compare and Swap オペレーションは指定した「現在値」と転送内容が一致するかどうかを比較して、Compare and Swap の成否をチェックできる。 Fetch and Add オペレーションの場合も、自分の書き換える前のリモートのメモリ内容が確認できる。

RDMA READ

リモートのメモリ領域は連続した単一のメモリ域(8バイト)となる。 ローカル側は scatter/gather が使える。 ただし書き込むデータ幅は 8 バイト固定なので、scatter/gather を使うメリットはない。

8. 完了待ち受けモデル

6.3 では送信や受信の完了は ibv_poll_cq() を使って CQ をチェックすることで行うと書いたが、これは一般にポーリング(polling)と呼ばれる方式で、完了を待っている間も CPU パワーを浪費する。

ポーリング方式とは別に、他のスレッドに CPU 使用権を待機リストに並んで、完了イベントが発生した時点で起き上がる割り込み方式も存在する。 これにはCompletion Channelを用いる。

8.1 Completion Channel

Completion channel は ibv_create_comp_channel() で作られる。 CQ は ibv_create_cq() で作成する際に、すでに存在している completion channel を一つだけ指定できる。 この関係は一対多なので、複数の CQ を単一の completion channel に割り当てることができる。

Completion channel は ibv_get_cq_event() を呼び出すと、完了イベントが発生した CQ を取り出すことができる。 完了イベントが発生していない CQ がない場合、自分の管理下の CQ のいずれかに完了イベントが発生するまで、カーネル内で待機することになる。

Completion channel にはもう少し技巧的な使い方があり、select()poll() で待つこともできる。 これは API の解説で説明する。

8.2 Completion Vector

Completion channel を使った完了イベントの実体は、HCA が発生させる MSI-X の割り込みである。 そのため性能を追求するなら、InfiniBand の完了を通知する MSI-X 割り込みは、その完了を受け持つ completion channel を ibv_get_cq_event() しているスレッドの論理 CPU で動かしたい。

そして IB デバイスは任意の論理 CPU で MSI-X 割り込みを起こせるように、可能であればシステムの論理 CPU 数以上の IRQ を事前に用意する。 これを InfiniBand プログラムからは Completion Vector という表現で呼ぶ。

例えば Mellanox HCA で mlx4 ドライバを動かすと、/proc/interrupts の下には以下のような行があらわれる。

ibv_create_cq() の 5 番目の引数 comp_vector は、この IRQ のいずれで割り込みあげるかを指定できる(何番目の comp_vector がどの IRQ に対応しているかはマニュアル化されていないので実機で調査する必要がある)。 事前に /proc/irq/*/smp_affinity を設定して completion vector と I/O affinity を調整することによって、プログラムの中から割り込みの発生位置をコントロールすることが可能になる。

Completion channel の実際の使い方は ibv_create_cq() の解説で行う。

9. 非同期エラー

InfiniBand Verbs は 非同期エラー(asynchronous error) または 非同期イベント(asynchronous event) の機構を用意している。 通信エラーは CQ を使った完了エラーで報告されるが、完了を返さないオペレーションの通信エラーや、ポートの Link-Up/Link-Down、致命的な故障などは非同期エラーとして返される。

プログラムからは ibv_get_async_event() を呼び出すことで非同期エラー(イベント)を取得することができる。 また非同期エラー(イベント)の到着を select()poll() で待つこともできる。

10. マルチキャスト

InfiniBand はマルチキャスト通信にも対応している。 ただしマルチキャストが使えるのはサービスタイプの中で UD のみである。 RC/RD/UC ではマルチキャストは利用できない。

InfiniBand にはサブネット内に閉じたマルチキャストが定義されており、LID のうちマルチキャスト用の 0xC000 〜 0xFFFE に割り当てられた値を用いて通信する。 スイッチはマルチキャストフォワーディングテーブルに従って、マルチキャストの届け先に応じて IB パケットを複製して届ける。

マルチキャストに参加したいノードは Subnet Manager に自分を登録してもらい、Subnet Manager に各スイッチのマルチキャストフォワーディングテーブルを書き換えてもらう必要がある。 登録依頼は 5.2 で述べた Subnet Administration の MCMemberRecord という MAD パケットを、Subnet Manager が動作している LID に対して送信することになる(悲しいことに MAD の送信は InfiniBand Verbs プログラムの枠を少しだけはみ出していて、InfiniBand Verbs プログラムだけでは自由にマルチキャストを使うことはできない)

マルチキャストの送信側は LID を指定するが、送信先の QP までは特定できない。 受信 QP は決まっていないためである。 そこで送信側は QP 番号を 0xFFFFFF を設定して送信する。 各ノードではマルチキャストの受取人になりたい QP は ibv_attach_mcast() を使って自身を登録する。 マルチキャストを受信した HCA は、パケットを複製して登録された QP 全てに届ける(同一のマルチキャスト LID に対して、登録できる QP 数にはハード固有の上限がある)。 一つの HCA 内でマルチキャスト LID に複数の QP が設定されている場合、その内のひとつの QP がマルチキャストメッセージを送信するとループバック動作して登録している他の全ての QP に届く。 ただしマルチキャストメッセージを送信した QP 自身には届かない。

マルチキャストはサブネットの準備さえ整えば、ユーザプログラムから ibv_post_send() を使って送信することは可能である。

InfiniBand にはサブネットを越えたマルチキャストも想定されており、マルチキャスト GID を使う。 ただし仕様の詳細が未定義のため、現実的に使うのは困難である。

11. 再送制御と輻輳制御

5. で述べたように InfiniBand には信頼性のあるサービスでは再送制御が行われる。 また 2. で述べたように輻輳制御が行われる。 この機構を説明する。

だがその前に TCP の再送・輻輳制御を思い出して欲しい。

11.1 TCP を振り返ってみる

TCP は送信側のソケットから受信側のソケットに渡すバイトストリームであり、送信側と受信側がどのバイトまで送った・受け取ったかを Packet Sequence Number(PSN) で管理していた。 送信側のパケットには PSN が埋め込まれ、受信側から受信確認できた位置を PSN で返す。 PSN は 32 ビット幅であり、これで 4G バイトのデータを表現できる。

PSN 以外に以下のような機構がある。

スライディング・ウィンドウ(Sliding window)
送信側が一度に送信する量、あるいは受信側が一度に受信できる量はスライディング・ウィンドウで調整していた。 このスライディング・ウィンドウ幅は送信が成功すれば増やし、失敗すれば減らすことで輻輳制御を行う。 スライディング・ウィンドウの最大サイズはノーマルでは 65,536 バイト、TCP Window Scale Option(WSopt) を使った場合は 2 16+14バイト(1GB)となる。
Coalescing ACK
TCP は送信パケット一つづつに ACK を返す必要はなく、最後に受信した PSN 分の ACK を返せば、それ以前の ACK はまとめて(coalescing)返したとみなされる。
Piggybacking ACK
TCP は通常は双方向に通信を行っており、アプリケーションの作り方として「クライアント」から「サーバー」へ「要求」を出し、「サーバー」から「クライアント」へ「応答」が返るのが普通である。 つまりパケットは交互に行き来している。 そこで ACK は専用のパケットで送るのではなく、受信側からの応答データを載せたパケットに相乗りする形で送信する(これを「豚の背に乗る(piggy-back)」と表現する)。
これによって ACK だけのパケットが流れる無駄を省いている。 ただしパケットが片方向にしか流れない状態が続くと、ペイロードが 0 バイトのパケットを使って ACK を送ることになる。

PSN によってバイトストリーム内に 4G バイトのウィンドウができるが、スライディング・ウィンドウの制限により一度に送るのは 1G バイトまでとなっている。 TCP では PSN が作る円環の 1/4 が受信域で、それ以外は過去に送ったパケットが遅延して届いたものだと判断される。

TCPのPSNが作る円環

11.2 再送制御

InfiniBand の信頼性のあるサービスも Packet Sequence Number(PSN) を用いる。 名称は TCP と同じだが、こちらはバイト位置ではなく、パケットを数えるカウンターである。 また 24 ビット幅になっている。

InfiniBand もパケットの MTU を持っており、これは 256/512/1024/2048/4096 バイトのいずれかになる。 この MTU はパケットのヘッダー等をのぞいたペイロード部分のサイズを意味するので、InfiniBand の 1 つのパケットは最大 4K バイトのデータを送ることができる。 そのため InfiniBand の PSN が作る円環は一周が 64G バイトに相当することになる。

InfiniBand には TCP のスライディング・ウィンドウにあたるものはない。 その代わり送信側は送信完了を確認できた最新の PSN を基点に見て、円環の半分より先にある PSN を持つ ACK が返ってきた場合は、それは過去に送ったパケットが遅延して届いたものだと判断する。 つまり MTU=4KB 換算で言うと 32GB のスライディング・ウィンドウを持っていると見做すこともできる。

TCPのPSNが作る円環

InfiniBand には piggy-back はなく、ACK タイプを持つ専用パケットを送り返す必要がある。 Coalescing ACK は存在する。

11.3 輻輳制御

InfiniBand にはまず隣接しているスイッチ間ではたらくリンク層の輻輳制御がある。 これは Flow Control Packet という特殊なやり取りすることで、互いのパケットのバッファサイズと空き容量を通知するという機構である。 InfiniBand は 12 ビットのクレジット(カウンター)でバッファサイズの絶対値を示す点が、他のシステムと違う点である。

もう一つ、これは RC サービスのみだが end-to-end の輻輳制御がある。 6.7 に書いたように SEND オペレーションと RDMA WRITE with Immediate オペレーションは、受信側が RQ に Receive WR を積んでおかなければならない。 もし RQ が空になると RNR NAK が返され、送信側が一定時間止まってしまう。 これを防ぐために SEND オペレーションと RDMA WRITE with Immediate オペレーションが成功した時の ACK には、RQ の WQE の残量を示すクレジットを 5 ビットのフィールドで埋め込む。 送信側はこのクレジット情報に基づき、以降の SEND オペレーション、RDMA WRITE with Immediate オペレーションの発行量を調整する。

この end-to-end の輻輳制御は SRQ を用いている場合には有効にならない。 また Immediate なしの RDMA WRITE オペレーション、RDMA READ オペレーション、ATOMIC オペレーションには効果を発揮しない。

12. 保護機構

12.1 RDMA のための保護機構

RDMA WRITE や RDMA READ はリモートからメモリアドレス空間を読み書きするオペレーションだが、これが通信相手の LID、QP 番号、PSN が分かるだけでできてしまうのは危険である。 これを防ぐために幾つかの保護機構が存在する。

まず プロテクションドメイン(Protection Domain) が存在する。 これまでの説明では端寄ったが、InfiniBand Verbs を使うプロセスは内部にプロテクションドメインと呼ばれる領域を複数作成することができ、QP や Memory Region はプロテクションドメインの中に作成する。 そのため QP の所属するプロテクションドメインを使い分けることで、外部からの RDMA アクセスの範囲を制限することができる。

Protection Domain

もう一つの機構は Memory Region は、それを作成した時に HCA が Remote Key(R_Key) という 32 ビット値を生成する。 RDMA READ や RDMA WRITE はこの R_Key を Send WR の中に含めて通信を行い、値が一致しない場合はリモート側(受信側)でエラーとして検知することができる。

12.2 UD サービスのための保護機構

UD サービスは多対多の通信であり LID と QP 番号によって宛先を決めるが、それだけでは誰でもメッセージを送信できてしまう。 これを防ぐために UD QP には Queue Key (Q_Key) を設定することができ、SEND オペレーションの Send WR には受信先の UD QP の Q_Key を設定する。 メッセージに含まれる Q_Key とリモート側(受信側)の QP の Q_Key が一致しない場合は報告なく破棄(silently drop)されるので、Q_Key を知らなければ通信ができない。

Q_Key は 32 ビット幅があるが、ユーザープログラムは最上位ビットを 0 にした値(0x0000,0000〜0x7FFF,FFFF)を使用することができる。 最上位ビットを 1 にした値(0x8000,0000〜0xFFFFF,FFFFF)はシステムや HCA が固有の情報を設定するのに使用するので、ユーザープログラムは使うべきではない。

12.3 Partition

InfiniBand にはサブネット上に Partition と呼ばれるレイヤーを作ることができる。 Partition は 16 ビットPartition Key(P_Key) で識別され、HCA のポートを特定の partition に参加させることができる。 ポートには P_Key を格納する表(Partition Key Table)があり、複数の partition に同時に属することができる。 Partition Key Table のエントリ数は max_pkeys で報告され、Mellanox ConnectX-3 の場合は 128 個になっている。

P_Key のうち 0xFFFF は Default Partition Key であり、Partition Key Table の 0 番目のエントリは必ず 0xFFFF に設定される。 つまりサブネット内の全ノードが参加する partition が必ず一つあるということである。 Partition Key Table を設定するのは Subnet Manager であり、この設定には SMP を用いる。 QP0 は partition にも属さず、チェックなしで送受信される。

実際に Partition は QP 毎に適用される。 ユーザープログラムは QP は Partition Key Table のうちどれか一つを設定することができる。 以降、この QP から送信されるメッセージのパケット内には P_Key が埋め込まれる。 受信先の QP が同じ partition に属している(同じ P_Key 値を持つ)のでなければ、パケットは破棄されることになる。

Partition のイメージ

InfiniBand の Partition とイーサネットの VLAN は実質的には異なる機構である。 イーサネットの VLAN はネットワークを完全に分離し、異なる VLAN のパケットを遮断することができる。 そのため一つのローカルネットワークを顧客に分割して使うなどの利用が可能である。

一方、InfiniBand の partition によるサブネットの分割は不十分である。 サブネット内で共通の partition が存在するし、SMP を自分自身に投げることで P_Key を書き換えることも可能となっており、セキュリティレベルは低い。 実際には IPoIB の VLAN の実装などに利用される。

コメント

コメントを書き込む

TOP    掲示板    戻る
Written by NAKAMURA Minoru, Email: nminoru atmark nminoru dot jp, Twitter:@nminoru_jp