Annotation of nono/vm/virtio_net.cpp, revision 1.1.1.8

1.1       root        1: //
                      2: // nono
                      3: // Copyright (C) 2024 nono project
                      4: // Licensed under nono-license.txt
                      5: //
                      6: 
                      7: //
                      8: // VirtIO ネットワーク
                      9: //
                     10: 
                     11: #include "virtio_net.h"
                     12: #include "virtio_def.h"
                     13: #include "config.h"
                     14: #include "ethernet.h"
1.1.1.7   root       15: #include "event.h"
1.1       root       16: #include "macaddr.h"
                     17: #include "memorystream.h"
1.1.1.3   root       18: #include "monitor.h"
1.1       root       19: #include "scheduler.h"
                     20: 
                     21: // デバイス構成レイアウト
                     22: class VirtIONetConfigWriter
                     23: {
                     24:  public:
                     25:        std::array<u8, 6> mac {};
                     26:        le16 status {};
                     27:        le16 max_virtqueue_pairs {};
                     28:        le16 mtu {};
                     29: 
                     30:  public:
                     31:        void WriteTo(uint8 *dst) const;
                     32: };
                     33: 
                     34: // コンストラクタ
1.1.1.2   root       35: VirtIONetDevice::VirtIONetDevice(uint slot_)
1.1       root       36:        : inherited(OBJ_VIRTIO_NET, slot_)
                     37: {
1.1.1.8 ! root       38:        // 割り込み名(とログ名)
        !            39:        strlcpy(intrname, "VIONet", sizeof(intrname));
        !            40:        SetName(intrname);
        !            41:        ClearAlias();
        !            42:        AddAlias(intrname);
1.1       root       43: 
1.1.1.2   root       44:        device_id = VirtIO::DEVICE_ID_NETWORK;
1.1.1.3   root       45:        vqueues.emplace_back(this, 0, "ReceiveQ1", 8);
                     46:        vqueues.emplace_back(this, 1, "TransmitQ1", 16);
1.1.1.2   root       47: 
1.1       root       48:        // 完了通知メッセージ
                     49:        msgid = MessageID::VIRTIO_NET_DONE;
                     50: 
                     51:        // モニタの行数。
                     52:        int vqlines = 0;
                     53:        for (const auto& q : vqueues) {
                     54:                vqlines += 7 + q.num_max;
                     55:        }
                     56: 
1.1.1.3   root       57:        monitor = gMonitorManager->Regist(ID_MONITOR_VIRTIO_NET, this);
1.1.1.8 ! root       58:        monitor->SetCallback(&VirtIONetDevice::MonitorScreen);
1.1.1.3   root       59:        monitor->SetSize(MONITOR_WIDTH, 3 + vqlines);
1.1       root       60: }
                     61: 
                     62: // デストラクタ
                     63: VirtIONetDevice::~VirtIONetDevice()
                     64: {
                     65:        // EthernetDevice と同じ…。
                     66:        if ((bool)hostnet) {
1.1.1.6   root       67:                hostnet->ResetRxCallback();
1.1       root       68:        }
                     69: }
                     70: 
                     71: // 動的なコンストラクション
                     72: bool
                     73: VirtIONetDevice::Create()
                     74: {
                     75:        if (inherited::Create() == false) {
                     76:                return false;
                     77:        }
                     78: 
                     79:        // EthernetDevice とほぼ同じ…。
1.1.1.5   root       80:        try {
1.1.1.6   root       81:                hostnet.reset(new HostNetDevice(this, 0, "VirtIO Network"));
1.1.1.5   root       82:        } catch (...) { }
                     83:        if ((bool)hostnet == false) {
                     84:                warnx("Failed to initialize HostNetDevice at %s", __method__);
                     85:                return false;
                     86:        }
1.1       root       87: 
1.1.1.6   root       88:        auto func = ToDeviceCallback(&VirtIONetDevice::HostRxCallback);
                     89:        hostnet->SetRxCallback(func, 0);
1.1       root       90: 
                     91:        return true;
                     92: }
                     93: 
                     94: // 初期化
                     95: bool
                     96: VirtIONetDevice::Init()
                     97: {
                     98:        if (inherited::Init() == false) {
                     99:                return false;
                    100:        }
                    101: 
                    102:        // MAC アドレスを取得。
                    103:        if (EthernetDevice::GetConfigMacAddr(0, &macaddr, false) == false) {
1.1.1.8 ! root      104:                // エラーメッセージは表示済み。
1.1       root      105:                return false;
                    106:        }
                    107:        // 表示用。
                    108:        macaddr_str = macaddr.ToString(':');
                    109: 
                    110:        // DEVICE_FEATURES と構成レイアウトを用意。
                    111:        VirtIONetConfigWriter cfg;
                    112:        SetDeviceFeatures(VIRTIO_NET_F_CSUM);
                    113:        SetDeviceFeatures(VIRTIO_NET_F_GUEST_CSUM);
1.1.1.8 ! root      114:        SetDeviceFeatures(VIRTIO_NET_F_MTU), cfg.mtu = 1500;
        !           115:        SetDeviceFeatures(VIRTIO_NET_F_MAC), macaddr.ExportTo(&cfg.mac[0]);
1.1       root      116:        SetDeviceFeatures(VIRTIO_NET_F_MRG_RXBUF);
                    117:        cfg.WriteTo(&device_config[0]);
                    118: 
                    119:        // ホストからの受信コールバックを登録
                    120:        scheduler->ConnectMessage(MessageID::HOSTNET_RX(0), this,
                    121:                ToMessageCallback(&VirtIONetDevice::RxMessage));
                    122: 
1.1.1.7   root      123:        auto evman = GetEventManager();
                    124:        event = evman->Regist(this,
                    125:                ToEventCallback(&VirtIONetDevice::RxEvent),
                    126:                "VirtIONet RX");
1.1.1.2   root      127: 
1.1       root      128:        return true;
                    129: }
                    130: 
                    131: void
