はじめに
前回の virtio-net 対応に引き続いて、virtio-blk に対応した。 virt queueのデータ構造や挙動はそのまま流用できる。 この辺り Virtio はうまく設計されているなと感動する。
7389ff59 カーネルコンパイルオプションの調整
ゲストカーネルからファイルシステムを経由してブロックIOを実現するにあたって、以下のオプションを有効にした。
CONFIG_VIRTIO_BLK=y
CONFIG_XFS_FS=y
CONFIG_EXT3_FS=y
CONFIG_EXT4_FS=y
4f4bbb78 virtio-blkの実装
さて、それでは本題である virtio-blk の実装に移っていく。 virtio-blk の挙動は virtio-net のものとほとんど同じなので、もし前回のブログを読んでいなければ、そちらを先に読むことをお勧めする。 差分はキュー数とdescripterテーブルのエントリが指す先のデータ構造だけである。 virtio-net では送受信のため2つのキューを必要としたが、virtio-blk の場合には 1つのキューで読み書きを実現する。 これはディスクへの読み書きはどちらもOS側からの発行となるため、外部割り込みを受ける必要がないためである。 descripterテーブルエントリが指すデータ構造は、以下のように3つのエントリがLinked Listの要領で繋がっている 6 。
1つ目のエントリが指すデータ構造は blkReq であり、typeフィールドが1なら書き込み、0なら読み込みを意味する。
sectorフィールドがディスクの先頭からのオフセットを意味する。
1セクタは512バイトなので、仮想ディスク用ファイルの sector x 512 バイト
目から読み書きすることを意味する。
type blkReq struct {
typ uint32
_ uint32
sector uint64
}
2つ目のエントリが実データを指す。ここに実際に読み書きしたいデータをバイナリで格納する。 3つ目のエントリがステータスである。エラーが発生した場合には0以外の数値を書き込む。
その他 Virt Queue の初期化方法や Avail Ring、Used Ring の使い方は virtio-net と全く同じ。
0819b1ed シナリオテストの追加
Go言語の標準的なテストフレームワーク $ go test
を使って、virtio-blk によって提供されたブロックデバイスが正しく動作していることをテストしたい。
やり方はいくつかあるだろうが、今回は vda.img というテスト用のディスクファイルを作り、これを ext2 でフォーマットした。
さらに vda.img をファイルシステムとしてマウントして、その中に index.html を配置しておいた。
Go言語のテストの中では、VMを起動してゲストがhttpサーバを立ち上げる。 そして、外部から先ほど作った index.html を curl コマンドで読み出すことにより、ゲスト内で virtio-blk が正しく動作していることを確認できるようになった。 無事、標準的なテストフレームワークに則った形で、シナリオテストを実装することができた。
終わりに
virtioを使って、ブロックIOやネットワーキングを実装できた。 デバイスの仮想化について、自分の手の中で動かせている感覚が徐々に育ってきたので面白い。 さて、これから何を目指していこうか。 もしデバイスが手に入りそうなら ARM や RISC-V のような別のアーキテクチャに挑戦したい。 あるいはライブマイグレーションのような発展的な実装も面白いだろう。
今回の実装の最中にもいくつか github.com でプルリクエストをもらった。 ニッチな領域だが、興味を持ってくれる方がいて嬉しい。