ラベル virtio の投稿を表示しています。 すべての投稿を表示
ラベル virtio の投稿を表示しています。 すべての投稿を表示

2022年3月25日金曜日

Linux KVM + virtio-net のレイテンシーを削減するヘンな方法

Linux KVM の virtio-net を利用している際にリモートホストへの PING 結果がマイクロ秒のオーダーで安定しない(ブレる)という話を見かけました。

Linux KVM の仕組みや QEMU のデバイスエミュレーションの仕組みを考えれば「そんなもの」かなと思い、調査および追加の実験をしてみました。なお、本記事は x86 アーキテクチャの Linux KVM の動作に基づきます。


パケットが送信される仕組み

ping コマンドなどにより発生した、送信パケットは OS のネットワークスタック(L3, L2)を通じてイーサネットのドライバに引き渡されます。
今回はLinux KVMのデバイスエミュレーションを利用した際のレイテンシーに注目したいため、プロセスからデバイスドライバまでデータ届く流れについては触れません。 Linux KVM の仮想マシンで使われる virtio-net に注目します。
仮想的なネットワークインターフェイスである virtio-net 、またそのベースとなる、ホスト−ゲスト間の通信に使われる virtio-pci では、ゲストOSのメモリ空間にある送信対象データをバッファへのポインタをリングバッファに積み、I/Oポートを叩くことで VMM (Linux KVM) 側に通知します。
このあたりの仕組み(PCIデバイスに模倣する virtio のゲスト・ホスト間通信の仕組み)は過去にエンジニアなら知っておきたい仮想マシンのしくみ スライドにて解説しているので、そちらもご確認ください。

https://github.com/torvalds/linux/blob/4f50ef152ec652cf1f1d3031019828b170406ebf/drivers/net/virtio_net.c#L1762 に、ホストへ通知すべき送信データが準備できた際ホスト側の virtio-net に通知するブロックがあります。
        if (kick || netif_xmit_stopped(txq)) {
            if (virtqueue_kick_prepare(sq->vq) && virtqueue_notify(sq->vq)) {
			u64_stats_update_begin(&sq->stats.syncp);
			sq->stats.kicks++;
			u64_stats_update_end(&sq->stats.syncp);
		}
	}

virtqueue_notify(sq->vq) は仮想デバイスの I/O ポートへのアクセスする実装となっています。そのセンシティブ命令を契機としてゲストから VMM に制御が移ります。デバイスモデル(qemu-kvm)のI/Oポートハンドラがゲストからの割り込み理由を判断し、 virtio-net のゲスト側リングバッファにあるデータをバックエンドのネットワークインターフェイスに渡します。
この際(Intel 表記で) VMX non-root → VMX root の kvm → qemu-kvm (ring3) と遷移し、場合によっては qemu-kvm がネットワークスタックにパケットを渡す前にプリエンプションが発生する可能性があり、送信タイミングが遅れる原因になり得ます。

 パケットが受信される仕組み


では、パケットが届いた場合はどうなるでしょうか? qemu-kvm がゲスト行きの受信データを受け取ると、予め用意されているゲスト側のメモリ領域に受信データを書き込み「ゲストに対して割り込みをかけます」。
本物のx86のプロセッサであれば、IRQ(Interrupt ReQuest)を受け付けるための信号線があり、それを介して割り込みを通知する方法と、割り込み通知用のアドレス空間に対するメモリ書き込みトランザクションにより割り込みを通知するMSI(MSI-X)割り込みがあります。仮想マシンの場合、仮想プロセッサにはIRQ線、MSI通知用のアドレス空間にメモリトランザクションを行ってもそれを受けてくれる相手がいないので、仮想マシンのプロセッサを「割り込みがかかった状態」にすることで割り込む挿入を実現することになります。