1.1.1.8 ! root      132: VirtIONetDevice::MonitorScreen(Monitor *, TextScreen& screen)
1.1       root      133: {
                    134:        int y = 0;
                    135: 
                    136:        screen.Clear();
                    137: 
1.1.1.8 ! root      138:        y = MonitorScreenDev(screen, y);
1.1       root      139: 
                    140:        screen.Puts(0, y, "MACAddress:");
                    141:        screen.Puts(12, y, macaddr_str.c_str());
                    142:        y++;
                    143: 
                    144:        y++;
                    145:        for (const auto& q : vqueues) {
1.1.1.8 ! root      146:                y = MonitorScreenVirtQueue(screen, y, q);
1.1       root      147:                y++;
1.1.1.8 ! root      148:                y = MonitorScreenVirtQDesc(screen, y, q);
1.1       root      149:                y++;
                    150:        }
                    151: }
                    152: 
1.1.1.3   root      153: // QUEUE_READY が変化したら呼ばれる。
1.1       root      154: void
1.1.1.3   root      155: VirtIONetDevice::QueueReadyChanged(VirtQueue *q)
1.1       root      156: {
1.1.1.3   root      157:        if (q->idx == 0) {
                    158:                // 受信開始/停止
                    159:                if (q->GetReady() != 0) {
                    160:                        hostnet->EnableRx(true);
                    161:                } else {
                    162:                        scheduler->StopEvent(event);
                    163:                        hostnet->EnableRx(false);
                    164:                }
1.1       root      165:        }
                    166: }
                    167: 
                    168: // ディスクリプタを一つ処理する。
                    169: void
1.1.1.2   root      170: VirtIONetDevice::ProcessDesc(VirtIOReq& req)
1.1       root      171: {
1.1.1.2   root      172:        VirtQueue *q = req.q;
                    173:        uint8 flags;
                    174:        uint8 gso_type;
                    175:        uint16 hdr_len;
                    176:        uint16 gso_size;
                    177:        uint16 csum_start;
                    178:        uint16 csum_offset;
                    179:        uint16 num_buffers;
1.1       root      180: 
                    181:        if (q->idx == 0) {
                    182:                putlog(0, "QUEUE_NOTIFY on rx queue (VQ%u)?", q->idx);
                    183:                return;
                    184:        }
                    185: 
1.1.1.2   root      186:        flags       = ReqReadU8(req);
                    187:        gso_type    = ReqReadU8(req);
                    188:        hdr_len     = ReqReadLE16(req);
                    189:        gso_size    = ReqReadLE16(req);
                    190:        csum_start  = ReqReadLE16(req);
                    191:        csum_offset = ReqReadLE16(req);
                    192:        num_buffers = ReqReadLE16(req);
1.1       root      193:        putlog(2, "req.hdr $%08x: flags=%02x gso=%02x hdr_len=%04x gso_size=%04x "
                    194:                "csum_start=%04x offset=%04x num=%04x",
1.1.1.5   root      195:                req.rbuf[0].addr,
1.1.1.2   root      196:                flags,
                    197:                gso_type,
                    198:                hdr_len,
                    199:                gso_size,
                    200:                csum_start,
                    201:                csum_offset,
                    202:                num_buffers);
1.1       root      203: 
                    204:        NetPacket tx_packet;
1.1.1.8 ! root      205:        uint32 len = req.rremain();
        !           206:        tx_packet.Resize(len);
        !           207:        uint32 rest = ReqReadRegion(req, tx_packet.Data(), len);
        !           208:        tx_packet.Resize(len - rest);
1.1.1.2   root      209: 
                    210:        hostnet->Tx(tx_packet);
1.1       root      211: }
                    212: 
                    213: // これは Host スレッドから呼ばれる
                    214: void
