| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Virtio Network Device | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright IBM, Corp. 2007 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Authors: | 
					
						
							|  |  |  |  *  Anthony Liguori   <aliguori@us.ibm.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This work is licensed under the terms of the GNU GPL, version 2.  See | 
					
						
							|  |  |  |  * the COPYING file in the top-level directory. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-27 18:04:05 +05:30
										 |  |  | #include "iov.h"
 | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | #include "virtio.h"
 | 
					
						
							|  |  |  | #include "net.h"
 | 
					
						
							| 
									
										
										
										
											2009-10-22 17:49:03 +01:00
										 |  |  | #include "net/checksum.h"
 | 
					
						
							| 
									
										
										
										
											2009-10-22 17:49:05 +01:00
										 |  |  | #include "net/tap.h"
 | 
					
						
							| 
									
										
										
										
											2010-02-18 16:24:31 +01:00
										 |  |  | #include "qemu-error.h"
 | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | #include "qemu-timer.h"
 | 
					
						
							|  |  |  | #include "virtio-net.h"
 | 
					
						
							| 
									
										
										
										
											2010-03-17 13:08:42 +02:00
										 |  |  | #include "vhost_net.h"
 | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-22 17:43:50 +01:00
										 |  |  | #define VIRTIO_NET_VM_VERSION    11
 | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:28 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-06-05 14:47:23 -06:00
										 |  |  | #define MAC_TABLE_ENTRIES    64
 | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:32 +00:00
										 |  |  | #define MAX_VLAN    (1 << 12)   /* Per 802.1Q definition */
 | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:04 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | typedef struct VirtIONet | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     VirtIODevice vdev; | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:12 +00:00
										 |  |  |     uint8_t mac[ETH_ALEN]; | 
					
						
							| 
									
										
										
										
											2009-01-08 19:46:33 +00:00
										 |  |  |     uint16_t status; | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  |     VirtQueue *rx_vq; | 
					
						
							|  |  |  |     VirtQueue *tx_vq; | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:16 +00:00
										 |  |  |     VirtQueue *ctrl_vq; | 
					
						
							| 
									
										
										
										
											2009-11-25 18:49:11 +00:00
										 |  |  |     NICState *nic; | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  |     QEMUTimer *tx_timer; | 
					
						
							| 
									
										
										
										
											2010-09-02 09:01:10 -06:00
										 |  |  |     QEMUBH *tx_bh; | 
					
						
							| 
									
										
										
										
											2010-09-02 09:00:50 -06:00
										 |  |  |     uint32_t tx_timeout; | 
					
						
							| 
									
										
										
										
											2010-09-02 09:00:57 -06:00
										 |  |  |     int32_t tx_burst; | 
					
						
							| 
									
										
										
										
											2010-09-02 09:01:04 -06:00
										 |  |  |     int tx_waiting; | 
					
						
							| 
									
										
										
										
											2009-10-22 17:43:45 +01:00
										 |  |  |     uint32_t has_vnet_hdr; | 
					
						
							| 
									
										
										
										
											2009-10-22 17:43:50 +01:00
										 |  |  |     uint8_t has_ufo; | 
					
						
							| 
									
										
										
										
											2009-06-18 18:21:36 +01:00
										 |  |  |     struct { | 
					
						
							|  |  |  |         VirtQueueElement elem; | 
					
						
							|  |  |  |         ssize_t len; | 
					
						
							|  |  |  |     } async_tx; | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  |     int mergeable_rx_bufs; | 
					
						
							| 
									
										
										
										
											2009-06-05 14:46:57 -06:00
										 |  |  |     uint8_t promisc; | 
					
						
							|  |  |  |     uint8_t allmulti; | 
					
						
							| 
									
										
										
										
											2009-06-05 14:47:18 -06:00
										 |  |  |     uint8_t alluni; | 
					
						
							|  |  |  |     uint8_t nomulti; | 
					
						
							|  |  |  |     uint8_t nouni; | 
					
						
							|  |  |  |     uint8_t nobcast; | 
					
						
							| 
									
										
										
										
											2010-03-17 13:08:42 +02:00
										 |  |  |     uint8_t vhost_started; | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:28 +00:00
										 |  |  |     struct { | 
					
						
							|  |  |  |         int in_use; | 
					
						
							| 
									
										
										
										
											2009-06-05 14:47:13 -06:00
										 |  |  |         int first_multi; | 
					
						
							| 
									
										
										
										
											2009-06-05 14:47:08 -06:00
										 |  |  |         uint8_t multi_overflow; | 
					
						
							|  |  |  |         uint8_t uni_overflow; | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:28 +00:00
										 |  |  |         uint8_t *macs; | 
					
						
							|  |  |  |     } mac_table; | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:32 +00:00
										 |  |  |     uint32_t *vlans; | 
					
						
							| 
									
										
										
										
											2010-06-25 11:09:28 -06:00
										 |  |  |     DeviceState *qdev; | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | } VirtIONet; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* TODO
 | 
					
						
							|  |  |  |  * - we could suppress RX interrupt if we were so inclined. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static VirtIONet *to_virtio_net(VirtIODevice *vdev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return (VirtIONet *)vdev; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:08 +00:00
										 |  |  | static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config) | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | { | 
					
						
							|  |  |  |     VirtIONet *n = to_virtio_net(vdev); | 
					
						
							|  |  |  |     struct virtio_net_config netcfg; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-03 21:42:28 +00:00
										 |  |  |     stw_p(&netcfg.status, n->status); | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:12 +00:00
										 |  |  |     memcpy(netcfg.mac, n->mac, ETH_ALEN); | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  |     memcpy(config, &netcfg, sizeof(netcfg)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:08 +00:00
										 |  |  | static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     VirtIONet *n = to_virtio_net(vdev); | 
					
						
							|  |  |  |     struct virtio_net_config netcfg; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     memcpy(&netcfg, config, sizeof(netcfg)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:12 +00:00
										 |  |  |     if (memcmp(netcfg.mac, n->mac, ETH_ALEN)) { | 
					
						
							|  |  |  |         memcpy(n->mac, netcfg.mac, ETH_ALEN); | 
					
						
							| 
									
										
										
										
											2009-11-25 18:49:11 +00:00
										 |  |  |         qemu_format_nic_info_str(&n->nic->nc, n->mac); | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:08 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-11-22 19:52:30 +02:00
										 |  |  | static bool virtio_net_started(VirtIONet *n, uint8_t status) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return (status & VIRTIO_CONFIG_S_DRIVER_OK) && | 
					
						
							| 
									
										
										
										
											2011-01-10 14:28:40 +02:00
										 |  |  |         (n->status & VIRTIO_NET_S_LINK_UP) && n->vdev.vm_running; | 
					
						
							| 
									
										
										
										
											2010-11-22 19:52:30 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) | 
					
						
							| 
									
										
										
										
											2010-09-27 18:41:30 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     if (!n->nic->nc.peer) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (n->nic->nc.peer->info->type != NET_CLIENT_TYPE_TAP) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!tap_get_vhost_net(n->nic->nc.peer)) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2011-02-09 18:45:09 +02:00
										 |  |  |     if (!!n->vhost_started == virtio_net_started(n, status) && | 
					
						
							|  |  |  |                               !n->nic->nc.peer->link_down) { | 
					
						
							| 
									
										
										
										
											2010-09-27 18:41:30 +02:00
										 |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (!n->vhost_started) { | 
					
						
							| 
									
										
										
										
											2011-02-01 22:13:42 +02:00
										 |  |  |         int r; | 
					
						
							|  |  |  |         if (!vhost_net_query(tap_get_vhost_net(n->nic->nc.peer), &n->vdev)) { | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         r = vhost_net_start(tap_get_vhost_net(n->nic->nc.peer), &n->vdev); | 
					
						
							| 
									
										
										
										
											2010-09-27 18:41:30 +02:00
										 |  |  |         if (r < 0) { | 
					
						
							| 
									
										
										
										
											2010-11-15 20:44:37 +00:00
										 |  |  |             error_report("unable to start vhost net: %d: " | 
					
						
							|  |  |  |                          "falling back on userspace virtio", -r); | 
					
						
							| 
									
										
										
										
											2010-09-27 18:41:30 +02:00
										 |  |  |         } else { | 
					
						
							|  |  |  |             n->vhost_started = 1; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         vhost_net_stop(tap_get_vhost_net(n->nic->nc.peer), &n->vdev); | 
					
						
							|  |  |  |         n->vhost_started = 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-11-22 19:52:30 +02:00
										 |  |  | static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     VirtIONet *n = to_virtio_net(vdev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     virtio_net_vhost_status(n, status); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!n->tx_waiting) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (virtio_net_started(n, status) && !n->vhost_started) { | 
					
						
							|  |  |  |         if (n->tx_timer) { | 
					
						
							|  |  |  |             qemu_mod_timer(n->tx_timer, | 
					
						
							| 
									
										
										
										
											2011-03-11 16:47:48 +01:00
										 |  |  |                            qemu_get_clock_ns(vm_clock) + n->tx_timeout); | 
					
						
							| 
									
										
										
										
											2010-11-22 19:52:30 +02:00
										 |  |  |         } else { | 
					
						
							|  |  |  |             qemu_bh_schedule(n->tx_bh); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         if (n->tx_timer) { | 
					
						
							|  |  |  |             qemu_del_timer(n->tx_timer); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             qemu_bh_cancel(n->tx_bh); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-25 18:49:11 +00:00
										 |  |  | static void virtio_net_set_link_status(VLANClientState *nc) | 
					
						
							| 
									
										
										
										
											2009-01-08 19:46:33 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-11-25 18:49:11 +00:00
										 |  |  |     VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; | 
					
						
							| 
									
										
										
										
											2009-01-08 19:46:33 +00:00
										 |  |  |     uint16_t old_status = n->status; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-25 18:49:11 +00:00
										 |  |  |     if (nc->link_down) | 
					
						
							| 
									
										
										
										
											2009-01-08 19:46:33 +00:00
										 |  |  |         n->status &= ~VIRTIO_NET_S_LINK_UP; | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |         n->status |= VIRTIO_NET_S_LINK_UP; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (n->status != old_status) | 
					
						
							|  |  |  |         virtio_notify_config(&n->vdev); | 
					
						
							| 
									
										
										
										
											2010-09-27 18:41:30 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     virtio_net_set_status(&n->vdev, n->vdev.status); | 
					
						
							| 
									
										
										
										
											2009-01-08 19:46:33 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:20 +00:00
										 |  |  | static void virtio_net_reset(VirtIODevice *vdev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     VirtIONet *n = to_virtio_net(vdev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Reset back to compatibility mode */ | 
					
						
							|  |  |  |     n->promisc = 1; | 
					
						
							|  |  |  |     n->allmulti = 0; | 
					
						
							| 
									
										
										
										
											2009-06-05 14:47:18 -06:00
										 |  |  |     n->alluni = 0; | 
					
						
							|  |  |  |     n->nomulti = 0; | 
					
						
							|  |  |  |     n->nouni = 0; | 
					
						
							|  |  |  |     n->nobcast = 0; | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:28 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:32 +00:00
										 |  |  |     /* Flush any MAC and VLAN filter table state */ | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:28 +00:00
										 |  |  |     n->mac_table.in_use = 0; | 
					
						
							| 
									
										
										
										
											2009-06-05 14:47:13 -06:00
										 |  |  |     n->mac_table.first_multi = 0; | 
					
						
							| 
									
										
										
										
											2009-06-05 14:47:08 -06:00
										 |  |  |     n->mac_table.multi_overflow = 0; | 
					
						
							|  |  |  |     n->mac_table.uni_overflow = 0; | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:28 +00:00
										 |  |  |     memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN); | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:32 +00:00
										 |  |  |     memset(n->vlans, 0, MAX_VLAN >> 3); | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:20 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-22 17:43:45 +01:00
										 |  |  | static int peer_has_vnet_hdr(VirtIONet *n) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2009-11-25 18:49:11 +00:00
										 |  |  |     if (!n->nic->nc.peer) | 
					
						
							| 
									
										
										
										
											2009-10-22 17:43:45 +01:00
										 |  |  |         return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-25 18:49:30 +00:00
										 |  |  |     if (n->nic->nc.peer->info->type != NET_CLIENT_TYPE_TAP) | 
					
						
							| 
									
										
										
										
											2009-10-22 17:43:45 +01:00
										 |  |  |         return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-25 18:49:11 +00:00
										 |  |  |     n->has_vnet_hdr = tap_has_vnet_hdr(n->nic->nc.peer); | 
					
						
							| 
									
										
										
										
											2009-10-22 17:43:45 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return n->has_vnet_hdr; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-22 17:43:50 +01:00
										 |  |  | static int peer_has_ufo(VirtIONet *n) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!peer_has_vnet_hdr(n)) | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-25 18:49:11 +00:00
										 |  |  |     n->has_ufo = tap_has_ufo(n->nic->nc.peer); | 
					
						
							| 
									
										
										
										
											2009-10-22 17:43:50 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return n->has_ufo; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-10 13:52:53 +02:00
										 |  |  | static uint32_t virtio_net_get_features(VirtIODevice *vdev, uint32_t features) | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-10-22 17:43:45 +01:00
										 |  |  |     VirtIONet *n = to_virtio_net(vdev); | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-12 20:50:17 +02:00
										 |  |  |     features |= (1 << VIRTIO_NET_F_MAC); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-22 17:43:45 +01:00
										 |  |  |     if (peer_has_vnet_hdr(n)) { | 
					
						
							| 
									
										
										
										
											2009-11-25 18:49:11 +00:00
										 |  |  |         tap_using_vnet_hdr(n->nic->nc.peer, 1); | 
					
						
							| 
									
										
										
										
											2010-01-10 13:52:53 +02:00
										 |  |  |     } else { | 
					
						
							|  |  |  |         features &= ~(0x1 << VIRTIO_NET_F_CSUM); | 
					
						
							|  |  |  |         features &= ~(0x1 << VIRTIO_NET_F_HOST_TSO4); | 
					
						
							|  |  |  |         features &= ~(0x1 << VIRTIO_NET_F_HOST_TSO6); | 
					
						
							|  |  |  |         features &= ~(0x1 << VIRTIO_NET_F_HOST_ECN); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         features &= ~(0x1 << VIRTIO_NET_F_GUEST_CSUM); | 
					
						
							|  |  |  |         features &= ~(0x1 << VIRTIO_NET_F_GUEST_TSO4); | 
					
						
							|  |  |  |         features &= ~(0x1 << VIRTIO_NET_F_GUEST_TSO6); | 
					
						
							|  |  |  |         features &= ~(0x1 << VIRTIO_NET_F_GUEST_ECN); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2009-10-22 17:43:45 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-10 13:52:53 +02:00
										 |  |  |     if (!peer_has_vnet_hdr(n) || !peer_has_ufo(n)) { | 
					
						
							|  |  |  |         features &= ~(0x1 << VIRTIO_NET_F_GUEST_UFO); | 
					
						
							|  |  |  |         features &= ~(0x1 << VIRTIO_NET_F_HOST_UFO); | 
					
						
							| 
									
										
										
										
											2009-10-22 17:43:45 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-03-17 13:08:42 +02:00
										 |  |  |     if (!n->nic->nc.peer || | 
					
						
							|  |  |  |         n->nic->nc.peer->info->type != NET_CLIENT_TYPE_TAP) { | 
					
						
							|  |  |  |         return features; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (!tap_get_vhost_net(n->nic->nc.peer)) { | 
					
						
							|  |  |  |         return features; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return vhost_net_get_features(tap_get_vhost_net(n->nic->nc.peer), features); | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-05 17:40:08 +00:00
										 |  |  | static uint32_t virtio_net_bad_features(VirtIODevice *vdev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     uint32_t features = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Linux kernel 2.6.25.  It understood MAC (as everyone must),
 | 
					
						
							|  |  |  |      * but also these: */ | 
					
						
							|  |  |  |     features |= (1 << VIRTIO_NET_F_MAC); | 
					
						
							| 
									
										
										
										
											2009-10-29 10:34:15 -05:00
										 |  |  |     features |= (1 << VIRTIO_NET_F_CSUM); | 
					
						
							|  |  |  |     features |= (1 << VIRTIO_NET_F_HOST_TSO4); | 
					
						
							|  |  |  |     features |= (1 << VIRTIO_NET_F_HOST_TSO6); | 
					
						
							|  |  |  |     features |= (1 << VIRTIO_NET_F_HOST_ECN); | 
					
						
							| 
									
										
										
										
											2009-04-05 17:40:08 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-10 13:52:53 +02:00
										 |  |  |     return features; | 
					
						
							| 
									
										
										
										
											2009-04-05 17:40:08 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     VirtIONet *n = to_virtio_net(vdev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     n->mergeable_rx_bufs = !!(features & (1 << VIRTIO_NET_F_MRG_RXBUF)); | 
					
						
							| 
									
										
										
										
											2009-10-22 17:43:47 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (n->has_vnet_hdr) { | 
					
						
							| 
									
										
										
										
											2009-11-25 18:49:11 +00:00
										 |  |  |         tap_set_offload(n->nic->nc.peer, | 
					
						
							| 
									
										
										
										
											2009-10-22 17:43:47 +01:00
										 |  |  |                         (features >> VIRTIO_NET_F_GUEST_CSUM) & 1, | 
					
						
							|  |  |  |                         (features >> VIRTIO_NET_F_GUEST_TSO4) & 1, | 
					
						
							|  |  |  |                         (features >> VIRTIO_NET_F_GUEST_TSO6) & 1, | 
					
						
							| 
									
										
										
										
											2009-10-22 17:43:49 +01:00
										 |  |  |                         (features >> VIRTIO_NET_F_GUEST_ECN)  & 1, | 
					
						
							|  |  |  |                         (features >> VIRTIO_NET_F_GUEST_UFO)  & 1); | 
					
						
							| 
									
										
										
										
											2009-10-22 17:43:47 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2010-03-31 21:20:31 +03:00
										 |  |  |     if (!n->nic->nc.peer || | 
					
						
							|  |  |  |         n->nic->nc.peer->info->type != NET_CLIENT_TYPE_TAP) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (!tap_get_vhost_net(n->nic->nc.peer)) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2010-05-09 14:35:43 +03:00
										 |  |  |     vhost_net_ack_features(tap_get_vhost_net(n->nic->nc.peer), features); | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:20 +00:00
										 |  |  | static int virtio_net_handle_rx_mode(VirtIONet *n, uint8_t cmd, | 
					
						
							|  |  |  |                                      VirtQueueElement *elem) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     uint8_t on; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (elem->out_num != 2 || elem->out_sg[1].iov_len != sizeof(on)) { | 
					
						
							| 
									
										
										
										
											2010-11-15 20:44:37 +00:00
										 |  |  |         error_report("virtio-net ctrl invalid rx mode command"); | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:20 +00:00
										 |  |  |         exit(1); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     on = ldub_p(elem->out_sg[1].iov_base); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (cmd == VIRTIO_NET_CTRL_RX_MODE_PROMISC) | 
					
						
							|  |  |  |         n->promisc = on; | 
					
						
							|  |  |  |     else if (cmd == VIRTIO_NET_CTRL_RX_MODE_ALLMULTI) | 
					
						
							|  |  |  |         n->allmulti = on; | 
					
						
							| 
									
										
										
										
											2009-06-05 14:47:18 -06:00
										 |  |  |     else if (cmd == VIRTIO_NET_CTRL_RX_MODE_ALLUNI) | 
					
						
							|  |  |  |         n->alluni = on; | 
					
						
							|  |  |  |     else if (cmd == VIRTIO_NET_CTRL_RX_MODE_NOMULTI) | 
					
						
							|  |  |  |         n->nomulti = on; | 
					
						
							|  |  |  |     else if (cmd == VIRTIO_NET_CTRL_RX_MODE_NOUNI) | 
					
						
							|  |  |  |         n->nouni = on; | 
					
						
							|  |  |  |     else if (cmd == VIRTIO_NET_CTRL_RX_MODE_NOBCAST) | 
					
						
							|  |  |  |         n->nobcast = on; | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:20 +00:00
										 |  |  |     else | 
					
						
							|  |  |  |         return VIRTIO_NET_ERR; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return VIRTIO_NET_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:28 +00:00
										 |  |  | static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd, | 
					
						
							|  |  |  |                                  VirtQueueElement *elem) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     struct virtio_net_ctrl_mac mac_data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (cmd != VIRTIO_NET_CTRL_MAC_TABLE_SET || elem->out_num != 3 || | 
					
						
							|  |  |  |         elem->out_sg[1].iov_len < sizeof(mac_data) || | 
					
						
							|  |  |  |         elem->out_sg[2].iov_len < sizeof(mac_data)) | 
					
						
							|  |  |  |         return VIRTIO_NET_ERR; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     n->mac_table.in_use = 0; | 
					
						
							| 
									
										
										
										
											2009-06-05 14:47:13 -06:00
										 |  |  |     n->mac_table.first_multi = 0; | 
					
						
							| 
									
										
										
										
											2009-06-05 14:47:08 -06:00
										 |  |  |     n->mac_table.uni_overflow = 0; | 
					
						
							|  |  |  |     n->mac_table.multi_overflow = 0; | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:28 +00:00
										 |  |  |     memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-01-25 11:55:14 +01:00
										 |  |  |     mac_data.entries = ldl_p(elem->out_sg[1].iov_base); | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:28 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (sizeof(mac_data.entries) + | 
					
						
							|  |  |  |         (mac_data.entries * ETH_ALEN) > elem->out_sg[1].iov_len) | 
					
						
							|  |  |  |         return VIRTIO_NET_ERR; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (mac_data.entries <= MAC_TABLE_ENTRIES) { | 
					
						
							|  |  |  |         memcpy(n->mac_table.macs, elem->out_sg[1].iov_base + sizeof(mac_data), | 
					
						
							|  |  |  |                mac_data.entries * ETH_ALEN); | 
					
						
							|  |  |  |         n->mac_table.in_use += mac_data.entries; | 
					
						
							|  |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2009-06-05 14:47:08 -06:00
										 |  |  |         n->mac_table.uni_overflow = 1; | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:28 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-06-05 14:47:13 -06:00
										 |  |  |     n->mac_table.first_multi = n->mac_table.in_use; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-01-25 11:55:14 +01:00
										 |  |  |     mac_data.entries = ldl_p(elem->out_sg[2].iov_base); | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:28 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (sizeof(mac_data.entries) + | 
					
						
							|  |  |  |         (mac_data.entries * ETH_ALEN) > elem->out_sg[2].iov_len) | 
					
						
							|  |  |  |         return VIRTIO_NET_ERR; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (mac_data.entries) { | 
					
						
							|  |  |  |         if (n->mac_table.in_use + mac_data.entries <= MAC_TABLE_ENTRIES) { | 
					
						
							|  |  |  |             memcpy(n->mac_table.macs + (n->mac_table.in_use * ETH_ALEN), | 
					
						
							|  |  |  |                    elem->out_sg[2].iov_base + sizeof(mac_data), | 
					
						
							|  |  |  |                    mac_data.entries * ETH_ALEN); | 
					
						
							|  |  |  |             n->mac_table.in_use += mac_data.entries; | 
					
						
							| 
									
										
										
										
											2009-06-05 14:47:08 -06:00
										 |  |  |         } else { | 
					
						
							|  |  |  |             n->mac_table.multi_overflow = 1; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:28 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return VIRTIO_NET_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:32 +00:00
										 |  |  | static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd, | 
					
						
							|  |  |  |                                         VirtQueueElement *elem) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     uint16_t vid; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (elem->out_num != 2 || elem->out_sg[1].iov_len != sizeof(vid)) { | 
					
						
							| 
									
										
										
										
											2010-11-15 20:44:37 +00:00
										 |  |  |         error_report("virtio-net ctrl invalid vlan command"); | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:32 +00:00
										 |  |  |         return VIRTIO_NET_ERR; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-01-25 11:55:14 +01:00
										 |  |  |     vid = lduw_p(elem->out_sg[1].iov_base); | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:32 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (vid >= MAX_VLAN) | 
					
						
							|  |  |  |         return VIRTIO_NET_ERR; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (cmd == VIRTIO_NET_CTRL_VLAN_ADD) | 
					
						
							|  |  |  |         n->vlans[vid >> 5] |= (1U << (vid & 0x1f)); | 
					
						
							|  |  |  |     else if (cmd == VIRTIO_NET_CTRL_VLAN_DEL) | 
					
						
							|  |  |  |         n->vlans[vid >> 5] &= ~(1U << (vid & 0x1f)); | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |         return VIRTIO_NET_ERR; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return VIRTIO_NET_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:16 +00:00
										 |  |  | static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:20 +00:00
										 |  |  |     VirtIONet *n = to_virtio_net(vdev); | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:16 +00:00
										 |  |  |     struct virtio_net_ctrl_hdr ctrl; | 
					
						
							|  |  |  |     virtio_net_ctrl_ack status = VIRTIO_NET_ERR; | 
					
						
							|  |  |  |     VirtQueueElement elem; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     while (virtqueue_pop(vq, &elem)) { | 
					
						
							|  |  |  |         if ((elem.in_num < 1) || (elem.out_num < 1)) { | 
					
						
							| 
									
										
										
										
											2010-11-15 20:44:37 +00:00
										 |  |  |             error_report("virtio-net ctrl missing headers"); | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:16 +00:00
										 |  |  |             exit(1); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (elem.out_sg[0].iov_len < sizeof(ctrl) || | 
					
						
							| 
									
										
										
										
											2009-03-13 15:04:02 +00:00
										 |  |  |             elem.in_sg[elem.in_num - 1].iov_len < sizeof(status)) { | 
					
						
							| 
									
										
										
										
											2010-11-15 20:44:37 +00:00
										 |  |  |             error_report("virtio-net ctrl header not in correct element"); | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:16 +00:00
										 |  |  |             exit(1); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         ctrl.class = ldub_p(elem.out_sg[0].iov_base); | 
					
						
							|  |  |  |         ctrl.cmd = ldub_p(elem.out_sg[0].iov_base + sizeof(ctrl.class)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:20 +00:00
										 |  |  |         if (ctrl.class == VIRTIO_NET_CTRL_RX_MODE) | 
					
						
							|  |  |  |             status = virtio_net_handle_rx_mode(n, ctrl.cmd, &elem); | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:28 +00:00
										 |  |  |         else if (ctrl.class == VIRTIO_NET_CTRL_MAC) | 
					
						
							|  |  |  |             status = virtio_net_handle_mac(n, ctrl.cmd, &elem); | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:32 +00:00
										 |  |  |         else if (ctrl.class == VIRTIO_NET_CTRL_VLAN) | 
					
						
							|  |  |  |             status = virtio_net_handle_vlan_table(n, ctrl.cmd, &elem); | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:20 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:16 +00:00
										 |  |  |         stb_p(elem.in_sg[elem.in_num - 1].iov_base, status); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         virtqueue_push(vq, &elem, sizeof(status)); | 
					
						
							|  |  |  |         virtio_notify(vdev, vq); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | /* RX */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2009-04-29 13:40:02 +01:00
										 |  |  |     VirtIONet *n = to_virtio_net(vdev); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-25 18:49:11 +00:00
										 |  |  |     qemu_flush_queued_packets(&n->nic->nc); | 
					
						
							| 
									
										
										
										
											2009-07-20 13:07:41 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /* We now have RX buffers, signal to the IO thread to break out of the
 | 
					
						
							|  |  |  |      * select to re-poll the tap file descriptor */ | 
					
						
							|  |  |  |     qemu_notify_event(); | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-25 18:49:11 +00:00
										 |  |  | static int virtio_net_can_receive(VLANClientState *nc) | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-11-25 18:49:11 +00:00
										 |  |  |     VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; | 
					
						
							| 
									
										
										
										
											2011-01-10 14:28:40 +02:00
										 |  |  |     if (!n->vdev.vm_running) { | 
					
						
							| 
									
										
										
										
											2010-11-22 19:52:19 +02:00
										 |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2009-10-27 18:16:38 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  |     if (!virtio_queue_ready(n->rx_vq) || | 
					
						
							|  |  |  |         !(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-27 18:16:38 +00:00
										 |  |  |     return 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int virtio_net_has_buffers(VirtIONet *n, int bufsize) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  |     if (virtio_queue_empty(n->rx_vq) || | 
					
						
							|  |  |  |         (n->mergeable_rx_bufs && | 
					
						
							|  |  |  |          !virtqueue_avail_bytes(n->rx_vq, bufsize, 0))) { | 
					
						
							|  |  |  |         virtio_queue_set_notification(n->rx_vq, 1); | 
					
						
							| 
									
										
										
										
											2010-02-08 10:10:01 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |         /* To avoid a race condition where the guest has made some buffers
 | 
					
						
							|  |  |  |          * available after the above check but before notification was | 
					
						
							|  |  |  |          * enabled, check for available buffers again. | 
					
						
							|  |  |  |          */ | 
					
						
							|  |  |  |         if (virtio_queue_empty(n->rx_vq) || | 
					
						
							|  |  |  |             (n->mergeable_rx_bufs && | 
					
						
							|  |  |  |              !virtqueue_avail_bytes(n->rx_vq, bufsize, 0))) | 
					
						
							|  |  |  |             return 0; | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     virtio_queue_set_notification(n->rx_vq, 0); | 
					
						
							|  |  |  |     return 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-22 17:43:48 +01:00
										 |  |  | /* dhclient uses AF_PACKET but doesn't pass auxdata to the kernel so
 | 
					
						
							|  |  |  |  * it never finds out that the packets don't have valid checksums.  This | 
					
						
							|  |  |  |  * causes dhclient to get upset.  Fedora's carried a patch for ages to | 
					
						
							|  |  |  |  * fix this with Xen but it hasn't appeared in an upstream release of | 
					
						
							|  |  |  |  * dhclient yet. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * To avoid breaking existing guests, we catch udp packets and add | 
					
						
							|  |  |  |  * checksums.  This is terrible but it's better than hacking the guest | 
					
						
							|  |  |  |  * kernels. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * N.B. if we introduce a zero-copy API, this operation is no longer free so | 
					
						
							|  |  |  |  * we should provide a mechanism to disable it to avoid polluting the host | 
					
						
							|  |  |  |  * cache. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void work_around_broken_dhclient(struct virtio_net_hdr *hdr, | 
					
						
							|  |  |  |                                         const uint8_t *buf, size_t size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if ((hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) && /* missing csum */ | 
					
						
							|  |  |  |         (size > 27 && size < 1500) && /* normal sized MTU */ | 
					
						
							|  |  |  |         (buf[12] == 0x08 && buf[13] == 0x00) && /* ethertype == IPv4 */ | 
					
						
							|  |  |  |         (buf[23] == 17) && /* ip.protocol == UDP */ | 
					
						
							|  |  |  |         (buf[34] == 0 && buf[35] == 67)) { /* udp.srcport == bootps */ | 
					
						
							|  |  |  |         /* FIXME this cast is evil */ | 
					
						
							|  |  |  |         net_checksum_calculate((uint8_t *)buf, size); | 
					
						
							|  |  |  |         hdr->flags &= ~VIRTIO_NET_HDR_F_NEEDS_CSUM; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | static int receive_header(VirtIONet *n, struct iovec *iov, int iovcnt, | 
					
						
							| 
									
										
										
										
											2008-12-17 19:45:40 +00:00
										 |  |  |                           const void *buf, size_t size, size_t hdr_len) | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-04-13 16:31:01 +00:00
										 |  |  |     struct virtio_net_hdr *hdr = (struct virtio_net_hdr *)iov[0].iov_base; | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  |     int offset = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     hdr->flags = 0; | 
					
						
							|  |  |  |     hdr->gso_type = VIRTIO_NET_HDR_GSO_NONE; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-22 17:43:45 +01:00
										 |  |  |     if (n->has_vnet_hdr) { | 
					
						
							|  |  |  |         memcpy(hdr, buf, sizeof(*hdr)); | 
					
						
							|  |  |  |         offset = sizeof(*hdr); | 
					
						
							| 
									
										
										
										
											2009-10-22 17:43:48 +01:00
										 |  |  |         work_around_broken_dhclient(hdr, buf + offset, size - offset); | 
					
						
							| 
									
										
										
										
											2009-10-22 17:43:45 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  |     /* We only ever receive a struct virtio_net_hdr from the tapfd,
 | 
					
						
							|  |  |  |      * but we may be passing along a larger header to the guest. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     iov[0].iov_base += hdr_len; | 
					
						
							|  |  |  |     iov[0].iov_len  -= hdr_len; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return offset; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:24 +00:00
										 |  |  | static int receive_filter(VirtIONet *n, const uint8_t *buf, int size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:32 +00:00
										 |  |  |     static const uint8_t vlan[] = {0x81, 0x00}; | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:24 +00:00
										 |  |  |     uint8_t *ptr = (uint8_t *)buf; | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:28 +00:00
										 |  |  |     int i; | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:24 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (n->promisc) | 
					
						
							|  |  |  |         return 1; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-22 17:43:45 +01:00
										 |  |  |     if (n->has_vnet_hdr) { | 
					
						
							|  |  |  |         ptr += sizeof(struct virtio_net_hdr); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:32 +00:00
										 |  |  |     if (!memcmp(&ptr[12], vlan, sizeof(vlan))) { | 
					
						
							|  |  |  |         int vid = be16_to_cpup((uint16_t *)(ptr + 14)) & 0xfff; | 
					
						
							|  |  |  |         if (!(n->vlans[vid >> 5] & (1U << (vid & 0x1f)))) | 
					
						
							|  |  |  |             return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-06-05 14:47:02 -06:00
										 |  |  |     if (ptr[0] & 1) { // multicast
 | 
					
						
							|  |  |  |         if (!memcmp(ptr, bcast, sizeof(bcast))) { | 
					
						
							| 
									
										
										
										
											2009-06-05 14:47:18 -06:00
										 |  |  |             return !n->nobcast; | 
					
						
							|  |  |  |         } else if (n->nomulti) { | 
					
						
							|  |  |  |             return 0; | 
					
						
							| 
									
										
										
										
											2009-06-05 14:47:08 -06:00
										 |  |  |         } else if (n->allmulti || n->mac_table.multi_overflow) { | 
					
						
							| 
									
										
										
										
											2009-06-05 14:47:02 -06:00
										 |  |  |             return 1; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2009-06-05 14:47:13 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |         for (i = n->mac_table.first_multi; i < n->mac_table.in_use; i++) { | 
					
						
							|  |  |  |             if (!memcmp(ptr, &n->mac_table.macs[i * ETH_ALEN], ETH_ALEN)) { | 
					
						
							|  |  |  |                 return 1; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2009-06-05 14:47:02 -06:00
										 |  |  |     } else { // unicast
 | 
					
						
							| 
									
										
										
										
											2009-06-05 14:47:18 -06:00
										 |  |  |         if (n->nouni) { | 
					
						
							|  |  |  |             return 0; | 
					
						
							|  |  |  |         } else if (n->alluni || n->mac_table.uni_overflow) { | 
					
						
							| 
									
										
										
										
											2009-06-05 14:47:08 -06:00
										 |  |  |             return 1; | 
					
						
							|  |  |  |         } else if (!memcmp(ptr, n->mac, ETH_ALEN)) { | 
					
						
							| 
									
										
										
										
											2009-06-05 14:47:02 -06:00
										 |  |  |             return 1; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:24 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-06-05 14:47:13 -06:00
										 |  |  |         for (i = 0; i < n->mac_table.first_multi; i++) { | 
					
						
							|  |  |  |             if (!memcmp(ptr, &n->mac_table.macs[i * ETH_ALEN], ETH_ALEN)) { | 
					
						
							|  |  |  |                 return 1; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:28 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:24 +00:00
										 |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-25 18:49:11 +00:00
										 |  |  | static ssize_t virtio_net_receive(VLANClientState *nc, const uint8_t *buf, size_t size) | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-11-25 18:49:11 +00:00
										 |  |  |     VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  |     struct virtio_net_hdr_mrg_rxbuf *mhdr = NULL; | 
					
						
							| 
									
										
										
										
											2010-06-22 16:22:49 +03:00
										 |  |  |     size_t guest_hdr_len, offset, i, host_hdr_len; | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-25 18:49:11 +00:00
										 |  |  |     if (!virtio_net_can_receive(&n->nic->nc)) | 
					
						
							| 
									
										
										
										
											2009-10-27 18:16:38 +00:00
										 |  |  |         return -1; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-06-06 18:53:10 +03:00
										 |  |  |     /* hdr_len refers to the header we supply to the guest */ | 
					
						
							| 
									
										
										
										
											2010-06-22 16:22:49 +03:00
										 |  |  |     guest_hdr_len = n->mergeable_rx_bufs ? | 
					
						
							| 
									
										
										
										
											2010-06-06 18:53:10 +03:00
										 |  |  |         sizeof(struct virtio_net_hdr_mrg_rxbuf) : sizeof(struct virtio_net_hdr); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-06-22 16:22:49 +03:00
										 |  |  |     host_hdr_len = n->has_vnet_hdr ? sizeof(struct virtio_net_hdr) : 0; | 
					
						
							|  |  |  |     if (!virtio_net_has_buffers(n, size + guest_hdr_len - host_hdr_len)) | 
					
						
							| 
									
										
										
										
											2009-04-29 13:40:02 +01:00
										 |  |  |         return 0; | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:24 +00:00
										 |  |  |     if (!receive_filter(n, buf, size)) | 
					
						
							| 
									
										
										
										
											2009-05-18 13:40:55 +01:00
										 |  |  |         return size; | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:24 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  |     offset = i = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     while (offset < size) { | 
					
						
							|  |  |  |         VirtQueueElement elem; | 
					
						
							|  |  |  |         int len, total; | 
					
						
							|  |  |  |         struct iovec sg[VIRTQUEUE_MAX_SIZE]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-13 16:24:43 +05:30
										 |  |  |         total = 0; | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-06-22 16:22:49 +03:00
										 |  |  |         if (virtqueue_pop(n->rx_vq, &elem) == 0) { | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  |             if (i == 0) | 
					
						
							| 
									
										
										
										
											2009-05-18 13:40:55 +01:00
										 |  |  |                 return -1; | 
					
						
							| 
									
										
										
										
											2010-11-15 20:44:37 +00:00
										 |  |  |             error_report("virtio-net unexpected empty queue: " | 
					
						
							| 
									
										
										
										
											2010-06-22 16:22:49 +03:00
										 |  |  |                     "i %zd mergeable %d offset %zd, size %zd, " | 
					
						
							| 
									
										
										
										
											2010-11-15 20:44:37 +00:00
										 |  |  |                     "guest hdr len %zd, host hdr len %zd guest features 0x%x", | 
					
						
							| 
									
										
										
										
											2010-06-22 16:22:49 +03:00
										 |  |  |                     i, n->mergeable_rx_bufs, offset, size, | 
					
						
							|  |  |  |                     guest_hdr_len, host_hdr_len, n->vdev.guest_features); | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  |             exit(1); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (elem.in_num < 1) { | 
					
						
							| 
									
										
										
										
											2010-11-15 20:44:37 +00:00
										 |  |  |             error_report("virtio-net receive queue contains no in buffers"); | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  |             exit(1); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-06-22 16:22:49 +03:00
										 |  |  |         if (!n->mergeable_rx_bufs && elem.in_sg[0].iov_len != guest_hdr_len) { | 
					
						
							| 
									
										
										
										
											2010-11-15 20:44:37 +00:00
										 |  |  |             error_report("virtio-net header not in first element"); | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  |             exit(1); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         memcpy(&sg, &elem.in_sg[0], sizeof(sg[0]) * elem.in_num); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (i == 0) { | 
					
						
							|  |  |  |             if (n->mergeable_rx_bufs) | 
					
						
							|  |  |  |                 mhdr = (struct virtio_net_hdr_mrg_rxbuf *)sg[0].iov_base; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             offset += receive_header(n, sg, elem.in_num, | 
					
						
							| 
									
										
										
										
											2010-06-22 16:22:49 +03:00
										 |  |  |                                      buf + offset, size - offset, guest_hdr_len); | 
					
						
							|  |  |  |             total += guest_hdr_len; | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* copy in packet.  ugh */ | 
					
						
							| 
									
										
										
										
											2010-04-27 18:04:05 +05:30
										 |  |  |         len = iov_from_buf(sg, elem.in_num, | 
					
						
							| 
									
										
										
										
											2011-07-11 15:02:23 +02:00
										 |  |  |                            buf + offset, 0, size - offset); | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  |         total += len; | 
					
						
							| 
									
										
										
										
											2010-06-22 16:22:49 +03:00
										 |  |  |         offset += len; | 
					
						
							|  |  |  |         /* If buffers can't be merged, at this point we
 | 
					
						
							|  |  |  |          * must have consumed the complete packet. | 
					
						
							|  |  |  |          * Otherwise, drop it. */ | 
					
						
							|  |  |  |         if (!n->mergeable_rx_bufs && offset < size) { | 
					
						
							|  |  |  | #if 0
 | 
					
						
							| 
									
										
										
										
											2010-11-15 20:44:37 +00:00
										 |  |  |             error_report("virtio-net truncated non-mergeable packet: " | 
					
						
							|  |  |  |                          "i %zd mergeable %d offset %zd, size %zd, " | 
					
						
							|  |  |  |                          "guest hdr len %zd, host hdr len %zd", | 
					
						
							|  |  |  |                          i, n->mergeable_rx_bufs, | 
					
						
							|  |  |  |                          offset, size, guest_hdr_len, host_hdr_len); | 
					
						
							| 
									
										
										
										
											2010-06-22 16:22:49 +03:00
										 |  |  | #endif
 | 
					
						
							|  |  |  |             return size; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         /* signal other side */ | 
					
						
							|  |  |  |         virtqueue_fill(n->rx_vq, &elem, total, i++); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-01-25 11:55:14 +01:00
										 |  |  |     if (mhdr) { | 
					
						
							| 
									
										
										
										
											2011-03-03 21:42:28 +00:00
										 |  |  |         stw_p(&mhdr->num_buffers, i); | 
					
						
							| 
									
										
										
										
											2011-01-25 11:55:14 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     virtqueue_flush(n->rx_vq, i); | 
					
						
							|  |  |  |     virtio_notify(&n->vdev, n->rx_vq); | 
					
						
							| 
									
										
										
										
											2009-05-18 13:40:55 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return size; | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-09-02 09:00:57 -06:00
										 |  |  | static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq); | 
					
						
							| 
									
										
										
										
											2009-06-18 18:21:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-25 18:49:11 +00:00
										 |  |  | static void virtio_net_tx_complete(VLANClientState *nc, ssize_t len) | 
					
						
							| 
									
										
										
										
											2009-06-18 18:21:36 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-11-25 18:49:11 +00:00
										 |  |  |     VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; | 
					
						
							| 
									
										
										
										
											2009-06-18 18:21:36 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     virtqueue_push(n->tx_vq, &n->async_tx.elem, n->async_tx.len); | 
					
						
							|  |  |  |     virtio_notify(&n->vdev, n->tx_vq); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     n->async_tx.elem.out_num = n->async_tx.len = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     virtio_queue_set_notification(n->tx_vq, 1); | 
					
						
							|  |  |  |     virtio_net_flush_tx(n, n->tx_vq); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | /* TX */ | 
					
						
							| 
									
										
										
										
											2010-09-02 09:00:57 -06:00
										 |  |  | static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq) | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | { | 
					
						
							|  |  |  |     VirtQueueElement elem; | 
					
						
							| 
									
										
										
										
											2010-09-02 09:00:57 -06:00
										 |  |  |     int32_t num_packets = 0; | 
					
						
							|  |  |  |     if (!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) { | 
					
						
							|  |  |  |         return num_packets; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-01-10 14:28:40 +02:00
										 |  |  |     assert(n->vdev.vm_running); | 
					
						
							| 
									
										
										
										
											2010-11-22 19:52:30 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-06-18 18:21:36 +01:00
										 |  |  |     if (n->async_tx.elem.out_num) { | 
					
						
							|  |  |  |         virtio_queue_set_notification(n->tx_vq, 0); | 
					
						
							| 
									
										
										
										
											2010-09-02 09:00:57 -06:00
										 |  |  |         return num_packets; | 
					
						
							| 
									
										
										
										
											2009-06-18 18:21:36 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  |     while (virtqueue_pop(vq, &elem)) { | 
					
						
							| 
									
										
										
										
											2009-06-18 18:21:36 +01:00
										 |  |  |         ssize_t ret, len = 0; | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  |         unsigned int out_num = elem.out_num; | 
					
						
							|  |  |  |         struct iovec *out_sg = &elem.out_sg[0]; | 
					
						
							|  |  |  |         unsigned hdr_len; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* hdr_len refers to the header received from the guest */ | 
					
						
							|  |  |  |         hdr_len = n->mergeable_rx_bufs ? | 
					
						
							|  |  |  |             sizeof(struct virtio_net_hdr_mrg_rxbuf) : | 
					
						
							|  |  |  |             sizeof(struct virtio_net_hdr); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (out_num < 1 || out_sg->iov_len != hdr_len) { | 
					
						
							| 
									
										
										
										
											2010-11-15 20:44:37 +00:00
										 |  |  |             error_report("virtio-net header not in first element"); | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  |             exit(1); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* ignore the header if GSO is not supported */ | 
					
						
							| 
									
										
										
										
											2009-10-22 17:43:45 +01:00
										 |  |  |         if (!n->has_vnet_hdr) { | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  |             out_num--; | 
					
						
							|  |  |  |             out_sg++; | 
					
						
							|  |  |  |             len += hdr_len; | 
					
						
							|  |  |  |         } else if (n->mergeable_rx_bufs) { | 
					
						
							|  |  |  |             /* tapfd expects a struct virtio_net_hdr */ | 
					
						
							|  |  |  |             hdr_len -= sizeof(struct virtio_net_hdr); | 
					
						
							|  |  |  |             out_sg->iov_len -= hdr_len; | 
					
						
							|  |  |  |             len += hdr_len; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-25 18:49:11 +00:00
										 |  |  |         ret = qemu_sendv_packet_async(&n->nic->nc, out_sg, out_num, | 
					
						
							| 
									
										
										
										
											2009-06-18 18:21:36 +01:00
										 |  |  |                                       virtio_net_tx_complete); | 
					
						
							|  |  |  |         if (ret == 0) { | 
					
						
							|  |  |  |             virtio_queue_set_notification(n->tx_vq, 0); | 
					
						
							|  |  |  |             n->async_tx.elem = elem; | 
					
						
							|  |  |  |             n->async_tx.len  = len; | 
					
						
							| 
									
										
										
										
											2010-09-02 09:00:57 -06:00
										 |  |  |             return -EBUSY; | 
					
						
							| 
									
										
										
										
											2009-06-18 18:21:36 +01:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         len += ret; | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         virtqueue_push(vq, &elem, len); | 
					
						
							|  |  |  |         virtio_notify(&n->vdev, vq); | 
					
						
							| 
									
										
										
										
											2010-09-02 09:00:57 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (++num_packets >= n->tx_burst) { | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2010-09-02 09:00:57 -06:00
										 |  |  |     return num_packets; | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-09-02 09:01:10 -06:00
										 |  |  | static void virtio_net_handle_tx_timer(VirtIODevice *vdev, VirtQueue *vq) | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | { | 
					
						
							|  |  |  |     VirtIONet *n = to_virtio_net(vdev); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-11-22 19:52:30 +02:00
										 |  |  |     /* This happens when device was stopped but VCPU wasn't. */ | 
					
						
							| 
									
										
										
										
											2011-01-10 14:28:40 +02:00
										 |  |  |     if (!n->vdev.vm_running) { | 
					
						
							| 
									
										
										
										
											2010-11-22 19:52:30 +02:00
										 |  |  |         n->tx_waiting = 1; | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-09-02 09:01:04 -06:00
										 |  |  |     if (n->tx_waiting) { | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  |         virtio_queue_set_notification(vq, 1); | 
					
						
							|  |  |  |         qemu_del_timer(n->tx_timer); | 
					
						
							| 
									
										
										
										
											2010-09-02 09:01:04 -06:00
										 |  |  |         n->tx_waiting = 0; | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  |         virtio_net_flush_tx(n, vq); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         qemu_mod_timer(n->tx_timer, | 
					
						
							| 
									
										
										
										
											2011-03-11 16:47:48 +01:00
										 |  |  |                        qemu_get_clock_ns(vm_clock) + n->tx_timeout); | 
					
						
							| 
									
										
										
										
											2010-09-02 09:01:04 -06:00
										 |  |  |         n->tx_waiting = 1; | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  |         virtio_queue_set_notification(vq, 0); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-09-02 09:01:10 -06:00
										 |  |  | static void virtio_net_handle_tx_bh(VirtIODevice *vdev, VirtQueue *vq) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     VirtIONet *n = to_virtio_net(vdev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (unlikely(n->tx_waiting)) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2010-11-22 19:52:30 +02:00
										 |  |  |     n->tx_waiting = 1; | 
					
						
							|  |  |  |     /* This happens when device was stopped but VCPU wasn't. */ | 
					
						
							| 
									
										
										
										
											2011-01-10 14:28:40 +02:00
										 |  |  |     if (!n->vdev.vm_running) { | 
					
						
							| 
									
										
										
										
											2010-11-22 19:52:30 +02:00
										 |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2010-09-02 09:01:10 -06:00
										 |  |  |     virtio_queue_set_notification(vq, 0); | 
					
						
							|  |  |  |     qemu_bh_schedule(n->tx_bh); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | static void virtio_net_tx_timer(void *opaque) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     VirtIONet *n = opaque; | 
					
						
							| 
									
										
										
										
											2011-01-10 14:28:40 +02:00
										 |  |  |     assert(n->vdev.vm_running); | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-09-02 09:01:04 -06:00
										 |  |  |     n->tx_waiting = 0; | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /* Just in case the driver is not ready on more */ | 
					
						
							|  |  |  |     if (!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     virtio_queue_set_notification(n->tx_vq, 1); | 
					
						
							|  |  |  |     virtio_net_flush_tx(n, n->tx_vq); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-09-02 09:01:10 -06:00
										 |  |  | static void virtio_net_tx_bh(void *opaque) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     VirtIONet *n = opaque; | 
					
						
							|  |  |  |     int32_t ret; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-01-10 14:28:40 +02:00
										 |  |  |     assert(n->vdev.vm_running); | 
					
						
							| 
									
										
										
										
											2010-11-22 19:52:30 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-09-02 09:01:10 -06:00
										 |  |  |     n->tx_waiting = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Just in case the driver is not ready on more */ | 
					
						
							|  |  |  |     if (unlikely(!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK))) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ret = virtio_net_flush_tx(n, n->tx_vq); | 
					
						
							|  |  |  |     if (ret == -EBUSY) { | 
					
						
							|  |  |  |         return; /* Notification re-enable handled by tx_complete */ | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* If we flush a full burst of packets, assume there are
 | 
					
						
							|  |  |  |      * more coming and immediately reschedule */ | 
					
						
							|  |  |  |     if (ret >= n->tx_burst) { | 
					
						
							|  |  |  |         qemu_bh_schedule(n->tx_bh); | 
					
						
							|  |  |  |         n->tx_waiting = 1; | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* If less than a full burst, re-enable notification and flush
 | 
					
						
							|  |  |  |      * anything that may have come in while we weren't looking.  If | 
					
						
							|  |  |  |      * we find something, assume the guest is still active and reschedule */ | 
					
						
							|  |  |  |     virtio_queue_set_notification(n->tx_vq, 1); | 
					
						
							|  |  |  |     if (virtio_net_flush_tx(n, n->tx_vq) > 0) { | 
					
						
							|  |  |  |         virtio_queue_set_notification(n->tx_vq, 0); | 
					
						
							|  |  |  |         qemu_bh_schedule(n->tx_bh); | 
					
						
							|  |  |  |         n->tx_waiting = 1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | static void virtio_net_save(QEMUFile *f, void *opaque) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     VirtIONet *n = opaque; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-09-27 18:41:30 +02:00
										 |  |  |     /* At this point, backend must be stopped, otherwise
 | 
					
						
							|  |  |  |      * it might keep writing to memory. */ | 
					
						
							|  |  |  |     assert(!n->vhost_started); | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  |     virtio_save(&n->vdev, f); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:12 +00:00
										 |  |  |     qemu_put_buffer(f, n->mac, ETH_ALEN); | 
					
						
							| 
									
										
										
										
											2010-09-02 09:01:04 -06:00
										 |  |  |     qemu_put_be32(f, n->tx_waiting); | 
					
						
							| 
									
										
										
										
											2009-01-07 17:50:45 +00:00
										 |  |  |     qemu_put_be32(f, n->mergeable_rx_bufs); | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:04 +00:00
										 |  |  |     qemu_put_be16(f, n->status); | 
					
						
							| 
									
										
										
										
											2009-06-05 14:46:57 -06:00
										 |  |  |     qemu_put_byte(f, n->promisc); | 
					
						
							|  |  |  |     qemu_put_byte(f, n->allmulti); | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:28 +00:00
										 |  |  |     qemu_put_be32(f, n->mac_table.in_use); | 
					
						
							|  |  |  |     qemu_put_buffer(f, n->mac_table.macs, n->mac_table.in_use * ETH_ALEN); | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:32 +00:00
										 |  |  |     qemu_put_buffer(f, (uint8_t *)n->vlans, MAX_VLAN >> 3); | 
					
						
							| 
									
										
										
										
											2009-10-22 17:43:45 +01:00
										 |  |  |     qemu_put_be32(f, n->has_vnet_hdr); | 
					
						
							| 
									
										
										
										
											2009-06-05 14:47:08 -06:00
										 |  |  |     qemu_put_byte(f, n->mac_table.multi_overflow); | 
					
						
							|  |  |  |     qemu_put_byte(f, n->mac_table.uni_overflow); | 
					
						
							| 
									
										
										
										
											2009-06-05 14:47:18 -06:00
										 |  |  |     qemu_put_byte(f, n->alluni); | 
					
						
							|  |  |  |     qemu_put_byte(f, n->nomulti); | 
					
						
							|  |  |  |     qemu_put_byte(f, n->nouni); | 
					
						
							|  |  |  |     qemu_put_byte(f, n->nobcast); | 
					
						
							| 
									
										
										
										
											2009-10-22 17:43:50 +01:00
										 |  |  |     qemu_put_byte(f, n->has_ufo); | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     VirtIONet *n = opaque; | 
					
						
							| 
									
										
										
										
											2009-06-05 14:47:13 -06:00
										 |  |  |     int i; | 
					
						
							| 
									
										
										
										
											2012-05-16 12:21:35 +02:00
										 |  |  |     int ret; | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:04 +00:00
										 |  |  |     if (version_id < 2 || version_id > VIRTIO_NET_VM_VERSION) | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  |         return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-16 12:21:35 +02:00
										 |  |  |     ret = virtio_load(&n->vdev, f); | 
					
						
							|  |  |  |     if (ret) { | 
					
						
							|  |  |  |         return ret; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:12 +00:00
										 |  |  |     qemu_get_buffer(f, n->mac, ETH_ALEN); | 
					
						
							| 
									
										
										
										
											2010-09-02 09:01:04 -06:00
										 |  |  |     n->tx_waiting = qemu_get_be32(f); | 
					
						
							| 
									
										
										
										
											2009-01-07 17:50:45 +00:00
										 |  |  |     n->mergeable_rx_bufs = qemu_get_be32(f); | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:04 +00:00
										 |  |  |     if (version_id >= 3) | 
					
						
							|  |  |  |         n->status = qemu_get_be16(f); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:20 +00:00
										 |  |  |     if (version_id >= 4) { | 
					
						
							| 
									
										
										
										
											2009-06-05 14:46:57 -06:00
										 |  |  |         if (version_id < 8) { | 
					
						
							|  |  |  |             n->promisc = qemu_get_be32(f); | 
					
						
							|  |  |  |             n->allmulti = qemu_get_be32(f); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             n->promisc = qemu_get_byte(f); | 
					
						
							|  |  |  |             n->allmulti = qemu_get_byte(f); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:20 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:28 +00:00
										 |  |  |     if (version_id >= 5) { | 
					
						
							|  |  |  |         n->mac_table.in_use = qemu_get_be32(f); | 
					
						
							|  |  |  |         /* MAC_TABLE_ENTRIES may be different from the saved image */ | 
					
						
							|  |  |  |         if (n->mac_table.in_use <= MAC_TABLE_ENTRIES) { | 
					
						
							|  |  |  |             qemu_get_buffer(f, n->mac_table.macs, | 
					
						
							|  |  |  |                             n->mac_table.in_use * ETH_ALEN); | 
					
						
							|  |  |  |         } else if (n->mac_table.in_use) { | 
					
						
							|  |  |  |             qemu_fseek(f, n->mac_table.in_use * ETH_ALEN, SEEK_CUR); | 
					
						
							| 
									
										
										
										
											2009-06-05 14:47:08 -06:00
										 |  |  |             n->mac_table.multi_overflow = n->mac_table.uni_overflow = 1; | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:28 +00:00
										 |  |  |             n->mac_table.in_use = 0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:32 +00:00
										 |  |  |     if (version_id >= 6) | 
					
						
							|  |  |  |         qemu_get_buffer(f, (uint8_t *)n->vlans, MAX_VLAN >> 3); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-22 17:43:45 +01:00
										 |  |  |     if (version_id >= 7) { | 
					
						
							|  |  |  |         if (qemu_get_be32(f) && !peer_has_vnet_hdr(n)) { | 
					
						
							| 
									
										
										
										
											2010-02-18 17:25:24 +01:00
										 |  |  |             error_report("virtio-net: saved image requires vnet_hdr=on"); | 
					
						
							| 
									
										
										
										
											2009-10-22 17:43:45 +01:00
										 |  |  |             return -1; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (n->has_vnet_hdr) { | 
					
						
							| 
									
										
										
										
											2009-11-25 18:49:11 +00:00
										 |  |  |             tap_using_vnet_hdr(n->nic->nc.peer, 1); | 
					
						
							|  |  |  |             tap_set_offload(n->nic->nc.peer, | 
					
						
							| 
									
										
										
										
											2010-01-10 13:52:47 +02:00
										 |  |  |                     (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_CSUM) & 1, | 
					
						
							|  |  |  |                     (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_TSO4) & 1, | 
					
						
							|  |  |  |                     (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_TSO6) & 1, | 
					
						
							|  |  |  |                     (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_ECN)  & 1, | 
					
						
							|  |  |  |                     (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_UFO)  & 1); | 
					
						
							| 
									
										
										
										
											2009-10-22 17:43:45 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2009-06-05 14:46:52 -06:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-06-05 14:47:08 -06:00
										 |  |  |     if (version_id >= 9) { | 
					
						
							|  |  |  |         n->mac_table.multi_overflow = qemu_get_byte(f); | 
					
						
							|  |  |  |         n->mac_table.uni_overflow = qemu_get_byte(f); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-06-05 14:47:18 -06:00
										 |  |  |     if (version_id >= 10) { | 
					
						
							|  |  |  |         n->alluni = qemu_get_byte(f); | 
					
						
							|  |  |  |         n->nomulti = qemu_get_byte(f); | 
					
						
							|  |  |  |         n->nouni = qemu_get_byte(f); | 
					
						
							|  |  |  |         n->nobcast = qemu_get_byte(f); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-22 17:43:50 +01:00
										 |  |  |     if (version_id >= 11) { | 
					
						
							|  |  |  |         if (qemu_get_byte(f) && !peer_has_ufo(n)) { | 
					
						
							| 
									
										
										
										
											2010-02-18 17:25:24 +01:00
										 |  |  |             error_report("virtio-net: saved image requires TUN_F_UFO support"); | 
					
						
							| 
									
										
										
										
											2009-10-22 17:43:50 +01:00
										 |  |  |             return -1; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-06-05 14:47:13 -06:00
										 |  |  |     /* Find the first multicast entry in the saved MAC filter */ | 
					
						
							|  |  |  |     for (i = 0; i < n->mac_table.in_use; i++) { | 
					
						
							|  |  |  |         if (n->mac_table.macs[i * ETH_ALEN] & 1) { | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     n->mac_table.first_multi = i; | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-25 18:49:11 +00:00
										 |  |  | static void virtio_net_cleanup(VLANClientState *nc) | 
					
						
							| 
									
										
										
										
											2009-04-17 17:11:08 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-11-25 18:49:11 +00:00
										 |  |  |     VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; | 
					
						
							| 
									
										
										
										
											2009-04-17 17:11:08 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-25 18:49:11 +00:00
										 |  |  |     n->nic = NULL; | 
					
						
							| 
									
										
										
										
											2009-04-17 17:11:08 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-25 18:49:11 +00:00
										 |  |  | static NetClientInfo net_virtio_info = { | 
					
						
							|  |  |  |     .type = NET_CLIENT_TYPE_NIC, | 
					
						
							|  |  |  |     .size = sizeof(NICState), | 
					
						
							|  |  |  |     .can_receive = virtio_net_can_receive, | 
					
						
							|  |  |  |     .receive = virtio_net_receive, | 
					
						
							|  |  |  |         .cleanup = virtio_net_cleanup, | 
					
						
							|  |  |  |     .link_status_changed = virtio_net_set_link_status, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-09-02 09:00:50 -06:00
										 |  |  | VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, | 
					
						
							|  |  |  |                               virtio_net_conf *net) | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | { | 
					
						
							|  |  |  |     VirtIONet *n; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-18 14:51:59 +01:00
										 |  |  |     n = (VirtIONet *)virtio_common_init("virtio-net", VIRTIO_ID_NET, | 
					
						
							|  |  |  |                                         sizeof(struct virtio_net_config), | 
					
						
							|  |  |  |                                         sizeof(VirtIONet)); | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:08 +00:00
										 |  |  |     n->vdev.get_config = virtio_net_get_config; | 
					
						
							|  |  |  |     n->vdev.set_config = virtio_net_set_config; | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  |     n->vdev.get_features = virtio_net_get_features; | 
					
						
							|  |  |  |     n->vdev.set_features = virtio_net_set_features; | 
					
						
							| 
									
										
										
										
											2009-04-05 17:40:08 +00:00
										 |  |  |     n->vdev.bad_features = virtio_net_bad_features; | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:20 +00:00
										 |  |  |     n->vdev.reset = virtio_net_reset; | 
					
						
							| 
									
										
										
										
											2010-03-17 13:08:42 +02:00
										 |  |  |     n->vdev.set_status = virtio_net_set_status; | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  |     n->rx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_rx); | 
					
						
							| 
									
										
										
										
											2010-09-02 09:01:10 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (net->tx && strcmp(net->tx, "timer") && strcmp(net->tx, "bh")) { | 
					
						
							| 
									
										
										
										
											2010-11-15 20:44:37 +00:00
										 |  |  |         error_report("virtio-net: " | 
					
						
							|  |  |  |                      "Unknown option tx=%s, valid options: \"timer\" \"bh\"", | 
					
						
							|  |  |  |                      net->tx); | 
					
						
							|  |  |  |         error_report("Defaulting to \"bh\""); | 
					
						
							| 
									
										
										
										
											2010-09-02 09:01:10 -06:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (net->tx && !strcmp(net->tx, "timer")) { | 
					
						
							|  |  |  |         n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx_timer); | 
					
						
							| 
									
										
										
										
											2011-03-11 16:47:48 +01:00
										 |  |  |         n->tx_timer = qemu_new_timer_ns(vm_clock, virtio_net_tx_timer, n); | 
					
						
							| 
									
										
										
										
											2010-09-02 09:01:10 -06:00
										 |  |  |         n->tx_timeout = net->txtimer; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx_bh); | 
					
						
							|  |  |  |         n->tx_bh = qemu_bh_new(virtio_net_tx_bh, n); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2009-06-05 14:47:23 -06:00
										 |  |  |     n->ctrl_vq = virtio_add_queue(&n->vdev, 64, virtio_net_handle_ctrl); | 
					
						
							| 
									
										
										
										
											2009-10-21 15:25:35 +02:00
										 |  |  |     qemu_macaddr_default_if_unset(&conf->macaddr); | 
					
						
							| 
									
										
										
										
											2009-10-28 14:07:23 +00:00
										 |  |  |     memcpy(&n->mac[0], &conf->macaddr, sizeof(n->mac)); | 
					
						
							| 
									
										
										
										
											2009-01-08 19:46:33 +00:00
										 |  |  |     n->status = VIRTIO_NET_S_LINK_UP; | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-04 11:17:51 -06:00
										 |  |  |     n->nic = qemu_new_nic(&net_virtio_info, conf, object_get_typename(OBJECT(dev)), dev->id, n); | 
					
						
							| 
									
										
										
										
											2009-11-25 18:49:11 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     qemu_format_nic_info_str(&n->nic->nc, conf->macaddr.a); | 
					
						
							| 
									
										
										
										
											2009-01-07 17:47:15 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-09-02 09:01:04 -06:00
										 |  |  |     n->tx_waiting = 0; | 
					
						
							| 
									
										
										
										
											2010-09-02 09:00:57 -06:00
										 |  |  |     n->tx_burst = net->txburst; | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  |     n->mergeable_rx_bufs = 0; | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:20 +00:00
										 |  |  |     n->promisc = 1; /* for compatibility */ | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-20 22:09:37 -05:00
										 |  |  |     n->mac_table.macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN); | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:28 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-20 22:09:37 -05:00
										 |  |  |     n->vlans = g_malloc0(MAX_VLAN >> 3); | 
					
						
							| 
									
										
										
										
											2009-02-05 22:36:32 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-06-25 11:09:28 -06:00
										 |  |  |     n->qdev = dev; | 
					
						
							|  |  |  |     register_savevm(dev, "virtio-net", -1, VIRTIO_NET_VM_VERSION, | 
					
						
							| 
									
										
										
										
											2008-12-17 19:13:11 +00:00
										 |  |  |                     virtio_net_save, virtio_net_load, n); | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:07 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-12-08 13:35:05 +02:00
										 |  |  |     add_boot_device_path(conf->bootindex, dev, "/ethernet-phy@0"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-18 14:51:59 +01:00
										 |  |  |     return &n->vdev; | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:07 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2009-10-21 15:25:35 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | void virtio_net_exit(VirtIODevice *vdev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     VirtIONet *n = DO_UPCAST(VirtIONet, vdev, vdev); | 
					
						
							| 
									
										
										
										
											2010-03-17 13:08:42 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-09-27 18:41:30 +02:00
										 |  |  |     /* This will stop vhost backend if appropriate. */ | 
					
						
							|  |  |  |     virtio_net_set_status(vdev, 0); | 
					
						
							| 
									
										
										
										
											2009-10-21 15:25:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-25 18:49:11 +00:00
										 |  |  |     qemu_purge_queued_packets(&n->nic->nc); | 
					
						
							| 
									
										
										
										
											2009-10-21 15:25:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-06-25 11:09:28 -06:00
										 |  |  |     unregister_savevm(n->qdev, "virtio-net", n); | 
					
						
							| 
									
										
										
										
											2009-10-21 15:25:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-20 22:09:37 -05:00
										 |  |  |     g_free(n->mac_table.macs); | 
					
						
							|  |  |  |     g_free(n->vlans); | 
					
						
							| 
									
										
										
										
											2009-10-21 15:25:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-09-02 09:01:10 -06:00
										 |  |  |     if (n->tx_timer) { | 
					
						
							|  |  |  |         qemu_del_timer(n->tx_timer); | 
					
						
							|  |  |  |         qemu_free_timer(n->tx_timer); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         qemu_bh_delete(n->tx_bh); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2009-10-21 15:25:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-25 18:49:11 +00:00
										 |  |  |     qemu_del_vlan_client(&n->nic->nc); | 
					
						
							| 
									
										
										
										
											2011-07-27 14:00:31 +05:30
										 |  |  |     virtio_cleanup(&n->vdev); | 
					
						
							| 
									
										
										
										
											2009-10-21 15:25:35 +02:00
										 |  |  | } |