|
|
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: {
38: // 短縮形
39: AddAlias("VNet");
40: AddAlias("Ethernet");
41:
1.1.1.2 root 42: device_id = VirtIO::DEVICE_ID_NETWORK;
1.1.1.3 root 43: vqueues.emplace_back(this, 0, "ReceiveQ1", 8);
44: vqueues.emplace_back(this, 1, "TransmitQ1", 16);
1.1.1.2 root 45:
1.1 root 46: // 割り込み名
47: strlcpy(intrname, "VIONet", sizeof(intrname));
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);
58: monitor->func = ToMonitorCallback(&VirtIONetDevice::MonitorUpdate);
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) {
104: // エラーメッセージ表示済み。
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.4 root 114: SetDeviceFeatures(VIRTIO_NET_F_MTU), macaddr.ExportTo(&cfg.mac[0]);
1.1 root 115: SetDeviceFeatures(VIRTIO_NET_F_MAC), cfg.mtu = 1500;
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
132: VirtIONetDevice::MonitorUpdate(Monitor *, TextScreen& screen)
133: {
134: int y = 0;
135:
136: screen.Clear();
137:
138: y = MonitorUpdateDev(screen, y);
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) {
146: y = MonitorUpdateVirtQueue(screen, y, q);
147: y++;
148: y = MonitorUpdateVirtQDesc(screen, y, q);
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.5 root 205: uint32 len = std::min((uint32)tx_packet.size(), req.rremain());
206: uint32 rest = ReqReadRegion(req, tx_packet.data(), len);
207: tx_packet.length = len - rest;
1.1.1.2 root 208:
209: hostnet->Tx(tx_packet);
1.1 root 210: }
211:
212: // これは Host スレッドから呼ばれる
213: void
1.1.1.6 root 214: VirtIONetDevice::HostRxCallback(uint32 dummy)
1.1 root 215: {
216: // スレッドを超えるためにメッセージを投げる
217: scheduler->SendMessage(MessageID::HOSTNET_RX(0));
218: }
219:
220: // パケット受信通知
221: void
222: VirtIONetDevice::RxMessage(MessageID msgid_, uint32 arg)
223: {
1.1.1.3 root 224: VirtQueue *q = &vqueues[0];
225:
226: if (__predict_false(q->GetReady() == 0)) {
227: return;
228: }
229:
1.1 root 230: // 受信イベントが止まっていれば動かす。
231: // 受信イベントがすでに動いていれば、1パケット受信完了後に
232: // ホストキューを確認するので、ここでは何もしなくてよい。
1.1.1.7 ! root 233: if (event->IsRunning() == false) {
! 234: event->time = 0;
1.1 root 235: scheduler->StartEvent(event);
236: }
237: }
238:
239: // 受信ループイベント。
240: void
1.1.1.7 ! root 241: VirtIONetDevice::RxEvent(Event *ev)
1.1 root 242: {
243: VirtQueue *q = &vqueues[0];
244:
1.1.1.3 root 245: if (__predict_false(q->GetReady() == 0)) {
246: return;
247: }
248:
1.1 root 249: // 空きディスクリプタがなければ、空くまでポーリングで待ち続ける。
250: uint16 avail_idx = ReadLE16(q->driver + 2);
251: if (q->last_avail_idx == avail_idx) {
1.1.1.7 ! root 252: event->time = 500_usec;
! 253: putlog(2, "%s: wait %u usec", __func__, (uint)(event->time / 1_usec));
1.1 root 254: scheduler->RestartEvent(event);
255: return;
256: }
257:
258: // ディスクリプタに空きが出来たので、ホストキューから1パケット取り出す。
259: assert(rx_packet.length == 0);
260: if (hostnet->Rx(&rx_packet) == false) {
261: // 取り出せなくなったら、ここでイベントループを終了。
262: rx_packet.Clear();
263: putlog(2, "Rx no more rx_packet");
264: return;
265: }
266:
267: putlog(2, "%s: %u bytes from host queue", __func__, rx_packet.length);
268:
269: // rx_packet が埋まって、空きディスクリプタがあるので受信処理へ。
270: Rx(q);
271:
272: rx_packet.Clear();
1.1.1.7 ! root 273: event->time = 500_usec;
1.1 root 274: scheduler->RestartEvent(event);
275: }
276:
277: // 受信。(rx_packet が埋まっていてかつ空きディスクリプタがある状態で呼ぶこと)
278: void
279: VirtIONetDevice::Rx(VirtQueue *q)
280: {
281: assert(rx_packet.length != 0);
282: assert(q->last_avail_idx != ReadLE16(q->driver + 2));
283:
284: // XXX どこでやるのがいいか
285: // VirtIO は FCS を含まない 1514 バイトが上限と決まっているが、
286: // HostNet の下の受信ドライバが FCS を含めているかどうかが分からない。
287: // 仕方ないので 1514 バイトを超える時だけ取り除く…。
288: if (rx_packet.length > 1514) {
289: rx_packet.length = 1514;
290: }
291:
1.1.1.2 root 292: VirtIOReq req;
293: req.q = q;
294: StartDesc(req);
1.1 root 295:
1.1.1.2 root 296: // ヘッダを用意。
297: virtio_net_hdr hdr;
298: memset(&hdr, 0, sizeof(hdr));
299: hdr.hdr_len = htole16(sizeof(hdr));
300: // num_buffers は実際に使ったセグメント数らしい。
301: uint nseg = 0;
1.1.1.5 root 302: uint32 len = 0;
303: for (; nseg < req.wbuf.size(); nseg++) {
304: len += req.wbuf[nseg].len;
305: if (len >= sizeof(hdr) + rx_packet.length) {
1.1.1.2 root 306: break;
1.1.1.5 root 307: }
1.1.1.2 root 308: }
309: hdr.num_buffers = htole16(nseg);
310:
311: // ヘッダを書き込む。
1.1.1.5 root 312: ReqWriteRegion(req, (const uint8 *)&hdr, sizeof(hdr));
1.1 root 313:
1.1.1.2 root 314: // パケット本体を書き込む。
1.1.1.5 root 315: ReqWriteRegion(req, rx_packet.data(), rx_packet.length);
1.1 root 316:
1.1.1.2 root 317: CommitDesc(req);
1.1 root 318:
319: // ここは VM スレッドなのでそのまま割り込みを上げる。
320: Done(q);
321: }
322:
1.1.1.4 root 323: // この宛先アドレスを受信するかどうか。
324: // これは HostNet スレッドで呼ばれる。
325: int
326: VirtIONetDevice::HWAddrFilter(const MacAddr& dstaddr) const
327: {
328: if (dstaddr.IsUnicast()) {
329: if (dstaddr != macaddr) {
330: return HPF_DROP_UNICAST;
331: }
332: }
333: // マルチキャストをホストデバイス側でフィルタする機構は
334: // CTRLQ 内にあるが、NetBSD のドライバは CTRLQ を実装していない。
335:
336: return HPF_PASS;
337: }
338:
1.1 root 339: const char *
1.1.1.2 root 340: VirtIONetDevice::GetFeatureName(uint feature) const
1.1 root 341: {
342: static std::pair<uint, const char *> names[] = {
343: { VIRTIO_NET_F_CSUM, "CSUM" },
344: { VIRTIO_NET_F_GUEST_CSUM, "G_CSUM" },
345: { VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, "C_G_OFFL" },
346: { VIRTIO_NET_F_MTU, "MTU" },
347: { VIRTIO_NET_F_MAC, "MAC" },
348: { VIRTIO_NET_F_GUEST_TSO4, "G_TSO4" },
349: { VIRTIO_NET_F_GUEST_TSO6, "G_TSO6" },
350: { VIRTIO_NET_F_GUEST_ECN, "G_ECN" },
351: { VIRTIO_NET_F_GUEST_UFO, "G_UFO" },
352: { VIRTIO_NET_F_HOST_TSO4, "H_TSO4" },
353: { VIRTIO_NET_F_HOST_TSO6, "H_TSO6" },
354: { VIRTIO_NET_F_HOST_ECN, "H_ECN" },
355: { VIRTIO_NET_F_HOST_UFO, "H_UFO" },
356: { VIRTIO_NET_F_MRG_RXBUF, "MRG_RX" },
357: { VIRTIO_NET_F_STATUS, "STATUS" },
358: { VIRTIO_NET_F_CTRL_VQ, "CTL_VQ" },
359: { VIRTIO_NET_F_CTRL_RX, "CTL_RX" },
360: { VIRTIO_NET_F_CTRL_VLAN, "CTL_VLAN" },
361: { VIRTIO_NET_F_GUEST_ANNOUNCE, "G_ANN" },
362: { VIRTIO_NET_F_MQ, "MQ" },
363: { VIRTIO_NET_F_CTRL_MAC_ADDR, "CTL_MAC" },
364: { VIRTIO_NET_F_RSC_EXT, "RSC_EXT" },
365: { VIRTIO_NET_F_STANDBY, "STANDBY" },
366: };
367:
368: for (auto& p : names) {
369: if (feature == p.first) {
370: return p.second;
371: }
372: }
373: return inherited::GetFeatureName(feature);
374: }
375:
376: // デバイス構成レイアウトを書き出す。
377: void
378: VirtIONetConfigWriter::WriteTo(uint8 *dst) const
379: {
380: MemoryStreamLE mem(dst);
381:
382: for (auto c : mac) {
1.1.1.2 root 383: mem.Write1(c);
1.1 root 384: }
1.1.1.2 root 385: mem.Write2(status);
386: mem.Write2(max_virtqueue_pairs);
387: mem.Write2(mtu);
1.1 root 388: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.