1.1.1.6   root      215: VirtIONetDevice::HostRxCallback(uint32 dummy)
1.1       root      216: {
                    217:        // スレッドを超えるためにメッセージを投げる
                    218:        scheduler->SendMessage(MessageID::HOSTNET_RX(0));
                    219: }
                    220: 
                    221: // パケット受信通知
                    222: void
                    223: VirtIONetDevice::RxMessage(MessageID msgid_, uint32 arg)
                    224: {
1.1.1.3   root      225:        VirtQueue *q = &vqueues[0];
                    226: 
                    227:        if (__predict_false(q->GetReady() == 0)) {
                    228:                return;
                    229:        }
                    230: 
1.1       root      231:        // 受信イベントが止まっていれば動かす。
                    232:        // 受信イベントがすでに動いていれば、1パケット受信完了後に
                    233:        // ホストキューを確認するので、ここでは何もしなくてよい。
1.1.1.7   root      234:        if (event->IsRunning() == false) {
                    235:                event->time = 0;
1.1       root      236:                scheduler->StartEvent(event);
                    237:        }
                    238: }
                    239: 
                    240: // 受信ループイベント。
                    241: void
1.1.1.7   root      242: VirtIONetDevice::RxEvent(Event *ev)
1.1       root      243: {
                    244:        VirtQueue *q = &vqueues[0];
                    245: 
1.1.1.3   root      246:        if (__predict_false(q->GetReady() == 0)) {
                    247:                return;
                    248:        }
                    249: 
1.1       root      250:        // 空きディスクリプタがなければ、空くまでポーリングで待ち続ける。
                    251:        uint16 avail_idx = ReadLE16(q->driver + 2);
                    252:        if (q->last_avail_idx == avail_idx) {
1.1.1.7   root      253:                event->time = 500_usec;
1.1.1.8 ! root      254:                putlog(2, "%s: wait %u usec", __func__,
        !           255:                        (uint)tsec_to_usec(event->time));
1.1       root      256:                scheduler->RestartEvent(event);
                    257:                return;
                    258:        }
                    259: 
                    260:        // ディスクリプタに空きが出来たので、ホストキューから1パケット取り出す。
1.1.1.8 ! root      261:        assert(rx_packet.Length() == 0);
1.1       root      262:        if (hostnet->Rx(&rx_packet) == false) {
                    263:                // 取り出せなくなったら、ここでイベントループを終了。
                    264:                rx_packet.Clear();
                    265:                putlog(2, "Rx no more rx_packet");
                    266:                return;
                    267:        }
                    268: 
1.1.1.8 ! root      269:        putlog(2, "%s: %u bytes from host queue", __func__, rx_packet.Length());
1.1       root      270: 
                    271:        // rx_packet が埋まって、空きディスクリプタがあるので受信処理へ。
                    272:        Rx(q);
                    273: 
                    274:        rx_packet.Clear();
1.1.1.7   root      275:        event->time = 500_usec;
1.1       root      276:        scheduler->RestartEvent(event);
                    277: }
                    278: 
                    279: // 受信。(rx_packet が埋まっていてかつ空きディスクリプタがある状態で呼ぶこと)
                    280: void
                    281: VirtIONetDevice::Rx(VirtQueue *q)
                    282: {
1.1.1.8 ! root      283:        assert(rx_packet.Length() != 0);
1.1       root      284:        assert(q->last_avail_idx != ReadLE16(q->driver + 2));
                    285: 
                    286:        // XXX どこでやるのがいいか
                    287:        // VirtIO は FCS を含まない 1514 バイトが上限と決まっているが、
                    288:        // HostNet の下の受信ドライバが FCS を含めているかどうかが分からない。
                    289:        // 仕方ないので 1514 バイトを超える時だけ取り除く…。
1.1.1.8 ! root      290:        if (rx_packet.Length() > 1514) {
        !           291:                rx_packet.Resize(1514);
1.1       root      292:        }
                    293: 
1.1.1.2   root      294:        VirtIOReq req;
                    295:        req.q = q;
                    296:        StartDesc(req);
1.1       root      297: 
1.1.1.2   root      298:        // ヘッダを用意。
                    299:        virtio_net_hdr hdr;
                    300:        memset(&hdr, 0, sizeof(hdr));
                    301:        hdr.hdr_len = htole16(sizeof(hdr));
                    302:        // num_buffers は実際に使ったセグメント数らしい。
                    303:        uint nseg = 0;
1.1.1.5   root      304:        uint32 len = 0;
                    305:        for (; nseg < req.wbuf.size(); nseg++) {
                    306:                len += req.wbuf[nseg].len;
1.1.1.8 ! root      307:                if (len >= sizeof(hdr) + rx_packet.Length()) {
1.1.1.2   root      308:                        break;
1.1.1.5   root      309:                }
1.1.1.2   root      310:        }
                    311:        hdr.num_buffers = htole16(nseg);
                    312: 
                    313:        // ヘッダを書き込む。
1.1.1.5   root      314:        ReqWriteRegion(req, (const uint8 *)&hdr, sizeof(hdr));
1.1       root      315: 
1.1.1.2   root      316:        // パケット本体を書き込む。
1.1.1.8 ! root      317:        ReqWriteRegion(req, rx_packet.Data(), rx_packet.Length());
1.1       root      318: 
1.1.1.2   root      319:        CommitDesc(req);
1.1       root      320: 
                    321:        // ここは VM スレッドなのでそのまま割り込みを上げる。
                    322:        Done(q);
                    323: }
                    324: 