https://github.com/torvalds/linux/blob/0564eeb71bbb0e1a566fb701f90155bef9e7a224/arch/x86/kvm/vmx/vmx.c#L4574 にある vmx_inject_irq() に、 Intel VT でゲストに割り込みを通知する実装があります。
static void vmx_inject_irq(struct kvm_vcpu *vcpu)
{
	struct vcpu_vmx *vmx = to_vmx(vcpu);
	uint32_t intr;
	int irq = vcpu->arch.interrupt.nr;

	trace_kvm_inj_virq(irq);

	++vcpu->stat.irq_injections;
	if (vmx->rmode.vm86_active) {
		int inc_eip = 0;
		if (vcpu->arch.interrupt.soft)
			inc_eip = vcpu->arch.event_exit_inst_len;
		kvm_inject_realmode_interrupt(vcpu, irq, inc_eip);
		return;
	}
	intr = irq | INTR_INFO_VALID_MASK;
	if (vcpu->arch.interrupt.soft) {
		intr |= INTR_TYPE_SOFT_INTR;
		vmcs_write32(VM_ENTRY_INSTRUCTION_LEN,
			     vmx->vcpu.arch.event_exit_inst_len);
	} else
		intr |= INTR_TYPE_EXT_INTR;
	vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, intr);

	vmx_clear_hlt(vcpu);
}
ここでのキモは intr |= INTR_TYPE_EXT_INTR もしくは INTR_TYPE_SOFT_INTR です。 Intel VT では、仮想プロセッサの状態を管理するVMCS構造体に対して特定のビットを立てておくと、「次にその仮想マシンを実行するときに、ゲスト側で割り込みハンドラに処理が移される」仕組みです。ただし、ここでは実際に仮想マシンの実行し、割り込みハンドラに処理を移してはいないことに注意が必要です。

では、具体的にいつ、仮想マシンの割り込みハンドラが実行されるのでしょうか?答えは、Linux KVM + qemu-kvm の場合 qemu-kvm のメインループでCPUスレッドが改めて VMX non-root モードに移行するように要求したときです。

「次に仮想CPUを実行するときは割り込みハンドラを実行する必要があるよ」設定されても、その次にスケジュールされるタイミングは qemu-kvm の仮想CPU実行スケジュール、Linuxのスケジューラに依存することになります。仮想マシンのCPU負荷が低い場合には、qemu-kvmはVMX non-rootへの遷移を抑制して他のプロセスへのプリエンプションを許すことにより負荷を下げます。しかし、パケットが届いた後にqemu-kvmのCPUスレッドにプリエンプションが発生し、VMX non-rootへ遷移するまでがネットワークレイテンシーの増加として見えることになります。


VMX non-root でのビジーループは解決策になるか

では、ゲストがビジー状態でCPUを使い続ければ他のプロセスへのプリエンプションが発生せず、レイテンシーに改善が見られるのではないか?という仮説がありました。この方法で、ゲストOSがHLT命令などを発効してCPUを手放すことが減るというメリットは確かにありそうですが、2つの考慮点があります。
  • 仮想プロセッサが割り込みハンドラに制御を移すのはVMX rootからVMX non-rootに制御を移すタイミングです。このため、VMX non-rootでビジーループを回っていても割り込みハンドラに制御が移るわけではありません。
  • VMX non-rootでビジーループを回り続けた果てにVMX rootに遷移したとき、Linux カーネルから見れば、そのCPUスレッドは「大量のCPU時間を消費した」プロセスと認識します。このため、負荷が高いシステムでは、Linuxカーネルは他のプロセスに積極的にプリエンプションしようとするでしょう。これでは、次回のVMX non-rootへの遷移が遅れてしまいます。


ちょっとしたカーネルモジュールでレイテンシー性能を改善する

ここでは、ちょっとしたカーネルモジュールをロードして、レイテンシー改善が可能かを試してみました。コードは https://github.com/hasegaw/cheetah/blob/master/cheetah.c です。以下の関数をカーネルスレッドとしてビジーループで回します。

static void kthread_main(void)
{
        u32 vmx_msr_low, vmx_msr_high;

        rdmsr(MSR_IA32_UCODE_REV, vmx_msr_low, vmx_msr_high);

        schedule();
}

