xref: /kvmtool/virtio/net.c (revision aaf0b4453e15de9f86ea31a5758b2b8957a2102a)
14f56d42cSAsias He #include "kvm/virtio-net.h"
24f56d42cSAsias He #include "kvm/virtio-pci.h"
331638bcaSCyrill Gorcunov #include "kvm/virtio-pci-dev.h"
44f56d42cSAsias He #include "kvm/virtio.h"
54f56d42cSAsias He #include "kvm/ioport.h"
64f56d42cSAsias He #include "kvm/types.h"
74f56d42cSAsias He #include "kvm/mutex.h"
84f56d42cSAsias He #include "kvm/util.h"
94f56d42cSAsias He #include "kvm/kvm.h"
104f56d42cSAsias He #include "kvm/pci.h"
112449f6e3SSasha Levin #include "kvm/irq.h"
124f56d42cSAsias He 
134f56d42cSAsias He #include <linux/virtio_net.h>
144f56d42cSAsias He #include <linux/if_tun.h>
154f56d42cSAsias He #include <net/if.h>
164f56d42cSAsias He #include <sys/ioctl.h>
174f56d42cSAsias He #include <assert.h>
184f56d42cSAsias He #include <fcntl.h>
19cb7202c1SSasha Levin #include <arpa/inet.h>
20cb7202c1SSasha Levin #include <sys/types.h>
21cb7202c1SSasha Levin #include <sys/socket.h>
2273b7d038SAmos Kong #include <unistd.h>
2373b7d038SAmos Kong #include <sys/wait.h>
244f56d42cSAsias He 
254f56d42cSAsias He #define VIRTIO_NET_QUEUE_SIZE		128
264f56d42cSAsias He #define VIRTIO_NET_NUM_QUEUES		2
274f56d42cSAsias He #define VIRTIO_NET_RX_QUEUE		0
284f56d42cSAsias He #define VIRTIO_NET_TX_QUEUE		1
294f56d42cSAsias He 
302449f6e3SSasha Levin static struct pci_device_header virtio_net_pci_device = {
312449f6e3SSasha Levin 	.vendor_id		= PCI_VENDOR_ID_REDHAT_QUMRANET,
322449f6e3SSasha Levin 	.device_id		= PCI_DEVICE_ID_VIRTIO_NET,
332449f6e3SSasha Levin 	.header_type		= PCI_HEADER_TYPE_NORMAL,
342449f6e3SSasha Levin 	.revision_id		= 0,
352449f6e3SSasha Levin 	.class			= 0x020000,
362449f6e3SSasha Levin 	.subsys_vendor_id	= PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
372449f6e3SSasha Levin 	.subsys_id		= PCI_SUBSYSTEM_ID_VIRTIO_NET,
382449f6e3SSasha Levin 	.bar[0]			= IOPORT_VIRTIO_NET | PCI_BASE_ADDRESS_SPACE_IO,
392449f6e3SSasha Levin };
402449f6e3SSasha Levin 
414f56d42cSAsias He struct net_device {
424f56d42cSAsias He 	pthread_mutex_t			mutex;
434f56d42cSAsias He 
444f56d42cSAsias He 	struct virt_queue		vqs[VIRTIO_NET_NUM_QUEUES];
454f56d42cSAsias He 	struct virtio_net_config	net_config;
463fdf659dSSasha Levin 	u32				host_features;
473fdf659dSSasha Levin 	u32				guest_features;
483fdf659dSSasha Levin 	u16				config_vector;
493fdf659dSSasha Levin 	u8				status;
507f5ffaf5SAsias He 	u8				isr;
513fdf659dSSasha Levin 	u16				queue_selector;
524f56d42cSAsias He 
53c4aa7c02SPekka Enberg 	pthread_t			io_rx_thread;
54c4aa7c02SPekka Enberg 	pthread_mutex_t			io_rx_mutex;
55c4aa7c02SPekka Enberg 	pthread_cond_t			io_rx_cond;
56c4aa7c02SPekka Enberg 
57c4aa7c02SPekka Enberg 	pthread_t			io_tx_thread;
58c4aa7c02SPekka Enberg 	pthread_mutex_t			io_tx_mutex;
59c4aa7c02SPekka Enberg 	pthread_cond_t			io_tx_cond;
60c4aa7c02SPekka Enberg 
614f56d42cSAsias He 	int				tap_fd;
624f56d42cSAsias He 	char				tap_name[IFNAMSIZ];
634f56d42cSAsias He };
644f56d42cSAsias He 
654f56d42cSAsias He static struct net_device net_device = {
664f56d42cSAsias He 	.mutex				= PTHREAD_MUTEX_INITIALIZER,
674f56d42cSAsias He 
684f56d42cSAsias He 	.net_config = {
694f56d42cSAsias He 		.mac			= { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 },
704f56d42cSAsias He 		.status			= VIRTIO_NET_S_LINK_UP,
714f56d42cSAsias He 	},
72407475bfSPekka Enberg 	.host_features			= 1UL << VIRTIO_NET_F_MAC
73407475bfSPekka Enberg 					| 1UL << VIRTIO_NET_F_CSUM
74407475bfSPekka Enberg 					| 1UL << VIRTIO_NET_F_HOST_UFO
75407475bfSPekka Enberg 					| 1UL << VIRTIO_NET_F_HOST_TSO4
76407475bfSPekka Enberg 					| 1UL << VIRTIO_NET_F_HOST_TSO6
77407475bfSPekka Enberg 					| 1UL << VIRTIO_NET_F_GUEST_UFO
78407475bfSPekka Enberg 					| 1UL << VIRTIO_NET_F_GUEST_TSO4
79407475bfSPekka Enberg 					| 1UL << VIRTIO_NET_F_GUEST_TSO6,
804f56d42cSAsias He };
814f56d42cSAsias He 
82c4aa7c02SPekka Enberg static void *virtio_net_rx_thread(void *p)
834f56d42cSAsias He {
844f56d42cSAsias He 	struct iovec iov[VIRTIO_NET_QUEUE_SIZE];
854f56d42cSAsias He 	struct virt_queue *vq;
86c4aa7c02SPekka Enberg 	struct kvm *self;
873fdf659dSSasha Levin 	u16 out, in;
883fdf659dSSasha Levin 	u16 head;
894f56d42cSAsias He 	int len;
904f56d42cSAsias He 
91c4aa7c02SPekka Enberg 	self	= p;
92c4aa7c02SPekka Enberg 	vq	= &net_device.vqs[VIRTIO_NET_RX_QUEUE];
93c4aa7c02SPekka Enberg 
94c4aa7c02SPekka Enberg 	while (1) {
95c4aa7c02SPekka Enberg 		mutex_lock(&net_device.io_rx_mutex);
96c4aa7c02SPekka Enberg 		if (!virt_queue__available(vq))
97c4aa7c02SPekka Enberg 			pthread_cond_wait(&net_device.io_rx_cond, &net_device.io_rx_mutex);
98c4aa7c02SPekka Enberg 		mutex_unlock(&net_device.io_rx_mutex);
994f56d42cSAsias He 
1004f56d42cSAsias He 		while (virt_queue__available(vq)) {
1014f56d42cSAsias He 			head	= virt_queue__get_iov(vq, iov, &out, &in, self);
102246c8347SAsias He 			len	= readv(net_device.tap_fd, iov, in);
103246c8347SAsias He 			virt_queue__set_used_elem(vq, head, len);
1047f5ffaf5SAsias He 
105c4aa7c02SPekka Enberg 			/* We should interrupt guest right now, otherwise latency is huge. */
1062449f6e3SSasha Levin 			virt_queue__trigger_irq(vq, virtio_net_pci_device.irq_line, &net_device.isr, self);
1074f56d42cSAsias He 		}
1084f56d42cSAsias He 
109c4aa7c02SPekka Enberg 	}
110c4aa7c02SPekka Enberg 
111c4aa7c02SPekka Enberg 	pthread_exit(NULL);
112c4aa7c02SPekka Enberg 	return NULL;
113c4aa7c02SPekka Enberg 
114c4aa7c02SPekka Enberg }
115c4aa7c02SPekka Enberg 
116c4aa7c02SPekka Enberg static void *virtio_net_tx_thread(void *p)
1174f56d42cSAsias He {
1184f56d42cSAsias He 	struct iovec iov[VIRTIO_NET_QUEUE_SIZE];
1194f56d42cSAsias He 	struct virt_queue *vq;
120c4aa7c02SPekka Enberg 	struct kvm *self;
1213fdf659dSSasha Levin 	u16 out, in;
1223fdf659dSSasha Levin 	u16 head;
1234f56d42cSAsias He 	int len;
1244f56d42cSAsias He 
125c4aa7c02SPekka Enberg 	self	= p;
126c4aa7c02SPekka Enberg 	vq	= &net_device.vqs[VIRTIO_NET_TX_QUEUE];
127c4aa7c02SPekka Enberg 
128c4aa7c02SPekka Enberg 	while (1) {
129c4aa7c02SPekka Enberg 		mutex_lock(&net_device.io_tx_mutex);
130c4aa7c02SPekka Enberg 		if (!virt_queue__available(vq))
131c4aa7c02SPekka Enberg 			pthread_cond_wait(&net_device.io_tx_cond, &net_device.io_tx_mutex);
132c4aa7c02SPekka Enberg 		mutex_unlock(&net_device.io_tx_mutex);
1334f56d42cSAsias He 
1344f56d42cSAsias He 		while (virt_queue__available(vq)) {
1354f56d42cSAsias He 			head	= virt_queue__get_iov(vq, iov, &out, &in, self);
136246c8347SAsias He 			len	= writev(net_device.tap_fd, iov, out);
1374f56d42cSAsias He 			virt_queue__set_used_elem(vq, head, len);
1384f56d42cSAsias He 		}
1394f56d42cSAsias He 
1402449f6e3SSasha Levin 		virt_queue__trigger_irq(vq, virtio_net_pci_device.irq_line, &net_device.isr, self);
1417f5ffaf5SAsias He 
1424f56d42cSAsias He 	}
1434f56d42cSAsias He 
144c4aa7c02SPekka Enberg 	pthread_exit(NULL);
145407475bfSPekka Enberg 
146c4aa7c02SPekka Enberg 	return NULL;
147c4aa7c02SPekka Enberg 
148c4aa7c02SPekka Enberg }
149407475bfSPekka Enberg 
1503fdf659dSSasha Levin static bool virtio_net_pci_io_device_specific_in(void *data, unsigned long offset, int size, u32 count)
1514f56d42cSAsias He {
1523fdf659dSSasha Levin 	u8 *config_space = (u8 *) &net_device.net_config;
1534f56d42cSAsias He 
1544f56d42cSAsias He 	if (size != 1 || count != 1)
1554f56d42cSAsias He 		return false;
1564f56d42cSAsias He 
1574f56d42cSAsias He 	if ((offset - VIRTIO_PCI_CONFIG_NOMSI) > sizeof(struct virtio_net_config))
1584f56d42cSAsias He 		error("config offset is too big: %li", offset - VIRTIO_PCI_CONFIG_NOMSI);
1594f56d42cSAsias He 
1604f56d42cSAsias He 	ioport__write8(data, config_space[offset - VIRTIO_PCI_CONFIG_NOMSI]);
1614f56d42cSAsias He 
1624f56d42cSAsias He 	return true;
1634f56d42cSAsias He }
1644f56d42cSAsias He 
1653fdf659dSSasha Levin static bool virtio_net_pci_io_in(struct kvm *self, u16 port, void *data, int size, u32 count)
1664f56d42cSAsias He {
1674f56d42cSAsias He 	unsigned long	offset	= port - IOPORT_VIRTIO_NET;
1684f56d42cSAsias He 	bool		ret	= true;
1694f56d42cSAsias He 
1704f56d42cSAsias He 	mutex_lock(&net_device.mutex);
1714f56d42cSAsias He 
1724f56d42cSAsias He 	switch (offset) {
1734f56d42cSAsias He 	case VIRTIO_PCI_HOST_FEATURES:
1744f56d42cSAsias He 		ioport__write32(data, net_device.host_features);
1754f56d42cSAsias He 		break;
1764f56d42cSAsias He 	case VIRTIO_PCI_GUEST_FEATURES:
1774f56d42cSAsias He 		ret = false;
1784f56d42cSAsias He 		break;
1794f56d42cSAsias He 	case VIRTIO_PCI_QUEUE_PFN:
1804f56d42cSAsias He 		ioport__write32(data, net_device.vqs[net_device.queue_selector].pfn);
1814f56d42cSAsias He 		break;
1824f56d42cSAsias He 	case VIRTIO_PCI_QUEUE_NUM:
1834f56d42cSAsias He 		ioport__write16(data, VIRTIO_NET_QUEUE_SIZE);
1844f56d42cSAsias He 		break;
1854f56d42cSAsias He 	case VIRTIO_PCI_QUEUE_SEL:
1864f56d42cSAsias He 	case VIRTIO_PCI_QUEUE_NOTIFY:
1874f56d42cSAsias He 		ret = false;
1884f56d42cSAsias He 		break;
1894f56d42cSAsias He 	case VIRTIO_PCI_STATUS:
1904f56d42cSAsias He 		ioport__write8(data, net_device.status);
1914f56d42cSAsias He 		break;
1924f56d42cSAsias He 	case VIRTIO_PCI_ISR:
1937f5ffaf5SAsias He 		ioport__write8(data, net_device.isr);
1942449f6e3SSasha Levin 		kvm__irq_line(self, virtio_net_pci_device.irq_line, VIRTIO_IRQ_LOW);
1957f5ffaf5SAsias He 		net_device.isr = VIRTIO_IRQ_LOW;
1964f56d42cSAsias He 		break;
1974f56d42cSAsias He 	case VIRTIO_MSI_CONFIG_VECTOR:
1984f56d42cSAsias He 		ioport__write16(data, net_device.config_vector);
1994f56d42cSAsias He 		break;
2004f56d42cSAsias He 	default:
2014f56d42cSAsias He 		ret = virtio_net_pci_io_device_specific_in(data, offset, size, count);
2024f56d42cSAsias He 	};
2034f56d42cSAsias He 
2044f56d42cSAsias He 	mutex_unlock(&net_device.mutex);
2054f56d42cSAsias He 
2064f56d42cSAsias He 	return ret;
2074f56d42cSAsias He }
2084f56d42cSAsias He 
2093fdf659dSSasha Levin static void virtio_net_handle_callback(struct kvm *self, u16 queue_index)
2104f56d42cSAsias He {
211407475bfSPekka Enberg 	switch (queue_index) {
212407475bfSPekka Enberg 	case VIRTIO_NET_TX_QUEUE: {
213c4aa7c02SPekka Enberg 		mutex_lock(&net_device.io_tx_mutex);
214c4aa7c02SPekka Enberg 		pthread_cond_signal(&net_device.io_tx_cond);
215c4aa7c02SPekka Enberg 		mutex_unlock(&net_device.io_tx_mutex);
216407475bfSPekka Enberg 		break;
217407475bfSPekka Enberg 	}
218407475bfSPekka Enberg 	case VIRTIO_NET_RX_QUEUE: {
219c4aa7c02SPekka Enberg 		mutex_lock(&net_device.io_rx_mutex);
220c4aa7c02SPekka Enberg 		pthread_cond_signal(&net_device.io_rx_cond);
221c4aa7c02SPekka Enberg 		mutex_unlock(&net_device.io_rx_mutex);
222407475bfSPekka Enberg 		break;
223407475bfSPekka Enberg 	}
224407475bfSPekka Enberg 	default:
225407475bfSPekka Enberg 		warning("Unknown queue index %u", queue_index);
226c4aa7c02SPekka Enberg 	}
2274f56d42cSAsias He }
2284f56d42cSAsias He 
2293fdf659dSSasha Levin static bool virtio_net_pci_io_out(struct kvm *self, u16 port, void *data, int size, u32 count)
2304f56d42cSAsias He {
2314f56d42cSAsias He 	unsigned long	offset			= port - IOPORT_VIRTIO_NET;
2324f56d42cSAsias He 	bool		ret			= true;
2334f56d42cSAsias He 
2344f56d42cSAsias He 	mutex_lock(&net_device.mutex);
2354f56d42cSAsias He 
2364f56d42cSAsias He 	switch (offset) {
2374f56d42cSAsias He 	case VIRTIO_PCI_GUEST_FEATURES:
2384f56d42cSAsias He 		net_device.guest_features	= ioport__read32(data);
2394f56d42cSAsias He 		break;
2404f56d42cSAsias He 	case VIRTIO_PCI_QUEUE_PFN: {
2414f56d42cSAsias He 		struct virt_queue *queue;
2424f56d42cSAsias He 		void *p;
2434f56d42cSAsias He 
2444f56d42cSAsias He 		assert(net_device.queue_selector < VIRTIO_NET_NUM_QUEUES);
2454f56d42cSAsias He 
2464f56d42cSAsias He 		queue				= &net_device.vqs[net_device.queue_selector];
2474f56d42cSAsias He 		queue->pfn			= ioport__read32(data);
248*aaf0b445SSasha Levin 		p				= guest_pfn_to_host(self, queue->pfn);
2494f56d42cSAsias He 
2504f56d42cSAsias He 		vring_init(&queue->vring, VIRTIO_NET_QUEUE_SIZE, p, 4096);
2514f56d42cSAsias He 
2524f56d42cSAsias He 		break;
2534f56d42cSAsias He 	}
2544f56d42cSAsias He 	case VIRTIO_PCI_QUEUE_SEL:
2554f56d42cSAsias He 		net_device.queue_selector	= ioport__read16(data);
2564f56d42cSAsias He 		break;
2574f56d42cSAsias He 	case VIRTIO_PCI_QUEUE_NOTIFY: {
2583fdf659dSSasha Levin 		u16 queue_index;
2594f56d42cSAsias He 		queue_index	= ioport__read16(data);
2604f56d42cSAsias He 		virtio_net_handle_callback(self, queue_index);
2614f56d42cSAsias He 		break;
2624f56d42cSAsias He 	}
2634f56d42cSAsias He 	case VIRTIO_PCI_STATUS:
2644f56d42cSAsias He 		net_device.status		= ioport__read8(data);
2654f56d42cSAsias He 		break;
2664f56d42cSAsias He 	case VIRTIO_MSI_CONFIG_VECTOR:
2674f56d42cSAsias He 		net_device.config_vector	= VIRTIO_MSI_NO_VECTOR;
2684f56d42cSAsias He 		break;
2694f56d42cSAsias He 	case VIRTIO_MSI_QUEUE_VECTOR:
2704f56d42cSAsias He 		break;
2714f56d42cSAsias He 	default:
2724f56d42cSAsias He 		ret				= false;
2734f56d42cSAsias He 	};
2744f56d42cSAsias He 
2754f56d42cSAsias He 	mutex_unlock(&net_device.mutex);
276407475bfSPekka Enberg 
2774f56d42cSAsias He 	return ret;
2784f56d42cSAsias He }
2794f56d42cSAsias He 
2804f56d42cSAsias He static struct ioport_operations virtio_net_io_ops = {
2814f56d42cSAsias He 	.io_in	= virtio_net_pci_io_in,
2824f56d42cSAsias He 	.io_out	= virtio_net_pci_io_out,
2834f56d42cSAsias He };
2844f56d42cSAsias He 
2853b02f580SSasha Levin static bool virtio_net__tap_init(const struct virtio_net_parameters *params)
2864f56d42cSAsias He {
287cb7202c1SSasha Levin 	int sock = socket(AF_INET, SOCK_STREAM, 0);
288246c8347SAsias He 	int i, pid, status, offload, hdr_len;
289cb7202c1SSasha Levin 	struct sockaddr_in sin = {0};
290246c8347SAsias He 	struct ifreq ifr;
2914f56d42cSAsias He 
292a4e724ddSSasha Levin 	for (i = 0 ; i < 6 ; i++)
293a4e724ddSSasha Levin 		net_device.net_config.mac[i] = params->guest_mac[i];
294a4e724ddSSasha Levin 
2954f56d42cSAsias He 	net_device.tap_fd = open("/dev/net/tun", O_RDWR);
2963b02f580SSasha Levin 	if (net_device.tap_fd < 0) {
297246c8347SAsias He 		warning("Unable to open /dev/net/tun");
2983b02f580SSasha Levin 		goto fail;
2993b02f580SSasha Levin 	}
3004f56d42cSAsias He 
3014f56d42cSAsias He 	memset(&ifr, 0, sizeof(ifr));
302246c8347SAsias He 	ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_VNET_HDR;
3033b02f580SSasha Levin 	if (ioctl(net_device.tap_fd, TUNSETIFF, &ifr) < 0) {
3043b02f580SSasha Levin 		warning("Config tap device error. Are you root?");
3053b02f580SSasha Levin 		goto fail;
3063b02f580SSasha Levin 	}
3074f56d42cSAsias He 
3084f56d42cSAsias He 	strncpy(net_device.tap_name, ifr.ifr_name, sizeof(net_device.tap_name));
3094f56d42cSAsias He 
310246c8347SAsias He 	if (ioctl(net_device.tap_fd, TUNSETNOCSUM, 1) < 0) {
311246c8347SAsias He 		warning("Config tap device TUNSETNOCSUM error");
312246c8347SAsias He 		goto fail;
313246c8347SAsias He 	}
314246c8347SAsias He 
315246c8347SAsias He 	hdr_len = sizeof(struct virtio_net_hdr);
316246c8347SAsias He 	if (ioctl(net_device.tap_fd, TUNSETVNETHDRSZ, &hdr_len) < 0) {
317246c8347SAsias He 		warning("Config tap device TUNSETVNETHDRSZ error");
318246c8347SAsias He 		goto fail;
319246c8347SAsias He 	}
320246c8347SAsias He 
321246c8347SAsias He 	offload = TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 | TUN_F_UFO;
322246c8347SAsias He 	if (ioctl(net_device.tap_fd, TUNSETOFFLOAD, offload) < 0) {
323246c8347SAsias He 		warning("Config tap device TUNSETOFFLOAD error");
324246c8347SAsias He 		goto fail;
325246c8347SAsias He 	}
3264f56d42cSAsias He 
32773b7d038SAmos Kong 	if (strcmp(params->script, "none")) {
32873b7d038SAmos Kong 		pid = fork();
32973b7d038SAmos Kong 		if (pid == 0) {
33073b7d038SAmos Kong 			execl(params->script, params->script, net_device.tap_name, NULL);
33173b7d038SAmos Kong 			_exit(1);
33273b7d038SAmos Kong 		} else {
33373b7d038SAmos Kong 			waitpid(pid, &status, 0);
33473b7d038SAmos Kong 			if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
33573b7d038SAmos Kong 				warning("Fail to setup tap by %s", params->script);
33673b7d038SAmos Kong 				goto fail;
33773b7d038SAmos Kong 			}
33873b7d038SAmos Kong 		}
33973b7d038SAmos Kong 	} else {
340cb7202c1SSasha Levin 		memset(&ifr, 0, sizeof(ifr));
341cb7202c1SSasha Levin 		strncpy(ifr.ifr_name, net_device.tap_name, sizeof(net_device.tap_name));
342bdfcfca6SSasha Levin 		sin.sin_addr.s_addr = inet_addr(params->host_ip);
343cb7202c1SSasha Levin 		memcpy(&(ifr.ifr_addr), &sin, sizeof(ifr.ifr_addr));
344cb7202c1SSasha Levin 		ifr.ifr_addr.sa_family = AF_INET;
3453b02f580SSasha Levin 		if (ioctl(sock, SIOCSIFADDR, &ifr) < 0) {
346246c8347SAsias He 			warning("Could not set ip address on tap device");
3473b02f580SSasha Levin 			goto fail;
3483b02f580SSasha Levin 		}
34973b7d038SAmos Kong 	}
350cb7202c1SSasha Levin 
351cb7202c1SSasha Levin 	memset(&ifr, 0, sizeof(ifr));
352cb7202c1SSasha Levin 	strncpy(ifr.ifr_name, net_device.tap_name, sizeof(net_device.tap_name));
353cb7202c1SSasha Levin 	ioctl(sock, SIOCGIFFLAGS, &ifr);
354cb7202c1SSasha Levin 	ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
355cb7202c1SSasha Levin 	if (ioctl(sock, SIOCSIFFLAGS, &ifr) < 0)
356cb7202c1SSasha Levin 		warning("Could not bring tap device up");
357cb7202c1SSasha Levin 
358cb7202c1SSasha Levin 	close(sock);
3593b02f580SSasha Levin 
3603b02f580SSasha Levin 	return 1;
3613b02f580SSasha Levin 
3623b02f580SSasha Levin fail:
3633b02f580SSasha Levin 	if (sock >= 0)
3643b02f580SSasha Levin 		close(sock);
3653b02f580SSasha Levin 	if (net_device.tap_fd >= 0)
3663b02f580SSasha Levin 		close(net_device.tap_fd);
3673b02f580SSasha Levin 
3683b02f580SSasha Levin 	return 0;
3694f56d42cSAsias He }
3704f56d42cSAsias He 
371c4aa7c02SPekka Enberg static void virtio_net__io_thread_init(struct kvm *self)
372c4aa7c02SPekka Enberg {
373c4aa7c02SPekka Enberg 	pthread_mutex_init(&net_device.io_rx_mutex, NULL);
374c4aa7c02SPekka Enberg 	pthread_cond_init(&net_device.io_tx_cond, NULL);
375c4aa7c02SPekka Enberg 
376c4aa7c02SPekka Enberg 	pthread_mutex_init(&net_device.io_rx_mutex, NULL);
377c4aa7c02SPekka Enberg 	pthread_cond_init(&net_device.io_tx_cond, NULL);
378c4aa7c02SPekka Enberg 
379c4aa7c02SPekka Enberg 	pthread_create(&net_device.io_rx_thread, NULL, virtio_net_rx_thread, (void *)self);
380c4aa7c02SPekka Enberg 	pthread_create(&net_device.io_tx_thread, NULL, virtio_net_tx_thread, (void *)self);
381c4aa7c02SPekka Enberg }
382c4aa7c02SPekka Enberg 
383bdfcfca6SSasha Levin void virtio_net__init(const struct virtio_net_parameters *params)
3844f56d42cSAsias He {
3853b02f580SSasha Levin 	if (virtio_net__tap_init(params)) {
3862449f6e3SSasha Levin 		u8 dev, line, pin;
3872449f6e3SSasha Levin 
3882449f6e3SSasha Levin 		if (irq__register_device(PCI_DEVICE_ID_VIRTIO_NET, &dev, &pin, &line) < 0)
3892449f6e3SSasha Levin 			return;
3902449f6e3SSasha Levin 
3912449f6e3SSasha Levin 		virtio_net_pci_device.irq_pin	= pin;
3922449f6e3SSasha Levin 		virtio_net_pci_device.irq_line	= line;
3932449f6e3SSasha Levin 		pci__register(&virtio_net_pci_device, dev);
3944f56d42cSAsias He 		ioport__register(IOPORT_VIRTIO_NET, &virtio_net_io_ops, IOPORT_VIRTIO_NET_SIZE);
395c4aa7c02SPekka Enberg 
396c4aa7c02SPekka Enberg 		virtio_net__io_thread_init(params->self);
3974f56d42cSAsias He 	}
3983b02f580SSasha Levin }
399