xref: /minix3/minix/drivers/net/virtio_net/virtio_net.c (revision 9f81acbc21316bcbade46d78b67a5f2e58d11bb9)
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 <minix/drivers.h>
14 #include <minix/netdriver.h>
15 #include <minix/virtio.h>
16 
17 #include <sys/queue.h>
18 
19 #include "virtio_net.h"
20 
21 #define VERBOSE 0
22 
23 #if VERBOSE
24 #define dput(s)		do { dprintf(s); printf("\n"); } while (0)
25 #define dprintf(s) do {						\
26 	printf("%s: ", netdriver_name());			\
27 	printf s;						\
28 } while (0)
29 #else
30 #define dput(s)
31 #define dprintf(s)
32 #endif
33 
34 static struct virtio_device *net_dev;
35 
36 enum queue {RX_Q, TX_Q, CTRL_Q};
37 
38 /* Number of packets to work with */
39 /* TODO: This should be an argument to the driver and possibly also
40  *       depend on the queue sizes offered by this device.
41  */
42 #define BUF_PACKETS		64
43 /* Maximum size of a packet */
44 #define MAX_PACK_SIZE		NDEV_ETH_PACKET_MAX
45 /* Buffer size needed for the payload of BUF_PACKETS */
46 #define PACKET_BUF_SZ		(BUF_PACKETS * MAX_PACK_SIZE)
47 
48 struct packet {
49 	int idx;
50 	struct virtio_net_hdr *vhdr;
51 	phys_bytes phdr;
52 	char *vdata;
53 	phys_bytes pdata;
54 	size_t len;
55 	STAILQ_ENTRY(packet) next;
56 };
57 
58 /* Allocated data chunks */
59 static char *data_vir;
60 static phys_bytes data_phys;
61 static struct virtio_net_hdr *hdrs_vir;
62 static phys_bytes hdrs_phys;
63 static struct packet *packets;
64 static int in_rx;
65 
66 /* Packets on this list can be given to the host */
67 static STAILQ_HEAD(free_list, packet) free_list;
68 
69 /* Packets on this list are to be given to inet */
70 static STAILQ_HEAD(recv_list, packet) recv_list;
71 
72 /* Various state data */
73 static int spurious_interrupt;
74 
75 /* Prototypes */
76 static int virtio_net_probe(unsigned int skip);
77 static void virtio_net_config(netdriver_addr_t *addr);
78 static int virtio_net_alloc_bufs(void);
79 static void virtio_net_init_queues(void);
80 
81 static void virtio_net_refill_rx_queue(void);
82 static void virtio_net_check_queues(void);
83 static void virtio_net_check_pending(void);
84 
85 static int virtio_net_init(unsigned int instance, netdriver_addr_t * addr,
86 	uint32_t * caps, unsigned int * ticks);
87 static void virtio_net_stop(void);
88 static int virtio_net_send(struct netdriver_data *data, size_t len);
89 static ssize_t virtio_net_recv(struct netdriver_data *data, size_t max);
90 static void virtio_net_intr(unsigned int mask);
91 
92 static const struct netdriver virtio_net_table = {
93 	.ndr_name	= "vio",
94 	.ndr_init	= virtio_net_init,
95 	.ndr_stop	= virtio_net_stop,
96 	.ndr_recv	= virtio_net_recv,
97 	.ndr_send	= virtio_net_send,
98 	.ndr_intr	= virtio_net_intr,
99 };
100 
101 /* TODO: Features are pretty much ignored */
102 static struct virtio_feature netf[] = {
103 	{ "partial csum",	VIRTIO_NET_F_CSUM,	0,	0	},
104 	{ "given mac",		VIRTIO_NET_F_MAC,	0,	1	},
105 	{ "status ",		VIRTIO_NET_F_STATUS,	0,	0	},
106 	{ "control channel",	VIRTIO_NET_F_CTRL_VQ,	0,	1	},
107 	{ "control channel rx",	VIRTIO_NET_F_CTRL_RX,	0,	0	}
108 };
109 
110 static int
111 virtio_net_probe(unsigned int skip)
112 {
113 	/* virtio-net has at least 2 queues */
114 	int queues = 2;
115 	net_dev= virtio_setup_device(0x00001, netdriver_name(), netf,
116 				     sizeof(netf) / sizeof(netf[0]),
117 				     1 /* threads */, skip);
118 	if (net_dev == NULL)
119 		return ENXIO;
120 
121 	/* If the host supports the control queue, allocate it as well */
122 	if (virtio_host_supports(net_dev, VIRTIO_NET_F_CTRL_VQ))
123 		queues += 1;
124 
125 	if (virtio_alloc_queues(net_dev, queues) != OK) {
126 		virtio_free_device(net_dev);
127 		return ENOMEM;
128 	}
129 
130 	return OK;
131 }
132 
133 static void
134 virtio_net_config(netdriver_addr_t * addr)
135 {
136 	u32_t mac14;
137 	u32_t mac56;
138 	int i;
139 
140 	if (virtio_host_supports(net_dev, VIRTIO_NET_F_MAC)) {
141 		dprintf(("Mac set by host: "));
142 		mac14 = virtio_sread32(net_dev, 0);
143 		mac56 = virtio_sread32(net_dev, 4);
144 		memcpy(&addr->na_addr[0], &mac14, 4);
145 		memcpy(&addr->na_addr[4], &mac56, 2);
146 
147 		for (i = 0; i < 6; i++)
148 			dprintf(("%02x%s", addr->na_addr[i],
149 					 i == 5 ? "\n" : ":"));
150 	} else {
151 		dput(("No mac"));
152 	}
153 
154 	if (virtio_host_supports(net_dev, VIRTIO_NET_F_STATUS)) {
155 		dput(("Current Status %x", (u32_t)virtio_sread16(net_dev, 6)));
156 	} else {
157 		dput(("No status"));
158 	}
159 
160 	if (virtio_host_supports(net_dev, VIRTIO_NET_F_CTRL_VQ))
161 		dput(("Host supports control channel"));
162 
163 	if (virtio_host_supports(net_dev, VIRTIO_NET_F_CTRL_RX))
164 		dput(("Host supports control channel for RX"));
165 }
166 
167 static int
168 virtio_net_alloc_bufs(void)
169 {
170 	data_vir = alloc_contig(PACKET_BUF_SZ, 0, &data_phys);
171 
172 	if (!data_vir)
173 		return ENOMEM;
174 
175 	hdrs_vir = alloc_contig(BUF_PACKETS * sizeof(hdrs_vir[0]),
176 				 0, &hdrs_phys);
177 
178 	if (!hdrs_vir) {
179 		free_contig(data_vir, PACKET_BUF_SZ);
180 		return ENOMEM;
181 	}
182 
183 	packets = malloc(BUF_PACKETS * sizeof(packets[0]));
184 
185 	if (!packets) {
186 		free_contig(data_vir, PACKET_BUF_SZ);
187 		free_contig(hdrs_vir, BUF_PACKETS * sizeof(hdrs_vir[0]));
188 		return ENOMEM;
189 	}
190 
191 	memset(data_vir, 0, PACKET_BUF_SZ);
192 	memset(hdrs_vir, 0, BUF_PACKETS * sizeof(hdrs_vir[0]));
193 	memset(packets, 0, BUF_PACKETS * sizeof(packets[0]));
194 
195 	return OK;
196 }
197 
198 static void
199 virtio_net_init_queues(void)
200 {
201 	int i;
202 	STAILQ_INIT(&free_list);
203 	STAILQ_INIT(&recv_list);
204 
205 	for (i = 0; i < BUF_PACKETS; i++) {
206 		packets[i].idx = i;
207 		packets[i].vhdr = &hdrs_vir[i];
208 		packets[i].phdr = hdrs_phys + i * sizeof(hdrs_vir[i]);
209 		packets[i].vdata = data_vir + i * MAX_PACK_SIZE;
210 		packets[i].pdata = data_phys + i * MAX_PACK_SIZE;
211 		STAILQ_INSERT_HEAD(&free_list, &packets[i], next);
212 	}
213 }
214 
215 static void
216 virtio_net_refill_rx_queue(void)
217 {
218 	struct vumap_phys phys[2];
219 	struct packet *p;
220 
221 	while ((in_rx < BUF_PACKETS / 2) && !STAILQ_EMPTY(&free_list)) {
222 		/* peek */
223 		p = STAILQ_FIRST(&free_list);
224 		/* remove */
225 		STAILQ_REMOVE_HEAD(&free_list, next);
226 
227 		phys[0].vp_addr = p->phdr;
228 		assert(!(phys[0].vp_addr & 1));
229 		phys[0].vp_size = sizeof(struct virtio_net_hdr);
230 
231 		phys[1].vp_addr = p->pdata;
232 		assert(!(phys[1].vp_addr & 1));
233 		phys[1].vp_size = MAX_PACK_SIZE;
234 
235 		/* RX queue needs write */
236 		phys[0].vp_addr |= 1;
237 		phys[1].vp_addr |= 1;
238 
239 		virtio_to_queue(net_dev, RX_Q, phys, 2, p);
240 		in_rx++;
241 	}
242 
243 	if (in_rx == 0 && STAILQ_EMPTY(&free_list))
244 		dput(("warning: rx queue underflow!"));
245 }
246 
247 static void
248 virtio_net_check_queues(void)
249 {
250 	struct packet *p;
251 	size_t len;
252 
253 	/* Put the received packets into the recv list */
254 	while (virtio_from_queue(net_dev, RX_Q, (void **)&p, &len) == 0) {
255 		p->len = len;
256 		STAILQ_INSERT_TAIL(&recv_list, p, next);
257 		in_rx--;
258 	}
259 
260 	/*
261 	 * Packets from the TX queue just indicated they are free to
262 	 * be reused now. inet already knows about them as being sent.
263 	 */
264 	while (virtio_from_queue(net_dev, TX_Q, (void **)&p, NULL) == 0) {
265 		memset(p->vhdr, 0, sizeof(*p->vhdr));
266 		memset(p->vdata, 0, MAX_PACK_SIZE);
267 		STAILQ_INSERT_HEAD(&free_list, p, next);
268 	}
269 }
270 
271 static void
272 virtio_net_check_pending(void)
273 {
274 
275 	/* Pending read and something in recv_list? */
276 	if (!STAILQ_EMPTY(&recv_list))
277 		netdriver_recv();
278 
279 	if (!STAILQ_EMPTY(&free_list))
280 		netdriver_send();
281 }
282 
283 static void
284 virtio_net_intr(unsigned int __unused mask)
285 {
286 
287 	/* Check and clear interrupt flag */
288 	if (virtio_had_irq(net_dev)) {
289 		virtio_net_check_queues();
290 	} else {
291 		if (!spurious_interrupt)
292 			dput(("Spurious interrupt"));
293 
294 		spurious_interrupt = 1;
295 	}
296 
297 	virtio_net_check_pending();
298 
299 	virtio_irq_enable(net_dev);
300 
301 	/* Readd packets to the receive queue as necessary. */
302 	virtio_net_refill_rx_queue();
303 }
304 
305 /*
306  * Put user bytes into a free packet buffer, forward this packet to the TX
307  * queue, and return OK.  If there are no free packet buffers, return SUSPEND.
308  */
309 static int
310 virtio_net_send(struct netdriver_data * data, size_t len)
311 {
312 	struct vumap_phys phys[2];
313 	struct packet *p;
314 
315 	if (STAILQ_EMPTY(&free_list))
316 		return SUSPEND;
317 
318 	p = STAILQ_FIRST(&free_list);
319 	STAILQ_REMOVE_HEAD(&free_list, next);
320 
321 	if (len > MAX_PACK_SIZE)
322 		panic("%s: packet too large to send: %zu",
323 		    netdriver_name(), len);
324 
325 	netdriver_copyin(data, 0, p->vdata, len);
326 
327 	phys[0].vp_addr = p->phdr;
328 	assert(!(phys[0].vp_addr & 1));
329 	phys[0].vp_size = sizeof(struct virtio_net_hdr);
330 	phys[1].vp_addr = p->pdata;
331 	assert(!(phys[1].vp_addr & 1));
332 	phys[1].vp_size = len;
333 	virtio_to_queue(net_dev, TX_Q, phys, 2, p);
334 
335 	return OK;
336 }
337 
338 /*
339  * Put a packet receive from the RX queue into a user buffer, and return the
340  * packet length.  If there are no received packets, return SUSPEND.
341  */
342 static ssize_t
343 virtio_net_recv(struct netdriver_data * data, size_t max)
344 {
345 	struct packet *p;
346 	ssize_t len;
347 
348 	/* Get the first received packet, if any. */
349 	if (STAILQ_EMPTY(&recv_list))
350 		return SUSPEND;
351 
352 	p = STAILQ_FIRST(&recv_list);
353 	STAILQ_REMOVE_HEAD(&recv_list, next);
354 
355 	/* Copy out the packet contents. */
356 	if (p->len < sizeof(struct virtio_net_hdr))
357 		panic("received packet does not have virtio header");
358 	len = p->len - sizeof(struct virtio_net_hdr);
359 	if ((size_t)len > max)
360 		len = (ssize_t)max;
361 
362 	/*
363 	 * HACK: due to lack of padding, received packets may in fact be
364 	 * smaller than the minimum ethernet packet size.  The TCP/IP service
365 	 * will accept the packets just fine if we increase the length to its
366 	 * minimum.  We already zeroed out the rest of the packet data, so this
367 	 * is safe.
368 	 */
369 	if (len < NDEV_ETH_PACKET_MIN)
370 		len = NDEV_ETH_PACKET_MIN;
371 
372 	netdriver_copyout(data, 0, p->vdata, len);
373 
374 	/* Clean the packet. */
375 	memset(p->vhdr, 0, sizeof(*p->vhdr));
376 	memset(p->vdata, 0, MAX_PACK_SIZE);
377 	STAILQ_INSERT_HEAD(&free_list, p, next);
378 
379 	/* Readd packets to the receive queue as necessary. */
380 	virtio_net_refill_rx_queue();
381 
382 	return len;
383 }
384 
385 /*
386  * Initialize the driver and the virtual hardware.
387  */
388 static int
389 virtio_net_init(unsigned int instance, netdriver_addr_t * addr,
390 	uint32_t * caps, unsigned int * ticks __unused)
391 {
392 	int r;
393 
394 	if ((r = virtio_net_probe(instance)) != OK)
395 		return r;
396 
397 	virtio_net_config(addr);
398 
399 	if (virtio_net_alloc_bufs() != OK)
400 		panic("%s: Buffer allocation failed", netdriver_name());
401 
402 	virtio_net_init_queues();
403 
404 	/* Add packets to the receive queue. */
405 	virtio_net_refill_rx_queue();
406 
407 	virtio_device_ready(net_dev);
408 
409 	virtio_irq_enable(net_dev);
410 
411 	*caps = NDEV_CAP_MCAST | NDEV_CAP_BCAST;
412 	return OK;
413 }
414 
415 /*
416  * The driver is terminating.  Clean up.
417  */
418 static void
419 virtio_net_stop(void)
420 {
421 
422 	dput(("Terminating"));
423 
424 	free_contig(data_vir, PACKET_BUF_SZ);
425 	free_contig(hdrs_vir, BUF_PACKETS * sizeof(hdrs_vir[0]));
426 	free(packets);
427 
428 	virtio_reset_device(net_dev);
429 	virtio_free_queues(net_dev);
430 	virtio_free_device(net_dev);
431 	net_dev = NULL;
432 }
433 
434 /*
435  * The virtio-net device driver.
436  */
437 int
438 main(int argc, char *argv[])
439 {
440 
441 	env_setargs(argc, argv);
442 
443 	netdriver_task(&virtio_net_table);
444 
445 	return 0;
446 }
447