ゲストのアドレスのページフォールト、第2段階変換の失敗、デバイスのDMA境界違反は、それぞれ異なる制御パスで発生する。まずCPU のアドレス変換とデバイスアクセスの認可 を明確にし、次に共有キューと移行について考察する。
参照目標 読書位置
アドレス、memslot およびフォールト 最初の3節
キューの所有権とバックエンド virtio、vhost
デバイスの認可と移行条件 VFIO、パsthrough移行
2段階アドレス変換
ゲストアドレスとホストアドレス:2段階変換とユーザー空間マッピング
GVA
ゲスト仮想アドレス
GPA
ゲスト物理アドレス
HPA
ホスト物理アドレス
ゲストページテーブル
EPT / NPT
HVA
VMM のユーザー仮想アドレス
memslot 対応関係
HVA はホストソフトウェアがバックアップを見つけるために使用されます。ハードウェアがゲストのメモリアクセスを実行する際、QEMU のページテーブルを先に照会することはありません。
ゲストOSはGVAからGPAへのページテーブルを管理し、x86のEPT/NPTはGPAをHPAにマッピングし、ホスト制御の権限を適用します。2段階の権限が、アクセスが許可されるかどうかを共同で決定します。VMMのHVAは、ホストソフトウェアがバックアップを管理する視点であり、ゲストが直接使用できるアドレスではありません。
古典的なユーザーメモリインターフェースは、memslot を使用して、GPAとVMMユーザー空間メモリの対応関係を登録します。登録は、すべての範囲の物理ページを一度にロックするわけではありません。ホストのページフォールト、回収、およびマッピングの無効化には、KVMによる調整が必要です。プライベートゲストメモリなどの新しいメカニズムは異なるインターフェースを持っており、すべてのVMメモリを通常の読み書き可能なHVAと仮定することはできません。メモリインターフェース
memslot のオフセット計算は物理ページ番号を返さない
通常のユーザーメモリスロットがGPA [0x10000000, 0x10003000) をカバーし、対応するHVAの起点が 0x70000000 であるとします。GPA 0x10001234 はスロット内にあり、オフセットは 0x1234 であるため、対応するHVAは 0x70001234 となります。この加算はVMMアドレス空間内の位置のみを返し、HPAを導き出すことはできません。ホストのページテーブルおよびバックアップの状態が、現在どの物理ページで保持されているかを決定します。
アドレスがスロット内にあるからといって、すべてのアクセスがその範囲内にあるとは限りません。例えば、0x10002ffc から8バイト読み込む場合、後4バイトはスロットの排他的上限を超えます。以下のモデルでは、空でないアクセスの全体が単一のスロット内に存在することを要求し、スロットをまたぐリクエストの分割は行いません。
GPA_BASE , HVA_BASE , SIZE = 0x 10000000 , 0x 70000000 , 0x 3000
def slot_hva ( gpa , length ) :
if not 0 <= gpa < 2 * * 64 or not 0 < length <= 2 * * 64 :
raise ValueError ( " 不正なアドレスまたは長さ" )
offset = gpa - GPA_BASE
if not 0 <= offset < SIZE or length > SIZE - offset :
raise ValueError ( " 全体アクセスは本スロット内に存在する必要があります" )
return HVA_BASE + offset
assert slot_hva ( 0x 10001234, 8 ) == 0x 70001234
assert slot_hva ( 0x 10002ffc, 4 ) == 0x 70002ffc
for gpa , length in [ ( 0x 10002ffc, 8 ) , ( 0x 10003000, 1 ) ,
( GPA_BASE - 1 , 1 ) , ( GPA_BASE , 0 ) , ( 2 * * 64 - 1 , 8 ) ] :
try :
slot_hva ( gpa , length )
except ValueError :
pass
else :
raise AssertionError ( " 境界外または不正なアクセスは拒否されるべきです" )
固定幅言語では、「長さが残りの空間を超えているかどうか」をチェックすることで、gpa + length を計算する際の整数オーバーフローを回避できます。実際のデバイスバックエンドでは、RAMスロットが見つからない場合、マシンアドレスマップに従ってMMIOデバイスまたは無効なアクセスに属するかどうかを判断する必要があり、空洞をすべて読み書き可能なデバイスと見なしてはいけません。
EPT violation はゲストがページテーブルを変更したことを意味しない
ゲストが自らのGVAページテーブルを更新しても、すべてのGPAからHPAへのマッピングを再構築する必要はありません。EPT violation は、第2段階変換が存在しないか、権限が満たされていないことを示しており、オンデマンドでのマッピング構築、書き込み保護の追跡、またはMMIOとして扱われる領域へのアクセスなどが考えられます。
ゲスト自身のページテーブルに起因するページフォールトと第2段階の問題も異なります。前者はゲストカーネルに処理を委ねる必要がある場合があります。診断時には、フォールトがどのレイヤーで発生したかを区別する必要があり、すべてのフォールトをゲストによるページテーブルの変更と見なしてはいけません。
ハードウェアによるネストされたページテーブルは、ソフトウェアシャドウページテーブルの保守負担を軽減しますが、KVM MMU には依然として複雑なマッピングおよび無効化の管理があり、ネスト仮想化も追加の組み合わせをもたらします。シャドウMMUを単に完全に存在しなくなった古いCPUの遺産と理解してはいけません。KVM MMU
ゲストPTEとEPTの権限をそれぞれ変更し、1回のメモリアクセスが第1段階または第2段階で停止するようにします。図では同じページ内オフセットを保持していますが、失敗後のアドレスは完了した変換の結果として表示されません。
図を準備しています 再読み込み
ゲストは書き込みを許可されているのに、なぜ第2段階で拒否されるのか? GVA→GPA と GPA→HPA は異なるマッピングであり、権限は共同で有効になります。EPT violation はゲストのページテーブルが直前に変更されたことを証明するものではなく、同じ種類のページフォールトをゲストに注入する必要があることを意味するものでもありません。
{"id":"guest-and-host-translation","title":"ゲストは書き込みを許可されているのに、なぜ第2段階で拒否されるのか?","summary":"GVA→GPA と GPA→HPA は異なるマッピングであり、権限は共同で有効になります。EPT violation はゲストのページテーブルが直前に変更されたことを証明するものではなく、同じ種類のページフォールトをゲストに注入する必要があることを意味するものでもありません。","height":1000,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><div class=\"presets\" role=\"group\" data-i18n-label=\"operation\"><button data-mode=\"read\" data-i18n=\"read\"></button><button data-mode=\"write\" data-i18n=\"write\"></button></div><div class=\"two\"><div class=\"card\"><header data-i18n=\"guest\"></header><label class=\"check\"><input type=\"checkbox\" id=\"gp\" checked><span data-i18n=\"gp\"></span></label><label class=\"check\"><input type=\"checkbox\" id=\"gw\" checked><span data-i18n=\"gw\"></span></label></div><div class=\"card\"><header data-i18n=\"host\"></header><label class=\"check\"><input type=\"checkbox\" id=\"ep\" checked><span data-i18n=\"ep\"></span></label><label class=\"check\"><input type=\"checkbox\" id=\"ew\"><span data-i18n=\"ew\"></span></label></div></div><div class=\"track-row\"><span data-i18n=\"translation\"></span><div id=\"translation\" class=\"track\"></div></div><div class=\"card\"><header data-i18n=\"result\"></header><div class=\"copy\"><p id=\"explanation\" role=\"status\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"guestFault\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"eptFault\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"ok\"></p></div></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.two{grid-template-columns:1fr}.check{overflow-wrap:anywhere}#translation{display:grid;grid-template-columns:1fr}#translation .cell{min-height:52px}","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 mode='write';function draw(){const write=mode==='write',g=$('#gp').checked&&(!write||$('#gw').checked),e=$('#ep').checked&&(!write||$('#ew').checked);cells('translation',[{text:'GVA 0x1234',cls:'on'},{text:g?'GPA 0x4234':'GPA —',cls:g?'on':'empty'},{text:g&&e?'HPA 0x9234':'HPA —',cls:g&&e?'gold':'empty'}]);set('explanation',t(!g?'guestFault':!e?'eptFault':'ok'));pressed(mode);}document.querySelectorAll('[data-mode]').forEach(e=>e.onclick=()=>{mode=e.dataset.mode;draw();});document.querySelectorAll('input').forEach(e=>e.onchange=draw);draw();","audio":false,"strings":{"ep":"EPT present","eptFault":"EPT violation:ゲストはGPAを既に提供していますが、ホストの第2段階ではまだ許可されていません。","ew":"EPT writable","gp":"ゲスト PTE present","guest":"第1段階:ゲスト制御","guestFault":"guest page fault:第1段階で今回のアクセスを満たしません。","gw":"ゲスト PTE writable","heading":"ゲストは書き込みを許可されているのに、なぜ第2段階で拒否されるのか?","host":"第2段階:ホスト制御","intro":"ゲストPTEとEPTの存在および書き込み権限をそれぞれ設定し、読み取りまたは書き込みを選択します。障害がどのレイヤーで停止するかによって、後のアドレスは完了した変換として使用できません。","ok":"両段階の条件が満たされ、同じページ内オフセット 0x234 を保持し、HPA を取得します。","operation":"アクセスタイプ","read":"読み取り","reset":"リセット","result":"失敗レイヤーまたは最終結果","scope":"固定 4 KiB ページ、GVA=0x1234 → GPA=0x4234 → HPA=0x9234。ゲストページテーブル自体はホストのバックアップにより既にアクセス可能であるため、最終的なデータマッピングのみをチェックします。TLB、アクセス/ダーティビット、実行権限、ページテーブルレベル数、 MMIO は省略します。固定されたトラバーサルアクセス回数は計算しません。","scopeLabel":"モデルの範囲と規約","translation":"アドレス変換結果","write":"書き込み"}}
ページテーブルのトラバーサルコストをなぜ24ステップに固定できないのか
4段階のゲストページテーブルに4段階のネストページテーブルを重ねると、理想的なキャッシュなし、大ページなしの条件下で、複数のアクセスが発生することが導き出せます。各ゲストページテーブルエントリ自体を取得するためにも、第2段階変換が必要です。実際のハードウェアにはTLB、ページテーブルトラバーサルキャッシュ、大ページ、および異なるレイヤー数があり、すべてのロードで完全なパスを再走行するわけではありません。
大ページは変換の負荷を軽減する可能性がありますが、ゲストとホストの両方の大ページ、アラインメント、および連続性を考慮する必要があります。ダーティページ追跡、共有、または無効化によりマッピングが分割される可能性もあり、ゲスト内でHugePagesを使用しているからといって、第2段階でも大ページが使われていると断言することはできません。
完全ミスした4段階トラバーサルの展開
ゲストページテーブルが g レベルを歩き、各ゲストページテーブルエントリが存在するGPAに対して完全な h レベルの第2段階ページテーブルを走行すると仮定します。関連するすべてのキャッシュがミスしており、大ページによる早期終了はなく、アクセスビット/ダーティビットの更新などの追加トランザクションは無視します。
各ゲストページテーブルエントリを取得するには、h 回の第2段階ページテーブルエントリの読み取りが必要で、さらにゲストページテーブルエントリ自体の読み取りが必要であり、合計 h+1 回となります。そのようなエントリが g 個あります。最終的なGPAを取得した後、さらに h 回の第2段階ページテーブルエントリの読み取りが必要で、最後に1回だけターゲットデータの読み取りを行います:
変換関連の読み取り:g × (h+1) + h。
最終的なデータの読み取りを加えると:(g+1) × (h+1)。
g h 変換関連の読み取り 最終的なデータの読み取りを含む
4 4 24 25
4 3 19 20
3 4 19 20
3 3 15 16
def uncached_walk ( g , h ) :
assert g >= 1 and h >= 1
translation = sum ( h + 1 for _ in range ( g ) ) + h
return translation , translation + 1
assert uncached_walk ( 4 , 4 ) == ( 24 , 25 )
assert uncached_walk ( 4 , 3 ) == uncached_walk ( 3 , 4 ) == ( 19 , 20 )
assert uncached_walk ( 3 , 3 ) == ( 15 , 16 )
これにより、「24」が通常何を数えているのか、そしてなぜそれがすべてのアクセスの固定コストとして記述できないのかが説明されます。TLB ヒット時には上記の完全なトラバーサルは発生しません。ページテーブルエントリが存在するページと最終的なデータページが、同じ第2段階のリーフレベルを持つ必要はありません。表ではそれらをすべて h に統一していますが、これは仮定の変化を比較しているに過ぎず、ゲストに大ページ1つを割り当てれば、すべての h が必ず1減ることを意味するものではありません。
virtio はデバイスプロトコルを定義するが、バックエンドの場所を限定しない
virtio ドライバとデバイスは、ネゴシエートされた機能と virtqueue を介してバッファ記述子を交換します。PCI、MMIO などは転送方式であり、virtio-net、virtio-blk などはデバイスタイプです。バックエンドは QEMU、カーネル vhost、ユーザー空間サービス、または互換性のあるハードウェアのいずれか也可以是です。Virtio
split virtqueue は、記述子テーブル、available および used 領域を分離します。packed virtqueue は、別の公開および回収形式を使用します。split のフィールドフローを使用して、すべての packed 実装を直接記述することはできません。
split virtqueue の段階 所有権および可視性の要件
ドライバの準備 まず記述子とデータを埋める
available の公開 要件に従ってソートし、デバイスに渡す
デバイスの処理および used の公開 完了状態がプロトコルに従って可視になるようにする
ドライバの回収 完了を確認してからバッファを再利用する
バリアと公開順序は、単なるパフォーマンス最適化ではなく、このプロトコルを維持します。
kick および割り込みは通知であり、1つのリクエストが厳密に1回の通知に対応するわけではありません。抑制やマージが可能であり、ドライバはポーリングも可能です。バッファをデバイスに渡した後、デバイスがまだ使用している間に随意に修正または解放することはできません。共有メモリは所有権プロトコルがないことを意味するものではなく、ゼロコピーを意味するものでもありません。Virtio 仕様
2つのリクエストが連続して公開されるが、回収順序が逆になる場合
VIRTIO_F_IN_ORDER がネゴシエートされていない split queue を例に挙げます。ドライバが記述子チェーンのヘッド 0 を最初に公開し、次にチェーンのヘッド 1 を公開した場合、デバイスは 1 を先に完了し、0 を後に完了させることができます。ドライバは、used 内のチェーンヘッドIDに基づいてリクエストを検索する必要があり、「最初に完了した項目」を「最初に送信されたリクエスト」に強制的に割り当てることはできません。IN_ORDER がネゴシエートされた場合は、その規則に従う必要があり、この逆順の例を適用してはいけません。
完了順序は変更可能であり、チェーンヘッドIDは依然として自らのリクエストを指す
公開順序
0:A → 1:B
デバイス完了順序
1:B → 0:A
ドライバはIDに基づいて回収
先に B を回収し、次に A を回収
0 は A が完了し、回収される前に、新しいリクエスト C を受け入れるために使用することはできません
split queue、IN_ORDER はネゴシエートされていない;図中の 0、1 は記述子チェーンのヘッドIDであり、used ring の位置ではありません。
以下では、デバイスの読み取り専用リクエストバッファ2つとそのライフサイクルのみをモデル化します。device 状態には、すでに公開され、デバイスがまだ開始していない、または処理中の期間が含まれます。used は、完了したがドライバがまだ回収していない公開済みを示します。キューインデックスのラップアラウンド、記述子チェーン、バリア、および通知メカニズムは、完全な実装によって別途処理されます。
from collections import deque
class Buffers :
def __init__ ( self ) :
self . state = { 0 : " driver" , 1 : " driver" }
self . data = { }
self . used = deque ( )
def write ( self , ident , payload ) :
if self . state [ ident ] != " driver" :
raise RuntimeError ( " バッファはドライバに戻っていないため、書き換えることはできません" )
self . data [ ident ] = bytes ( payload )
def publish ( self , ident ) :
assert self . state [ ident ] == " driver" and ident in self . data
self . state [ ident ] = " device"
def complete ( self , ident ) :
assert self . state [ ident ] == " device"
self . state [ ident ] = " used"
self . used . append ( ident )
def reclaim ( self ) :
ident = self . used . popleft ( )
assert self . state [ ident ] == " used"
self . state [ ident ] = " driver"
return ident , self . data [ ident ]
queue = Buffers ( )
queue . write ( 0 , b " request A" ) ; queue . publish ( 0 )
queue . write ( 1 , b " request B" ) ; queue . publish ( 1 )
try :
queue . write ( 0 , b " overwritten" )
except RuntimeError :
pass
else :
raise AssertionError ( " 早期に再利用することはできません" )
queue . complete ( 1 )
assert queue . reclaim ( ) == ( 1 , b " request B" )
assert queue . state [ 0 ] == " device"
queue . complete ( 0 )
assert queue . reclaim ( ) == ( 0 , b " request A" )
queue . write ( 0 , b " request C" )
queue . publish ( 0 )
assert queue . state == { 0 : " device" , 1 : " driver" }
「割り込みを受信していない」からといって、まだ完了していないと断言することはできません。「割り込みを1つ受信した」からといって、1つのリクエストのみが完了したと断言することもできません。回収は、プロトコルに従って可視な used 情報に基づいて行われます。割り込みと kick は、他側がキューをチェックするよう促すだけです。メモリ順序の要件は、受信側がインデックスの進捗を見る際に、記述子、データ、または完了情報がすでに可視であることを保証しますが、上記の順序のPython呼び出しは、実際の弱いメモリ順序を検証していません。Virtio split queue と IN_ORDER
vhost と vhost-user の境界
カーネル vhost-net などのバックエンドは、キューデータの処理の一部をカーネルに配置し、QEMU デバイスモデルへの対応するリクエストの往復を削減します。QEMU は設定と制御を担当し、プロセスが完全に仮想マシン実行から退出するわけではありません。
vhost-user はバックエンドを別のユーザー空間プロセスに配置し、プロトコルを介してキュー、メモリ、および通知情報を伝達します。名前に vhost が含まれていても、すべてのバックエンドがカーネル内にあるわけではありません。vhost-vsock は AF_VSOC シマンティクスを処理し、「AF_UNIX を VM 境界間で超える」と記述してはいけません。
バックエンドの選択は、コピー、スケジューリング、メモリ共有、およびフォールト分離パスを変更します。パフォーマンスについては、実際のキュー数、パケットまたはブロックサイズ、CPU 配置、および負荷測定に基づいて評価する必要があり、遅延が一律に50%削減されると約束することはできません。
VFIO:デバイスアクセス権の割り当てとDMA分離
VFIO は制御されたデバイスアクセス能力を公開し、パsthroughやユーザー空間ドライバに使用できます。CPU の EPT/NPT は、デバイスによるDMAを自動的に制限しません。IOMMU マッピング、デバイス分離トポロジ、および割り込みリマッピングなどは、それぞれ個別に構成する必要があります。VFIO
従来のインターフェースにおける IOMMU グループは、分離可能なデバイスの集合を表しており、必ずしも1つのPCI functionに等しいわけではありません。デバイスをアンバインドする前に、同じグループ内のデバイス、リセット範囲、およびホストの依存関係を理解する必要があります。理論的なチュートリアルでは、1行の unbind でライフサイクル全体の設定を完了したかのように偽装してはいけません。
新しいインターフェースでは、IOMMUFD などの方法を使用してデバイスアドレス空間を管理する場合がありますが、これにより基盤となるハードウェアの分離制限を無視してはいけません。DMA が認可された範囲のみアクセス可能であることは、正しい構成後の目標であり、「vfio-pci のバインド」という1つの操作で自動的にすべての保証が完了するものではありません。
パsthrough移行は絶対不可能ではない
移行前にデバイスが提供する必要がある回復材料
移行には、デバイス状態の保存と復元、進行中のDMAの処理、およびメモリに対するデバイスの変更の正確な記録が必要です。これらの機能を持たないデバイスは、透過的に移行できません。VFIO migration のサポートを備え、QEMU およびターゲットの互換性条件を満たすデバイスは、対応する移行プロセスをサポートする可能性があります。VFIO 移行
したがって、「すべてのパsthroughデバイスがオンライン移行できない」と記述するのではなく、機能と互換性の問題として記述する必要があります。VM全体のメモリ、CPU、およびデバイスの一貫性の境界については、QEMU と KVM の協力 を参照してください。