xref: /minix3/minix/drivers/net/virtio_net/virtio_net.c (revision 433d6423c39e34ec4b79c950597bb2d236f886be)
1 /* virtio net driver for MINIX 3
2  *
3  * Copyright (c) 2013, A. Welzel, <arne.welzel@gmail.com>
4  *
5  * This software is released under the BSD license. See the LICENSE file
6  * included in the main directory of this source distribution for the
7  * license terms and conditions.
8  */
9 
10 #include <assert.h>
11 #include <sys/types.h>
12 
13 #include <net/gen/ether.h>
14 #include <net/gen/eth_io.h>
15 
16 #include <minix/drivers.h>
17 #include <minix/netdriver.h>
18 #include <minix/sysutil.h>
19 #include <minix/virtio.h>
20 
21 #include <sys/queue.h>
22 
23 #include "virtio_net.h"
24 
25 #define dput(s)		do { dprintf(s); printf("\n"); } while (0)
26 #define dprintf(s) do {						\
27 	printf("%s: ", name);					\
28 	printf s;						\
29 } while (0)
30 
31 static struct virtio_device *net_dev;
32 
33 static const char *const name = "virtio-net";
34 
35 enum queue {RX_Q, TX_Q, CTRL_Q};
36 
37 /* Number of packets to work with */
38 /* TODO: This should be an argument to the driver and possibly also
39  *       depend on the queue sizes offered by this device.
40  */
41 #define BUF_PACKETS		64
42 /* Maximum size of a packet */
43 #define MAX_PACK_SIZE		ETH_MAX_PACK_SIZE
44 /* Buffer size needed for the payload of BUF_PACKETS */
45 #define PACKET_BUF_SZ		(BUF_PACKETS * MAX_PACK_SIZE)
46 
47 struct packet {
48 	int idx;
49 	struct virtio_net_hdr *vhdr;
50 	phys_bytes phdr;
51 	char *vdata;
52 	phys_bytes pdata;
53 	STAILQ_ENTRY(packet) next;
54 };
55 
56 /* Allocated data chunks */
57 static char *data_vir;
58 static phys_bytes data_phys;
59 static struct virtio_net_hdr *hdrs_vir;
60 static phys_bytes hdrs_phys;
61 static struct packet *packets;
62 static int in_rx;
63 static int started;
64 
65 /* Packets on this list can be given to the host */
66 static STAILQ_HEAD(free_list, packet) free_list;
67 
68 /* Packets on this list are to be given to inet */
69 static STAILQ_HEAD(recv_list, packet) recv_list;
70 
71 /* State about pending inet messages */
72 static int rx_pending;
73 static message pending_rx_msg;
74 static int tx_pending;
75 static message pending_tx_msg;
76 
77 /* Various state data */
78 static u8_t virtio_net_mac[6];
79 static eth_stat_t virtio_net_stats;
80 static int spurious_interrupt;
81 
82 
83 /* Prototypes */
84 static int virtio_net_probe(int skip);
85 static int virtio_net_config(void);
86 static int virtio_net_alloc_bufs(void);
87 static void virtio_net_init_queues(void);
88 
89 static void virtio_net_refill_rx_queue(void);
90 static void virtio_net_check_queues(void);
91 static void virtio_net_check_pending(void);
92 
93 static void virtio_net_fetch_iovec(iovec_s_t *iov, message *m,
94 	cp_grant_id_t grant, size_t count);
95 static int virtio_net_cpy_to_user(message *m);
96 static int virtio_net_cpy_from_user(message *m);
97 
98 static void virtio_net_intr(message *m);
99 static void virtio_net_write(message *m);
100 static void virtio_net_read(message *m);
101 static void virtio_net_conf(message *m);
102 static void virtio_net_getstat(message *m);
103 
104 static void virtio_net_notify(message *m);
105 static void virtio_net_msg(message *m);
106 static void virtio_net_main_loop(void);
107 
108 static void sef_local_startup(void);
109 static int sef_cb_init_fresh(int type, sef_init_info_t *info);
110 static void sef_cb_signal_handler(int signo);
111 
112 
113 /* TODO: Features are pretty much ignored */
114 struct virtio_feature netf[] = {
115 	{ "partial csum",	VIRTIO_NET_F_CSUM,	0,	0	},
116 	{ "given mac",		VIRTIO_NET_F_MAC,	0,	1	},
117 	{ "status ",		VIRTIO_NET_F_STATUS,	0,	0	},
118 	{ "control channel",	VIRTIO_NET_F_CTRL_VQ,	0,	1	},
119 	{ "control channel rx",	VIRTIO_NET_F_CTRL_RX,	0,	0	}
120 };
121 
122 static int
123 virtio_net_probe(int skip)
124 {
125 	/* virtio-net has at least 2 queues */
126 	int queues = 2;
127 	net_dev= virtio_setup_device(0x00001, name, netf,
128 				     sizeof(netf) / sizeof(netf[0]),
129 				     1 /* threads */, skip);
130 	if (net_dev == NULL)
131 		return ENXIO;
132 
133 	/* If the host supports the control queue, allocate it as well */
134 	if (virtio_host_supports(net_dev, VIRTIO_NET_F_CTRL_VQ))
135 		queues += 1;
136 
137 	if (virtio_alloc_queues(net_dev, queues) != OK) {
138 		virtio_free_device(net_dev);
139 		return ENOMEM;
140 	}
141 
142 	return OK;
143 }
144 
145 static int
146 virtio_net_config(void)
147 {
148 	u32_t mac14;
149 	u32_t mac56;
150 	int i;
151 
152 	if (virtio_host_supports(net_dev, VIRTIO_NET_F_MAC)) {
153 		dprintf(("Mac set by host: "));
154 		mac14 = virtio_sread32(net_dev, 0);
155 		mac56 = virtio_sread32(net_dev, 4);
156 		*(u32_t*)virtio_net_mac = mac14;
157 		*(u16_t*)(virtio_net_mac + 4) = mac56;
158 
159 		for (i = 0; i < 6; i++)
160 			printf("%02x%s", virtio_net_mac[i],
161 					 i == 5 ? "\n" : ":");
162 	} else {
163 		dput(("No mac"));
164 	}
165 
166 	if (virtio_host_supports(net_dev, VIRTIO_NET_F_STATUS)) {
167 		dput(("Current Status %x", (u32_t)virtio_sread16(net_dev, 6)));
168 	} else {
169 		dput(("No status"));
170 	}
171 
172 	if (virtio_host_supports(net_dev, VIRTIO_NET_F_CTRL_VQ))
173 		dput(("Host supports control channel"));
174 
175 	if (virtio_host_supports(net_dev, VIRTIO_NET_F_CTRL_RX))
176 		dput(("Host supports control channel for RX"));
177 
178 	return OK;
179 }
180 
181 static int
182 virtio_net_alloc_bufs(void)
183 {
184 	data_vir = alloc_contig(PACKET_BUF_SZ, 0, &data_phys);
185 
186 	if (!data_vir)
187 		return ENOMEM;
188 
189 	hdrs_vir = alloc_contig(BUF_PACKETS * sizeof(hdrs_vir[0]),
190 				 0, &hdrs_phys);
191 
192 	if (!hdrs_vir) {
193 		free_contig(data_vir, PACKET_BUF_SZ);
194 		return ENOMEM;
195 	}
196 
197 	packets = malloc(BUF_PACKETS * sizeof(packets[0]));
198 
199 	if (!packets) {
200 		free_contig(data_vir, PACKET_BUF_SZ);
201 		free_contig(hdrs_vir, BUF_PACKETS * sizeof(hdrs_vir[0]));
202 		return ENOMEM;
203 	}
204 
205 	memset(data_vir, 0, PACKET_BUF_SZ);
206 	memset(hdrs_vir, 0, BUF_PACKETS * sizeof(hdrs_vir[0]));
207 	memset(packets, 0, BUF_PACKETS * sizeof(packets[0]));
208 
209 	return OK;
210 }
211 
212 static void
213 virtio_net_init_queues(void)
214 {
215 	int i;
216 	STAILQ_INIT(&free_list);
217 	STAILQ_INIT(&recv_list);
218 
219 	for (i = 0; i < BUF_PACKETS; i++) {
220 		packets[i].idx = i;
221 		packets[i].vhdr = &hdrs_vir[i];
222 		packets[i].phdr = hdrs_phys + i * sizeof(hdrs_vir[i]);
223 		packets[i].vdata = data_vir + i * MAX_PACK_SIZE;
224 		packets[i].pdata = data_phys + i * MAX_PACK_SIZE;
225 		STAILQ_INSERT_HEAD(&free_list, &packets[i], next);
226 	}
227 }
228 
229 static void
230 virtio_net_refill_rx_queue(void)
231 {
232 	struct vumap_phys phys[2];
233 	struct packet *p;
234 
235 	while ((in_rx < BUF_PACKETS / 2) && !STAILQ_EMPTY(&free_list)) {
236 
237 		/* peek */
238 		p = STAILQ_FIRST(&free_list);
239 		/* remove */
240 		STAILQ_REMOVE_HEAD(&free_list, next);
241 
242 		phys[0].vp_addr = p->phdr;
243 		assert(!(phys[0].vp_addr & 1));
244 		phys[0].vp_size = sizeof(struct virtio_net_hdr);
245 
246 		phys[1].vp_addr = p->pdata;
247 		assert(!(phys[1].vp_addr & 1));
248 		phys[1].vp_size = MAX_PACK_SIZE;
249 
250 		/* RX queue needs write */
251 		phys[0].vp_addr |= 1;
252 		phys[1].vp_addr |= 1;
253 
254 		virtio_to_queue(net_dev, RX_Q, phys, 2, p);
255 		in_rx++;
256 
257 	}
258 
259 	if (in_rx == 0 && STAILQ_EMPTY(&free_list)) {
260 		dput(("warning: rx queue underflow!"));
261 		virtio_net_stats.ets_fifoUnder++;
262 	}
263 }
264 
265 static void
266 virtio_net_check_queues(void)
267 {
268 	struct packet *p;
269 
270 	/* Put the received packets into the recv list */
271 	while (virtio_from_queue(net_dev, RX_Q, (void **)&p) == 0) {
272 		STAILQ_INSERT_TAIL(&recv_list, p, next);
273 		in_rx--;
274 		virtio_net_stats.ets_packetR++;
275 	}
276 
277 	/* Packets from the TX queue just indicated they are free to
278 	 * be reused now. inet already knows about them as being sent.
279 	 */
280 	while (virtio_from_queue(net_dev, TX_Q, (void **)&p) == 0) {
281 		memset(p->vhdr, 0, sizeof(*p->vhdr));
282 		memset(p->vdata, 0, MAX_PACK_SIZE);
283 		STAILQ_INSERT_HEAD(&free_list, p, next);
284 		virtio_net_stats.ets_packetT++;
285 	}
286 }
287 
288 static void
289 virtio_net_check_pending(void)
290 {
291 	int dst = 0xDEAD;
292 	int r;
293 
294 	message reply;
295 	reply.m_type = DL_TASK_REPLY;
296 	reply.m_netdrv_net_dl_task.flags = DL_NOFLAGS;
297 	reply.m_netdrv_net_dl_task.count = 0;
298 
299 	/* Pending read and something in recv_list? */
300 	if (!STAILQ_EMPTY(&recv_list) && rx_pending) {
301 		dst = pending_rx_msg.m_source;
302 		reply.m_netdrv_net_dl_task.count =
303 			virtio_net_cpy_to_user(&pending_rx_msg);
304 		reply.m_netdrv_net_dl_task.flags |= DL_PACK_RECV;
305 		rx_pending = 0;
306 	}
307 
308 	if (!STAILQ_EMPTY(&free_list) && tx_pending) {
309 		dst = pending_tx_msg.m_source;
310 		virtio_net_cpy_from_user(&pending_tx_msg);
311 		reply.m_netdrv_net_dl_task.flags |= DL_PACK_SEND;
312 		tx_pending = 0;
313 	}
314 
315 	/* Only reply if a pending request was handled */
316 	if (reply.m_netdrv_net_dl_task.flags != DL_NOFLAGS)
317 		if ((r = ipc_send(dst, &reply)) != OK)
318 			panic("%s: ipc_send to %d failed (%d)", name, dst, r);
319 }
320 
321 static void
322 virtio_net_fetch_iovec(iovec_s_t *iov, message *m, cp_grant_id_t grant, size_t count)
323 {
324 	int r;
325 	r = sys_safecopyfrom(m->m_source, grant, 0, (vir_bytes)iov,
326 		count * sizeof(iov[0]));
327 
328 	if (r != OK)
329 		panic("%s: iovec fail for %d (%d)", name, m->m_source, r);
330 }
331 
332 static int
333 virtio_net_cpy_to_user(message *m)
334 {
335 	/* Hmm, this looks so similar to cpy_from_user... TODO */
336 	int i, r, size, ivsz;
337 	int left = MAX_PACK_SIZE;	/* Try copying the whole packet */
338 	int bytes = 0;
339 	iovec_s_t iovec[NR_IOREQS];
340 	struct packet *p;
341 
342 	/* This should only be called if recv_list has some entries */
343 	assert(!STAILQ_EMPTY(&recv_list));
344 
345 	p = STAILQ_FIRST(&recv_list);
346 	STAILQ_REMOVE_HEAD(&recv_list, next);
347 
348 	virtio_net_fetch_iovec(iovec, m, m->m_net_netdrv_dl_readv_s.grant,
349 		 m->m_net_netdrv_dl_readv_s.count);
350 
351 	for (i = 0; i < m->m_net_netdrv_dl_readv_s.count && left > 0; i++) {
352 		ivsz = iovec[i].iov_size;
353 		size = left > ivsz ? ivsz : left;
354 		r = sys_safecopyto(m->m_source, iovec[i].iov_grant, 0,
355 				   (vir_bytes) p->vdata + bytes, size);
356 
357 		if (r != OK)
358 			panic("%s: copy to %d failed (%d)", name,
359 							    m->m_source,
360 							    r);
361 
362 		left -= size;
363 		bytes += size;
364 	}
365 
366 	if (left != 0)
367 		dput(("Uhm... left=%d", left));
368 
369 	/* Clean the packet */
370 	memset(p->vhdr, 0, sizeof(*p->vhdr));
371 	memset(p->vdata, 0, MAX_PACK_SIZE);
372 	STAILQ_INSERT_HEAD(&free_list, p, next);
373 
374 	return bytes;
375 }
376 
377 static int
378 sys_easy_vsafecopy_from(endpoint_t src_proc, iovec_s_t *iov, int count,
379 			vir_bytes dst, size_t max, size_t *copied)
380 {
381 	int i, r;
382 	size_t left = max;
383 	vir_bytes cur_off = 0;
384 	struct vscp_vec vv[NR_IOREQS];
385 
386 	for (i = 0; i < count && left > 0; i++) {
387 		vv[i].v_from = src_proc;
388 		vv[i].v_to = SELF;
389 		vv[i].v_gid = iov[i].iov_grant;
390 		vv[i].v_offset = 0;
391 		vv[i].v_addr = dst + cur_off;
392 		vv[i].v_bytes = iov[i].iov_size;
393 
394 		/* More data in iov than the buffer can hold, this should be
395 		 * manageable by the caller.
396 		 */
397 		if (left - vv[i].v_bytes > left) {
398 			printf("sys_easy_vsafecopy_from: buf too small!\n");
399 			return ENOMEM;
400 		}
401 
402 		left -= iov[i].iov_size;
403 		cur_off += iov[i].iov_size;
404 	}
405 
406 	/* Now that we prepared the vscp_vec, we can call vsafecopy() */
407 	if ((r = sys_vsafecopy(vv, count)) != OK)
408 		printf("sys_vsafecopy: failed: (%d)\n", r);
409 
410 	if (copied)
411 		*copied = cur_off;
412 
413 	return OK;
414 }
415 
416 static int
417 virtio_net_cpy_from_user(message *m)
418 {
419 	/* Put user bytes into a a free packet buffer and
420 	 * then forward this packet to the TX queue.
421 	 */
422 	int r;
423 	iovec_s_t iovec[NR_IOREQS];
424 	struct vumap_phys phys[2];
425 	struct packet *p;
426 	size_t bytes;
427 
428 	/* This should only be called if free_list has some entries */
429 	assert(!STAILQ_EMPTY(&free_list));
430 
431 	p = STAILQ_FIRST(&free_list);
432 	STAILQ_REMOVE_HEAD(&free_list, next);
433 
434 	virtio_net_fetch_iovec(iovec, m, m->m_net_netdrv_dl_writev_s.grant,
435 		 m->m_net_netdrv_dl_writev_s.count);
436 
437 	r = sys_easy_vsafecopy_from(m->m_source, iovec,
438 		m->m_net_netdrv_dl_writev_s.count, (vir_bytes)p->vdata,
439 		MAX_PACK_SIZE, &bytes);
440 
441 	if (r != OK)
442 		panic("%s: copy from %d failed", name, m->m_source);
443 
444 
445 	phys[0].vp_addr = p->phdr;
446 	assert(!(phys[0].vp_addr & 1));
447 	phys[0].vp_size = sizeof(struct virtio_net_hdr);
448 	phys[1].vp_addr = p->pdata;
449 	assert(!(phys[1].vp_addr & 1));
450 	phys[1].vp_size = bytes;
451 	virtio_to_queue(net_dev, TX_Q, phys, 2, p);
452 	return bytes;
453 }
454 
455 static void
456 virtio_net_intr(message *m)
457 {
458 	/* Check and clear interrupt flag */
459 	if (virtio_had_irq(net_dev)) {
460 		virtio_net_check_queues();
461 	} else {
462 		if (!spurious_interrupt)
463 			dput(("Spurious interrupt"));
464 
465 		spurious_interrupt = 1;
466 	}
467 
468 	virtio_net_check_pending();
469 
470 	virtio_irq_enable(net_dev);
471 }
472 
473 static void
474 virtio_net_write(message *m)
475 {
476 	int r;
477 	message reply;
478 
479 	reply.m_type = DL_TASK_REPLY;
480 	reply.m_netdrv_net_dl_task.flags = DL_NOFLAGS;
481 	reply.m_netdrv_net_dl_task.count = 0;
482 
483 
484 	if (!STAILQ_EMPTY(&free_list)) {
485 		/* free_list contains at least one  packet, use it */
486 		reply.m_netdrv_net_dl_task.count = virtio_net_cpy_from_user(m);
487 		reply.m_netdrv_net_dl_task.flags = DL_PACK_SEND;
488 	} else {
489 		pending_tx_msg = *m;
490 		tx_pending = 1;
491 	}
492 
493 	if ((r = ipc_send(m->m_source, &reply)) != OK)
494 		panic("%s: ipc_send to %d failed (%d)", name, m->m_source, r);
495 }
496 
497 static void
498 virtio_net_read(message *m)
499 {
500 	int r;
501 	message reply;
502 
503 	reply.m_type = DL_TASK_REPLY;
504 	reply.m_netdrv_net_dl_task.flags = DL_NOFLAGS;
505 	reply.m_netdrv_net_dl_task.count = 0;
506 
507 	if (!STAILQ_EMPTY(&recv_list)) {
508 		/* recv_list contains at least one  packet, copy it */
509 		reply.m_netdrv_net_dl_task.count = virtio_net_cpy_to_user(m);
510 		reply.m_netdrv_net_dl_task.flags = DL_PACK_RECV;
511 	} else {
512 		rx_pending = 1;
513 		pending_rx_msg = *m;
514 	}
515 
516 	if ((r = ipc_send(m->m_source, &reply)) != OK)
517 		panic("%s: ipc_send to %d failed (%d)", name, m->m_source, r);
518 }
519 
520 static void
521 virtio_net_conf(message *m)
522 {
523 	/* TODO: Add the multicast, broadcast filtering etc. */
524 	int i, r;
525 
526 	message reply;
527 
528 	/* If this is the first CONF message we see, fully initialize
529 	 * the device now.
530 	 */
531 	if (!started) {
532 		started = 1;
533 		virtio_device_ready(net_dev);
534 		virtio_irq_enable(net_dev);
535 	}
536 
537 	/* Prepare reply */
538 	memcpy(reply.m_netdrv_net_dl_conf.hw_addr, virtio_net_mac,
539 		sizeof(reply.m_netdrv_net_dl_conf.hw_addr));
540 
541 	reply.m_type = DL_CONF_REPLY;
542 	reply.m_netdrv_net_dl_conf.stat = OK;
543 
544 	if ((r = ipc_send(m->m_source, &reply)) != OK)
545 		panic("%s: ipc_send to %d failed (%d)", name, m->m_source, r);
546 }
547 
548 static void
549 virtio_net_getstat(message *m)
550 {
551 	int r;
552 	message reply;
553 
554 	reply.m_type = DL_STAT_REPLY;
555 
556 	r = sys_safecopyto(m->m_source, m->m_net_netdrv_dl_getstat_s.grant, 0,
557 			   (vir_bytes)&virtio_net_stats,
558 			   sizeof(virtio_net_stats));
559 
560 	if (r != OK)
561 		panic("%s: copy to %d failed (%d)", name, m->m_source, r);
562 
563 	if ((r = ipc_send(m->m_source, &reply)) != OK)
564 		panic("%s: ipc_send to %d failed (%d)", name, m->m_source, r);
565 }
566 
567 static void
568 virtio_net_notify(message *m)
569 {
570 	if (_ENDPOINT_P(m->m_source) == HARDWARE)
571 		virtio_net_intr(m);
572 }
573 
574 static void
575 virtio_net_msg(message *m)
576 {
577 	switch (m->m_type) {
578 	case DL_WRITEV_S:
579 		virtio_net_write(m);
580 		break;
581 	case DL_READV_S:
582 		virtio_net_read(m);
583 		break;
584 	case DL_CONF:
585 		virtio_net_conf(m);
586 		break;
587 	case DL_GETSTAT_S:
588 		virtio_net_getstat(m);
589 		break;
590 	default:
591 		panic("%s: illegal message: %d", name, m->m_type);
592 	}
593 }
594 
595 static void
596 virtio_net_main_loop(void)
597 {
598 	message m;
599 	int ipc_status;
600 	int r;
601 
602 	while (TRUE) {
603 
604 		virtio_net_refill_rx_queue();
605 
606 		if ((r = netdriver_receive(ANY, &m, &ipc_status)) != OK)
607 			panic("%s: netdriver_receive failed: %d", name, r);
608 
609 		if (is_ipc_notify(ipc_status))
610 			virtio_net_notify(&m);
611 		else
612 			virtio_net_msg(&m);
613 	}
614 }
615 
616 int
617 main(int argc, char *argv[])
618 {
619 	env_setargs(argc, argv);
620 	sef_local_startup();
621 
622 	virtio_net_main_loop();
623 }
624 
625 static void
626 sef_local_startup()
627 {
628 	sef_setcb_init_fresh(sef_cb_init_fresh);
629 	sef_setcb_init_lu(sef_cb_init_fresh);
630 	sef_setcb_init_restart(sef_cb_init_fresh);
631 
632 	sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready);
633 	sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_workfree);
634 
635 	sef_setcb_signal_handler(sef_cb_signal_handler);
636 
637 	sef_startup();
638 }
639 
640 static int
641 sef_cb_init_fresh(int type, sef_init_info_t *info)
642 {
643 	long instance = 0;
644 	env_parse("instance", "d", 0, &instance, 0, 255);
645 
646 	if (virtio_net_probe((int)instance) != OK)
647 		panic("%s: No device found", name);
648 
649 	if (virtio_net_config() != OK)
650 		panic("%s: No device found", name);
651 
652 	if (virtio_net_alloc_bufs() != OK)
653 		panic("%s: Buffer allocation failed", name);
654 
655 	virtio_net_init_queues();
656 
657 	netdriver_announce();
658 
659 	return(OK);
660 }
661 
662 static void
663 sef_cb_signal_handler(int signo)
664 {
665 	if (signo != SIGTERM)
666 		return;
667 
668 	dput(("Terminating"));
669 
670 	free_contig(data_vir, PACKET_BUF_SZ);
671 	free_contig(hdrs_vir, BUF_PACKETS * sizeof(hdrs_vir[0]));
672 	free(packets);
673 
674 	virtio_reset_device(net_dev);
675 	virtio_free_queues(net_dev);
676 	virtio_free_device(net_dev);
677 	net_dev = NULL;
678 
679 	exit(1);
680 }
681