DMAとIOMMUによるデバイスメモリアクセス
デバイスがCPUを介さず直接メモリへ書き込む仕組みと、その暴走を防ぐIOMMUの原理がわかります。スキャッタギャザーやコヒーレンシ管理まで押さえ、I/O性能と安全性の勘所を掴めます。
- 1.DMAはデバイスがCPUを介さず物理メモリへ直接読み書きする仕組みで、CPUは転送開始の指示と完了割り込みの受信だけを担う。
- 2.IOMMU(Intel VT-d/AMD-Vi)はデバイスが出すアドレスをページテーブルで変換・検査し、許可された範囲外への不正アクセスを物理的に遮断する。
- 3.コヒーレントDMAは整合性をハードウェアが保証し、ストリーミングDMAはdma_map/unmapとsync呼び出しでキャッシュのフラッシュ/無効化を明示する。
DMAが解く問題
NIC やストレージから大量のデータを取り込むとき、1バイトずつ CPU がデバイスのレジスタを読んでメモリへ写していては CPU が転送に拘束され、本来の計算が進みません。これは 割り込みと入出力 で触れた PIO(Programmed I/O)の限界です。
DMA(Direct Memory Access) は、デバイス自身が物理メモリへ直接読み書きできるようにします。CPU はディスクリプタに「どの物理アドレスへ/何バイト」を書いて転送を指示するだけで、実際のバス転送はデバイス側のDMAエンジンが行います。転送が終わると、デバイスは 割り込み で完了を通知します。CPU が解放される間、スケジューラ は別のプロセスを走らせられます。
[PIO] デバイス → CPUレジスタ → メモリ (CPUが1ワードずつ介在)
[DMA] デバイス ───────────→ メモリ (CPUは開始指示と完了通知だけ)
スキャッタギャザー:物理的に飛び地のメモリへ
ここで問題になるのが、アプリの連続したバッファが物理的には連続していないことです。ユーザー空間の数十KBのバッファも、ページ単位で見れば物理フレームは 仮想記憶 によってバラバラの位置に散っています。デバイスのDMAエンジンは仮想アドレスを知らないため、本来は物理フレームごとに転送を分割する必要があります。
これを1回の転送として扱うのが**スキャッタギャザー(scatter-gather, SG)です。(物理アドレス, 長さ) の組を並べたSGリスト(ディスクリプタチェーン)**をデバイスに渡すと、DMAエンジンがリストを辿って飛び地を順に処理し、論理的には1つの転送として完結させます。
SGリスト(例)
[ addr=0x1A000, len=4096 ] → ページA
[ addr=0x7C000, len=4096 ] → ページB(物理的に離れている)
[ addr=0x3F000, len=2048 ] → ページCの一部
古いデバイスはアドレス線が32bitしかなく、4GiB以上の物理アドレスを生成できません。転送先が範囲外にあるとき、カーネルは下位アドレスに確保した中継領域(バウンスバッファ)へ一度コピーしてからDMAします。Linux の swiotlb がこの仕組みで、後述のIOMMUがあれば多くの場合これを回避できます。
IOMMU:デバイスにとってのMMU
DMAの本質的な危険は、デバイスが物理アドレスを直接指定してメモリへ書ける点にあります。ドライバのバグやマルウェア化したファームウェア、悪意あるPCIeデバイスが任意の物理アドレスを書けば、カーネル領域の改竄すら可能です。仮想化環境でゲストにデバイスを直接割り当てる(パススルー)場合、ゲストが書いたアドレスをそのまま信じることはできません。
IOMMU(I/O Memory Management Unit、Intel では VT-d、AMD では AMD-Vi/IOMMU、ARM では SMMU) は、CPU の MMU がプロセスに対して行うことを、デバイスに対して行います。デバイスが出すアドレスを IOVA(I/O仮想アドレス) とみなし、専用のページテーブルで物理アドレスへ変換し、同時に権限を検査します。
デバイス → [IOVA] → IOMMU(変換 + 保護) → [物理アドレス] → メモリ
↑
デバイス単位のページテーブル
変換と保護の単位はデバイスです。VT-d は PCIe の BDF(Bus/Device/Function)番号で各デバイスを識別し、ルートテーブル→コンテキストテーブルを引いて、そのデバイス専用のページテーブル(第2段変換)に到達します。これにより「デバイスXはこの領域だけアクセス可」を強制でき、未登録アドレスへの転送はハードウェアが遮断してフォルトを上げます。
| 観点 | MMU | IOMMU |
|---|---|---|
| 変換の主体 | CPUコアのメモリアクセス | デバイスのDMAアクセス |
| 保護の単位 | プロセス(アドレス空間) | デバイス(BDF/ストリームID) |
| 入力アドレス | 仮想アドレス | IOVA(I/O仮想アドレス) |
| キャッシュ | TLB | IOTLB(デバイス側TLB) |
| 主な目的 | プロセス分離・仮想記憶 | DMA保護・デバイスパススルー |
IOMMUにも変換キャッシュ(IOTLB)があり、マッピングを解除したらこれを無効化しなければ古い対応が残ります。dma_unmap のたびにIOTLBフラッシュを行うと高コストなため、複数の解除をまとめて遅延フラッシュする最適化(deferred invalidation)が使われます。ただし無効化前のIOVA再利用は脆弱性になり得るため、安全性と性能のトレードオフがあります。
DMA APIとコヒーレンシ管理
ドライバが物理アドレスを直接扱うと、IOMMUの有無やバウンスバッファの要否、キャッシュ整合性をすべて自前で処理する羽目になります。Linux の DMA API はこれを抽象化し、ドライバには DMAアドレス(dma_addr_t) という統一的なハンドルを返します。IOMMUがあればこれはIOVA、無ければ物理アドレス(やバウンスバッファのアドレス)であり、ドライバはどちらかを意識しません。
コヒーレンシ管理が必要なのは、CPU が書いた内容がキャッシュに留まったままだと、DMAでメモリを読むデバイスが古い値を見てしまうからです(キャッシュコヒーレンシ はコア間の話で、I/Oデバイスは必ずしもこの一貫性網に参加しません)。DMA APIは用途で2系統に分かれます。
| 観点 | コヒーレント(consistent)DMA | ストリーミングDMA |
|---|---|---|
| 確保 | dma_alloc_coherent | 既存バッファを dma_map_single/sg |
| 整合性 | ハードウェア/非キャッシュで常に一貫 | map/unmap と sync で明示管理 |
| 向く用途 | 頻繁に双方向アクセスする小領域(記述子リング) | 一度きりの大きなデータ転送 |
| コスト | 確保が高価・低速になりがち | 確保は安いが同期呼び出しが必須 |
ストリーミングDMAでは方向(DMA_TO_DEVICE / DMA_FROM_DEVICE)が重要です。CPUからデバイスへ送る前はキャッシュをフラッシュしてメモリに書き戻し、デバイスからCPUへ受け取る前はキャッシュを無効化して古い行を捨てます。この向きを dma_map_* と dma_sync_*_for_device / for_cpu の呼び出しで指定します。
/* デバイスへ送る場合の典型フロー */
addr = dma_map_single(dev, buf, len, DMA_TO_DEVICE); /* キャッシュ書き戻し+IOVA確保 */
/* ... デバイスに addr を渡して転送を起動、完了割り込みを待つ ... */
dma_unmap_single(dev, addr, len, DMA_TO_DEVICE); /* IOVA解放+IOTLB無効化 */
mapしてからunmap/syncするまで、そのバッファの所有権はデバイス側にあります。この間にCPUがバッファを読み書きすると、キャッシュとメモリの不整合や転送中データの破壊が起きます。for_cpu / for_device の sync は「所有権を一時的に受け渡す」操作であり、向きの取り違えはデバッグが極めて困難なデータ破壊バグを生みます。
「DMAはCPUを介さずデバイスが直接メモリにアクセスする」「IOMMU/VT-dはデバイスのアドレスを変換・保護し、不正DMAを遮断する」「コヒーレント/ストリーミングの2種があり、後者はsyncでキャッシュ整合性を明示管理する」の3点が頻出です。IOMMUは性能機構ではなく保護機構である点を取り違えないこと。
まとめ
- DMA はデバイスが物理メモリへ直接読み書きし、CPUを転送から解放する。完了は割り込みで通知される。
- スキャッタギャザー は物理的に飛び地のページをSGリストで束ね、1転送として扱う。
- IOMMU(VT-d/AMD-Vi/SMMU) はデバイスのMMUとして、IOVAを物理アドレスへ変換しつつデバイス単位で保護する。不正アクセスはハードウェアが遮断する。
- DMA API はIOMMUやバウンスバッファの差を吸収し、コヒーレント/ストリーミングの2系統でキャッシュ整合性を管理する。所有権モデルとsyncの向きを誤るとデータ破壊に直結する。
仮想化でのデバイス直接割り当ては ハードウェア仮想化拡張 の二段変換と組み合わさり、ゲストへの安全なパススルーを実現します。
OS Article
DMAとIOMMUによるデバイスメモリアクセスを実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
DMA
比較で見る軸
難易度: advanced / カテゴリ: OS / タグ数: 5
導入後に効く点
IOMMU(Intel VT-d/AMD-Vi)はデバイスが出すアドレスをページテーブルで変換・検査し、許可された範囲外への不正アクセスを物理的に遮断する。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- OS
- タグ数
- 5
判断チェックリスト
- 自社の用途が「DMA / IOMMU」に近いか確認する。
- 強みである「DMAはデバイスがCPUを介さず物理メモリへ直接読み書きする仕組みで、CPUは転送開始の指示と完了割り込みの受信だけを担う。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。