rdmsr は、 x86 アーキテクチャにある Machine-Specific Register を読み取ります。この命令はデバイスモデルでのエミュレーションが必要になるセンシティブ命令であるため、強制的に VMEXIT が発生して VMX non-root → VMX root への遷移が発生し、qemu-kvmでRDMSRのエミュレーションが行われ、その結果をもって再度VMENTERされます。
RDMSRのエミュレーションは未改造のqemu-kvmにおいて比較的コストの低いエミュレーション処理で、特に副作用が想定されないため、これを選んでいます。HLTなどを利用するとVMMがすぐにゲストに処理を戻さないかもしれませんが、RDMSRであれば、他プロセスへのプリエンプションがない限りは、すぐに再度VMENTERによりVMX non-rootに移行することが想定され、このタイミングで割り込みハンドラへの制御が移るわけです。


測定結果

Linux KVMで動作するシステム上のLinuxゲスト間でpingコマンドを実行した際のレイテンシーを測定してみました。下記が90%パーセンタイル値です。
  • RDMSRのビジーループなし(通常の状態) —— 614us (100.0%)
  • RDMSRのビジーループなし、ゲスト内で perl -e 'while(1) {}' でビジーループ —— 516us (84.0%)
  • RDMSRのビジーループあり —— 482us (78.5%)
ごく一般的なLinuxゲストと比較すると、先のRDMSRのビジーループを回したLinuxゲストでは20%程度のレイテンシー低下が確認できました。

このデータは2021年3月(本記事の執筆時点からちょうど1年前)に割とノリで試したもので、であまり細かいデータを取っていないため、上記の値しか残っていないことにはご容赦ください。


割り込み通知からポーリングループへの移行

ゲストがビジー状態で回り続けていても通知を受けられないのなら、ひとつの選択肢としてはポーリングループでホストからの通知が受けられたらいいのに、という発想になるかと思います。実際、DPDKではプロセスがループでポーリングし続けることで大幅にレイテンシーを短縮しているわけです。ホストOSからゲストOSへの通知をポーリングループで監視すればよいのではないでしょうか?

今回は、virtio-netに対して手を入れるような事はしなかったのですが、ホストOSからの割り込み通知を待たずにリングバッファへ新着データを拾いにいくことは可能な気がします。しかし、本当にレイテンシー面を気にするのでれば SRIOV などを検討するほうがいいでしょう。

Linux KVM ではありませんが、同じく Intel VT ベースで仮想マシン技術を提供する VMware 社のホワイトペーパー Best Practices for Performance Tuning of Latency-Sensitive Workloads in vSphere VMs には、下記の記述があります。



まとめ

Linux KVM の virtio-net を利用している際にリモートホストへの PING 結果がマイクロ秒のオーダーで安定しない(ブレる)理由について、Linux KVM、qemu-kvmとゲスト側カーネルの間で何がおきるかを考えてみました。
また、ゲストへの割り込み挿入タイミングを増やすために、ゲストカーネル上で数行で書けるカーネルスレッドを動かすことにとって、実際にレイテンシー性能がいくらか向上することを確認しました。
マイクロ秒オーダーの時間のブレを気にする場合は、最終的にはSRIOVやポーリングモードの導入、そもそも(VMENTER/VMEXITだって遅いので)VMM上ではなく物理的なマシンの利用が解決策になると思いますが、仮想マシンのしくみがこんな物なんだと理解しておくのは悪くない事かと思います。




2011年4月28日木曜日

Xen Meets virtio

開発版バージョンである xen-unstable と最新の qemu を組み合わせることによって virtio-net が Xen でも使えるようになってきたので紹介したいと思います。


■ 必要なもの



  • xen-unstable をレポジトリからひっぱってくる

  • qemu を git://xenbits.xen.org/people/aperard/qemu-dm.git から引っ張ってくる。ブランチ qemu-dm-v14 をチェックアウト。

  • QEMU のクラッシュを防ぐ vmport パッチ http://ysr.jp/~hasegaw/upstream-qemu_vmport.patch

    (vmportにアクセスしてくるような最近のdistroを動かす際に必要)


  • virtio対応のゲストOS


