1 /* virtio net driver for MINIX 3
3 * Copyright (c) 2013, A. Welzel, <arne.welzel@gmail.com>
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.
11 #include <sys/types.h>
13 #include <net/gen/ether.h>
14 #include <net/gen/eth_io.h>
16 #include <minix/drivers.h>
17 #include <minix/netdriver.h>
18 #include <minix/sysutil.h>
19 #include <minix/virtio.h>
21 #include <sys/queue.h>
23 #include "virtio_net.h"
25 #define dput(s) do { dprintf(s); printf("\n"); } while (0)
26 #define dprintf(s) do { \
27 printf("%s: ", name); \
31 static struct virtio_device
*net_dev
;
33 static const char *const name
= "virtio-net";
35 enum queue
{RX_Q
, TX_Q
, CTRL_Q
};
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.
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)
49 struct virtio_net_hdr
*vhdr
;
53 STAILQ_ENTRY(packet
) next
;
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
;
65 /* Packets on this list can be given to the host */
66 static STAILQ_HEAD(free_list
, packet
) free_list
;
68 /* Packets on this list are to be given to inet */
69 static STAILQ_HEAD(recv_list
, packet
) recv_list
;
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
;
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
;
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);
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);
93 static void virtio_net_fetch_iovec(iovec_s_t
*iov
, message
*m
);
94 static int virtio_net_cpy_to_user(message
*m
);
95 static int virtio_net_cpy_from_user(message
*m
);
97 static void virtio_net_intr(message
*m
);
98 static void virtio_net_write(message
*m
);
99 static void virtio_net_read(message
*m
);
100 static void virtio_net_conf(message
*m
);
101 static void virtio_net_getstat(message
*m
);
103 static void virtio_net_notify(message
*m
);
104 static void virtio_net_msg(message
*m
);
105 static void virtio_net_main_loop(void);
107 static void sef_local_startup(void);
108 static int sef_cb_init_fresh(int type
, sef_init_info_t
*info
);
109 static void sef_cb_signal_handler(int signo
);
112 /* TODO: Features are pretty much ignored */
113 struct virtio_feature netf
[] = {
114 { "partial csum", VIRTIO_NET_F_CSUM
, 0, 0 },
115 { "given mac", VIRTIO_NET_F_MAC
, 0, 0 },
116 { "status ", VIRTIO_NET_F_STATUS
, 0, 0 },
117 { "control channel", VIRTIO_NET_F_CTRL_VQ
, 0, 0 },
118 { "control channel rx", VIRTIO_NET_F_CTRL_RX
, 0, 0 }
122 virtio_net_probe(int skip
)
124 /* virtio-net has at least 2 queues */
126 net_dev
= virtio_setup_device(0x00001, name
, netf
,
127 sizeof(netf
) / sizeof(netf
[0]),
128 1 /* threads */, skip
);
132 /* If the host supports the control queue, allocate it as well */
133 if (virtio_host_supports(net_dev
, VIRTIO_NET_F_CTRL_VQ
))
136 if (virtio_alloc_queues(net_dev
, queues
) != OK
) {
137 virtio_free_device(net_dev
);
145 virtio_net_config(void)
151 if (virtio_host_supports(net_dev
, VIRTIO_NET_F_MAC
)) {
152 dprintf(("Mac set by host: "));
153 mac14
= virtio_sread32(net_dev
, 0);
154 mac56
= virtio_sread32(net_dev
, 4);
155 *(u32_t
*)virtio_net_mac
= mac14
;
156 *(u16_t
*)(virtio_net_mac
+ 4) = mac56
;
158 for (i
= 0; i
< 6; i
++)
159 printf("%02x%s", virtio_net_mac
[i
],
160 i
== 5 ? "\n" : ":");
165 if (virtio_host_supports(net_dev
, VIRTIO_NET_F_STATUS
)) {
166 dput(("Current Status %x", (u32_t
)virtio_sread16(net_dev
, 6)));
171 if (virtio_host_supports(net_dev
, VIRTIO_NET_F_CTRL_VQ
))
172 dput(("Host supports control channel"));
174 if (virtio_host_supports(net_dev
, VIRTIO_NET_F_CTRL_RX
))
175 dput(("Host supports control channel for RX"));
181 virtio_net_alloc_bufs(void)
183 data_vir
= alloc_contig(PACKET_BUF_SZ
, 0, &data_phys
);
188 hdrs_vir
= alloc_contig(BUF_PACKETS
* sizeof(hdrs_vir
[0]),
192 free_contig(data_vir
, PACKET_BUF_SZ
);
196 packets
= malloc(BUF_PACKETS
* sizeof(packets
[0]));
199 free_contig(data_vir
, PACKET_BUF_SZ
);
200 free_contig(hdrs_vir
, BUF_PACKETS
* sizeof(hdrs_vir
[0]));
204 memset(data_vir
, 0, PACKET_BUF_SZ
);
205 memset(hdrs_vir
, 0, BUF_PACKETS
* sizeof(hdrs_vir
[0]));
206 memset(packets
, 0, BUF_PACKETS
* sizeof(packets
[0]));
212 virtio_net_init_queues(void)
215 STAILQ_INIT(&free_list
);
216 STAILQ_INIT(&recv_list
);
218 for (i
= 0; i
< BUF_PACKETS
; i
++) {
220 packets
[i
].vhdr
= &hdrs_vir
[i
];
221 packets
[i
].phdr
= hdrs_phys
+ i
* sizeof(hdrs_vir
[i
]);
222 packets
[i
].vdata
= data_vir
+ i
* MAX_PACK_SIZE
;
223 packets
[i
].pdata
= data_phys
+ i
* MAX_PACK_SIZE
;
224 STAILQ_INSERT_HEAD(&free_list
, &packets
[i
], next
);
229 virtio_net_refill_rx_queue(void)
231 struct vumap_phys phys
[2];
234 while ((in_rx
< BUF_PACKETS
/ 2) && !STAILQ_EMPTY(&free_list
)) {
237 p
= STAILQ_FIRST(&free_list
);
239 STAILQ_REMOVE_HEAD(&free_list
, next
);
241 phys
[0].vp_addr
= p
->phdr
;
242 assert(!(phys
[0].vp_addr
& 1));
243 phys
[0].vp_size
= sizeof(struct virtio_net_hdr
);
245 phys
[1].vp_addr
= p
->pdata
;
246 assert(!(phys
[1].vp_addr
& 1));
247 phys
[1].vp_size
= MAX_PACK_SIZE
;
249 /* RX queue needs write */
250 phys
[0].vp_addr
|= 1;
251 phys
[1].vp_addr
|= 1;
253 virtio_to_queue(net_dev
, RX_Q
, phys
, 2, p
);
258 if (in_rx
== 0 && STAILQ_EMPTY(&free_list
)) {
259 dput(("warning: rx queue underflow!"));
260 virtio_net_stats
.ets_fifoUnder
++;
265 virtio_net_check_queues(void)
269 /* Put the received packets into the recv list */
270 while (virtio_from_queue(net_dev
, RX_Q
, (void **)&p
) == 0) {
271 STAILQ_INSERT_TAIL(&recv_list
, p
, next
);
273 virtio_net_stats
.ets_packetR
++;
276 /* Packets from the TX queue just indicated they are free to
277 * be reused now. inet already knows about them as being sent.
279 while (virtio_from_queue(net_dev
, TX_Q
, (void **)&p
) == 0) {
280 memset(p
->vhdr
, 0, sizeof(*p
->vhdr
));
281 memset(p
->vdata
, 0, MAX_PACK_SIZE
);
282 STAILQ_INSERT_HEAD(&free_list
, p
, next
);
283 virtio_net_stats
.ets_packetT
++;
288 virtio_net_check_pending(void)
294 reply
.m_type
= DL_TASK_REPLY
;
295 reply
.DL_FLAGS
= DL_NOFLAGS
;
298 /* Pending read and something in recv_list? */
299 if (!STAILQ_EMPTY(&recv_list
) && rx_pending
) {
300 dst
= pending_rx_msg
.m_source
;
301 reply
.DL_COUNT
= virtio_net_cpy_to_user(&pending_rx_msg
);
302 reply
.DL_FLAGS
|= DL_PACK_RECV
;
306 if (!STAILQ_EMPTY(&free_list
) && tx_pending
) {
307 dst
= pending_tx_msg
.m_source
;
308 virtio_net_cpy_from_user(&pending_tx_msg
);
309 reply
.DL_FLAGS
|= DL_PACK_SEND
;
313 /* Only reply if a pending request was handled */
314 if (reply
.DL_FLAGS
!= DL_NOFLAGS
)
315 if ((r
= send(dst
, &reply
)) != OK
)
316 panic("%s: send to %d failed (%d)", name
, dst
, r
);
320 virtio_net_fetch_iovec(iovec_s_t
*iov
, message
*m
)
323 r
= sys_safecopyfrom(m
->m_source
, m
->DL_GRANT
, 0, (vir_bytes
)iov
,
324 m
->DL_COUNT
* sizeof(iov
[0]));
327 panic("%s: iovec fail for %d (%d)", name
, m
->m_source
, r
);
331 virtio_net_cpy_to_user(message
*m
)
333 /* Hmm, this looks so similar to cpy_from_user... TODO */
334 int i
, r
, size
, ivsz
;
335 int left
= MAX_PACK_SIZE
; /* Try copying the whole packet */
337 iovec_s_t iovec
[NR_IOREQS
];
340 /* This should only be called if recv_list has some entries */
341 assert(!STAILQ_EMPTY(&recv_list
));
343 p
= STAILQ_FIRST(&recv_list
);
344 STAILQ_REMOVE_HEAD(&recv_list
, next
);
346 virtio_net_fetch_iovec(iovec
, m
);
348 for (i
= 0; i
< m
->DL_COUNT
&& left
> 0; i
++) {
349 ivsz
= iovec
[i
].iov_size
;
350 size
= left
> ivsz
? ivsz
: left
;
351 r
= sys_safecopyto(m
->m_source
, iovec
[i
].iov_grant
, 0,
352 (vir_bytes
) p
->vdata
+ bytes
, size
);
355 panic("%s: copy to %d failed (%d)", name
,
364 dput(("Uhm... left=%d", left
));
366 /* Clean the packet */
367 memset(p
->vhdr
, 0, sizeof(*p
->vhdr
));
368 memset(p
->vdata
, 0, MAX_PACK_SIZE
);
369 STAILQ_INSERT_HEAD(&free_list
, p
, next
);
375 sys_easy_vsafecopy_from(endpoint_t src_proc
, iovec_s_t
*iov
, int count
,
376 vir_bytes dst
, size_t max
, size_t *copied
)
380 vir_bytes cur_off
= 0;
381 struct vscp_vec vv
[NR_IOREQS
];
383 for (i
= 0; i
< count
&& left
> 0; i
++) {
384 vv
[i
].v_from
= src_proc
;
386 vv
[i
].v_gid
= iov
[i
].iov_grant
;
388 vv
[i
].v_addr
= dst
+ cur_off
;
389 vv
[i
].v_bytes
= iov
[i
].iov_size
;
391 /* More data in iov than the buffer can hold, this should be
392 * manageable by the caller.
394 if (left
- vv
[i
].v_bytes
> left
) {
395 printf("sys_easy_vsafecopy_from: buf too small!\n");
399 left
-= iov
[i
].iov_size
;
400 cur_off
+= iov
[i
].iov_size
;
403 /* Now that we prepared the vscp_vec, we can call vsafecopy() */
404 if ((r
= sys_vsafecopy(vv
, count
)) != OK
)
405 printf("sys_vsafecopy: failed: (%d)\n", r
);
414 virtio_net_cpy_from_user(message
*m
)
416 /* Put user bytes into a a free packet buffer and
417 * then forward this packet to the TX queue.
420 iovec_s_t iovec
[NR_IOREQS
];
421 struct vumap_phys phys
[2];
425 /* This should only be called if free_list has some entries */
426 assert(!STAILQ_EMPTY(&free_list
));
428 p
= STAILQ_FIRST(&free_list
);
429 STAILQ_REMOVE_HEAD(&free_list
, next
);
431 virtio_net_fetch_iovec(iovec
, m
);
433 r
= sys_easy_vsafecopy_from(m
->m_source
, iovec
, m
->DL_COUNT
,
434 (vir_bytes
)p
->vdata
, MAX_PACK_SIZE
,
438 panic("%s: copy from %d failed", name
, m
->m_source
);
441 phys
[0].vp_addr
= p
->phdr
;
442 assert(!(phys
[0].vp_addr
& 1));
443 phys
[0].vp_size
= sizeof(struct virtio_net_hdr
);
444 phys
[1].vp_addr
= p
->pdata
;
445 assert(!(phys
[1].vp_addr
& 1));
446 phys
[1].vp_size
= bytes
;
447 virtio_to_queue(net_dev
, TX_Q
, phys
, 2, p
);
452 virtio_net_intr(message
*m
)
454 /* Check and clear interrupt flag */
455 if (virtio_had_irq(net_dev
)) {
456 virtio_net_check_queues();
458 if (!spurious_interrupt
)
459 dput(("Spurious interrupt"));
461 spurious_interrupt
= 1;
464 virtio_net_check_pending();
466 virtio_irq_enable(net_dev
);
470 virtio_net_write(message
*m
)
475 reply
.m_type
= DL_TASK_REPLY
;
476 reply
.DL_FLAGS
= DL_NOFLAGS
;
480 if (!STAILQ_EMPTY(&free_list
)) {
481 /* free_list contains at least one packet, use it */
482 reply
.DL_COUNT
= virtio_net_cpy_from_user(m
);
483 reply
.DL_FLAGS
= DL_PACK_SEND
;
489 if ((r
= send(m
->m_source
, &reply
)) != OK
)
490 panic("%s: send to %d failed (%d)", name
, m
->m_source
, r
);
494 virtio_net_read(message
*m
)
499 reply
.m_type
= DL_TASK_REPLY
;
500 reply
.DL_FLAGS
= DL_NOFLAGS
;
503 if (!STAILQ_EMPTY(&recv_list
)) {
504 /* recv_list contains at least one packet, copy it */
505 reply
.DL_COUNT
= virtio_net_cpy_to_user(m
);
506 reply
.DL_FLAGS
= DL_PACK_RECV
;
512 if ((r
= send(m
->m_source
, &reply
)) != OK
)
513 panic("%s: send to %d failed (%d)", name
, m
->m_source
, r
);
517 virtio_net_conf(message
*m
)
519 /* TODO: Add the multicast, broadcast filtering etc. */
524 /* If this is the first CONF message we see, fully initialize
529 virtio_device_ready(net_dev
);
530 virtio_irq_enable(net_dev
);
534 for (i
= 0; i
< sizeof(virtio_net_mac
); i
++)
535 ((u8_t
*)reply
.DL_HWADDR
)[i
] = virtio_net_mac
[i
];
537 reply
.m_type
= DL_CONF_REPLY
;
541 if ((r
= send(m
->m_source
, &reply
)) != OK
)
542 panic("%s: send to %d failed (%d)", name
, m
->m_source
, r
);
546 virtio_net_getstat(message
*m
)
551 reply
.m_type
= DL_STAT_REPLY
;
556 r
= sys_safecopyto(m
->m_source
, m
->DL_GRANT
, 0,
557 (vir_bytes
)&virtio_net_stats
,
558 sizeof(virtio_net_stats
));
561 panic("%s: copy to %d failed (%d)", name
, m
->m_source
, r
);
563 if ((r
= send(m
->m_source
, &reply
)) != OK
)
564 panic("%s: send to %d failed (%d)", name
, m
->m_source
, r
);
568 virtio_net_notify(message
*m
)
570 if (_ENDPOINT_P(m
->m_source
) == HARDWARE
)
575 virtio_net_msg(message
*m
)
588 virtio_net_getstat(m
);
591 panic("%s: illegal message: %d", name
, m
->m_type
);
596 virtio_net_main_loop(void)
604 virtio_net_refill_rx_queue();
606 if ((r
= netdriver_receive(ANY
, &m
, &ipc_status
)) != OK
)
607 panic("%s: netdriver_receive failed: %d", name
, r
);
609 if (is_ipc_notify(ipc_status
))
610 virtio_net_notify(&m
);
617 main(int argc
, char *argv
[])
619 env_setargs(argc
, argv
);
622 virtio_net_main_loop();
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
);
632 sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready
);
633 sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_workfree
);
635 sef_setcb_signal_handler(sef_cb_signal_handler
);
641 sef_cb_init_fresh(int type
, sef_init_info_t
*info
)
644 env_parse("instance", "d", 0, &instance
, 0, 255);
646 if (virtio_net_probe((int)instance
) != OK
)
647 panic("%s: No device found", name
);
649 if (virtio_net_config() != OK
)
650 panic("%s: No device found", name
);
652 if (virtio_net_alloc_bufs() != OK
)
653 panic("%s: Buffer allocation failed", name
);
655 virtio_net_init_queues();
657 netdriver_announce();
663 sef_cb_signal_handler(int signo
)
665 if (signo
!= SIGTERM
)
668 dput(("Terminating"));
670 free_contig(data_vir
, PACKET_BUF_SZ
);
671 free_contig(hdrs_vir
, BUF_PACKETS
* sizeof(hdrs_vir
[0]));
674 virtio_reset_device(net_dev
);
675 virtio_free_queues(net_dev
);
676 virtio_free_device(net_dev
);