xref: /minix3/minix/drivers/net/virtio_net/virtio_net.c (revision ef8d499e2d2af900e9b2ab297171d7b088652482)
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