■ 設定方法


ドメイン定義ファイルで vif 行を下記イメージで記述します。

vif = [ 'type=ioemu, bridge=br0,mac=00:16:3e:09:ac:cd,model=virtio' ]


またドメインの作成には xl コマンドを使ってください。 xm コマンドだと新しい QEMU に対応できない可能性があります。


■ ゲストOS側の留意事項


Xen と QEMU の割り込み関係の相性が悪いのか、 MSI(-X) による割り込み通知が正しく inject されないようです。現時点の回避策として、ゲスト OS として Linux を起動する際に下記のカーネルパラメータを付けると問題を回避できます。

pci=nomsi


■ 参考


QEMUUpstream - Xen Wiki





2010年12月22日水曜日

FreeBSD用virtioドライバ ステータスアップデート (2010/12/21)

10月頃に作成したFreeBSD用virtioドライバですが、その後もボチボチと作業を続けております。

これまで開発した範囲では性能問題なども一通り解決しているので、
もうすこしコードを整理して、 -STABLE 品質として必要な機能を追加したら
ドライバをメーリングリストなどに投稿したいと思っています。

各ドライバのステータスなどについて紹介します。



■ 機能ドライバのステータス


  • virtio-net


    • vCPU = 1, 2の場合に e1000 エミュレーションよりスループットが高くなるよう改善

    • シャドウページテーブル環境下におけるパフォーマンス悪化を改善

    • メモリ管理方式の変更(malloc→bus_dmaによる連続したGuest-Physicalメモリ空間の確保)

    • OSにより確保されるmbufへの直接データ受信、バッファの再利用による高速化

    • 途中: Merged RX Buffers

    • 未: Hardware Checksum Offload

    • 未: GSO

    • 未: TSO

    • 未: Promiscous mode support, MAC Address filtering, Tagged VLAN Support

    • 未: MSI-X support

    • 未: Driver Media Status/Statics support

  • virtio-blk (新たに実装)


    • vCPU = 1, 2 の場合に SCSI Emulation よりも高性能、かつ CPU 負荷が低下するよう実装

      ※ qemu-kvm + FreeBSD は相性が悪いらしく、これだけでは性能は不十分

    • 済: VIRTIO_BLK_T オペレーション (ディスクへの単純読み書き)

    • 済: FLUSH オペレーション

    • 未: SCSI Device Passthrough

その他 Memory Balloon, Console は未着手

2010年12月5日日曜日

FreeBSD + KVMの環境でちょっと過ごしてみてわかってきたノウハウ(1)

最近 FreeBSD 用の virtio ドライバに割と情熱を注いでいるのですが(そろそろ公開したい!)、
ドライバの性能が出ないという状況になって調査したときに多少ノウハウが得られたのでメモがてら掲載しておきます。

まずの一つ目のネタ。kvm_statコマンド。

kvm statistics

 efer_reload                  0       0
 exits                401230913    2152
 fpu_reload               52997       0
 halt_exits           133189538     757
 halt_wakeup            5259576      15
 host_state_reload    170030018     977
 hypercalls                   0       0
 insn_emulation       252392427    1359
 insn_emulation_fail          0       0
 invlpg                 2684292       0
 io_exits               4130425      17
 irq_exits              8935347      39
 irq_injections         3195682       0
 irq_window                   0       0
 largepages                   0       0
 mmio_exits            28482276     179
 mmu_cache_miss          698023       0
 mmu_flooded              40079       0
 mmu_pde_zapped          216076       0
 mmu_pte_updated         180285       0
 mmu_pte_write          1931218       0
 mmu_recycled                 0       0
 mmu_shadow_zapped       696204       0
 mmu_unsync                 877       0
 nmi_injections               0       0
 nmi_window                   0       0
 pf_fixed               4719781       0
 pf_guest                817765       0
 remote_tlb_flush        234452       0
 request_irq                  0       0
 signal_exits                 0       0
 tlb_flush              4903428       0