1.1.1.4   root      325: // この宛先アドレスを受信するかどうか。
                    326: // これは HostNet スレッドで呼ばれる。
                    327: int
                    328: VirtIONetDevice::HWAddrFilter(const MacAddr& dstaddr) const
                    329: {
                    330:        if (dstaddr.IsUnicast()) {
                    331:                if (dstaddr != macaddr) {
                    332:                        return HPF_DROP_UNICAST;
                    333:                }
                    334:        }
                    335:        // マルチキャストをホストデバイス側でフィルタする機構は
                    336:        // CTRLQ 内にあるが、NetBSD のドライバは CTRLQ を実装していない。
                    337: 
                    338:        return HPF_PASS;
                    339: }
                    340: 
1.1       root      341: const char *
1.1.1.2   root      342: VirtIONetDevice::GetFeatureName(uint feature) const
1.1       root      343: {
                    344:        static std::pair<uint, const char *> names[] = {
                    345:                { VIRTIO_NET_F_CSUM,                    "CSUM" },
                    346:                { VIRTIO_NET_F_GUEST_CSUM,              "G_CSUM" },
                    347:                { VIRTIO_NET_F_CTRL_GUEST_OFFLOADS,     "C_G_OFFL" },
                    348:                { VIRTIO_NET_F_MTU,                             "MTU" },
                    349:                { VIRTIO_NET_F_MAC,                             "MAC" },
                    350:                { VIRTIO_NET_F_GUEST_TSO4,              "G_TSO4" },
                    351:                { VIRTIO_NET_F_GUEST_TSO6,              "G_TSO6" },
                    352:                { VIRTIO_NET_F_GUEST_ECN,               "G_ECN" },
                    353:                { VIRTIO_NET_F_GUEST_UFO,               "G_UFO" },
                    354:                { VIRTIO_NET_F_HOST_TSO4,               "H_TSO4" },
                    355:                { VIRTIO_NET_F_HOST_TSO6,               "H_TSO6" },
                    356:                { VIRTIO_NET_F_HOST_ECN,                "H_ECN" },
                    357:                { VIRTIO_NET_F_HOST_UFO,                "H_UFO" },
                    358:                { VIRTIO_NET_F_MRG_RXBUF,               "MRG_RX" },
                    359:                { VIRTIO_NET_F_STATUS,                  "STATUS" },
                    360:                { VIRTIO_NET_F_CTRL_VQ,                 "CTL_VQ" },
                    361:                { VIRTIO_NET_F_CTRL_RX,                 "CTL_RX" },
                    362:                { VIRTIO_NET_F_CTRL_VLAN,               "CTL_VLAN" },
                    363:                { VIRTIO_NET_F_GUEST_ANNOUNCE,  "G_ANN" },
                    364:                { VIRTIO_NET_F_MQ,                              "MQ" },
                    365:                { VIRTIO_NET_F_CTRL_MAC_ADDR,   "CTL_MAC" },
                    366:                { VIRTIO_NET_F_RSC_EXT,                 "RSC_EXT" },
                    367:                { VIRTIO_NET_F_STANDBY,                 "STANDBY" },
                    368:        };
                    369: 
                    370:        for (auto& p : names) {
                    371:                if (feature == p.first) {
                    372:                        return p.second;
                    373:                }
                    374:        }
                    375:        return inherited::GetFeatureName(feature);
                    376: }
                    377: 
                    378: // デバイス構成レイアウトを書き出す。
                    379: void
                    380: VirtIONetConfigWriter::WriteTo(uint8 *dst) const
                    381: {
                    382:        MemoryStreamLE mem(dst);
                    383: 
                    384:        for (auto c : mac) {
1.1.1.2   root      385:                mem.Write1(c);
1.1       root      386:        }
1.1.1.2   root      387:        mem.Write2(status);
                    388:        mem.Write2(max_virtqueue_pairs);
                    389:        mem.Write2(mtu);
1.1       root      390: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.