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