性能が出ていない場合には非常に参考になる内容です。ソースコードなどを見て何をしてくれているのか調査したわけではないのですが・・・





exits
発生したVMEXITすべての回数を示します。VMEXITとはIntel VT/AMD-VのCPU仮想化支援技術においてVMを実行し、何らかの理由(I/Oとかメモリ管理で何かがおきてVMMの介入が必要になったとか、タイムスライスを使い切ったとか色々)によりゲストを停止させた回数になります。仮想化支援技術があったとしてもVMMモード/ゲストモードの切り替えは非常にコストの高いものなので、これをいかに削減するかがポイントになります。



insn_emulation
それまでに発生したハードウェアエミュレーションの回数?を示しているようですが、細かいことは未調査。

io_exits

I/Oポートに対するアクセスで発生したVMEXITの回数を示します。
この値が大きくなる場合はハードウェアへのI/Oが頻発しており
性能劣化につながっていることが予想されます。
このような操作は全てVMEXITを引き起こしますので、いかにI/Oポートアクセスなどを少ない回数で済まさせるかが、VMの実質実効時間をのばすためにVMEXIT回数を削減するという観点で気になる数字です。

irq_exits

VMの実行中、物理ハードウェアにからの割り込みによってVMEXITした回数を示しているようです。この数字が大きい場合は物理ハードウェア側のIRQ割り込みをいかに減らすかの検討が必要になりそうです……。

irq_injections

ハードウェアエミュレータ(qemu-kvm)がVMに対して発行した割り込みの回数を示します。
IRQを使用している場合、IRQが発生すると、IRQをシェアするデバイスドライバ群が
ISR(Interrupt Status Register)をポーリングします。
このため割り込み回数×IRQ・・・と、共有するデバイス数が多いほどVMEXITの発生回数も増加にも繋がることから、ドライバを作るのであればこれをいかに低く抑えるかがポイントになります。

mmio_exits

メモリマップドI/OのためのVMEXIT回数です。

mmu_pte_write

シャドウページテーブル利用時、VM内のページテーブルのエントリ(Page Table Entry)を更新した数を示すようです。この値は、たとえばゲストOS上でmalloc()やfree()を行った場合にも実装次第で発生します。値が大きい場合は仮想マシン上のメモリ管理機構について再検討する余地があると言えます。この値はEPT/NPTが利用できないハードウェアでシャドウページテーブルによる負荷を見るのに利用できます。ここはゲスト内のプログラムのメモリ管理を工夫することで削減が可能です。ドライバなどであれば普段利用中にゼロから動かさせない、ぐらい高い目標を持ってもよいでしょう。


2010年10月20日水曜日

virtio for FreeBSD: Current Status (2010/10/20)

Hello World!
I decided to write virtio frontend drivers for FreeBSD because I wanted to understand how virtio works.
My virtio-net drivers began to work in the middle of October, so I'm guessing that I would offer the driver for FreeBSD community.
Actually there are some tasks to do before release it, so I need some time (and some help:).


Current Status (virtio-net)

  • virtio-net driver is working on a 1vCPU virtual machine. Tested more than 300GB of TX and RX.

  • almost same performance with e1000 (check out the graph below)

  • no offload functions provided (I think it is not so needed now)


Development Environment

  • Host: Fedora 14 alpha x86_64

  • Guest: FreeBSD 8.1-RELEASE, amd64


Least Required Tasks to offer this

  • non-indirect table mode support (needs test and debug)

  • split virtio PCI device framework and virtio-net driver

  • SMP support


If you're interested in development of virtio drivers in FreeBSD, please
contact me. I'm not a good kernel-hacker (actually this is my first
work for kernel mode program and also my first C work), so any help are
welcome.

