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