--- nono/vm/virtio_net.cpp 2026/04/29 17:05:33 1.1.1.3 +++ nono/vm/virtio_net.cpp 2026/04/29 17:06:00 1.1.1.8 @@ -12,11 +12,11 @@ #include "virtio_def.h" #include "config.h" #include "ethernet.h" +#include "event.h" #include "macaddr.h" #include "memorystream.h" #include "monitor.h" #include "scheduler.h" -#include "syncer.h" // デバイス構成レイアウト class VirtIONetConfigWriter @@ -35,16 +35,16 @@ class VirtIONetConfigWriter VirtIONetDevice::VirtIONetDevice(uint slot_) : inherited(OBJ_VIRTIO_NET, slot_) { - // 短縮形 - AddAlias("VNet"); - AddAlias("Ethernet"); + // 割り込み名(とログ名) + strlcpy(intrname, "VIONet", sizeof(intrname)); + SetName(intrname); + ClearAlias(); + AddAlias(intrname); device_id = VirtIO::DEVICE_ID_NETWORK; vqueues.emplace_back(this, 0, "ReceiveQ1", 8); vqueues.emplace_back(this, 1, "TransmitQ1", 16); - // 割り込み名 - strlcpy(intrname, "VIONet", sizeof(intrname)); // 完了通知メッセージ msgid = MessageID::VIRTIO_NET_DONE; @@ -55,7 +55,7 @@ VirtIONetDevice::VirtIONetDevice(uint sl } monitor = gMonitorManager->Regist(ID_MONITOR_VIRTIO_NET, this); - monitor->func = ToMonitorCallback(&VirtIONetDevice::MonitorUpdate); + monitor->SetCallback(&VirtIONetDevice::MonitorScreen); monitor->SetSize(MONITOR_WIDTH, 3 + vqlines); } @@ -64,7 +64,7 @@ VirtIONetDevice::~VirtIONetDevice() { // EthernetDevice と同じ…。 if ((bool)hostnet) { - hostnet->SetRxCallback(NULL); + hostnet->ResetRxCallback(); } } @@ -77,9 +77,16 @@ VirtIONetDevice::Create() } // EthernetDevice とほぼ同じ…。 - hostnet.reset(new HostNetDevice(this, 0)); + try { + hostnet.reset(new HostNetDevice(this, 0, "VirtIO Network")); + } catch (...) { } + if ((bool)hostnet == false) { + warnx("Failed to initialize HostNetDevice at %s", __method__); + return false; + } - hostnet->SetRxCallback(ToDeviceCallback(&VirtIONetDevice::HostRxCallback)); + auto func = ToDeviceCallback(&VirtIONetDevice::HostRxCallback); + hostnet->SetRxCallback(func, 0); return true; } @@ -93,13 +100,10 @@ VirtIONetDevice::Init() } // MAC アドレスを取得。 - macaddr_t macaddr; if (EthernetDevice::GetConfigMacAddr(0, &macaddr, false) == false) { - // エラーメッセージ表示済み。 + // エラーメッセージは表示済み。 return false; } - // この先設定するタイミングがないのでここで設定する? - hostnet->SetMyAddr(macaddr); // 表示用。 macaddr_str = macaddr.ToString(':'); @@ -107,8 +111,8 @@ VirtIONetDevice::Init() VirtIONetConfigWriter cfg; SetDeviceFeatures(VIRTIO_NET_F_CSUM); SetDeviceFeatures(VIRTIO_NET_F_GUEST_CSUM); - SetDeviceFeatures(VIRTIO_NET_F_MTU), cfg.mac = macaddr; - SetDeviceFeatures(VIRTIO_NET_F_MAC), cfg.mtu = 1500; + SetDeviceFeatures(VIRTIO_NET_F_MTU), cfg.mtu = 1500; + SetDeviceFeatures(VIRTIO_NET_F_MAC), macaddr.ExportTo(&cfg.mac[0]); SetDeviceFeatures(VIRTIO_NET_F_MRG_RXBUF); cfg.WriteTo(&device_config[0]); @@ -116,22 +120,22 @@ VirtIONetDevice::Init() scheduler->ConnectMessage(MessageID::HOSTNET_RX(0), this, ToMessageCallback(&VirtIONetDevice::RxMessage)); - // time は都度設定する - event.func = ToEventCallback(&VirtIONetDevice::RxEvent); - event.SetName("VirtIONet RX"); - scheduler->RegistEvent(event); + auto evman = GetEventManager(); + event = evman->Regist(this, + ToEventCallback(&VirtIONetDevice::RxEvent), + "VirtIONet RX"); return true; } void -VirtIONetDevice::MonitorUpdate(Monitor *, TextScreen& screen) +VirtIONetDevice::MonitorScreen(Monitor *, TextScreen& screen) { int y = 0; screen.Clear(); - y = MonitorUpdateDev(screen, y); + y = MonitorScreenDev(screen, y); screen.Puts(0, y, "MACAddress:"); screen.Puts(12, y, macaddr_str.c_str()); @@ -139,9 +143,9 @@ VirtIONetDevice::MonitorUpdate(Monitor * y++; for (const auto& q : vqueues) { - y = MonitorUpdateVirtQueue(screen, y, q); + y = MonitorScreenVirtQueue(screen, y, q); y++; - y = MonitorUpdateVirtQDesc(screen, y, q); + y = MonitorScreenVirtQDesc(screen, y, q); y++; } } @@ -188,7 +192,7 @@ VirtIONetDevice::ProcessDesc(VirtIOReq& num_buffers = ReqReadLE16(req); putlog(2, "req.hdr $%08x: flags=%02x gso=%02x hdr_len=%04x gso_size=%04x " "csum_start=%04x offset=%04x num=%04x", - req.buf[0].addr, + req.rbuf[0].addr, flags, gso_type, hdr_len, @@ -198,42 +202,17 @@ VirtIONetDevice::ProcessDesc(VirtIOReq& num_buffers); NetPacket tx_packet; - while (req.pos < req.len) { - tx_packet.Append(ReqReadU8(req)); - } - - // パフォーマンス測定用のギミック。 - // VirtIO-Net を特定のパケットが通過すると、 - // VM 電源オン時から現在までの実時間をログに出力する。NetBSD の - // マルチユーザブートがあらかた終わってログインプロンプトが出る手前の - // /etc/rc.local に - // - // ping6 -X 1 -c 1 ff02::db8:db8:9090%vioif0 > /dev/null 2>&1 & - // - // と書いておくことで、ここまでにかかる時間を表示できる。 - // この値はホスト性能に依存するため、同一環境下での相対比較のみ可能。 - // - // この宛先はリンクローカルマルチキャストでドキュメント用に予約されて - // いるアドレスなので厳密には出すべきではないかもだが、知らずに - // ドキュメント通りにやってみた人のために、ちゃんとしたネットワークなら - // 安全に廃棄しているはずなので、事故は少ないかと。 - // このマルチキャストアドレスに対する宛先 MAC アドレスは - // 33:33:0d:b8:90:90 になる。 - static const std::array perf_dstaddr { - 0x33, 0x33, 0x0d, 0xb8, 0x90, 0x90 - }; - if (__predict_true(tx_packet.length >= 6) && - __predict_false(memcmp(&tx_packet[0], &perf_dstaddr[0], 6) == 0)) - { - GetSyncer()->PutlogRealtime(); - } + uint32 len = req.rremain(); + tx_packet.Resize(len); + uint32 rest = ReqReadRegion(req, tx_packet.Data(), len); + tx_packet.Resize(len - rest); hostnet->Tx(tx_packet); } // これは Host スレッドから呼ばれる void -VirtIONetDevice::HostRxCallback() +VirtIONetDevice::HostRxCallback(uint32 dummy) { // スレッドを超えるためにメッセージを投げる scheduler->SendMessage(MessageID::HOSTNET_RX(0)); @@ -252,15 +231,15 @@ VirtIONetDevice::RxMessage(MessageID msg // 受信イベントが止まっていれば動かす。 // 受信イベントがすでに動いていれば、1パケット受信完了後に // ホストキューを確認するので、ここでは何もしなくてよい。 - if (event.IsRunning() == false) { - event.time = 0; + if (event->IsRunning() == false) { + event->time = 0; scheduler->StartEvent(event); } } // 受信ループイベント。 void -VirtIONetDevice::RxEvent(Event& ev) +VirtIONetDevice::RxEvent(Event *ev) { VirtQueue *q = &vqueues[0]; @@ -271,14 +250,15 @@ VirtIONetDevice::RxEvent(Event& ev) // 空きディスクリプタがなければ、空くまでポーリングで待ち続ける。 uint16 avail_idx = ReadLE16(q->driver + 2); if (q->last_avail_idx == avail_idx) { - event.time = 500_usec; - putlog(2, "%s: wait %u usec", __func__, (uint)(event.time / 1_usec)); + event->time = 500_usec; + putlog(2, "%s: wait %u usec", __func__, + (uint)tsec_to_usec(event->time)); scheduler->RestartEvent(event); return; } // ディスクリプタに空きが出来たので、ホストキューから1パケット取り出す。 - assert(rx_packet.length == 0); + assert(rx_packet.Length() == 0); if (hostnet->Rx(&rx_packet) == false) { // 取り出せなくなったら、ここでイベントループを終了。 rx_packet.Clear(); @@ -286,13 +266,13 @@ VirtIONetDevice::RxEvent(Event& ev) return; } - putlog(2, "%s: %u bytes from host queue", __func__, rx_packet.length); + putlog(2, "%s: %u bytes from host queue", __func__, rx_packet.Length()); // rx_packet が埋まって、空きディスクリプタがあるので受信処理へ。 Rx(q); rx_packet.Clear(); - event.time = 500_usec; + event->time = 500_usec; scheduler->RestartEvent(event); } @@ -300,15 +280,15 @@ VirtIONetDevice::RxEvent(Event& ev) void VirtIONetDevice::Rx(VirtQueue *q) { - assert(rx_packet.length != 0); + assert(rx_packet.Length() != 0); assert(q->last_avail_idx != ReadLE16(q->driver + 2)); // XXX どこでやるのがいいか // VirtIO は FCS を含まない 1514 バイトが上限と決まっているが、 // HostNet の下の受信ドライバが FCS を含めているかどうかが分からない。 // 仕方ないので 1514 バイトを超える時だけ取り除く…。 - if (rx_packet.length > 1514) { - rx_packet.length = 1514; + if (rx_packet.Length() > 1514) { + rx_packet.Resize(1514); } VirtIOReq req; @@ -321,27 +301,20 @@ VirtIONetDevice::Rx(VirtQueue *q) hdr.hdr_len = htole16(sizeof(hdr)); // num_buffers は実際に使ったセグメント数らしい。 uint nseg = 0; - for (; nseg < req.buf.size(); nseg++) { - if (req.buf[nseg].pos >= sizeof(hdr) + rx_packet.length) + uint32 len = 0; + for (; nseg < req.wbuf.size(); nseg++) { + len += req.wbuf[nseg].len; + if (len >= sizeof(hdr) + rx_packet.Length()) { break; + } } hdr.num_buffers = htole16(nseg); // ヘッダを書き込む。 - const uint8 *s = (const uint8 *)&hdr; - for (int i = 0; i < sizeof(hdr); i++) { - ReqWriteU8(req, *s++); - } + ReqWriteRegion(req, (const uint8 *)&hdr, sizeof(hdr)); // パケット本体を書き込む。 - for (int i = 0; i < rx_packet.length; i++) { - if (ReqWriteU8(req, rx_packet[i]) == false) { - // どうする? - putlog(0, "buffer too small: buflen=%u rx_packet=%u", - req.len, rx_packet.length); - break; - } - } + ReqWriteRegion(req, rx_packet.Data(), rx_packet.Length()); CommitDesc(req); @@ -349,6 +322,22 @@ VirtIONetDevice::Rx(VirtQueue *q) Done(q); } +// この宛先アドレスを受信するかどうか。 +// これは HostNet スレッドで呼ばれる。 +int +VirtIONetDevice::HWAddrFilter(const MacAddr& dstaddr) const +{ + if (dstaddr.IsUnicast()) { + if (dstaddr != macaddr) { + return HPF_DROP_UNICAST; + } + } + // マルチキャストをホストデバイス側でフィルタする機構は + // CTRLQ 内にあるが、NetBSD のドライバは CTRLQ を実装していない。 + + return HPF_PASS; +} + const char * VirtIONetDevice::GetFeatureName(uint feature) const {