1
- ハイパーバイザーの作り方 第20回 bhyveにおける仮想ディスクの実装
1
+ ---
2
+ authors :
3
+ -
' Takuya ASADA [email protected] '
4
+ title : |
5
+ ハイパーバイザの作り方~ちゃんと理解する仮想化技術~
6
+ 第20回 bhyveにおける仮想ディスクの実装
7
+ ...
2
8
3
9
はじめに
10
+ ========
4
11
前回の記事では、bhyveにおける仮想NICの実装についてTAPデバイスを用いたホストドライバの実現方法を例に挙げ解説しました。
12
+
5
13
今回の記事では、bhyveにおける仮想ディスクの実装について解説していきます。
6
14
7
15
bhyveにおける仮想ディスクの実装
16
+ ===============================
8
17
bhyveがゲストマシンに提供する仮想IOデバイスは、全てユーザプロセスである/usr/sbin/bhyve上に実装されています(図1)。
18
+
9
19
bhyveは実機上のディスクコントローラと異なり、ホストOSのファイルシステム上のディスクイメージファイルに対してディスクIOを行います。
20
+
10
21
これを実現するために、/usr/sbin/bhyve上の仮想ディスクコントローラは、ゲストOSからのIOリクエストをディスクイメージファイルへのファイルIOへ変換します。
11
22
12
23
以下に、ディスク読み込み手順と全体図(図1)を示します。
13
- 1,ゲストOSはvirtio-blkドライバを用いて、共有メモリ上のリングバッファにIOリクエストを書き込みます。そして、IOポートアクセスによってハイパーバイザにリクエスト送出を通知する。IOポートアクセスによってVMExitが発生し、CPUの制御がホストOSのvmm.koのコードに戻る。
24
+
25
+ 1. ゲストOSはvirtio-blkドライバを用いて、共有メモリ上のリングバッファにIOリクエストを書き込みます。そして、IOポートアクセスによってハイパーバイザにリクエスト送出を通知する。IOポートアクセスによってVMExitが発生し、CPUの制御がホストOSのvmm.koのコードに戻る。
14
26
bhyveの仮想ディスクコントローラのエミュレーションは、ユーザランドで行われています。vmm.koはこのVMExitを受けてioctlをreturnし/usr/sbin/bhyveへ制御を移す。
15
- 2,ioctlのreturnを受け取った/usr/sbin/bhyveは、仮想ディスクコントローラの共有メモリ上のリングバッファからリクエストを取り出します。
16
- 3,2で取り出したリクエストをパースし、ディスクイメージファイルにread()を行います。
17
- 4,読み出したデータを共有メモリ上のリングバッファに乗せ、ゲストOSに割り込みを送ります。5,ゲストOSは割り込みを受け、リングバッファからデータを読み出します。
18
- 図1,ディスク読み込み手順
27
+ 2. ioctlのreturnを受け取った/usr/sbin/bhyveは、仮想ディスクコントローラの共有メモリ上のリングバッファからリクエストを取り出します。
28
+ 3. 2で取り出したリクエストをパースし、ディスクイメージファイルにread()を行います。
29
+ 4. 読み出したデータを共有メモリ上のリングバッファに乗せ、ゲストOSに割り込みを送ります。
30
+ 5. ゲストOSは割り込みを受け、リングバッファからデータを読み出します。
31
+
32
+ 
19
33
20
34
書き込み処理では、リクエストと共にデータをリングバッファを用いて送りますが、それ以外は読み込みと同様です。
21
35
22
- virtio-blkの仕組み
36
+ virtio-blkのしくみ
37
+ ==================
23
38
これまでに、準仮想化I/Oの仕組みとして、virtioとVirtqueue、virtio-netについて解説してきました。ここでは、ブロックデバイスを準仮想化する、virtio-blkについて解説を行います。
24
- virtio-netは受信キュー、送信キュー、コントロールキューの3つのVirtqueueからなっていましたが、virtio-blkでは単一のVirtqueueを用います。これは、ディスクコントローラの挙動がNICとは異なり、必ずOSからコマンド送信を行った後にデバイスからレスポンスが返るという順序になるためです。
39
+
40
+ virtio-netは受信キュー、送信キュー、コントロールキューの3つのVirtqueueからなっていましたが、virtio-blkでは単一のVirtqueueを用います。これは、ディスクコントローラの挙動がNICとは異なり、必ずOSからコマンド送信を行った後にデバイスからレスポンスが返るという順序になるためです[^1]。
41
+
25
42
ブロックIOのリクエストは連載第12回の「ゲスト→ホスト方向のデータ転送方法」で解説した手順で送信されます。
26
- virtio-blkでは1つのブロックIOリクエストに対して、以下のようにDescriptor群を使用します。1個目のDescriptorはstruct
27
- virtio~ b ~ lk ~ o ~ uthdr (表1)を指します。
28
- この構造体にはリクエストの種類、リクエスト優先度、アクセス先オフセットを指定します。2〜(n−1)個目以降のDescriptorはリクエストに使用するバッファを指します。リクエストがreadな場合は読み込み結果を入れる空きバッファを、writeな場合は書き込むデータを含むバッファを指定します。
43
+
44
+ virtio-blkでは1つのブロックIOリクエストに対して、以下のようにDescriptor[^2]群を使用します。1個目のDescriptorはstruct virtio_blk_outhdr (表1)を指します。この構造体にはリクエストの種類、リクエスト優先度、アクセス先オフセットを指定します。2〜(n−1)個目以降のDescriptorはリクエストに使用するバッファを指します。リクエストがreadな場合は読み込み結果を入れる空きバッファを、writeな場合は書き込むデータを含むバッファを指定します 。
45
+
29
46
バッファのアドレスは物理アドレス指定になるため、仮想アドレスで連続した領域でも物理的配置がバラバラな状態な場合があります。これをサポートするためにバッファ用Descriptorを複数に別けて確保出来るようになっています。
30
- struct
31
- virtio~ b~ lk~ o~ uthdrにはバッファ長のフィールドがありませんが、これはDescriptorのlenフィールドを用いてホストへ通知されます。n個目のDescriptorは1byteのステータスコード(表2)のアドレスを指します。このフィールドはホスト側がリクエストの実行結果を返すために使われます。
32
47
33
- 表1,struct virtio~ b~ lk~ o~ uthdr
48
+ struct virtio_blk_outhdrにはバッファ長のフィールドがありませんが、これはDescriptorのlenフィールドを用いてホストへ通知されます。n個目のDescriptorは1byteのステータスコード(表2)のアドレスを指します。このフィールドはホスト側がリクエストの実行結果を返すために使われます。
49
+
50
+ type member description
51
+ ----- ------- --------------------------------------------------
52
+ u32 type リクエストの種類(read=0x0, write=0x1, ident=0x8)
53
+ u32 ioprio リクエスト優先度
54
+ u64 sector セクタ番号(オフセット値)
55
+
56
+ Table : struct virtio_blk_outhdr
57
+
58
+ type member description
59
+ ----- ------- ---------------------------
60
+ 0 OK 正常終了
61
+ 1 IOERR IOエラー
62
+ 2 UNSUPP サポートされないリクエスト
34
63
35
- 表2, ステータスコード
64
+ Table : ステータスコード
36
65
37
66
ディスクイメージへのIO
38
- /usr/sbin/bhyveはvirtio-blkを通じてゲストOSからディスクIOリクエストを受け取り、ディスクイメージへ読み書きを行います。bhyveが対応するディスクイメージはRAW形式のみなので、ディスクイメージへの読み書きはとても単純です。ゲストOSから指定されたオフセット値とバッファ長をそのまま用いてディスクイメージへ読み書きを行えばよいだけです。
67
+ ======================
68
+ /usr/sbin/bhyveはvirtio-blkを通じてゲストOSからディスクIOリクエストを受け取り、ディスクイメージへ読み書きを行います。bhyveが対応するディスクイメージはRAW形式のみなので、ディスクイメージへの読み書きはとても単純です。ゲストOSから指定されたオフセット値とバッファ長をそのまま用いてディスクイメージへ読み書きを行えばよいだけです[^3]。
69
+
39
70
それでは、このディスクイメージへのIOの部分についてbhyveのコードを実際に確認してみましょう。/usr/sbin/bhyveの仮想ディスクIO処理のコードをコードリスト1に示します。
40
71
72
+ コードリスト1,/usr/sbin/bhyveの仮想ディスクIO処理
73
+ ---------------------------------------------------
41
74
` ` `
42
75
/* ゲストOSからIO要求があった時に呼ばれる */
43
76
static void
@@ -54,11 +87,14 @@ pci_vtblk_proc(struct pci_vtblk_softc *sc, struct vqueue_info *vq)
54
87
uint16_t flags[VTBLK_MAXSEGS + 2];
55
88
/* iovに1リクエスト分のDescriptorを取り出し */
56
89
n = vq_getchain(vq, iov, VTBLK_MAXSEGS + 2, flags);
57
- 〜 略 〜
/* 一つ目のDescriptorはstruct virtio_blk_outhdr */
58
- vbh = iov[0].iov_base;
/* 最後のDescriptorはステータスコード */
90
+ 〜 略 〜
91
+ /* 一つ目のDescriptorはstruct virtio_blk_outhdr */
92
+ vbh = iov[0].iov_base;
93
+ /* 最後のDescriptorはステータスコード */
59
94
status = iov[--n].iov_base;
60
95
〜 略 〜
61
- /* リクエストの種類 */
type = vbh->vbh_type;
96
+ /* リクエストの種類 */
97
+ type = vbh->vbh_type;
62
98
writeop = (type == VBH_OP_WRITE);
63
99
/* オフセットをsectorからbyteに変換 */
64
100
offset = vbh->vbh_sector * DEV_BSIZE;
@@ -104,12 +140,12 @@ pci_vtblk_proc(struct pci_vtblk_softc *sc, struct vqueue_info *vq)
104
140
vq_relchain(vq, 1);
105
141
}
106
142
` ` `
107
- コードリスト1,/usr/sbin/bhyveの仮想ディスクIO処理
108
143
109
- (1) NICではOSから何もリクエストを送らなくてもネットワーク上の他のノードからパケットが届くのでデータが送られてきます。このため、必ずOSからリクエストを送ってから届くというような処理にはなりません。
110
- (2) Virtqueue上でデータ転送をおこなうための構造体。第12回のVirtqueueの項目を参照。
111
- (3) QCOW2形式などのより複雑なフォーマットでは未使用領域を圧縮するため、ゲスト・ホスト間でオフセット値が一致しなくなり、またメタデータを持つ必要が出てくるのでRAWイメージと比較して複雑な実装になります。
144
+ [^1] : NICではOSから何もリクエストを送らなくてもネットワーク上の他のノードからパケットが届くのでデータが送られてきます。このため、必ずOSからリクエストを送ってから届くというような処理にはなりません。
145
+ [^2] : Virtqueue上でデータ転送をおこなうための構造体。第12回のVirtqueueの項目を参照。
146
+ [^3] : QCOW2形式などのより複雑なフォーマットでは未使用領域を圧縮するため、ゲスト・ホスト間でオフセット値が一致しなくなり、またメタデータを持つ必要が出てくるのでRAWイメージと比較して複雑な実装になります。
112
147
113
- ## まとめ
148
+ まとめ
149
+ ======
114
150
今回は仮想マシンのストレージデバイスについて解説しました。
115
151
次回は、仮想マシンのコンソールデバイスについて解説します。
0 commit comments