Virtual I/O Device (VritIO)について

Virtual I/O Device(以降VirtIO)は仮想マシン向けに提供されるI/Oフレームワークです.
ホストマシン上でソフトウェアとして動かし, ゲストマシンには物理デバイスの様に見える準仮想化デバイスとして作り仮想マシンの動作速度の向上を図るものです.
(動作速度向上については他の記事をあたってもらうとして)
そのVirtIOデバイスを作るにあたり必要な情報をまとめて備忘録として残します.

VirtIOデバイス

VirtIOの仕様は以下のリンクから読めます.
Virtual I/O Device (VIRTIO) Version 1.1

VirtIOではゲストマシンとのやり取りについて規定することで様々なデバイスに変身するための汎用性があります.
ネットワークカード(NIC)やブロックデバイス, コンソールデバイスなど様々なデバイスに適用することができます.

VirtIOデバイスはゲストマシンとの接続方法として仕様では以下の3種類があります.

  • PCI
  • MMIO
  • Channel I/O

今回は組み込みに適したMMIOについて深掘りしました.

VirtIOとMMIO

MMIOはメモリマップドI/Oの略ですが, CPUのアドレス空間上にメインメモリやI/Oデバイスが存在する方式です.
VirtIOデバイスがMMIOを用いるためには明示的にゲストマシンへ教える必要があるので, その方法としてはデバイスツリーを用います.
サンプルを以下に示します.
メモリの範囲とサイズを指定し, 割り込みについても記述しています.

// EXAMPLE: virtio_block device taking 512 bytes at 0x1e000, interrupt 42. 
virtio_block@1e000 { 
        compatible = "virtio,mmio"; 
        reg = <0x1e000 0x200>; 
        interrupts = <42>; 
}

