NetBSD vio9p(4)をQEMU-virtfsで使ってみる

[NetBSD Advent Calendar 2019](https://qiita.com/advent-calendar/2019/netbsd) 8日目の記事です。超絶遅れてごめんなさい🙇

@ozaki-r さんがNetBSDに[vip9p(4)][vio9p]を実装して下さいました！
これとQEMUの `-virtfs` を使えば、QEMU guestのNetBSDからhost Linux[^1]のファイルを直接読み書きできるようになります。NFSサーバー要らずで便利[^2]。

[vio9p]: https://netbsd.gw.com/cgi-bin/man-cgi?vio9p+4+NetBSD-current

[^1]: `-virtfs` はLinuxでしか使えません。Linux依存なコードはほとんど無いようなので（Linux独自の `struct stat` のフィールドを使っているくらい）そのうちmacOSでも動作させたい。

[^2]: `-nic user,smb=dir[,smbserver=addr]` でsambaマウントするという手もありますが、unix的な使い方をするには微妙…（ファイルのownerが無いとか）

## vip9p(4)を有効にしたkernelのビルド・インストール

[`sys/arch/amd64/conf/GENERIC` の `vio9p`][generic] を有効にしてGENERIC kernelをビルドします。

```sh
vio9p*	at virtio?			# Virtio 9P device
```

[generic]: https://github.com/NetBSD/src/blob/ec563b11b3b74880a3a2dc6d804f16f879ec32a7/sys/arch/amd64/conf/GENERIC#L1075

ビルドしたkernelをインストールして、試しに起動してみましょう。

```sh
# host (Linux)

mkdir ~/tmp/
touch ~/tmp/foo/

qemu-system-x86_64 -m 1G -hda /path/to/netbsd.qcow2 \
  -virtfs local,path=$HOME/tmp/,mount_tag=vfstag_tmp,security_model=mapped \
  -nographic -nic user,hostfwd=tcp::10022-:22 -enable-kvm  -no-reboot -rtc base=utc
```

`vio9p0` at `virtio0` at `pci0` が認識されます。


```hs
...
[   1.0228059] wm0 at pci0 dev 3 function 0: Intel i82540EM 1000BASE-T Ethernet (rev. 0x03)
[   1.0228059] wm0: interrupting at ioapic0 pin 11
[   1.0228059] wm0: Ethernet address 52:54:00:12:34:56
[   1.0228059] makphy0 at wm0 phy 1: Marvell 88E1011 Gigabit PHY, rev. 0
[   1.0228059] makphy0: 10baseT, 10baseT-FDX, 100baseTX, 100baseTX-FDX, auto
[   1.0228059] virtio0 at pci0 dev 4 function 0
[   1.0228059] virtio0: Virtio 9P Transport Device (rev. 0x00)
[   1.0228059] vio9p0 at virtio0: Features: 0x10000000<INDIRECT_DESC>
[   1.0228059] vio9p0: tagged as vfstag_tmp
[   1.0228059] virtio0: interrupting at ioapic0 pin 11
[   1.0228059] isa0 at pcib0
[   1.0228059] com0 at isa0 port 0x3f8-0x3ff irq 4: ns16550a, working fifo
[   1.0228059] com0: console
...
```

ちなみにvip9p(4)が有効になっていない場合はこんな感じです：

```hs
[   1.0228059] makphy0: 10baseT, 10baseT-FDX, 100baseTX, 100baseTX-FDX, auto
[   1.0228059] vendor 1af4 product 1009 (prehistoric, subclass 0x02) at pci0 dev 5 function 0 not configured
[   1.0228059] isa0 at pcib0
```

`pcictl` でも確認できます。

```sh
# guest (NetBSD)
pcictl pci0 list -N | grep virt
# 000:04:0: Qumranet Virtio (prehistoric, subclass 0x02) [virtio0]
```

## mknod

`/dev/vio9p*` を手動で作ります。

```sh
# guest (NetBSD)

# majorの確認
cat /proc/devices | grep vio9p  # 356 vio9p

# 16個（適当）作っておく
mknod /dev/vio9p0 c 356 0
mknod /dev/vio9p1 c 356 1
mknod /dev/vio9p2 c 356 2
mknod /dev/vio9p3 c 356 3
mknod /dev/vio9p4 c 356 4
mknod /dev/vio9p5 c 356 5
mknod /dev/vio9p6 c 356 6
mknod /dev/vio9p7 c 356 7
mknod /dev/vio9p8 c 356 8
mknod /dev/vio9p9 c 356 9
mknod /dev/vio9p10 c 356 10
mknod /dev/vio9p11 c 356 11
mknod /dev/vio9p12 c 356 12
mknod /dev/vio9p13 c 356 13
mknod /dev/vio9p14 c 356 14
mknod /dev/vio9p15 c 356 15
```

## `/usr/sbin/mount_9p` の更新

kernelだけでなく `mount_9p` もvio9p対応版 (`-c` が使える版) にする必要があります。本家のsets/baseから持ってくることにします。

```sh
# guest (NetBSD)
mv /usr/sbin/mount_9p /usr/sbin/mount_9p.orig

# host (Linux)
cd 汚しても良い適当な作業ディレクトリ
wget http://nycdn.netbsd.org/pub/NetBSD-daily/HEAD/latest/amd64/binary/sets/base.tar.xz
tar xvJf base.tar.xz
scp -P10022 ./usr/sbin/mount_9p root@localhost:/usr/sbin/

# guest (NetBSD)
/usr/sbin/mount_9p -h
# usage: mount_9p [-su] [-o mntopts] [-p port] [user@]server[:path] mountpoint
#        mount_9p -c [-su] [-o mntopts] devfile mountpoint
```

マウントしてみましょう。

```sh
# guest (NetBSD)

mkdir /root/tmp_mnt/
mount_9p -cu /dev/vio9p0 /root/tmp_mnt/
mount        # /dev/vio9p0 on /root/tmp_mnt type puffs|9p
ls tmp_mnt/  # foo (hostの ~/tmp/foo)
umount tmp_mnt/
```

`-virtfs` が複数ある場合は、`/dev/vio9p0, /dev/vio9p1, /dev/vio9p2, ...` と順番に割り当てられます。`mount_tag` とdmesgを比較しても確認できます。

```hs
qemu:
-virtfs local,path=$HOME/tmp/,mount_tag=vfstag_tmp,security_model=mapped
                                        ^^^^^^^^^^
-virtfs local,path=$HOME/tmp2/,mount_tag=vfstag_tmp2,security_model=mapped
                                         ^^^^^^^^^^^
dmesg:
[   1.0228059] vio9p0: tagged as vfstag_tmp
               ^^^^^^            ^^^^^^^^^^
[   1.0228059] vio9p1: tagged as vfstag_tmp2
               ^^^^^^            ^^^^^^^^^^^
```

## /etc/fstab

`/etc/fstab` で設定する場合はこんな感じです。

```
/dev/vio9p0 /root/tmp_mnt0/ 9p rw,-cu
/dev/vio9p1 /root/tmp_mnt1/ 9p rw,-cu
```

rebootする前に `mount -a` でapply、`mount` で確認しておきましょう。

`-virtfs` を付けずに起動した場合は

```hs
Starting syslogd.
Mounting all file systems...
mount_9p: /dev/vio9p0: Device not configured
mount_9p: /dev/vio9p1: Device not configured
/etc/rc.d/mountall exited with code 1
```

とエラーメッセージが出ますがマウントせずに普通に起動します。