vm162# ifconfig
em0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
        options=209b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM,WOL_MAGIC>
        ether 52:54:00:1f:61:0d
        inet 192.168.44.162 netmask 0xfffffe00 broadcast 192.168.45.255
        media: Ethernet autoselect (1000baseT <full-duplex>)
        status: active
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
        options=3<RXCSUM,TXCSUM>
        inet6 fe80::1%lo0 prefixlen 64 scopeid 0x2
        inet6 ::1 prefixlen 128
        inet 127.0.0.1 netmask 0xff000000
        nd6 options=3<PERFORMNUD,ACCEPT_RTADV>

vm162# make load
/sbin/kldload -v /usr/src/sys/dev/xen/virtio/virtio_pci.ko
virtio_pci0: <virtio-net Virtual Network Interface> port 0xcb00-0xcb1f mem 0xf2054000-0xf2054fff irq 11 at device 8.0 on pci0
virtio_pci0: assigning PCI resouces: <1>mem <0>ioport <0>irq callback
virtio_pci0: [GIANT-LOCKED]
virtio_pci0: [ITHREAD]
vn0: Ethernet address: 52:54:00:1f:61:0e
Loaded /usr/src/sys/dev/xen/virtio/virtio_pci.ko, id=2

vm162# ifconfig vn0 up
vm162# ifconfig vn0 192.168.90.1 255.255.255.0
vm162# ifconfig
em0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
        options=209b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM,WOL_MAGIC>
        ether 52:54:00:1f:61:0d
        inet 192.168.44.162 netmask 0xfffffe00 broadcast 192.168.45.255
        media: Ethernet autoselect (1000baseT <full-duplex>)
        status: active
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
        options=3<RXCSUM,TXCSUM>
        inet6 fe80::1%lo0 prefixlen 64 scopeid 0x2
        inet6 ::1 prefixlen 128
        inet 127.0.0.1 netmask 0xff000000
        nd6 options=3<PERFORMNUD,ACCEPT_RTADV>
vn0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
        ether 52:54:00:1f:61:0e
        inet 192.168.90.1 netmask 0xffffff00 broadcast 255.255.255.0

vm162# ping 192.168.90.2
PING 192.168.90.2 (192.168.90.2): 56 data bytes
64 bytes from 192.168.90.2: icmp_seq=0 ttl=64 time=3.696 ms
64 bytes from 192.168.90.2: icmp_seq=1 ttl=64 time=1.004 ms
64 bytes from 192.168.90.2: icmp_seq=2 ttl=64 time=1.051 ms
64 bytes from 192.168.90.2: icmp_seq=3 ttl=64 time=1.044 ms
64 bytes from 192.168.90.2: icmp_seq=4 ttl=64 time=1.218 ms
64 bytes from 192.168.90.2: icmp_seq=5 ttl=64 time=0.970 ms
^C
--- 192.168.90.2 ping statistics ---
6 packets transmitted, 6 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.970/1.497/3.696/0.986 ms

vm162# iperf -c 192.168.90.2
------------------------------------------------------------
Client connecting to 192.168.90.2, TCP port 5001
TCP window size: 32.5 KByte (default)
------------------------------------------------------------
[  3] local 192.168.90.1 port 15185 connected with 192.168.90.2 port 5001
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-10.7 sec   142 MBytes   112 Mbits/sec
vm162#


performance comparison between fxp(e1000) and vn(virtio)


2010年10月12日火曜日

FreeBSD用virtioドライバを作ってみた

なんとなくノリで「virtioのドライバを書いてみたくなった」ので9月末から作業を開始したのですが、この週末で大体動くようになりました。


コードが整理できたら、FreeBSDソースツリーへのコミット目指して頑張ってみますかねぇ。まぁ、予定は未定です。また、実際にドライバが書ける程度まではvirtioを研究したので、また機会を見て情報共有できればと思っています。


■ どんなかんじ?



見てもらうのが早いですよね。こんなかんじ。

vm162# ifconfig
em0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
        options=209b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM,WOL_MAGIC>
        ether 52:54:00:1f:61:0d
        inet 192.168.44.162 netmask 0xfffffe00 broadcast 192.168.45.255
        media: Ethernet autoselect (1000baseT <full-duplex>)
        status: active
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
        options=3<RXCSUM,TXCSUM>
        inet6 fe80::1%lo0 prefixlen 64 scopeid 0x2
        inet6 ::1 prefixlen 128
        inet 127.0.0.1 netmask 0xff000000
        nd6 options=3<PERFORMNUD,ACCEPT_RTADV>

