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