|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.