1d30ea906Sjfb8856606 /* SPDX-License-Identifier: BSD-3-Clause
2d30ea906Sjfb8856606  * Copyright(c) 2010-2016 Intel Corporation
3a9643ea8Slogwang  */
4a9643ea8Slogwang 
5a9643ea8Slogwang #include <stdint.h>
6a9643ea8Slogwang #include <sys/types.h>
7a9643ea8Slogwang #include <unistd.h>
82bfe3f2eSlogwang #include <fcntl.h>
9*2d9fd380Sjfb8856606 #include <linux/major.h>
10*2d9fd380Sjfb8856606 #include <sys/stat.h>
11*2d9fd380Sjfb8856606 #include <sys/sysmacros.h>
122bfe3f2eSlogwang #include <sys/socket.h>
13a9643ea8Slogwang 
14a9643ea8Slogwang #include <rte_malloc.h>
15a9643ea8Slogwang #include <rte_kvargs.h>
162bfe3f2eSlogwang #include <rte_ethdev_vdev.h>
172bfe3f2eSlogwang #include <rte_bus_vdev.h>
182bfe3f2eSlogwang #include <rte_alarm.h>
194418919fSjohnjiang #include <rte_cycles.h>
20a9643ea8Slogwang 
21a9643ea8Slogwang #include "virtio_ethdev.h"
22a9643ea8Slogwang #include "virtio_logs.h"
23a9643ea8Slogwang #include "virtio_pci.h"
24a9643ea8Slogwang #include "virtqueue.h"
25a9643ea8Slogwang #include "virtio_rxtx.h"
26a9643ea8Slogwang #include "virtio_user/virtio_user_dev.h"
274418919fSjohnjiang #include "virtio_user/vhost.h"
28a9643ea8Slogwang 
29a9643ea8Slogwang #define virtio_user_get_dev(hw) \
30a9643ea8Slogwang 	((struct virtio_user_dev *)(hw)->virtio_user_dev)
31a9643ea8Slogwang 
324418919fSjohnjiang static void
virtio_user_reset_queues_packed(struct rte_eth_dev * dev)334418919fSjohnjiang virtio_user_reset_queues_packed(struct rte_eth_dev *dev)
344418919fSjohnjiang {
354418919fSjohnjiang 	struct virtio_hw *hw = dev->data->dev_private;
364418919fSjohnjiang 	struct virtnet_rx *rxvq;
374418919fSjohnjiang 	struct virtnet_tx *txvq;
384418919fSjohnjiang 	uint16_t i;
394418919fSjohnjiang 
404418919fSjohnjiang 	/* Add lock to avoid queue contention. */
414418919fSjohnjiang 	rte_spinlock_lock(&hw->state_lock);
424418919fSjohnjiang 	hw->started = 0;
434418919fSjohnjiang 
444418919fSjohnjiang 	/*
454418919fSjohnjiang 	 * Waitting for datapath to complete before resetting queues.
464418919fSjohnjiang 	 * 1 ms should be enough for the ongoing Tx/Rx function to finish.
474418919fSjohnjiang 	 */
484418919fSjohnjiang 	rte_delay_ms(1);
494418919fSjohnjiang 
504418919fSjohnjiang 	/* Vring reset for each Tx queue and Rx queue. */
514418919fSjohnjiang 	for (i = 0; i < dev->data->nb_rx_queues; i++) {
524418919fSjohnjiang 		rxvq = dev->data->rx_queues[i];
534418919fSjohnjiang 		virtqueue_rxvq_reset_packed(rxvq->vq);
544418919fSjohnjiang 		virtio_dev_rx_queue_setup_finish(dev, i);
554418919fSjohnjiang 	}
564418919fSjohnjiang 
574418919fSjohnjiang 	for (i = 0; i < dev->data->nb_tx_queues; i++) {
584418919fSjohnjiang 		txvq = dev->data->tx_queues[i];
594418919fSjohnjiang 		virtqueue_txvq_reset_packed(txvq->vq);
604418919fSjohnjiang 	}
614418919fSjohnjiang 
624418919fSjohnjiang 	hw->started = 1;
634418919fSjohnjiang 	rte_spinlock_unlock(&hw->state_lock);
644418919fSjohnjiang }
654418919fSjohnjiang 
664418919fSjohnjiang 
67d30ea906Sjfb8856606 static int
virtio_user_server_reconnect(struct virtio_user_dev * dev)68d30ea906Sjfb8856606 virtio_user_server_reconnect(struct virtio_user_dev *dev)
69d30ea906Sjfb8856606 {
70*2d9fd380Sjfb8856606 	int ret, connectfd, old_status;
71d30ea906Sjfb8856606 	struct rte_eth_dev *eth_dev = &rte_eth_devices[dev->port_id];
724418919fSjohnjiang 	struct virtio_hw *hw = eth_dev->data->dev_private;
73*2d9fd380Sjfb8856606 	uint64_t protocol_features;
74d30ea906Sjfb8856606 
75d30ea906Sjfb8856606 	connectfd = accept(dev->listenfd, NULL, NULL);
76d30ea906Sjfb8856606 	if (connectfd < 0)
77d30ea906Sjfb8856606 		return -1;
78d30ea906Sjfb8856606 
79d30ea906Sjfb8856606 	dev->vhostfd = connectfd;
80*2d9fd380Sjfb8856606 	old_status = vtpci_get_status(hw);
81*2d9fd380Sjfb8856606 
82*2d9fd380Sjfb8856606 	vtpci_reset(hw);
83*2d9fd380Sjfb8856606 
84*2d9fd380Sjfb8856606 	vtpci_set_status(hw, VIRTIO_CONFIG_STATUS_ACK);
85*2d9fd380Sjfb8856606 
86*2d9fd380Sjfb8856606 	vtpci_set_status(hw, VIRTIO_CONFIG_STATUS_DRIVER);
87*2d9fd380Sjfb8856606 
88d30ea906Sjfb8856606 	if (dev->ops->send_request(dev, VHOST_USER_GET_FEATURES,
89d30ea906Sjfb8856606 				   &dev->device_features) < 0) {
90d30ea906Sjfb8856606 		PMD_INIT_LOG(ERR, "get_features failed: %s",
91d30ea906Sjfb8856606 			     strerror(errno));
92d30ea906Sjfb8856606 		return -1;
93d30ea906Sjfb8856606 	}
94d30ea906Sjfb8856606 
95*2d9fd380Sjfb8856606 	if (dev->device_features &
96*2d9fd380Sjfb8856606 			(1ULL << VHOST_USER_F_PROTOCOL_FEATURES)) {
97*2d9fd380Sjfb8856606 		if (dev->ops->send_request(dev,
98*2d9fd380Sjfb8856606 					VHOST_USER_GET_PROTOCOL_FEATURES,
99*2d9fd380Sjfb8856606 					&protocol_features))
100*2d9fd380Sjfb8856606 			return -1;
101*2d9fd380Sjfb8856606 
102*2d9fd380Sjfb8856606 		/* Offer VHOST_USER_PROTOCOL_F_STATUS */
103*2d9fd380Sjfb8856606 		dev->protocol_features |=
104*2d9fd380Sjfb8856606 			(1ULL << VHOST_USER_PROTOCOL_F_STATUS);
105*2d9fd380Sjfb8856606 		dev->protocol_features &= protocol_features;
106*2d9fd380Sjfb8856606 
107*2d9fd380Sjfb8856606 		if (dev->ops->send_request(dev,
108*2d9fd380Sjfb8856606 					VHOST_USER_SET_PROTOCOL_FEATURES,
109*2d9fd380Sjfb8856606 					&dev->protocol_features))
110*2d9fd380Sjfb8856606 			return -1;
111*2d9fd380Sjfb8856606 
112*2d9fd380Sjfb8856606 		if (!(dev->protocol_features &
113*2d9fd380Sjfb8856606 				(1ULL << VHOST_USER_PROTOCOL_F_MQ)))
114*2d9fd380Sjfb8856606 			dev->unsupported_features |= (1ull << VIRTIO_NET_F_MQ);
115*2d9fd380Sjfb8856606 	}
116*2d9fd380Sjfb8856606 
117d30ea906Sjfb8856606 	dev->device_features |= dev->frontend_features;
118d30ea906Sjfb8856606 
119d30ea906Sjfb8856606 	/* umask vhost-user unsupported features */
120d30ea906Sjfb8856606 	dev->device_features &= ~(dev->unsupported_features);
121d30ea906Sjfb8856606 
122d30ea906Sjfb8856606 	dev->features &= dev->device_features;
123d30ea906Sjfb8856606 
1244418919fSjohnjiang 	/* For packed ring, resetting queues is required in reconnection. */
1250c6bd470Sfengbojiang 	if (vtpci_packed_queue(hw) &&
126*2d9fd380Sjfb8856606 	   (old_status & VIRTIO_CONFIG_STATUS_DRIVER_OK)) {
1274418919fSjohnjiang 		PMD_INIT_LOG(NOTICE, "Packets on the fly will be dropped"
1284418919fSjohnjiang 				" when packed ring reconnecting.");
1294418919fSjohnjiang 		virtio_user_reset_queues_packed(eth_dev);
1304418919fSjohnjiang 	}
1314418919fSjohnjiang 
132*2d9fd380Sjfb8856606 	vtpci_set_status(hw, VIRTIO_CONFIG_STATUS_FEATURES_OK);
133*2d9fd380Sjfb8856606 
134*2d9fd380Sjfb8856606 	/* Start the device */
135*2d9fd380Sjfb8856606 	vtpci_set_status(hw, VIRTIO_CONFIG_STATUS_DRIVER_OK);
136*2d9fd380Sjfb8856606 	if (!dev->started)
137d30ea906Sjfb8856606 		return -1;
138d30ea906Sjfb8856606 
139d30ea906Sjfb8856606 	if (dev->queue_pairs > 1) {
140d30ea906Sjfb8856606 		ret = virtio_user_handle_mq(dev, dev->queue_pairs);
141d30ea906Sjfb8856606 		if (ret != 0) {
142d30ea906Sjfb8856606 			PMD_INIT_LOG(ERR, "Fails to enable multi-queue pairs!");
143d30ea906Sjfb8856606 			return -1;
144d30ea906Sjfb8856606 		}
145d30ea906Sjfb8856606 	}
146d30ea906Sjfb8856606 	if (eth_dev->data->dev_flags & RTE_ETH_DEV_INTR_LSC) {
147d30ea906Sjfb8856606 		if (rte_intr_disable(eth_dev->intr_handle) < 0) {
148d30ea906Sjfb8856606 			PMD_DRV_LOG(ERR, "interrupt disable failed");
149d30ea906Sjfb8856606 			return -1;
150d30ea906Sjfb8856606 		}
151d30ea906Sjfb8856606 		rte_intr_callback_unregister(eth_dev->intr_handle,
152d30ea906Sjfb8856606 					     virtio_interrupt_handler,
153d30ea906Sjfb8856606 					     eth_dev);
154d30ea906Sjfb8856606 		eth_dev->intr_handle->fd = connectfd;
155d30ea906Sjfb8856606 		rte_intr_callback_register(eth_dev->intr_handle,
156d30ea906Sjfb8856606 					   virtio_interrupt_handler, eth_dev);
157d30ea906Sjfb8856606 
158d30ea906Sjfb8856606 		if (rte_intr_enable(eth_dev->intr_handle) < 0) {
159d30ea906Sjfb8856606 			PMD_DRV_LOG(ERR, "interrupt enable failed");
160d30ea906Sjfb8856606 			return -1;
161d30ea906Sjfb8856606 		}
162d30ea906Sjfb8856606 	}
163d30ea906Sjfb8856606 	PMD_INIT_LOG(NOTICE, "server mode virtio-user reconnection succeeds!");
164d30ea906Sjfb8856606 	return 0;
165d30ea906Sjfb8856606 }
166d30ea906Sjfb8856606 
167a9643ea8Slogwang static void
virtio_user_delayed_handler(void * param)1682bfe3f2eSlogwang virtio_user_delayed_handler(void *param)
1692bfe3f2eSlogwang {
1702bfe3f2eSlogwang 	struct virtio_hw *hw = (struct virtio_hw *)param;
171d30ea906Sjfb8856606 	struct rte_eth_dev *eth_dev = &rte_eth_devices[hw->port_id];
172d30ea906Sjfb8856606 	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
1732bfe3f2eSlogwang 
174d30ea906Sjfb8856606 	if (rte_intr_disable(eth_dev->intr_handle) < 0) {
175d30ea906Sjfb8856606 		PMD_DRV_LOG(ERR, "interrupt disable failed");
176d30ea906Sjfb8856606 		return;
177d30ea906Sjfb8856606 	}
178d30ea906Sjfb8856606 	rte_intr_callback_unregister(eth_dev->intr_handle,
179d30ea906Sjfb8856606 				     virtio_interrupt_handler, eth_dev);
180d30ea906Sjfb8856606 	if (dev->is_server) {
181d30ea906Sjfb8856606 		if (dev->vhostfd >= 0) {
182d30ea906Sjfb8856606 			close(dev->vhostfd);
183d30ea906Sjfb8856606 			dev->vhostfd = -1;
184*2d9fd380Sjfb8856606 			/* Until the featuers are negotiated again, don't assume
185*2d9fd380Sjfb8856606 			 * the backend supports VHOST_USER_PROTOCOL_F_STATUS
186*2d9fd380Sjfb8856606 			 */
187*2d9fd380Sjfb8856606 			dev->protocol_features &=
188*2d9fd380Sjfb8856606 				~(1ULL << VHOST_USER_PROTOCOL_F_STATUS);
189d30ea906Sjfb8856606 		}
190d30ea906Sjfb8856606 		eth_dev->intr_handle->fd = dev->listenfd;
191d30ea906Sjfb8856606 		rte_intr_callback_register(eth_dev->intr_handle,
192d30ea906Sjfb8856606 					   virtio_interrupt_handler, eth_dev);
193d30ea906Sjfb8856606 		if (rte_intr_enable(eth_dev->intr_handle) < 0) {
194d30ea906Sjfb8856606 			PMD_DRV_LOG(ERR, "interrupt enable failed");
195d30ea906Sjfb8856606 			return;
196d30ea906Sjfb8856606 		}
197d30ea906Sjfb8856606 	}
1982bfe3f2eSlogwang }
1992bfe3f2eSlogwang 
2002bfe3f2eSlogwang static void
virtio_user_read_dev_config(struct virtio_hw * hw,size_t offset,void * dst,int length)201a9643ea8Slogwang virtio_user_read_dev_config(struct virtio_hw *hw, size_t offset,
202a9643ea8Slogwang 		     void *dst, int length)
203a9643ea8Slogwang {
204a9643ea8Slogwang 	int i;
205a9643ea8Slogwang 	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
206a9643ea8Slogwang 
207a9643ea8Slogwang 	if (offset == offsetof(struct virtio_net_config, mac) &&
2084418919fSjohnjiang 	    length == RTE_ETHER_ADDR_LEN) {
2094418919fSjohnjiang 		for (i = 0; i < RTE_ETHER_ADDR_LEN; ++i)
210a9643ea8Slogwang 			((uint8_t *)dst)[i] = dev->mac_addr[i];
211a9643ea8Slogwang 		return;
212a9643ea8Slogwang 	}
213a9643ea8Slogwang 
2142bfe3f2eSlogwang 	if (offset == offsetof(struct virtio_net_config, status)) {
2152bfe3f2eSlogwang 		char buf[128];
2162bfe3f2eSlogwang 
2172bfe3f2eSlogwang 		if (dev->vhostfd >= 0) {
2182bfe3f2eSlogwang 			int r;
2192bfe3f2eSlogwang 			int flags;
2202bfe3f2eSlogwang 
2212bfe3f2eSlogwang 			flags = fcntl(dev->vhostfd, F_GETFL);
2222bfe3f2eSlogwang 			if (fcntl(dev->vhostfd, F_SETFL,
2232bfe3f2eSlogwang 					flags | O_NONBLOCK) == -1) {
2242bfe3f2eSlogwang 				PMD_DRV_LOG(ERR, "error setting O_NONBLOCK flag");
2252bfe3f2eSlogwang 				return;
2262bfe3f2eSlogwang 			}
2272bfe3f2eSlogwang 			r = recv(dev->vhostfd, buf, 128, MSG_PEEK);
2282bfe3f2eSlogwang 			if (r == 0 || (r < 0 && errno != EAGAIN)) {
2290c6bd470Sfengbojiang 				dev->net_status &= (~VIRTIO_NET_S_LINK_UP);
2302bfe3f2eSlogwang 				PMD_DRV_LOG(ERR, "virtio-user port %u is down",
2312bfe3f2eSlogwang 					    hw->port_id);
232d30ea906Sjfb8856606 
233d30ea906Sjfb8856606 				/* This function could be called in the process
234d30ea906Sjfb8856606 				 * of interrupt handling, callback cannot be
235d30ea906Sjfb8856606 				 * unregistered here, set an alarm to do it.
2362bfe3f2eSlogwang 				 */
2372bfe3f2eSlogwang 				rte_eal_alarm_set(1,
2382bfe3f2eSlogwang 						  virtio_user_delayed_handler,
2392bfe3f2eSlogwang 						  (void *)hw);
2402bfe3f2eSlogwang 			} else {
2410c6bd470Sfengbojiang 				dev->net_status |= VIRTIO_NET_S_LINK_UP;
2422bfe3f2eSlogwang 			}
243d30ea906Sjfb8856606 			if (fcntl(dev->vhostfd, F_SETFL,
244d30ea906Sjfb8856606 					flags & ~O_NONBLOCK) == -1) {
245d30ea906Sjfb8856606 				PMD_DRV_LOG(ERR, "error clearing O_NONBLOCK flag");
246d30ea906Sjfb8856606 				return;
2472bfe3f2eSlogwang 			}
248d30ea906Sjfb8856606 		} else if (dev->is_server) {
2490c6bd470Sfengbojiang 			dev->net_status &= (~VIRTIO_NET_S_LINK_UP);
250d30ea906Sjfb8856606 			if (virtio_user_server_reconnect(dev) >= 0)
2510c6bd470Sfengbojiang 				dev->net_status |= VIRTIO_NET_S_LINK_UP;
252d30ea906Sjfb8856606 		}
253d30ea906Sjfb8856606 
2540c6bd470Sfengbojiang 		*(uint16_t *)dst = dev->net_status;
2552bfe3f2eSlogwang 	}
256a9643ea8Slogwang 
257a9643ea8Slogwang 	if (offset == offsetof(struct virtio_net_config, max_virtqueue_pairs))
258a9643ea8Slogwang 		*(uint16_t *)dst = dev->max_queue_pairs;
259a9643ea8Slogwang }
260a9643ea8Slogwang 
261a9643ea8Slogwang static void
virtio_user_write_dev_config(struct virtio_hw * hw,size_t offset,const void * src,int length)262a9643ea8Slogwang virtio_user_write_dev_config(struct virtio_hw *hw, size_t offset,
263a9643ea8Slogwang 		      const void *src, int length)
264a9643ea8Slogwang {
265a9643ea8Slogwang 	int i;
266a9643ea8Slogwang 	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
267a9643ea8Slogwang 
268a9643ea8Slogwang 	if ((offset == offsetof(struct virtio_net_config, mac)) &&
2694418919fSjohnjiang 	    (length == RTE_ETHER_ADDR_LEN))
2704418919fSjohnjiang 		for (i = 0; i < RTE_ETHER_ADDR_LEN; ++i)
271a9643ea8Slogwang 			dev->mac_addr[i] = ((const uint8_t *)src)[i];
272a9643ea8Slogwang 	else
2732bfe3f2eSlogwang 		PMD_DRV_LOG(ERR, "not supported offset=%zu, len=%d",
274a9643ea8Slogwang 			    offset, length);
275a9643ea8Slogwang }
276a9643ea8Slogwang 
277a9643ea8Slogwang static void
virtio_user_reset(struct virtio_hw * hw)2782bfe3f2eSlogwang virtio_user_reset(struct virtio_hw *hw)
2792bfe3f2eSlogwang {
2802bfe3f2eSlogwang 	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
2812bfe3f2eSlogwang 
2822bfe3f2eSlogwang 	if (dev->status & VIRTIO_CONFIG_STATUS_DRIVER_OK)
2832bfe3f2eSlogwang 		virtio_user_stop_device(dev);
2842bfe3f2eSlogwang }
2852bfe3f2eSlogwang 
2862bfe3f2eSlogwang static void
virtio_user_set_status(struct virtio_hw * hw,uint8_t status)287a9643ea8Slogwang virtio_user_set_status(struct virtio_hw *hw, uint8_t status)
288a9643ea8Slogwang {
289a9643ea8Slogwang 	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
290*2d9fd380Sjfb8856606 	uint8_t old_status = dev->status;
291a9643ea8Slogwang 
292*2d9fd380Sjfb8856606 	if (status & VIRTIO_CONFIG_STATUS_FEATURES_OK &&
293*2d9fd380Sjfb8856606 			~old_status & VIRTIO_CONFIG_STATUS_FEATURES_OK)
294*2d9fd380Sjfb8856606 		virtio_user_dev_set_features(dev);
295a9643ea8Slogwang 	if (status & VIRTIO_CONFIG_STATUS_DRIVER_OK)
296a9643ea8Slogwang 		virtio_user_start_device(dev);
2972bfe3f2eSlogwang 	else if (status == VIRTIO_CONFIG_STATUS_RESET)
2982bfe3f2eSlogwang 		virtio_user_reset(hw);
299*2d9fd380Sjfb8856606 
300*2d9fd380Sjfb8856606 	virtio_user_dev_set_status(dev, status);
301a9643ea8Slogwang }
302a9643ea8Slogwang 
303a9643ea8Slogwang static uint8_t
virtio_user_get_status(struct virtio_hw * hw)304a9643ea8Slogwang virtio_user_get_status(struct virtio_hw *hw)
305a9643ea8Slogwang {
306a9643ea8Slogwang 	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
307a9643ea8Slogwang 
308*2d9fd380Sjfb8856606 	virtio_user_dev_update_status(dev);
309*2d9fd380Sjfb8856606 
310a9643ea8Slogwang 	return dev->status;
311a9643ea8Slogwang }
312a9643ea8Slogwang 
313a9643ea8Slogwang static uint64_t
virtio_user_get_features(struct virtio_hw * hw)314a9643ea8Slogwang virtio_user_get_features(struct virtio_hw *hw)
315a9643ea8Slogwang {
316a9643ea8Slogwang 	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
317a9643ea8Slogwang 
3182bfe3f2eSlogwang 	/* unmask feature bits defined in vhost user protocol */
3192bfe3f2eSlogwang 	return dev->device_features & VIRTIO_PMD_SUPPORTED_GUEST_FEATURES;
320a9643ea8Slogwang }
321a9643ea8Slogwang 
322a9643ea8Slogwang static void
virtio_user_set_features(struct virtio_hw * hw,uint64_t features)323a9643ea8Slogwang virtio_user_set_features(struct virtio_hw *hw, uint64_t features)
324a9643ea8Slogwang {
325a9643ea8Slogwang 	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
326a9643ea8Slogwang 
3272bfe3f2eSlogwang 	dev->features = features & dev->device_features;
328a9643ea8Slogwang }
329a9643ea8Slogwang 
330a9643ea8Slogwang static uint8_t
virtio_user_get_isr(struct virtio_hw * hw __rte_unused)331a9643ea8Slogwang virtio_user_get_isr(struct virtio_hw *hw __rte_unused)
332a9643ea8Slogwang {
3332bfe3f2eSlogwang 	/* rxq interrupts and config interrupt are separated in virtio-user,
3342bfe3f2eSlogwang 	 * here we only report config change.
335a9643ea8Slogwang 	 */
3362bfe3f2eSlogwang 	return VIRTIO_PCI_ISR_CONFIG;
337a9643ea8Slogwang }
338a9643ea8Slogwang 
339a9643ea8Slogwang static uint16_t
virtio_user_set_config_irq(struct virtio_hw * hw __rte_unused,uint16_t vec __rte_unused)340a9643ea8Slogwang virtio_user_set_config_irq(struct virtio_hw *hw __rte_unused,
341a9643ea8Slogwang 		    uint16_t vec __rte_unused)
342a9643ea8Slogwang {
3432bfe3f2eSlogwang 	return 0;
3442bfe3f2eSlogwang }
3452bfe3f2eSlogwang 
3462bfe3f2eSlogwang static uint16_t
virtio_user_set_queue_irq(struct virtio_hw * hw __rte_unused,struct virtqueue * vq __rte_unused,uint16_t vec)3472bfe3f2eSlogwang virtio_user_set_queue_irq(struct virtio_hw *hw __rte_unused,
3482bfe3f2eSlogwang 			  struct virtqueue *vq __rte_unused,
3492bfe3f2eSlogwang 			  uint16_t vec)
3502bfe3f2eSlogwang {
3512bfe3f2eSlogwang 	/* pretend we have done that */
3522bfe3f2eSlogwang 	return vec;
353a9643ea8Slogwang }
354a9643ea8Slogwang 
355a9643ea8Slogwang /* This function is to get the queue size, aka, number of descs, of a specified
356a9643ea8Slogwang  * queue. Different with the VHOST_USER_GET_QUEUE_NUM, which is used to get the
357a9643ea8Slogwang  * max supported queues.
358a9643ea8Slogwang  */
359a9643ea8Slogwang static uint16_t
virtio_user_get_queue_num(struct virtio_hw * hw,uint16_t queue_id __rte_unused)360a9643ea8Slogwang virtio_user_get_queue_num(struct virtio_hw *hw, uint16_t queue_id __rte_unused)
361a9643ea8Slogwang {
362a9643ea8Slogwang 	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
363a9643ea8Slogwang 
364a9643ea8Slogwang 	/* Currently, each queue has same queue size */
365a9643ea8Slogwang 	return dev->queue_size;
366a9643ea8Slogwang }
367a9643ea8Slogwang 
3684418919fSjohnjiang static void
virtio_user_setup_queue_packed(struct virtqueue * vq,struct virtio_user_dev * dev)3694418919fSjohnjiang virtio_user_setup_queue_packed(struct virtqueue *vq,
3704418919fSjohnjiang 			       struct virtio_user_dev *dev)
371a9643ea8Slogwang {
3724418919fSjohnjiang 	uint16_t queue_idx = vq->vq_queue_index;
3734418919fSjohnjiang 	struct vring_packed *vring;
3744418919fSjohnjiang 	uint64_t desc_addr;
3754418919fSjohnjiang 	uint64_t avail_addr;
3764418919fSjohnjiang 	uint64_t used_addr;
3774418919fSjohnjiang 	uint16_t i;
3784418919fSjohnjiang 
3794418919fSjohnjiang 	vring  = &dev->packed_vrings[queue_idx];
3804418919fSjohnjiang 	desc_addr = (uintptr_t)vq->vq_ring_virt_mem;
3814418919fSjohnjiang 	avail_addr = desc_addr + vq->vq_nentries *
3824418919fSjohnjiang 		sizeof(struct vring_packed_desc);
3834418919fSjohnjiang 	used_addr = RTE_ALIGN_CEIL(avail_addr +
3844418919fSjohnjiang 			   sizeof(struct vring_packed_desc_event),
3854418919fSjohnjiang 			   VIRTIO_PCI_VRING_ALIGN);
3864418919fSjohnjiang 	vring->num = vq->vq_nentries;
3874418919fSjohnjiang 	vring->desc = (void *)(uintptr_t)desc_addr;
3884418919fSjohnjiang 	vring->driver = (void *)(uintptr_t)avail_addr;
3894418919fSjohnjiang 	vring->device = (void *)(uintptr_t)used_addr;
3904418919fSjohnjiang 	dev->packed_queues[queue_idx].avail_wrap_counter = true;
3914418919fSjohnjiang 	dev->packed_queues[queue_idx].used_wrap_counter = true;
3924418919fSjohnjiang 
3934418919fSjohnjiang 	for (i = 0; i < vring->num; i++)
3944418919fSjohnjiang 		vring->desc[i].flags = 0;
3954418919fSjohnjiang }
3964418919fSjohnjiang 
3974418919fSjohnjiang static void
virtio_user_setup_queue_split(struct virtqueue * vq,struct virtio_user_dev * dev)3984418919fSjohnjiang virtio_user_setup_queue_split(struct virtqueue *vq, struct virtio_user_dev *dev)
3994418919fSjohnjiang {
400a9643ea8Slogwang 	uint16_t queue_idx = vq->vq_queue_index;
401a9643ea8Slogwang 	uint64_t desc_addr, avail_addr, used_addr;
402a9643ea8Slogwang 
403a9643ea8Slogwang 	desc_addr = (uintptr_t)vq->vq_ring_virt_mem;
404a9643ea8Slogwang 	avail_addr = desc_addr + vq->vq_nentries * sizeof(struct vring_desc);
405a9643ea8Slogwang 	used_addr = RTE_ALIGN_CEIL(avail_addr + offsetof(struct vring_avail,
406a9643ea8Slogwang 							 ring[vq->vq_nentries]),
407a9643ea8Slogwang 				   VIRTIO_PCI_VRING_ALIGN);
408a9643ea8Slogwang 
409a9643ea8Slogwang 	dev->vrings[queue_idx].num = vq->vq_nentries;
410a9643ea8Slogwang 	dev->vrings[queue_idx].desc = (void *)(uintptr_t)desc_addr;
411a9643ea8Slogwang 	dev->vrings[queue_idx].avail = (void *)(uintptr_t)avail_addr;
412a9643ea8Slogwang 	dev->vrings[queue_idx].used = (void *)(uintptr_t)used_addr;
4134418919fSjohnjiang }
4144418919fSjohnjiang 
4154418919fSjohnjiang static int
virtio_user_setup_queue(struct virtio_hw * hw,struct virtqueue * vq)4164418919fSjohnjiang virtio_user_setup_queue(struct virtio_hw *hw, struct virtqueue *vq)
4174418919fSjohnjiang {
4184418919fSjohnjiang 	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
4194418919fSjohnjiang 
4204418919fSjohnjiang 	if (vtpci_packed_queue(hw))
4214418919fSjohnjiang 		virtio_user_setup_queue_packed(vq, dev);
4224418919fSjohnjiang 	else
4234418919fSjohnjiang 		virtio_user_setup_queue_split(vq, dev);
424a9643ea8Slogwang 
425a9643ea8Slogwang 	return 0;
426a9643ea8Slogwang }
427a9643ea8Slogwang 
428a9643ea8Slogwang static void
virtio_user_del_queue(struct virtio_hw * hw,struct virtqueue * vq)429a9643ea8Slogwang virtio_user_del_queue(struct virtio_hw *hw, struct virtqueue *vq)
430a9643ea8Slogwang {
431a9643ea8Slogwang 	/* For legacy devices, write 0 to VIRTIO_PCI_QUEUE_PFN port, QEMU
432a9643ea8Slogwang 	 * correspondingly stops the ioeventfds, and reset the status of
433a9643ea8Slogwang 	 * the device.
434a9643ea8Slogwang 	 * For modern devices, set queue desc, avail, used in PCI bar to 0,
435a9643ea8Slogwang 	 * not see any more behavior in QEMU.
436a9643ea8Slogwang 	 *
437a9643ea8Slogwang 	 * Here we just care about what information to deliver to vhost-user
438a9643ea8Slogwang 	 * or vhost-kernel. So we just close ioeventfd for now.
439a9643ea8Slogwang 	 */
440a9643ea8Slogwang 	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
441a9643ea8Slogwang 
442a9643ea8Slogwang 	close(dev->callfds[vq->vq_queue_index]);
443a9643ea8Slogwang 	close(dev->kickfds[vq->vq_queue_index]);
444a9643ea8Slogwang }
445a9643ea8Slogwang 
446a9643ea8Slogwang static void
virtio_user_notify_queue(struct virtio_hw * hw,struct virtqueue * vq)447a9643ea8Slogwang virtio_user_notify_queue(struct virtio_hw *hw, struct virtqueue *vq)
448a9643ea8Slogwang {
449a9643ea8Slogwang 	uint64_t buf = 1;
450a9643ea8Slogwang 	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
451a9643ea8Slogwang 
452a9643ea8Slogwang 	if (hw->cvq && (hw->cvq->vq == vq)) {
4534418919fSjohnjiang 		if (vtpci_packed_queue(vq->hw))
4544418919fSjohnjiang 			virtio_user_handle_cq_packed(dev, vq->vq_queue_index);
4554418919fSjohnjiang 		else
456a9643ea8Slogwang 			virtio_user_handle_cq(dev, vq->vq_queue_index);
457a9643ea8Slogwang 		return;
458a9643ea8Slogwang 	}
459a9643ea8Slogwang 
460a9643ea8Slogwang 	if (write(dev->kickfds[vq->vq_queue_index], &buf, sizeof(buf)) < 0)
4612bfe3f2eSlogwang 		PMD_DRV_LOG(ERR, "failed to kick backend: %s",
462a9643ea8Slogwang 			    strerror(errno));
463a9643ea8Slogwang }
464a9643ea8Slogwang 
4652bfe3f2eSlogwang const struct virtio_pci_ops virtio_user_ops = {
466a9643ea8Slogwang 	.read_dev_cfg	= virtio_user_read_dev_config,
467a9643ea8Slogwang 	.write_dev_cfg	= virtio_user_write_dev_config,
468a9643ea8Slogwang 	.get_status	= virtio_user_get_status,
469a9643ea8Slogwang 	.set_status	= virtio_user_set_status,
470a9643ea8Slogwang 	.get_features	= virtio_user_get_features,
471a9643ea8Slogwang 	.set_features	= virtio_user_set_features,
472a9643ea8Slogwang 	.get_isr	= virtio_user_get_isr,
473a9643ea8Slogwang 	.set_config_irq	= virtio_user_set_config_irq,
4742bfe3f2eSlogwang 	.set_queue_irq	= virtio_user_set_queue_irq,
475a9643ea8Slogwang 	.get_queue_num	= virtio_user_get_queue_num,
476a9643ea8Slogwang 	.setup_queue	= virtio_user_setup_queue,
477a9643ea8Slogwang 	.del_queue	= virtio_user_del_queue,
478a9643ea8Slogwang 	.notify_queue	= virtio_user_notify_queue,
479a9643ea8Slogwang };
480a9643ea8Slogwang 
481a9643ea8Slogwang static const char *valid_args[] = {
482a9643ea8Slogwang #define VIRTIO_USER_ARG_QUEUES_NUM     "queues"
483a9643ea8Slogwang 	VIRTIO_USER_ARG_QUEUES_NUM,
484a9643ea8Slogwang #define VIRTIO_USER_ARG_CQ_NUM         "cq"
485a9643ea8Slogwang 	VIRTIO_USER_ARG_CQ_NUM,
486a9643ea8Slogwang #define VIRTIO_USER_ARG_MAC            "mac"
487a9643ea8Slogwang 	VIRTIO_USER_ARG_MAC,
488a9643ea8Slogwang #define VIRTIO_USER_ARG_PATH           "path"
489a9643ea8Slogwang 	VIRTIO_USER_ARG_PATH,
490a9643ea8Slogwang #define VIRTIO_USER_ARG_QUEUE_SIZE     "queue_size"
491a9643ea8Slogwang 	VIRTIO_USER_ARG_QUEUE_SIZE,
4922bfe3f2eSlogwang #define VIRTIO_USER_ARG_INTERFACE_NAME "iface"
4932bfe3f2eSlogwang 	VIRTIO_USER_ARG_INTERFACE_NAME,
494d30ea906Sjfb8856606 #define VIRTIO_USER_ARG_SERVER_MODE    "server"
495d30ea906Sjfb8856606 	VIRTIO_USER_ARG_SERVER_MODE,
496d30ea906Sjfb8856606 #define VIRTIO_USER_ARG_MRG_RXBUF      "mrg_rxbuf"
497d30ea906Sjfb8856606 	VIRTIO_USER_ARG_MRG_RXBUF,
498d30ea906Sjfb8856606 #define VIRTIO_USER_ARG_IN_ORDER       "in_order"
499d30ea906Sjfb8856606 	VIRTIO_USER_ARG_IN_ORDER,
5004418919fSjohnjiang #define VIRTIO_USER_ARG_PACKED_VQ      "packed_vq"
5014418919fSjohnjiang 	VIRTIO_USER_ARG_PACKED_VQ,
502*2d9fd380Sjfb8856606 #define VIRTIO_USER_ARG_SPEED          "speed"
503*2d9fd380Sjfb8856606 	VIRTIO_USER_ARG_SPEED,
504*2d9fd380Sjfb8856606 #define VIRTIO_USER_ARG_VECTORIZED     "vectorized"
505*2d9fd380Sjfb8856606 	VIRTIO_USER_ARG_VECTORIZED,
506a9643ea8Slogwang 	NULL
507a9643ea8Slogwang };
508a9643ea8Slogwang 
509a9643ea8Slogwang #define VIRTIO_USER_DEF_CQ_EN	0
510a9643ea8Slogwang #define VIRTIO_USER_DEF_Q_NUM	1
511a9643ea8Slogwang #define VIRTIO_USER_DEF_Q_SZ	256
512d30ea906Sjfb8856606 #define VIRTIO_USER_DEF_SERVER_MODE	0
513a9643ea8Slogwang 
514a9643ea8Slogwang static int
get_string_arg(const char * key __rte_unused,const char * value,void * extra_args)515a9643ea8Slogwang get_string_arg(const char *key __rte_unused,
516a9643ea8Slogwang 	       const char *value, void *extra_args)
517a9643ea8Slogwang {
518a9643ea8Slogwang 	if (!value || !extra_args)
519a9643ea8Slogwang 		return -EINVAL;
520a9643ea8Slogwang 
521a9643ea8Slogwang 	*(char **)extra_args = strdup(value);
522a9643ea8Slogwang 
5232bfe3f2eSlogwang 	if (!*(char **)extra_args)
5242bfe3f2eSlogwang 		return -ENOMEM;
5252bfe3f2eSlogwang 
526a9643ea8Slogwang 	return 0;
527a9643ea8Slogwang }
528a9643ea8Slogwang 
529a9643ea8Slogwang static int
get_integer_arg(const char * key __rte_unused,const char * value,void * extra_args)530a9643ea8Slogwang get_integer_arg(const char *key __rte_unused,
531a9643ea8Slogwang 		const char *value, void *extra_args)
532a9643ea8Slogwang {
5330c6bd470Sfengbojiang 	uint64_t integer = 0;
534a9643ea8Slogwang 	if (!value || !extra_args)
535a9643ea8Slogwang 		return -EINVAL;
5360c6bd470Sfengbojiang 	errno = 0;
5370c6bd470Sfengbojiang 	integer = strtoull(value, NULL, 0);
5380c6bd470Sfengbojiang 	/* extra_args keeps default value, it should be replaced
5390c6bd470Sfengbojiang 	 * only in case of successful parsing of the 'value' arg
5400c6bd470Sfengbojiang 	 */
5410c6bd470Sfengbojiang 	if (errno == 0)
5420c6bd470Sfengbojiang 		*(uint64_t *)extra_args = integer;
5430c6bd470Sfengbojiang 	return -errno;
544a9643ea8Slogwang }
545a9643ea8Slogwang 
546*2d9fd380Sjfb8856606 static uint32_t
vdpa_dynamic_major_num(void)547*2d9fd380Sjfb8856606 vdpa_dynamic_major_num(void)
548*2d9fd380Sjfb8856606 {
549*2d9fd380Sjfb8856606 	FILE *fp;
550*2d9fd380Sjfb8856606 	char *line = NULL;
551*2d9fd380Sjfb8856606 	size_t size;
552*2d9fd380Sjfb8856606 	char name[11];
553*2d9fd380Sjfb8856606 	bool found = false;
554*2d9fd380Sjfb8856606 	uint32_t num;
555*2d9fd380Sjfb8856606 
556*2d9fd380Sjfb8856606 	fp = fopen("/proc/devices", "r");
557*2d9fd380Sjfb8856606 	if (fp == NULL) {
558*2d9fd380Sjfb8856606 		PMD_INIT_LOG(ERR, "Cannot open /proc/devices: %s",
559*2d9fd380Sjfb8856606 			     strerror(errno));
560*2d9fd380Sjfb8856606 		return UNNAMED_MAJOR;
561*2d9fd380Sjfb8856606 	}
562*2d9fd380Sjfb8856606 
563*2d9fd380Sjfb8856606 	while (getline(&line, &size, fp) > 0) {
564*2d9fd380Sjfb8856606 		char *stripped = line + strspn(line, " ");
565*2d9fd380Sjfb8856606 		if ((sscanf(stripped, "%u %10s", &num, name) == 2) &&
566*2d9fd380Sjfb8856606 		    (strncmp(name, "vhost-vdpa", 10) == 0)) {
567*2d9fd380Sjfb8856606 			found = true;
568*2d9fd380Sjfb8856606 			break;
569*2d9fd380Sjfb8856606 		}
570*2d9fd380Sjfb8856606 	}
571*2d9fd380Sjfb8856606 	fclose(fp);
572*2d9fd380Sjfb8856606 	return found ? num : UNNAMED_MAJOR;
573*2d9fd380Sjfb8856606 }
574*2d9fd380Sjfb8856606 
575*2d9fd380Sjfb8856606 static enum virtio_user_backend_type
virtio_user_backend_type(const char * path)576*2d9fd380Sjfb8856606 virtio_user_backend_type(const char *path)
577*2d9fd380Sjfb8856606 {
578*2d9fd380Sjfb8856606 	struct stat sb;
579*2d9fd380Sjfb8856606 
580*2d9fd380Sjfb8856606 	if (stat(path, &sb) == -1) {
581*2d9fd380Sjfb8856606 		if (errno == ENOENT)
582*2d9fd380Sjfb8856606 			return VIRTIO_USER_BACKEND_VHOST_USER;
583*2d9fd380Sjfb8856606 
584*2d9fd380Sjfb8856606 		PMD_INIT_LOG(ERR, "Stat fails: %s (%s)\n", path,
585*2d9fd380Sjfb8856606 			     strerror(errno));
586*2d9fd380Sjfb8856606 		return VIRTIO_USER_BACKEND_UNKNOWN;
587*2d9fd380Sjfb8856606 	}
588*2d9fd380Sjfb8856606 
589*2d9fd380Sjfb8856606 	if (S_ISSOCK(sb.st_mode)) {
590*2d9fd380Sjfb8856606 		return VIRTIO_USER_BACKEND_VHOST_USER;
591*2d9fd380Sjfb8856606 	} else if (S_ISCHR(sb.st_mode)) {
592*2d9fd380Sjfb8856606 		if (major(sb.st_rdev) == MISC_MAJOR)
593*2d9fd380Sjfb8856606 			return VIRTIO_USER_BACKEND_VHOST_KERNEL;
594*2d9fd380Sjfb8856606 		if (major(sb.st_rdev) == vdpa_dynamic_major_num())
595*2d9fd380Sjfb8856606 			return VIRTIO_USER_BACKEND_VHOST_VDPA;
596*2d9fd380Sjfb8856606 	}
597*2d9fd380Sjfb8856606 	return VIRTIO_USER_BACKEND_UNKNOWN;
598*2d9fd380Sjfb8856606 }
599*2d9fd380Sjfb8856606 
600a9643ea8Slogwang static struct rte_eth_dev *
virtio_user_eth_dev_alloc(struct rte_vdev_device * vdev)6012bfe3f2eSlogwang virtio_user_eth_dev_alloc(struct rte_vdev_device *vdev)
602a9643ea8Slogwang {
603a9643ea8Slogwang 	struct rte_eth_dev *eth_dev;
604a9643ea8Slogwang 	struct rte_eth_dev_data *data;
605a9643ea8Slogwang 	struct virtio_hw *hw;
606a9643ea8Slogwang 	struct virtio_user_dev *dev;
607a9643ea8Slogwang 
6082bfe3f2eSlogwang 	eth_dev = rte_eth_vdev_allocate(vdev, sizeof(*hw));
609a9643ea8Slogwang 	if (!eth_dev) {
610a9643ea8Slogwang 		PMD_INIT_LOG(ERR, "cannot alloc rte_eth_dev");
611a9643ea8Slogwang 		return NULL;
612a9643ea8Slogwang 	}
613a9643ea8Slogwang 
614a9643ea8Slogwang 	data = eth_dev->data;
6152bfe3f2eSlogwang 	hw = eth_dev->data->dev_private;
616a9643ea8Slogwang 
617a9643ea8Slogwang 	dev = rte_zmalloc(NULL, sizeof(*dev), 0);
618a9643ea8Slogwang 	if (!dev) {
619a9643ea8Slogwang 		PMD_INIT_LOG(ERR, "malloc virtio_user_dev failed");
620a9643ea8Slogwang 		rte_eth_dev_release_port(eth_dev);
621a9643ea8Slogwang 		return NULL;
622a9643ea8Slogwang 	}
623a9643ea8Slogwang 
6242bfe3f2eSlogwang 	hw->port_id = data->port_id;
6252bfe3f2eSlogwang 	dev->port_id = data->port_id;
6262bfe3f2eSlogwang 	virtio_hw_internal[hw->port_id].vtpci_ops = &virtio_user_ops;
6272bfe3f2eSlogwang 	/*
6282bfe3f2eSlogwang 	 * MSIX is required to enable LSC (see virtio_init_device).
6292bfe3f2eSlogwang 	 * Here just pretend that we support msix.
6302bfe3f2eSlogwang 	 */
6312bfe3f2eSlogwang 	hw->use_msix = 1;
632a9643ea8Slogwang 	hw->modern   = 0;
633*2d9fd380Sjfb8856606 	hw->use_vec_rx = 0;
634*2d9fd380Sjfb8856606 	hw->use_vec_tx = 0;
635d30ea906Sjfb8856606 	hw->use_inorder_rx = 0;
636d30ea906Sjfb8856606 	hw->use_inorder_tx = 0;
637a9643ea8Slogwang 	hw->virtio_user_dev = dev;
638a9643ea8Slogwang 	return eth_dev;
639a9643ea8Slogwang }
640a9643ea8Slogwang 
641a9643ea8Slogwang static void
virtio_user_eth_dev_free(struct rte_eth_dev * eth_dev)642a9643ea8Slogwang virtio_user_eth_dev_free(struct rte_eth_dev *eth_dev)
643a9643ea8Slogwang {
644a9643ea8Slogwang 	struct rte_eth_dev_data *data = eth_dev->data;
645a9643ea8Slogwang 	struct virtio_hw *hw = data->dev_private;
646a9643ea8Slogwang 
647a9643ea8Slogwang 	rte_free(hw->virtio_user_dev);
648a9643ea8Slogwang 	rte_eth_dev_release_port(eth_dev);
649a9643ea8Slogwang }
650a9643ea8Slogwang 
651a9643ea8Slogwang /* Dev initialization routine. Invoked once for each virtio vdev at
6522bfe3f2eSlogwang  * EAL init time, see rte_bus_probe().
653a9643ea8Slogwang  * Returns 0 on success.
654a9643ea8Slogwang  */
655a9643ea8Slogwang static int
virtio_user_pmd_probe(struct rte_vdev_device * dev)6562bfe3f2eSlogwang virtio_user_pmd_probe(struct rte_vdev_device *dev)
657a9643ea8Slogwang {
658a9643ea8Slogwang 	struct rte_kvargs *kvlist = NULL;
659a9643ea8Slogwang 	struct rte_eth_dev *eth_dev;
660a9643ea8Slogwang 	struct virtio_hw *hw;
661*2d9fd380Sjfb8856606 	enum virtio_user_backend_type backend_type = VIRTIO_USER_BACKEND_UNKNOWN;
662a9643ea8Slogwang 	uint64_t queues = VIRTIO_USER_DEF_Q_NUM;
663a9643ea8Slogwang 	uint64_t cq = VIRTIO_USER_DEF_CQ_EN;
664a9643ea8Slogwang 	uint64_t queue_size = VIRTIO_USER_DEF_Q_SZ;
665d30ea906Sjfb8856606 	uint64_t server_mode = VIRTIO_USER_DEF_SERVER_MODE;
666d30ea906Sjfb8856606 	uint64_t mrg_rxbuf = 1;
667d30ea906Sjfb8856606 	uint64_t in_order = 1;
6684418919fSjohnjiang 	uint64_t packed_vq = 0;
669*2d9fd380Sjfb8856606 	uint64_t vectorized = 0;
670a9643ea8Slogwang 	char *path = NULL;
6712bfe3f2eSlogwang 	char *ifname = NULL;
672a9643ea8Slogwang 	char *mac_addr = NULL;
673a9643ea8Slogwang 	int ret = -1;
674a9643ea8Slogwang 
6751646932aSjfb8856606 	if (rte_eal_process_type() == RTE_PROC_SECONDARY) {
6761646932aSjfb8856606 		const char *name = rte_vdev_device_name(dev);
6771646932aSjfb8856606 		eth_dev = rte_eth_dev_attach_secondary(name);
6781646932aSjfb8856606 		if (!eth_dev) {
6790c6bd470Sfengbojiang 			PMD_INIT_LOG(ERR, "Failed to probe %s", name);
6801646932aSjfb8856606 			return -1;
6811646932aSjfb8856606 		}
6821646932aSjfb8856606 
6831646932aSjfb8856606 		if (eth_virtio_dev_init(eth_dev) < 0) {
6841646932aSjfb8856606 			PMD_INIT_LOG(ERR, "eth_virtio_dev_init fails");
6851646932aSjfb8856606 			rte_eth_dev_release_port(eth_dev);
6861646932aSjfb8856606 			return -1;
6871646932aSjfb8856606 		}
6881646932aSjfb8856606 
6891646932aSjfb8856606 		eth_dev->dev_ops = &virtio_user_secondary_eth_dev_ops;
6901646932aSjfb8856606 		eth_dev->device = &dev->device;
6911646932aSjfb8856606 		rte_eth_dev_probing_finish(eth_dev);
6921646932aSjfb8856606 		return 0;
6931646932aSjfb8856606 	}
6941646932aSjfb8856606 
6952bfe3f2eSlogwang 	kvlist = rte_kvargs_parse(rte_vdev_device_args(dev), valid_args);
696a9643ea8Slogwang 	if (!kvlist) {
697a9643ea8Slogwang 		PMD_INIT_LOG(ERR, "error when parsing param");
698a9643ea8Slogwang 		goto end;
699a9643ea8Slogwang 	}
700a9643ea8Slogwang 
701a9643ea8Slogwang 	if (rte_kvargs_count(kvlist, VIRTIO_USER_ARG_PATH) == 1) {
702a9643ea8Slogwang 		if (rte_kvargs_process(kvlist, VIRTIO_USER_ARG_PATH,
703a9643ea8Slogwang 				       &get_string_arg, &path) < 0) {
704a9643ea8Slogwang 			PMD_INIT_LOG(ERR, "error to parse %s",
705a9643ea8Slogwang 				     VIRTIO_USER_ARG_PATH);
706a9643ea8Slogwang 			goto end;
707a9643ea8Slogwang 		}
708a9643ea8Slogwang 	} else {
7092bfe3f2eSlogwang 		PMD_INIT_LOG(ERR, "arg %s is mandatory for virtio_user",
710d30ea906Sjfb8856606 			     VIRTIO_USER_ARG_PATH);
711a9643ea8Slogwang 		goto end;
712a9643ea8Slogwang 	}
713a9643ea8Slogwang 
714*2d9fd380Sjfb8856606 	backend_type = virtio_user_backend_type(path);
715*2d9fd380Sjfb8856606 	if (backend_type == VIRTIO_USER_BACKEND_UNKNOWN) {
716*2d9fd380Sjfb8856606 		PMD_INIT_LOG(ERR,
717*2d9fd380Sjfb8856606 			     "unable to determine backend type for path %s",
718*2d9fd380Sjfb8856606 			path);
719*2d9fd380Sjfb8856606 		goto end;
720*2d9fd380Sjfb8856606 	}
721*2d9fd380Sjfb8856606 	PMD_INIT_LOG(INFO, "Backend type detected: %s",
722*2d9fd380Sjfb8856606 		     virtio_user_backend_strings[backend_type]);
723*2d9fd380Sjfb8856606 
7242bfe3f2eSlogwang 	if (rte_kvargs_count(kvlist, VIRTIO_USER_ARG_INTERFACE_NAME) == 1) {
725*2d9fd380Sjfb8856606 		if (backend_type != VIRTIO_USER_BACKEND_VHOST_KERNEL) {
7262bfe3f2eSlogwang 			PMD_INIT_LOG(ERR,
7272bfe3f2eSlogwang 				"arg %s applies only to vhost-kernel backend",
7282bfe3f2eSlogwang 				VIRTIO_USER_ARG_INTERFACE_NAME);
7292bfe3f2eSlogwang 			goto end;
7302bfe3f2eSlogwang 		}
7312bfe3f2eSlogwang 
7322bfe3f2eSlogwang 		if (rte_kvargs_process(kvlist, VIRTIO_USER_ARG_INTERFACE_NAME,
7332bfe3f2eSlogwang 				       &get_string_arg, &ifname) < 0) {
7342bfe3f2eSlogwang 			PMD_INIT_LOG(ERR, "error to parse %s",
7352bfe3f2eSlogwang 				     VIRTIO_USER_ARG_INTERFACE_NAME);
7362bfe3f2eSlogwang 			goto end;
7372bfe3f2eSlogwang 		}
7382bfe3f2eSlogwang 	}
7392bfe3f2eSlogwang 
740a9643ea8Slogwang 	if (rte_kvargs_count(kvlist, VIRTIO_USER_ARG_MAC) == 1) {
741a9643ea8Slogwang 		if (rte_kvargs_process(kvlist, VIRTIO_USER_ARG_MAC,
742a9643ea8Slogwang 				       &get_string_arg, &mac_addr) < 0) {
743a9643ea8Slogwang 			PMD_INIT_LOG(ERR, "error to parse %s",
744a9643ea8Slogwang 				     VIRTIO_USER_ARG_MAC);
745a9643ea8Slogwang 			goto end;
746a9643ea8Slogwang 		}
747a9643ea8Slogwang 	}
748a9643ea8Slogwang 
749a9643ea8Slogwang 	if (rte_kvargs_count(kvlist, VIRTIO_USER_ARG_QUEUE_SIZE) == 1) {
750a9643ea8Slogwang 		if (rte_kvargs_process(kvlist, VIRTIO_USER_ARG_QUEUE_SIZE,
751a9643ea8Slogwang 				       &get_integer_arg, &queue_size) < 0) {
752a9643ea8Slogwang 			PMD_INIT_LOG(ERR, "error to parse %s",
753a9643ea8Slogwang 				     VIRTIO_USER_ARG_QUEUE_SIZE);
754a9643ea8Slogwang 			goto end;
755a9643ea8Slogwang 		}
756a9643ea8Slogwang 	}
757a9643ea8Slogwang 
758a9643ea8Slogwang 	if (rte_kvargs_count(kvlist, VIRTIO_USER_ARG_QUEUES_NUM) == 1) {
759a9643ea8Slogwang 		if (rte_kvargs_process(kvlist, VIRTIO_USER_ARG_QUEUES_NUM,
760a9643ea8Slogwang 				       &get_integer_arg, &queues) < 0) {
761a9643ea8Slogwang 			PMD_INIT_LOG(ERR, "error to parse %s",
762a9643ea8Slogwang 				     VIRTIO_USER_ARG_QUEUES_NUM);
763a9643ea8Slogwang 			goto end;
764a9643ea8Slogwang 		}
765a9643ea8Slogwang 	}
766a9643ea8Slogwang 
767d30ea906Sjfb8856606 	if (rte_kvargs_count(kvlist, VIRTIO_USER_ARG_SERVER_MODE) == 1) {
768d30ea906Sjfb8856606 		if (rte_kvargs_process(kvlist, VIRTIO_USER_ARG_SERVER_MODE,
769d30ea906Sjfb8856606 				       &get_integer_arg, &server_mode) < 0) {
770d30ea906Sjfb8856606 			PMD_INIT_LOG(ERR, "error to parse %s",
771d30ea906Sjfb8856606 				     VIRTIO_USER_ARG_SERVER_MODE);
772d30ea906Sjfb8856606 			goto end;
773d30ea906Sjfb8856606 		}
774d30ea906Sjfb8856606 	}
775d30ea906Sjfb8856606 
776a9643ea8Slogwang 	if (rte_kvargs_count(kvlist, VIRTIO_USER_ARG_CQ_NUM) == 1) {
777a9643ea8Slogwang 		if (rte_kvargs_process(kvlist, VIRTIO_USER_ARG_CQ_NUM,
778a9643ea8Slogwang 				       &get_integer_arg, &cq) < 0) {
779a9643ea8Slogwang 			PMD_INIT_LOG(ERR, "error to parse %s",
780a9643ea8Slogwang 				     VIRTIO_USER_ARG_CQ_NUM);
781a9643ea8Slogwang 			goto end;
782a9643ea8Slogwang 		}
783a9643ea8Slogwang 	} else if (queues > 1) {
784a9643ea8Slogwang 		cq = 1;
785a9643ea8Slogwang 	}
786a9643ea8Slogwang 
7874418919fSjohnjiang 	if (rte_kvargs_count(kvlist, VIRTIO_USER_ARG_PACKED_VQ) == 1) {
7884418919fSjohnjiang 		if (rte_kvargs_process(kvlist, VIRTIO_USER_ARG_PACKED_VQ,
7894418919fSjohnjiang 				       &get_integer_arg, &packed_vq) < 0) {
7904418919fSjohnjiang 			PMD_INIT_LOG(ERR, "error to parse %s",
7914418919fSjohnjiang 				     VIRTIO_USER_ARG_PACKED_VQ);
7924418919fSjohnjiang 			goto end;
7934418919fSjohnjiang 		}
7944418919fSjohnjiang 	}
7954418919fSjohnjiang 
796*2d9fd380Sjfb8856606 	if (rte_kvargs_count(kvlist, VIRTIO_USER_ARG_VECTORIZED) == 1) {
797*2d9fd380Sjfb8856606 		if (rte_kvargs_process(kvlist, VIRTIO_USER_ARG_VECTORIZED,
798*2d9fd380Sjfb8856606 				       &get_integer_arg, &vectorized) < 0) {
799*2d9fd380Sjfb8856606 			PMD_INIT_LOG(ERR, "error to parse %s",
800*2d9fd380Sjfb8856606 				     VIRTIO_USER_ARG_VECTORIZED);
801*2d9fd380Sjfb8856606 			goto end;
802*2d9fd380Sjfb8856606 		}
803*2d9fd380Sjfb8856606 	}
804*2d9fd380Sjfb8856606 
805a9643ea8Slogwang 	if (queues > 1 && cq == 0) {
806a9643ea8Slogwang 		PMD_INIT_LOG(ERR, "multi-q requires ctrl-q");
807a9643ea8Slogwang 		goto end;
808a9643ea8Slogwang 	}
809a9643ea8Slogwang 
8102bfe3f2eSlogwang 	if (queues > VIRTIO_MAX_VIRTQUEUE_PAIRS) {
8112bfe3f2eSlogwang 		PMD_INIT_LOG(ERR, "arg %s %" PRIu64 " exceeds the limit %u",
8122bfe3f2eSlogwang 			VIRTIO_USER_ARG_QUEUES_NUM, queues,
8132bfe3f2eSlogwang 			VIRTIO_MAX_VIRTQUEUE_PAIRS);
8142bfe3f2eSlogwang 		goto end;
8152bfe3f2eSlogwang 	}
8162bfe3f2eSlogwang 
817d30ea906Sjfb8856606 	if (rte_kvargs_count(kvlist, VIRTIO_USER_ARG_MRG_RXBUF) == 1) {
818d30ea906Sjfb8856606 		if (rte_kvargs_process(kvlist, VIRTIO_USER_ARG_MRG_RXBUF,
819d30ea906Sjfb8856606 				       &get_integer_arg, &mrg_rxbuf) < 0) {
820d30ea906Sjfb8856606 			PMD_INIT_LOG(ERR, "error to parse %s",
821d30ea906Sjfb8856606 				     VIRTIO_USER_ARG_MRG_RXBUF);
822d30ea906Sjfb8856606 			goto end;
823d30ea906Sjfb8856606 		}
824d30ea906Sjfb8856606 	}
825d30ea906Sjfb8856606 
826d30ea906Sjfb8856606 	if (rte_kvargs_count(kvlist, VIRTIO_USER_ARG_IN_ORDER) == 1) {
827d30ea906Sjfb8856606 		if (rte_kvargs_process(kvlist, VIRTIO_USER_ARG_IN_ORDER,
828d30ea906Sjfb8856606 				       &get_integer_arg, &in_order) < 0) {
829d30ea906Sjfb8856606 			PMD_INIT_LOG(ERR, "error to parse %s",
830d30ea906Sjfb8856606 				     VIRTIO_USER_ARG_IN_ORDER);
831d30ea906Sjfb8856606 			goto end;
832d30ea906Sjfb8856606 		}
833d30ea906Sjfb8856606 	}
834d30ea906Sjfb8856606 
8352bfe3f2eSlogwang 	eth_dev = virtio_user_eth_dev_alloc(dev);
836a9643ea8Slogwang 	if (!eth_dev) {
837a9643ea8Slogwang 		PMD_INIT_LOG(ERR, "virtio_user fails to alloc device");
838a9643ea8Slogwang 		goto end;
839a9643ea8Slogwang 	}
840a9643ea8Slogwang 
841a9643ea8Slogwang 	hw = eth_dev->data->dev_private;
842a9643ea8Slogwang 	if (virtio_user_dev_init(hw->virtio_user_dev, path, queues, cq,
8431646932aSjfb8856606 			 queue_size, mac_addr, &ifname, server_mode,
844*2d9fd380Sjfb8856606 			 mrg_rxbuf, in_order, packed_vq, backend_type) < 0) {
845a9643ea8Slogwang 		PMD_INIT_LOG(ERR, "virtio_user_dev_init fails");
846a9643ea8Slogwang 		virtio_user_eth_dev_free(eth_dev);
847a9643ea8Slogwang 		goto end;
848a9643ea8Slogwang 	}
849d30ea906Sjfb8856606 
8500c6bd470Sfengbojiang 	/* previously called by pci probing for physical dev */
851a9643ea8Slogwang 	if (eth_virtio_dev_init(eth_dev) < 0) {
852a9643ea8Slogwang 		PMD_INIT_LOG(ERR, "eth_virtio_dev_init fails");
853a9643ea8Slogwang 		virtio_user_eth_dev_free(eth_dev);
854a9643ea8Slogwang 		goto end;
855a9643ea8Slogwang 	}
856d30ea906Sjfb8856606 
857*2d9fd380Sjfb8856606 	if (vectorized) {
858*2d9fd380Sjfb8856606 		if (packed_vq) {
859*2d9fd380Sjfb8856606 #if defined(CC_AVX512_SUPPORT)
860*2d9fd380Sjfb8856606 			hw->use_vec_rx = 1;
861*2d9fd380Sjfb8856606 			hw->use_vec_tx = 1;
862*2d9fd380Sjfb8856606 #else
863*2d9fd380Sjfb8856606 			PMD_INIT_LOG(INFO,
864*2d9fd380Sjfb8856606 				"building environment do not support packed ring vectorized");
865*2d9fd380Sjfb8856606 #endif
866*2d9fd380Sjfb8856606 		} else {
867*2d9fd380Sjfb8856606 			hw->use_vec_rx = 1;
868*2d9fd380Sjfb8856606 		}
869*2d9fd380Sjfb8856606 	}
870*2d9fd380Sjfb8856606 
871d30ea906Sjfb8856606 	rte_eth_dev_probing_finish(eth_dev);
872a9643ea8Slogwang 	ret = 0;
873a9643ea8Slogwang 
874a9643ea8Slogwang end:
875a9643ea8Slogwang 	if (kvlist)
876a9643ea8Slogwang 		rte_kvargs_free(kvlist);
877a9643ea8Slogwang 	if (path)
878a9643ea8Slogwang 		free(path);
879a9643ea8Slogwang 	if (mac_addr)
880a9643ea8Slogwang 		free(mac_addr);
8812bfe3f2eSlogwang 	if (ifname)
8822bfe3f2eSlogwang 		free(ifname);
883a9643ea8Slogwang 	return ret;
884a9643ea8Slogwang }
885a9643ea8Slogwang 
886a9643ea8Slogwang static int
virtio_user_pmd_remove(struct rte_vdev_device * vdev)8872bfe3f2eSlogwang virtio_user_pmd_remove(struct rte_vdev_device *vdev)
888a9643ea8Slogwang {
8892bfe3f2eSlogwang 	const char *name;
890a9643ea8Slogwang 	struct rte_eth_dev *eth_dev;
891a9643ea8Slogwang 
8922bfe3f2eSlogwang 	if (!vdev)
893a9643ea8Slogwang 		return -EINVAL;
894a9643ea8Slogwang 
8952bfe3f2eSlogwang 	name = rte_vdev_device_name(vdev);
8962bfe3f2eSlogwang 	PMD_DRV_LOG(INFO, "Un-Initializing %s", name);
897a9643ea8Slogwang 	eth_dev = rte_eth_dev_allocated(name);
8984418919fSjohnjiang 	/* Port has already been released by close. */
899a9643ea8Slogwang 	if (!eth_dev)
9004418919fSjohnjiang 		return 0;
901a9643ea8Slogwang 
9021646932aSjfb8856606 	if (rte_eal_process_type() != RTE_PROC_PRIMARY)
9031646932aSjfb8856606 		return rte_eth_dev_release_port(eth_dev);
9041646932aSjfb8856606 
905a9643ea8Slogwang 	/* make sure the device is stopped, queues freed */
906*2d9fd380Sjfb8856606 	return rte_eth_dev_close(eth_dev->data->port_id);
907*2d9fd380Sjfb8856606 }
908*2d9fd380Sjfb8856606 
virtio_user_pmd_dma_map(struct rte_vdev_device * vdev,void * addr,uint64_t iova,size_t len)909*2d9fd380Sjfb8856606 static int virtio_user_pmd_dma_map(struct rte_vdev_device *vdev, void *addr,
910*2d9fd380Sjfb8856606 		uint64_t iova, size_t len)
911*2d9fd380Sjfb8856606 {
912*2d9fd380Sjfb8856606 	const char *name;
913*2d9fd380Sjfb8856606 	struct rte_eth_dev *eth_dev;
914*2d9fd380Sjfb8856606 	struct virtio_user_dev *dev;
915*2d9fd380Sjfb8856606 	struct virtio_hw *hw;
916*2d9fd380Sjfb8856606 
917*2d9fd380Sjfb8856606 	if (!vdev)
918*2d9fd380Sjfb8856606 		return -EINVAL;
919*2d9fd380Sjfb8856606 
920*2d9fd380Sjfb8856606 	name = rte_vdev_device_name(vdev);
921*2d9fd380Sjfb8856606 	eth_dev = rte_eth_dev_allocated(name);
922*2d9fd380Sjfb8856606 	/* Port has already been released by close. */
923*2d9fd380Sjfb8856606 	if (!eth_dev)
924*2d9fd380Sjfb8856606 		return 0;
925*2d9fd380Sjfb8856606 
926*2d9fd380Sjfb8856606 	hw = (struct virtio_hw *)eth_dev->data->dev_private;
927*2d9fd380Sjfb8856606 	dev = hw->virtio_user_dev;
928*2d9fd380Sjfb8856606 
929*2d9fd380Sjfb8856606 	if (dev->ops->dma_map)
930*2d9fd380Sjfb8856606 		return dev->ops->dma_map(dev, addr, iova, len);
931*2d9fd380Sjfb8856606 
932*2d9fd380Sjfb8856606 	return 0;
933*2d9fd380Sjfb8856606 }
934*2d9fd380Sjfb8856606 
virtio_user_pmd_dma_unmap(struct rte_vdev_device * vdev,void * addr,uint64_t iova,size_t len)935*2d9fd380Sjfb8856606 static int virtio_user_pmd_dma_unmap(struct rte_vdev_device *vdev, void *addr,
936*2d9fd380Sjfb8856606 		uint64_t iova, size_t len)
937*2d9fd380Sjfb8856606 {
938*2d9fd380Sjfb8856606 	const char *name;
939*2d9fd380Sjfb8856606 	struct rte_eth_dev *eth_dev;
940*2d9fd380Sjfb8856606 	struct virtio_user_dev *dev;
941*2d9fd380Sjfb8856606 	struct virtio_hw *hw;
942*2d9fd380Sjfb8856606 
943*2d9fd380Sjfb8856606 	if (!vdev)
944*2d9fd380Sjfb8856606 		return -EINVAL;
945*2d9fd380Sjfb8856606 
946*2d9fd380Sjfb8856606 	name = rte_vdev_device_name(vdev);
947*2d9fd380Sjfb8856606 	eth_dev = rte_eth_dev_allocated(name);
948*2d9fd380Sjfb8856606 	/* Port has already been released by close. */
949*2d9fd380Sjfb8856606 	if (!eth_dev)
950*2d9fd380Sjfb8856606 		return 0;
951*2d9fd380Sjfb8856606 
952*2d9fd380Sjfb8856606 	hw = (struct virtio_hw *)eth_dev->data->dev_private;
953*2d9fd380Sjfb8856606 	dev = hw->virtio_user_dev;
954*2d9fd380Sjfb8856606 
955*2d9fd380Sjfb8856606 	if (dev->ops->dma_unmap)
956*2d9fd380Sjfb8856606 		return dev->ops->dma_unmap(dev, addr, iova, len);
957a9643ea8Slogwang 
958a9643ea8Slogwang 	return 0;
959a9643ea8Slogwang }
960a9643ea8Slogwang 
9612bfe3f2eSlogwang static struct rte_vdev_driver virtio_user_driver = {
9622bfe3f2eSlogwang 	.probe = virtio_user_pmd_probe,
9632bfe3f2eSlogwang 	.remove = virtio_user_pmd_remove,
964*2d9fd380Sjfb8856606 	.dma_map = virtio_user_pmd_dma_map,
965*2d9fd380Sjfb8856606 	.dma_unmap = virtio_user_pmd_dma_unmap,
966a9643ea8Slogwang };
967a9643ea8Slogwang 
9682bfe3f2eSlogwang RTE_PMD_REGISTER_VDEV(net_virtio_user, virtio_user_driver);
9692bfe3f2eSlogwang RTE_PMD_REGISTER_ALIAS(net_virtio_user, virtio_user);
9702bfe3f2eSlogwang RTE_PMD_REGISTER_PARAM_STRING(net_virtio_user,
971a9643ea8Slogwang 	"path=<path> "
972a9643ea8Slogwang 	"mac=<mac addr> "
973a9643ea8Slogwang 	"cq=<int> "
974a9643ea8Slogwang 	"queue_size=<int> "
9752bfe3f2eSlogwang 	"queues=<int> "
976d30ea906Sjfb8856606 	"iface=<string> "
977d30ea906Sjfb8856606 	"server=<0|1> "
978d30ea906Sjfb8856606 	"mrg_rxbuf=<0|1> "
9794418919fSjohnjiang 	"in_order=<0|1> "
980*2d9fd380Sjfb8856606 	"packed_vq=<0|1> "
981*2d9fd380Sjfb8856606 	"speed=<int> "
982*2d9fd380Sjfb8856606 	"vectorized=<0|1>");
983