デバイスツリーとして組み込むことでLinuxはVirtIOデバイスを認識することができます.
認識されると起動時にデバイスやドライバの初期化がなされデバイスとして/dev/*に現れます.

VirtIOデバイスのレジスタレイアウト

MMIOデバイスとしてのレジスタレイアウトは4.2.2 MMIO Device Register Layoutにあります.
すべてのレジスタは4バイトで構成され, リトルエンディアンです.

オフセットR/W名前メモ
0x000RMagicValue0x74726976マジックナンバーでありasciiで”virt”を意味する.
この数値をゲスト側が確認してVirtIOデバイスであることを確認する
0x004RVersion0x2VirtIOデバイスの仕様バージョン
Legacyデバイスは0x1を指定
0x008RDeviceIDデバイスの種類に応じて設定
0x0であればNIC, 0x1であればブロックデバイスなど
0x00CRVendorIDデバイスのベンダーID
特に規定は書かれていない
0x010RDeviceFeaturesVirtIOデバイスが対応している機能のビットを立ててゲスト側に示す
(DeviceFeaturesは合計64ビットありDeviceFeaturesSelによって0~31と32~63を切り替えて読み取ることができる)
0x014WDeviceFeaturesSel0x0 or 0x1DeviceFeaturesSel=0のときDeviceFeatures=0~31
DeviceFeaturesSel=1のときDeviceFeatures=32~63
(DeviceFeaturesSelは最下位ビットのみ有効)
0x020WDriverFeatures有効にする機能に対応したビットを立ててデバイスへ指定する
(DeviceFeaturesと同様合計64ビットある)
0x024WDriverFeaturesSelDriverFeaturesSel=0のときDriverFeatures=0~31
DriverFeaturesSel=1のときDriverFeatures=32~63
(DriverFeaturesSelは最下位ビットのみ有効)
0x030WQueueSel操作するVirtqueueを選択
デバイスの種類によってVirtqueueを使用する個数は異なる
(ブロックデバイスは1つ)
0x034RQueueNumMaxVirtqueue内で使用可能な最大の要素数
0x038WQueueNum最大の要素数を受けて使用する要素数を指定する
0x044RWQueueReady0x0 or 0x1Virtqueueの準備が完了したかを表す.
0x1で完了を示し使用可能である.
0x050WQueueNotifyVirtqueueに処理すべき新しいバッファがあることを
デバイスへ通知する.
0x060RInterruptStatusデバイスが割り込みをした原因のビットを返す
ビット0 : 少なくとも1つのアクティブなVirtqueueを使用したことを示す.
ビット1 : デバイスの構成が変更された
0x064WInterruptACK割り込み原因に対処したことをデバイスに通知する
0x070RWStatus現在のデバイスの状態を示す
0以外を書き込むとドライバの進行状況として設定される
0を書き込むとデバイスはリセットする
ビット0 : ACKNOWLEDGE
このビットによりOS側からVirtIOデバイスと認識される
ビット1 : DRIVER
デバイスを制御する意思があることを示す
ビット2 : DRIVER_OK
DRIVERがセットされたことに対しての応答
ビット3 : FEATURES_OK
OSとデバイスが機能についてネゴシエーション完了
ビット4 : DEVICE_NEEDS_RESET
デバイスに回復不可能なエラーが発生した
ビット5 : FAILED
OS側で問題が発生しデバイスの使用を断念した
2.1 Device Status Field
0x080
0x084
WQueueDescLow
QueueDescHigh
メモリ
アドレス
VirtqueueのDescriptor領域の物理アドレス
64ビット幅なので2つの領域を使用
0x090
0x094
WQueueDriverLow
QueueDriverHigh
メモリ
アドレス
VirtqueueのDriver領域の物理アドレス
(Available Ringに対応)
64ビット幅なので2つの領域を使用
0x0a0
0x0a4
WQueueDeviceLow
QueueDeviceHigh
メモリ
アドレス
VirtqueueのDescriptor領域の物理アドレス
(Used Ringに対応)
64ビット幅なので2つの領域を使用
0x0fcRConfigGenerationデバイスの構成のバージョンを表す.
(Configを変更した場合に変更される?)
0x100+RWConfigデバイスによって自由に使用可能な空間

Virtqueue

VirtIOではVirtqueueと呼ばれるリングキューを用いてデバイスとゲストでやり取りを行う.
NICでは最低でも2つ, ブロックデバイスでは1つのVirtqueueを使用する.
VirtqueueにはAvailable RingとUsed Ringと呼ばれるリングキューとDescriptor Tableと呼ばれる処理対象を示すバッファが存在する.

リングキューはキュー内のエントリーの先頭を示すidxと状況を示すフラグを持っている.
idxを元にring[]を辿るとDescriptor Tableのidxが得られる.
得られた値をもとにDescriptor Tableへアクセスすることで処理内容を得ることができる.

Descriptor Tableの要素には以下の内容が含まれている.

  • addr : バッファのメモリアドレス
  • len : バッファの長さ
  • flags
    VRING_DESC_F_NEXT(0x1) : nextフィールドは有効
    VRING_DESC_F_WRITE(0x2) : このバッファは書き込み専用(逆は読み取り専用)
  • next : 次のDescriptor要素のid

Descriptor Tableは一つのエントリーだけで完結するものから複数のエントリーを使うものもある.
(例えばブロックデバイスは3つ使用している)
複数使用している場合にはVRING_DESC_F_NEXTフラグが立っており, nextフィールドに有効な次のエントリー番号が書き込まれている.
VRING_DESC_F_WRITEはバッファのメモリアドレスが読み取りか書き込みかどうかを示す.
lenはそのバッファの読み書きサイズを示す.

まとめ

VirtIOデバイスのフレームワークについて基礎知識から最低限理解すべきVirtqueueについて
経験を踏まえて解説しました.
またMMIOデバイスとして扱うためのレジスタについても解説しました.
VirtIOの仕様書はあるものの体系的な解説は少なくVirtqueueの理解が難しく感じました.

参考文献

[转]virtio之vring 2022/08/24
Virtqueueについての構造について解説されている.
virtio-queue 2022/08/24
Virtqueueについてどういう流れで読み書きしてるか解説されている.

コメント

タイトルとURLをコピーしました