ワークサブミットが成功した後、デバイスがまだソースバッファを読んでいる可能性があります。また、リモートメモリが更新された後、アプリケーションがまだそれを消費していない可能性があります。RDMA は特定データパスの処理とコピーを削減しますが、リソースの認可、完了、再利用 を明確にする必要があります。
参照目標 読書位置
トランスポート、キュー、メモリ登録 第 1~3 節
オペレーションと完了セマンティクス 第 4~5 節
アプリケーションプロトコル、バイパス、観察 第 6~8 節
RDMA オペレーションでは、まずメモリとキューの認可を確立する必要がある
イニシエータ側アプリケーション
ローカルバッファと WR を準備
QP と RNIC
キー、範囲、権限を検証
リモート登録メモリ
アドレス範囲 + rkey 認可
ローカル CQ 完了
ステータスを確認し、ローカルの存続期間を管理
リモートアプリケーションプロトコル
通知、消費、バージョン、確認
自動的にビジネスロジックの完了を意味しない
通常の RDMA WRITE はリモートアプリケーション通知を自動生成しません。ローカル完了がビジネスロジックのコミットを意味するわけではありません。
1. InfiniBand、RoCE、iWARP
トランスポート ネットワーク基盤 理解すべき条件
InfiniBand IB ネットワークおよびアダプタ リンク、サブネット管理、および関連するルーティング/サービス
RoCEv1 イーサネットレイヤー2 IP ルーティングベースの RoCEv2 とは異なる
RoCEv2 UDP/IP、標準宛先ポート 4791 輻輳、パケットロス回復、および NIC 能力がパフォーマンスに影響
iWARP TCP 上の RDMA プロトコルスタック TCP トランスポートと RDMA リソース/完了セマンティクスの共同作用
RoCE のデプロイメントでは、ECN、輻輳制御、および適切な PFC ポリシーと併用されることが多いですが、PFC は「RoCEv2 がある場合は無条件にネットワーク全体で有効にする」という論理的前提ではありません。パケットロスおよびパージポリシーの実際の効果は、ハードウェアおよびネットワーク設計に依存し、明確な検証が必要です。また、PFC 自体がヘッドオブラインブロックや輻輳伝播を引き起こす可能性があります。
Linux の RXE、SIW などのソフトウェア実装は、対応する RDMA インターフェースの学習やテストに役立ちますが、データプレーンでは依然として CPU を消費し、ソフトウェア処理を経ます。ソフトウェア RDMA で測定された動作を、ハードウェア RNIC のパフォーマンスやバイパスの証明としてそのまま扱うことはできません。
2. QP、CQ、PD は個別に管理されるオブジェクト
QP には通常、SQ(Send Queue)と RQ(Receive Queue)が含まれます。SQ は能動的に送信される SEND、READ、WRITE などのワークを受信します。RQ は SEND などの操作に必要なバッファを提供します。CQ は個別に作成され関連付けられる オブジェクトであり、QP 構造体の定義内に必ずプライベートな第 3 のキューがあるわけではありません。
複数の QP が CQ を共有できます。send_cq と recv_cq は同じでも異なっていても構いません。SRQ を使用する場合は、複数の QP が受信リソースを共有できます。作成時に要求した能力とプロバイダが返した実際の能力を確認する必要があります。特に最大 WR、SGE、およびインラインデータのサイズが重要です。ibv_create_qp を参照してください。
PD(Protection Domain)は、QP、MR などのリソースへのアクセス制御に関与します。オブジェクトを正しい PD に配置し、アドレス、キー、アクセスフラグなどの要件を満たして初めて、登録されたメモリを合法に使用できます。ポインタの値を知っていれば DMA を送信できるわけではありません。
RC QP は通常、RESET、INIT、RTR、RTS の状態遷移を経ます。アドレス、パス、PSN、アクセス権限、リトライなどのパラメータを設定する必要があります。RDMA CM は接続管理を支援できますが、パラメータの交換と設定はプログラムで別途行うこともでき、すべてのアプリケーションが同じ CM フローを使用する必要はありません。UD などの QP タイプでは、接続、信頼性、およびサポートされるオペレーションが異なります。
3. Memory Region:登録が約束するもの
通常の MR 登録は、プロバイダ/RNIC に対してメモリアドレス範囲と権限を確立します。通常、ページ固定、DMA マッピング、およびハードウェア変換テーブルが含まれ、制限されたリソースを消費します。lkey はローカル SGE アクセスの検証に使用され、rkey は対応するリモートアクセスの検証に使用されます。
ODP(On-Demand Paging)などのメカニズムは、サポートされている環境でページマッピングを必要に応じて処理でき、作成から破棄まで全ページを固定する必要はありません。したがって、「登録は必ず全ページを永久にロックする」や「RDMA は常にページフォルトが発生しない」という主張は極端すぎます。
リモート側が受信するアドレスの意味は、MR の IOVA/ゼロベースなどの設定に依存し、常に未変換のローカル CPU ポインタとして扱うことはできません。アクセスフラグは、リモート読み取り/書き取り/アトミック操作が許可されるかどうかを決定します。リモート書き込みが必要な場合、ローカル書き込み権限などの関連要件もインターフェース設定に従って設定する必要があります。ibv_reg_mr を参照してください。
キーは暗号化キーではない
rkey はアクセス認可に関連する識別子であり、トランスポートの暗号化、認証、またはアプリケーションデータの機密性保護を提供しません。アドレス、長さなどの情報とともに信頼できるプロトコルを通じて交換され、露出範囲が制限され、リソースの取り消し時にはリモート側で進行中の操作が処理される必要があります。
プロセス全体の大きなアドレス範囲を登録すると管理回数が減少する可能性がありますが、誤ったリモート書き込みの影響範囲も拡大します。MR/MW の範囲、権限、キーの存続期間、および接続アイデンティティは、登録オーバーヘッドの削減だけを追求するのではなく、共同で設計する必要があります。
4. SEND、WRITE、READ の役割の違い
オペレーション イニシエータ側が提供 リモート側が準備 通知の意味
SEND ローカルデータ SGE 既に post された受信バッファ 受信側 CQE でメッセージの到着を報告可能
RDMA WRITE ローカルソース、リモートアドレスと rkey リモート書き込み可能なメモリ範囲 通常の WRITE はリモート受信 CQE を自動生成しない
RDMA READ ローカルターゲット、リモートアドレスと rkey リモート読み取り可能なメモリ範囲 イニシエータの完了後に契約に従って結果を読み取る
WRITE_WITH_IMM WRITE パラメータと即値データ データ MR および受信通知リソース 指定アドレスにデータを書き込み、対応する受信完了を生成
RC SEND は、リモート受信リソースが不足している場合、RNR(Resource Not Ready)とリトライをトリガーする可能性があり、最終的には失敗する可能性があります。WRITE_WITH_IMM はデータを指定されたリモートアドレスに書き込みますが、通常の SEND がデータを receive SGE に書き込むものだと誤解しないでください。ただし、受信通知には対応する RQ/SRQ のワークリソースが消費されます。
RDMA アトミック操作は、サポートされるサイズ、アライメント、操作、およびスコープの原子性のみを提供し、任意のマルチフィールドデータ構造に自動的にトランザクション整合性を持たせるものではありません。
5. Post 成功と完了成功
ibv_post_send() が成功したことは、ワークがサブミットされて受け入れられたことを示しますが、デバイスがデータの使用を完了したことを意味しません。バッチ WR チェーンの途中で失敗した場合、bad_wr は最初の失敗項目を指しますが、それ以前の項目は既に成功してサブミットされている可能性があります。エラーが見つかるとすぐにバッチ全体を解放してはいけません。
インライン以外の送信バッファは、対応する完了条件が満たされるまで存続する必要があります。インラインデータはサブミット構造体にコピーされた後、インライン契約に従って元のデータをより早く再利用できますが、キューリソースと完了管理は依然として存在します。ibv_post_send を参照してください。
CQ の返却数と WC ステータスは別々に確認する
poll 結果 意味 次のステップ
負数 呼び出し失敗 呼び出しエラーを処理
0 現在完了がない ポーリング/通知戦略に従って続行
正数 複数の WC を取得 ステータスを個別に確認し、すべて成功と見なさない
完了レコードの数と各ワークの成功与否は、2 段階の判断です。
失敗した完了では、wr_id、status、qp_num、vendor_err などの一部のフィールドのみが有効であることが保証されます。CQ イベント通知は WC 自体ではありません。イベントはアプリケーションに poll を促し、poll によって初めて完了レコードが取得されます。通知インターフェースの契約に従ってイベントを再要求し、確認して、スリープと新しい完了の間の競合を避けてください。
unsignaled 送信を選択すると CQE の数は減少しますが、すべてのバッファを即座に再利用できるわけではありません。QP の順序、操作/fence セマンティクス、および後続の signaled 完了に基づいて回収水位を設定する必要があり、エラーパスも考慮する必要があります。CQ を長期間消費しないとオーバーランする可能性があるため、単に統計ログが1件失われるだけの小さな問題として扱ってはいけません。ibv_poll_cq を参照してください。
6. リモートメモリ更新はアプリケーショントランザクションコミットではない
リモートのレコードスロットを、完全に管理が必要なオブジェクトとして扱います。
フェーズ プロトコルが回答すべき内容
スロットの取得 誰が割り当てるか、古い読者は終了したか?
書き込みと公開 新しいデータが完全に可視化されたことをどう判断するか?
読み取りと検証 サーバーはバージョンと整合性をどう確認するか?
返却と上書き 誰が再利用可能であることを通知するか?
RNIC によるデータ搬送は、これらの状態遷移をアプリケーションに自動的に補完するものではありません。
「まず payload を WRITE し、次に ready=1 を WRITE する」という手法は、QP、操作順序、メモリの可視性、および relaxed ordering などの条件を無視して、一般的な公開プロトコルとして扱うことはできません。QP を跨ぐ操作には、自動的にグローバルな順序性が保証されません。トランスポート/プロバイダの完了と fence 契約に従い、CPU の同期方法と組み合わせて通知を設計する必要があります。
リモート更新、消費、永続化は個別に確認する
通常の WRITE のローカル成功完了は、サーバーアプリケーションがデータを消費したことを意味するものでも、サーバーがデータをディスクに永続化したことを意味するものでもありません。ビジネスで永続化の確認が必要な場合、サーバー側で対応するストレージとメタデータのステップを完了した後に返信するか、永続化セマンティクスを明示的にサポートする専用ソリューションを採用する必要があります。
RDMA READ もまた、一貫性のあるスナップショットを自動的に取得するものではありません。読み取り中にリモート側がマルチフィールドオブジェクトを変更している可能性があります。バージョン番号の検証、ロック、またはコピープロトコルは依然として必要です。
以下に示すのは、リクエスト、トランスポート、CQE、リモートアプリケーションの消費、永続保存の順に見えますが、各アクションは自身の条件のみを履行します。ローカル完了の直後にソースを再利用し、その後でリモートコンテンツがすでに永続化されているかどうかを確認できます。
図を準備しています 再読み込み
ローカル CQE、リモート消費、永続化は3つの確認 post 成功はデバイスがソースメモリの使用を停止したことを意味しません。ローカル成功完了は契約に従ってソースを再利用可能ですが、リモートアプリケーションが消費したことを意味するものでも、永続化されたことを意味するものでもありません。
{"id":"rdma-completion-boundaries","title":"ローカル CQE、リモート消費、永続化は3つの確認","summary":"post 成功はデバイスがソースメモリの使用を停止したことを意味しません。ローカル成功完了は契約に従ってソースを再利用可能ですが、リモートアプリケーションが消費したことを意味するものでも、永続化されたことを意味するものでもありません。","height":1000,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><div class=\"actions\"><button id=\"submit\" data-i18n=\"submit\"></button><button id=\"transfer\" data-i18n=\"transfer\"></button></div><div class=\"actions\"><button id=\"cqe\" data-i18n=\"cqe\"></button><button id=\"reuse\" data-i18n=\"reuse\"></button></div><div class=\"track-row\"><span data-i18n=\"source\"></span><div id=\"source\" class=\"track\"></div></div><div class=\"track-row\"><span data-i18n=\"work\"></span><div id=\"work\" class=\"track\"></div></div><div class=\"track-row\"><span data-i18n=\"remote\"></span><div id=\"remote\" class=\"track\"></div></div><div class=\"actions\"><button id=\"consume\" data-i18n=\"consume\"></button><button id=\"persist\" data-i18n=\"persist\"></button></div><div class=\"track-row\"><span data-i18n=\"application\"></span><div id=\"application\" class=\"track\"></div></div><div class=\"track-row\"><span data-i18n=\"stable\"></span><div id=\"stable\" class=\"track\"></div></div><div class=\"actions\"><button id=\"crash\" data-i18n=\"crash\"></button></div><div class=\"copy\"><p id=\"explanation\" role=\"status\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"note\"></p></div><div class=\"actions\"><button id=\"reset\" data-i18n=\"reset\"></button></div><details class=\"scope\"><summary data-i18n=\"scopeLabel\"></summary><p data-i18n=\"scope\"></p></details>","css":".intro{margin:8px 0 18px;color:var(--muted);font-size:14px}.caption,.hint{font-size:12px;color:var(--muted);margin-top:12px}.control{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 12px;align-items:center;margin:14px 0;font-size:13px}.control label{font-size:13px}.control output{color:var(--accent);min-width:5em;text-align:right;font-variant-numeric:tabular-nums}.control input{grid-column:1/-1;width:100%;margin:0}.presets{display:flex;gap:4px;align-items:stretch}.presets button{flex:1 1 0;min-width:0;overflow-wrap:anywhere;font-size:13px}.actions{display:flex;gap:8px;flex-wrap:wrap;margin:14px 0}.actions button{flex:1;min-width:0;font-size:13px;min-height:42px}.copy{display:grid;font-size:14px;margin:16px 0}.copy>*{grid-area:1/1;margin:0}.reserve{visibility:hidden}.scope{margin-top:14px;color:var(--muted);font-size:12px}.scope summary{padding:8px 0;cursor:pointer}.scope p{margin-top:10px}.two{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.card{border:1px solid var(--rule);border-radius:9px;padding:14px;min-width:0}.card header{font-size:14px;font-weight:600;margin-bottom:10px}.card button{font-size:12px;width:100%;min-width:0}.card small{display:block;font-size:12px;color:var(--muted)}.card code{font-size:12px;overflow-wrap:anywhere}.metrics{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.metrics>div{border-left:2px solid var(--accent);padding-left:10px;min-width:0}.metrics span{display:block;min-height:3.2em;color:var(--muted);font-size:12px}.metrics strong{font-size:23px;display:block;font-weight:500;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.formula{border-top:1px solid var(--rule);padding-top:12px;margin-top:14px;font-size:13px;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.value{font:500 20px var(--viz-font-ui)}.legend{display:flex;flex-wrap:wrap;gap:6px 14px;font-size:12px;color:var(--muted);margin:12px 0}.legend span:before{content:'';display:inline-block;width:8px;height:8px;border-radius:2px;background:var(--accent);margin-right:6px}.legend .second:before{background:var(--second)}.check{display:flex;align-items:center;gap:8px;font-size:13px;margin:12px 0}.check input{width:16px;height:16px;flex-shrink:0;accent-color:var(--accent)}select,input[type=text],input[type=number]{background:var(--paper);color:var(--ink);font:inherit;font-size:13px;padding:10px;border:1px solid var(--rule);border-radius:7px;max-width:100%;min-width:0}select{width:100%;min-height:42px}.band{height:20px;display:flex;background:var(--surface);border-radius:4px;overflow:hidden}.band i{height:100%;display:block;background:var(--accent);transition:width .25s}.band i.second{background:var(--second)}button:disabled{cursor:default}.status{font-size:13px;min-height:4.8em}.mono{font-family:var(--viz-font-mono);font-variant-numeric:tabular-nums}.muted{color:var(--muted)}\n@media(max-width:480px){.two{gap:8px}.card{padding:12px 9px}.actions button,.presets button{font-size:12px}.metrics strong{font-size:20px}.metrics{gap:8px}}\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{transition:none!important;animation:none!important}}\n.track-row{margin:16px 0}.track-row>span{display:block;font-size:12px;color:var(--muted);margin-bottom:7px}.track{display:flex;gap:5px;min-width:0}.cell{flex:1;min-width:0;min-height:48px;border:1px solid var(--rule);border-radius:5px;background:var(--surface);display:flex;align-items:center;justify-content:center;text-align:center;padding:5px 3px;font-size:12px;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.cell.on{background:var(--accent-soft);border-color:var(--accent)}.cell.gold{background:var(--second-soft);border-color:var(--second)}.cell.empty{border-style:dashed;color:var(--muted)}.cell.error{border-color:var(--second);text-decoration:line-through}.choices{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;margin:14px 0}.choices button{min-width:0;padding:10px 4px;font-size:12px;overflow-wrap:anywhere}.choices button[aria-current=true]{border-color:var(--accent);background:var(--accent-soft)}.relation{display:grid;grid-template-columns:minmax(0,1fr) 28px minmax(0,1fr);align-items:center;gap:6px;margin:12px 0}.relation .cell{min-height:72px}.relation>i{text-align:center;font-style:normal;color:var(--accent)}.readout{font-size:13px;white-space:pre-line;min-height:6.4em;overflow-wrap:anywhere}.plot{position:relative;border-left:1px solid var(--rule);border-bottom:1px solid var(--rule);padding:8px 0;margin:18px 0}.plot-row{height:28px;position:relative;margin:4px 0;background:var(--surface)}.plot-row i{position:absolute;inset-block:5px;background:var(--accent);min-width:2px;transition:width .2s,left .2s}.plot-row b{position:relative;padding:4px 6px;font:11px/28px var(--viz-font-ui);background:var(--paper);border-radius:3px}.axis{display:flex;justify-content:space-between;font-size:11px;color:var(--muted)}\n\n","js":"const $=s=>document.querySelector(s);const set=(id,v)=>$('#'+id).textContent=v;const t=k=>viz.t(k);function cells(id,values){$('#'+id).replaceChildren(...values.map(v=>{const e=document.createElement('div');e.className='cell '+(v.cls||'');e.textContent=v.text;e.title=v.title||v.text;return e;}));}function pressed(mode){document.querySelectorAll('[data-mode]').forEach(e=>e.setAttribute('aria-pressed',String(e.dataset.mode===mode)));}let phase=0,source='N',remote='O',app='—',stable='O',dead=false;function draw(){cells('source',[{text:source,cls:phase===1||phase===2?'gold':'on'}]);cells('work',[{text:t(['ready','inflight','completed','done'][phase]),cls:phase===3?'on':'empty'}]);cells('remote',[{text:remote,cls:'on'}]);cells('application',[{text:app,cls:app==='—'?'empty':'on'}]);cells('stable',[{text:stable,cls:'gold'}]);$('#submit').disabled=phase!==0;$('#transfer').disabled=phase!==1;$('#cqe').disabled=phase!==2;$('#reuse').disabled=phase!==3||source==='X';$('#consume').disabled=$('#persist').disabled=phase<2||dead;$('#crash').disabled=phase!==3||dead;set('explanation',t('note'));}$('#submit').onclick=()=>{phase=1;draw();};$('#transfer').onclick=()=>{remote=source;phase=2;draw();};$('#cqe').onclick=()=>{phase=3;draw();};$('#reuse').onclick=()=>{source='X';draw();};$('#consume').onclick=()=>{app=remote;draw();};$('#persist').onclick=()=>{stable=remote;draw();};$('#crash').onclick=()=>{remote=stable;app='—';dead=true;draw();};$('#reset').onclick=()=>{phase=0;source='N';remote=stable='O';app='—';dead=false;draw();};draw();","audio":false,"strings":{"application":"リモートアプリケーションが読み取った値","completed":"転送完了;CQE が待機中","consume":"リモートアプリケーションが読み取り","cqe":"ローカル成功 CQE を消費","crash":"リモートがクラッシュし回復","done":"ローカル成功 CQE が取得済み","heading":"ローカル CQE、リモート消費、永続化は3つの確認","inflight":"在途;ソースバッファは依然として拘束されている","intro":"非インライン RDMA WRITE を1件サブミットし、トランスポート、ローカル完了、リモート消費、永続化をそれぞれ進めます。ソースバッファを早期に再利用しようと試み、リモート側の値がクラッシュ前後でどう変化するか比較します。","note":"ローカル CQE のみを取得してクラッシュした場合、リモート側は古い内容 O を回復する可能性がある。まずリモート側で N を安定して保存し、その後でクラッシュさせて初めて、ここでの永続化回復の根拠となる。","persist":"リモートが保存し確認","ready":"未コミット","remote":"リモートメモリ","reset":"リセット","reuse":"ソースバッファを X に変更","scope":"有効な MR/key を持つ RC QP、インライン以外かつ signaled の WRITE、トランスポートエラーなし。トランスポート完了アクションには、このモデルに必要な順序とリモート可視性同期が含まれる。通常の WRITE はリモート受信 CQE を生成しない。リモート消費/保存は追加のアプリケーションプロトコルアクションであり、クラッシュはローカル CQE の後にのみシミュレートされ、障害中の QP 回復は省略される。","scopeLabel":"モデルの範囲と規約","source":"ローカルソースバッファ","stable":"リモートの安定した内容","submit":"WRITE を post","transfer":"転送完了","work":"WQE / ローカル完了"}}
7. 「カーネルバイパス」の実際の範囲
ハードウェアユーザーランド verbs の高速パスは、プロバイダがマップされたキュー/ドアベルに直接アクセスできるため、操作ごとにシステムコールを削減します。しかし、カーネルは依然としてリソースの作成、マッピング認可、イベント、例外、ODP、およびデバイスのライフサイクルを処理し、アプリケーションも依然としてサブミットと poll のロジックを実行します。
パフォーマンス比較時は、まず測定境界を固定する
ビジーポーリングは CPU を使用して低レイテンシを実現します。イベント駆動はアイドル時間を削減しますが、通知コストを導入します。バッファの事前登録、メッセージサイズ、NUMA、キュー深度、輻輳、およびプロバイダがすべて効果に影響します。TCP と RDMA を比較する際は、単一操作のレイテンシ、スループット、CPU/バイト、またはアプリケーションエンドツーエンドの完了時間のいずれを指しているかを明確にする必要があります。
一部のハードウェア RoCE データパスは、通常のホスト IP ソケットスタックを経ないため、nftables のソケット/転送ルールや tcpdump がすべての RDMA トラフィックを自動的にカバーすると仮定してはいけません。リソース権限、NIC/スイッチのカウンタ、および RDMA イベントを共同で観察する必要があります。ユーザーランド verbs を参照してください。
8. RDMA デバイスとポートの読み取り専用表示
from pathlib import Path
root = Path ( ' /sys/class/infiniband' )
devices = sorted ( root . glob ( ' *' ) )
if not devices :
print ( ' no RDMA devices visible in this environment' )
for dev in devices :
print ( dev . name )
for port in sorted ( ( dev / ' ports' ) . glob ( ' *' ) ) :
for field in ( ' state' , ' phys_state' , ' link_layer' ) :
try :
print ( ' ' , port . name , field , ( port / field ) . read_text ( ) . strip ( ) )
except OSError :
pass
rdma link show などのツールは、カーネル RDMA オブジェクト情報を補完できます。デバイスが見えることは、リモート QP、MR、ルーティング、またはアプリケーションプロトコルがすでに正常に設定されていることを証明するものではありません。可視なデバイスがないことは、現在の環境のみを記述するものであり、実際のトランスポートを検証するためにこれを使用することはできません。
実装の詳細については、rdma-core の libibverbs、librdmacm、プロバイダ、およびカーネル drivers/infiniband/ を参照してください。post 関数だけを眺めるのではなく、リソースの作成、1回の完了、およびエラークリーンアップを優先して追跡してください。