vm162# make load
/sbin/kldload -v /usr/src/sys/dev/xen/virtio/virtio_pci.ko
virtio_pci0: <virtio-net Virtual Network Interface> port 0xcb00-0xcb1f mem 0xf2054000-0xf2054fff irq 11 at device 8.0 on pci0
virtio_pci0: assigning PCI resouces: <1>mem <0>ioport <0>irq callback
virtio_pci0: [GIANT-LOCKED]
virtio_pci0: [ITHREAD]
vn0: Ethernet address: 52:54:00:1f:61:0e
Loaded /usr/src/sys/dev/xen/virtio/virtio_pci.ko, id=2

vm162# ifconfig vn0 up
vm162# ifconfig vn0 192.168.90.1 255.255.255.0
vm162# ifconfig
em0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
        options=209b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM,WOL_MAGIC>
        ether 52:54:00:1f:61:0d
        inet 192.168.44.162 netmask 0xfffffe00 broadcast 192.168.45.255
        media: Ethernet autoselect (1000baseT <full-duplex>)
        status: active
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
        options=3<RXCSUM,TXCSUM>
        inet6 fe80::1%lo0 prefixlen 64 scopeid 0x2
        inet6 ::1 prefixlen 128
        inet 127.0.0.1 netmask 0xff000000
        nd6 options=3<PERFORMNUD,ACCEPT_RTADV>
vn0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
        ether 52:54:00:1f:61:0e
        inet 192.168.90.1 netmask 0xffffff00 broadcast 255.255.255.0

vm162# ping 192.168.90.2
PING 192.168.90.2 (192.168.90.2): 56 data bytes
64 bytes from 192.168.90.2: icmp_seq=0 ttl=64 time=3.696 ms
64 bytes from 192.168.90.2: icmp_seq=1 ttl=64 time=1.004 ms
64 bytes from 192.168.90.2: icmp_seq=2 ttl=64 time=1.051 ms
64 bytes from 192.168.90.2: icmp_seq=3 ttl=64 time=1.044 ms
64 bytes from 192.168.90.2: icmp_seq=4 ttl=64 time=1.218 ms
64 bytes from 192.168.90.2: icmp_seq=5 ttl=64 time=0.970 ms
^C
--- 192.168.90.2 ping statistics ---
6 packets transmitted, 6 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.970/1.497/3.696/0.986 ms

vm162# iperf -c 192.168.90.2
------------------------------------------------------------
Client connecting to 192.168.90.2, TCP port 5001
TCP window size: 32.5 KByte (default)
------------------------------------------------------------
[  3] local 192.168.90.1 port 15185 connected with 192.168.90.2 port 5001
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-10.7 sec   142 MBytes   112 Mbits/sec
vm162#


■ ベンチマーク

非常にいい加減なベンチマークですが、デフォルト設定の e1000 エミュレーションより微妙に速いようです。





e1000 オフロードエンジン等を ON/OFF すると性能が変わるようなので、まぁだいたい同じぐらいのパフォーマンスが出ていると考えればよさそうです。

■ 実装状況


  • ロック皆無!たぶん……いや、間違いなく、SMPだと即死します。

  • virtio PCIインターフェイスとvirtio-netがまだ綺麗に分離できていない。

  • キャパビリティ (features bit)のほとんどは未実装

■ その他今後の課題


  • virtio デバイスを実装するためのフレームワーク検討

  • ハードウェア チェックサム、その他キャパビリティのサポート

  • 一部 GPL ライセンスされたコードが含まれるため除去

  • 安定性の向上

  • チューニング

■ 作業環境


  • Host: Fedora 14: Fedora release 14 (Branched) Alpha Release, x86_64

  • Guest: FreeBSD 8.1-RELEASE, amd64