Mewz (WebAssembly x Unikernel) を libkrun で動かしてみた - 外部記憶装置 の詳細を記すシリーズ
- その1 libkrun を試す
- その2 libkrun の構造
- その3 Mewz 追加実装(Linux zeropage, kernel cmd params)
- その4 Mewz 追加実装(Virtio MMIO)
- その5 Mewz on libkrun してみた
- その6 Mewz 追加実装(virtio-console)
- その7 virtio-vsock について ← この記事
- その8 Mewz 追加実装(virtio-vsock)
- その9 TSI の仕組み
目次
Virtio Socket (virtio-vsock) について
Virtio Socket (virtio-vsock) はゲストとホスト間をいわゆるソケットを用いて通信を行うためのデバイスである。 virtio-vsock のデバイス自体は Virtuqueue を3つ(rx, tx, event) 持つだけのシンプルな構造である。 ただし、ソケット相当の機能を提供するためにそれらの Virtqueue 上でソケットの multiplexing やフロー制御を行う必要がある。
virtio-vsock の概要
virtio-vsock では、ホスト・ゲストの識別子として 64bit 長の Context ID (CID) を持つ。また、ポートの概念も持ち、32bit 長の識別子で扱う。
これらの識別子については TCP, UDP のようにヘッダに格納されペイロードと合わせてパケットとして Virtqueue を流れる。
https://docs.oasis-open.org/virtio/virtio/v1.3/csd01/virtio-v1.3-csd01.html#x1-4800006
virtio-vsock ではソケットの管理やAPIについては言及されていないため、ゲスト側でソケットと送受信するパケットを適切に振り分ける仕組みが別途必要となる。 そのため、これまでの virtio-net や virtio-console と異なり、ドライバ単体だけでなく OS 側のソケットのサブシステム自体にも手をいれることになる。
通信の種類
virtio-vsock では、VIRTIO_VSOCK_TYPE_STREAM
と VIRTIO_VSOCK_TYPE_SEQPACKET
の2種類の通信方式が定義されている。
VIRTIO_VSOCK_TYPE_STREAM
はいわゆる SOCK_STREAM
に相当するものであり、順序保証付きコネクション指向型のメッセージ境界がない(=ストリーム形式) 通信を提供する。プロトコルとしては TCP 相当の機能を提供する。
VIRTIO_VSOCK_TYPE_SEQPACKET
はいわゆる SOCK_SEQPACKET
に相当するものであり、順序保証付きコネクション指向型でメッセージ境界が存在する通信を提供する。IP で利用されるプロトコルとしては SCTP が近い。
virtio-vsock ではこれらの通信を用途に応じて使い分けることができる。
プロトコル
virtio-vsock はコネクション指向型の通信を提供するため、セッションの確立および切断について上図に示すプロトコルが存在する。
接続
接続の起点は VIRTIO_VSOCK_OP_REQUEST
を送信するところからである。
dst_cid
,dst_port
に対応するソケットが存在し接続を受け入れる場合は VIRTIO_VSOCK_OP_RESPONSE
を返す。接続を拒否する場合は VIRTIO_VSOCK_OP_RST
を返す。接続については以上で完了する。
データの送受信
その後、VIRTIO_VSOCK_OP_RW
でデータの送受信を行う。virtio-vsock はロスレスな完全性が保証された通信路を前提とするため、Ack を行う必要はない。
データの送受信中、ピアのソケットバッファの状況を把握するためのリクエストとして VIRTIO_VSOCK_OP_CREDIT_REQUEST
を送信すると、ピアは VIRTIO_VSOCK_OP_CREDIT_UPDATE
としてバッファの状況をヘッダに記して返信する。VIRTIO_VSOCK_OP_CREDIT_UPDATE
についてはリクエストがない場合一方的に送っても構わない。
切断
切断については VIRTIO_VSOCK_OP_SHUTDOWN
により行う。フラグとして、ピアは以降いかなるデータも受信しないことを示す VIRTIO_VSOCK_SHUTDOWN_F_RECEIVE
と、いかなるデータも送信しないことを示す VIRTIO_VSOCK_SHUTDOWN_F_SEND
がある。これらのフラグを用いて、ピアに対してデータのこぼれが生じないように終了処理を行う。
いかなるデータの送受信が行われることが無いことが確認された時点で、切断処理は完了する。つまり、必要に応じて複数回のVIRTIO_VSOCK_OP_SHUTDOWN
がやり取りされる場合がある。例えば、VSockA→VSockBでVIRTIO_VSOCK_SHTDOWN_F_RECEIVE
とし、VSockB→VSockA で VIRTIO_VSOCK_SHTUDOWN_F_RECEIVE
とした場合、両者ともにデータの受信を行わないことを通知しているため切断とみなすことができ、VSockA→VSockB でVIRTIO_VSOCK_OP_RST
を送信して切断処理を完了する。なお、一定時間VIRIO_VSOCK_OP_RST
が届かなかった場合、VIRTIO_VSOCK_OP_RST
を送信して終了とする。
SEQPACKET について
VIRTIO_VSOCK_TYPE_SEQPACKET
では、2種類のメッセージ境界(VIRTIO_VSOCK_SEQ_EOM
, VIRTIO_VSOCK_SEQ_EOR
)を扱うことができる。最小単位は "Message" であり、メッセージを複数の RW パケットに分割後、最後のパケットに VIRTIO_VSOCK_SEQ_EOM
フラグを立てることで境界となる。
もう一つのメッセージ境界としては、複数のメッセージをまとめたものである "Record" がある。Record については 一連の連続した Message をまとめ、Record 間の区切りは VIRTIO_VSOCK_SEQ_EOR
フラグを RW パケットに立てることで行う。
フロー制御
virtio-vsock はパケットロスが一切許容されないロスレスな通信路を前提としているため再送機構を持たない。 そのため、相手のバッファ溢れによるパケットロスが生じないようにフロー制御を行っている。
フロー制御としてはシンプルなものであり、ピアから通知されるバッファサイズや転送カウントを元に、バッファの空き状況に応じてデータの送信可否を決定する。
https://docs.oasis-open.org/virtio/virtio/v1.3/csd01/virtio-v1.3-csd01.html#x1-4800006
実際の計算では、(ピアのバッファサイズ) - (送信済みバイト数 - ピアが処理したバイト数) となる。
つまり、RW パケット送信時点で相手が少なくとも持ちうる空き容量を計算している。
ピアのバッファサイズや処理したバイト数については、ピアが送信する RW パケットや VIRTIO_VSOCK_OP_CREDIT_UPDATE
パケットヘッダから得ることができる。
まとめ
virtio-vsock のデバイス自体はシンプルであるが、その上にソケットの仕組みを実装することになる。 そのため、カーネルが持つソケット相当のサブシステムに対して上手く組み込む必要がある。 次回は実際に Mewz に対してソケットとの統合も含めて virtio-vsock の実装を行う。