15566a3e3SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause 25566a3e3SBruce Richardson * Copyright(c) 2010-2016 Intel Corporation 3e9efa4d9SJianfeng Tan */ 4e9efa4d9SJianfeng Tan 5e9efa4d9SJianfeng Tan #include <stdint.h> 6e9efa4d9SJianfeng Tan #include <sys/types.h> 7e9efa4d9SJianfeng Tan #include <unistd.h> 8ef53b603SJianfeng Tan #include <fcntl.h> 9ef53b603SJianfeng Tan #include <sys/socket.h> 10e9efa4d9SJianfeng Tan 11ce2eabddSJianfeng Tan #include <rte_malloc.h> 12ce2eabddSJianfeng Tan #include <rte_kvargs.h> 13050fe6e9SJan Blunck #include <rte_ethdev_vdev.h> 14d4a586d2SJianfeng Tan #include <rte_bus_vdev.h> 15ef53b603SJianfeng Tan #include <rte_alarm.h> 16ce2eabddSJianfeng Tan 17ce2eabddSJianfeng Tan #include "virtio_ethdev.h" 18e9efa4d9SJianfeng Tan #include "virtio_logs.h" 19e9efa4d9SJianfeng Tan #include "virtio_pci.h" 20e9efa4d9SJianfeng Tan #include "virtqueue.h" 211b69528eSJianfeng Tan #include "virtio_rxtx.h" 22e9efa4d9SJianfeng Tan #include "virtio_user/virtio_user_dev.h" 23e9efa4d9SJianfeng Tan 24e9efa4d9SJianfeng Tan #define virtio_user_get_dev(hw) \ 25e9efa4d9SJianfeng Tan ((struct virtio_user_dev *)(hw)->virtio_user_dev) 26e9efa4d9SJianfeng Tan 27*bd8f50a4SZhiyong Yang static int 28*bd8f50a4SZhiyong Yang virtio_user_server_reconnect(struct virtio_user_dev *dev) 29*bd8f50a4SZhiyong Yang { 30*bd8f50a4SZhiyong Yang int ret; 31*bd8f50a4SZhiyong Yang int flag; 32*bd8f50a4SZhiyong Yang int connectfd; 33*bd8f50a4SZhiyong Yang struct rte_eth_dev *eth_dev = &rte_eth_devices[dev->port_id]; 34*bd8f50a4SZhiyong Yang 35*bd8f50a4SZhiyong Yang connectfd = accept(dev->listenfd, NULL, NULL); 36*bd8f50a4SZhiyong Yang if (connectfd < 0) 37*bd8f50a4SZhiyong Yang return -1; 38*bd8f50a4SZhiyong Yang 39*bd8f50a4SZhiyong Yang dev->vhostfd = connectfd; 40*bd8f50a4SZhiyong Yang flag = fcntl(connectfd, F_GETFD); 41*bd8f50a4SZhiyong Yang fcntl(connectfd, F_SETFL, flag | O_NONBLOCK); 42*bd8f50a4SZhiyong Yang 43*bd8f50a4SZhiyong Yang ret = virtio_user_start_device(dev); 44*bd8f50a4SZhiyong Yang if (ret < 0) 45*bd8f50a4SZhiyong Yang return -1; 46*bd8f50a4SZhiyong Yang 47*bd8f50a4SZhiyong Yang if (eth_dev->data->dev_flags & RTE_ETH_DEV_INTR_LSC) { 48*bd8f50a4SZhiyong Yang if (rte_intr_disable(eth_dev->intr_handle) < 0) { 49*bd8f50a4SZhiyong Yang PMD_DRV_LOG(ERR, "interrupt disable failed"); 50*bd8f50a4SZhiyong Yang return -1; 51*bd8f50a4SZhiyong Yang } 52*bd8f50a4SZhiyong Yang rte_intr_callback_unregister(eth_dev->intr_handle, 53*bd8f50a4SZhiyong Yang virtio_interrupt_handler, 54*bd8f50a4SZhiyong Yang eth_dev); 55*bd8f50a4SZhiyong Yang eth_dev->intr_handle->fd = connectfd; 56*bd8f50a4SZhiyong Yang rte_intr_callback_register(eth_dev->intr_handle, 57*bd8f50a4SZhiyong Yang virtio_interrupt_handler, eth_dev); 58*bd8f50a4SZhiyong Yang 59*bd8f50a4SZhiyong Yang if (rte_intr_enable(eth_dev->intr_handle) < 0) { 60*bd8f50a4SZhiyong Yang PMD_DRV_LOG(ERR, "interrupt enable failed"); 61*bd8f50a4SZhiyong Yang return -1; 62*bd8f50a4SZhiyong Yang } 63*bd8f50a4SZhiyong Yang } 64*bd8f50a4SZhiyong Yang PMD_INIT_LOG(NOTICE, "server mode virtio-user reconnection succeeds!"); 65*bd8f50a4SZhiyong Yang return 0; 66*bd8f50a4SZhiyong Yang } 67*bd8f50a4SZhiyong Yang 68e9efa4d9SJianfeng Tan static void 69ef53b603SJianfeng Tan virtio_user_delayed_handler(void *param) 70ef53b603SJianfeng Tan { 71ef53b603SJianfeng Tan struct virtio_hw *hw = (struct virtio_hw *)param; 72*bd8f50a4SZhiyong Yang struct rte_eth_dev *eth_dev = &rte_eth_devices[hw->port_id]; 73*bd8f50a4SZhiyong Yang struct virtio_user_dev *dev = virtio_user_get_dev(hw); 74ef53b603SJianfeng Tan 75*bd8f50a4SZhiyong Yang if (rte_intr_disable(eth_dev->intr_handle) < 0) { 76*bd8f50a4SZhiyong Yang PMD_DRV_LOG(ERR, "interrupt disable failed"); 77*bd8f50a4SZhiyong Yang return; 78*bd8f50a4SZhiyong Yang } 79*bd8f50a4SZhiyong Yang rte_intr_callback_unregister(eth_dev->intr_handle, 80*bd8f50a4SZhiyong Yang virtio_interrupt_handler, eth_dev); 81*bd8f50a4SZhiyong Yang if (dev->is_server) { 82*bd8f50a4SZhiyong Yang if (dev->vhostfd >= 0) { 83*bd8f50a4SZhiyong Yang close(dev->vhostfd); 84*bd8f50a4SZhiyong Yang dev->vhostfd = -1; 85*bd8f50a4SZhiyong Yang } 86*bd8f50a4SZhiyong Yang eth_dev->intr_handle->fd = dev->listenfd; 87*bd8f50a4SZhiyong Yang rte_intr_callback_register(eth_dev->intr_handle, 88*bd8f50a4SZhiyong Yang virtio_interrupt_handler, eth_dev); 89*bd8f50a4SZhiyong Yang if (rte_intr_enable(eth_dev->intr_handle) < 0) { 90*bd8f50a4SZhiyong Yang PMD_DRV_LOG(ERR, "interrupt enable failed"); 91*bd8f50a4SZhiyong Yang return; 92*bd8f50a4SZhiyong Yang } 93*bd8f50a4SZhiyong Yang } 94ef53b603SJianfeng Tan } 95ef53b603SJianfeng Tan 96ef53b603SJianfeng Tan static void 97e9efa4d9SJianfeng Tan virtio_user_read_dev_config(struct virtio_hw *hw, size_t offset, 98e9efa4d9SJianfeng Tan void *dst, int length) 99e9efa4d9SJianfeng Tan { 100e9efa4d9SJianfeng Tan int i; 101e9efa4d9SJianfeng Tan struct virtio_user_dev *dev = virtio_user_get_dev(hw); 102e9efa4d9SJianfeng Tan 103e9efa4d9SJianfeng Tan if (offset == offsetof(struct virtio_net_config, mac) && 104e9efa4d9SJianfeng Tan length == ETHER_ADDR_LEN) { 105e9efa4d9SJianfeng Tan for (i = 0; i < ETHER_ADDR_LEN; ++i) 106e9efa4d9SJianfeng Tan ((uint8_t *)dst)[i] = dev->mac_addr[i]; 107e9efa4d9SJianfeng Tan return; 108e9efa4d9SJianfeng Tan } 109e9efa4d9SJianfeng Tan 110ef53b603SJianfeng Tan if (offset == offsetof(struct virtio_net_config, status)) { 111ef53b603SJianfeng Tan char buf[128]; 112ef53b603SJianfeng Tan 113ef53b603SJianfeng Tan if (dev->vhostfd >= 0) { 114ef53b603SJianfeng Tan int r; 115ef53b603SJianfeng Tan int flags; 116ef53b603SJianfeng Tan 117ef53b603SJianfeng Tan flags = fcntl(dev->vhostfd, F_GETFL); 1182fd826a3SSebastian Basierski if (fcntl(dev->vhostfd, F_SETFL, 1192fd826a3SSebastian Basierski flags | O_NONBLOCK) == -1) { 1202fd826a3SSebastian Basierski PMD_DRV_LOG(ERR, "error setting O_NONBLOCK flag"); 1212fd826a3SSebastian Basierski return; 1222fd826a3SSebastian Basierski } 123ef53b603SJianfeng Tan r = recv(dev->vhostfd, buf, 128, MSG_PEEK); 124ef53b603SJianfeng Tan if (r == 0 || (r < 0 && errno != EAGAIN)) { 125ef53b603SJianfeng Tan dev->status &= (~VIRTIO_NET_S_LINK_UP); 126ef53b603SJianfeng Tan PMD_DRV_LOG(ERR, "virtio-user port %u is down", 127ef53b603SJianfeng Tan hw->port_id); 128*bd8f50a4SZhiyong Yang 129*bd8f50a4SZhiyong Yang /* This function could be called in the process 130*bd8f50a4SZhiyong Yang * of interrupt handling, callback cannot be 131*bd8f50a4SZhiyong Yang * unregistered here, set an alarm to do it. 132ef53b603SJianfeng Tan */ 133ef53b603SJianfeng Tan rte_eal_alarm_set(1, 134ef53b603SJianfeng Tan virtio_user_delayed_handler, 135ef53b603SJianfeng Tan (void *)hw); 136ef53b603SJianfeng Tan } else { 137ef53b603SJianfeng Tan dev->status |= VIRTIO_NET_S_LINK_UP; 138ef53b603SJianfeng Tan } 139f76ef453SSebastian Basierski if (fcntl(dev->vhostfd, F_SETFL, 140f76ef453SSebastian Basierski flags & ~O_NONBLOCK) == -1) { 141f76ef453SSebastian Basierski PMD_DRV_LOG(ERR, "error clearing O_NONBLOCK flag"); 142f76ef453SSebastian Basierski return; 143f76ef453SSebastian Basierski } 144*bd8f50a4SZhiyong Yang } else if (dev->is_server) { 145*bd8f50a4SZhiyong Yang dev->status &= (~VIRTIO_NET_S_LINK_UP); 146*bd8f50a4SZhiyong Yang if (virtio_user_server_reconnect(dev) >= 0) 147*bd8f50a4SZhiyong Yang dev->status |= VIRTIO_NET_S_LINK_UP; 148ef53b603SJianfeng Tan } 149*bd8f50a4SZhiyong Yang 150e9efa4d9SJianfeng Tan *(uint16_t *)dst = dev->status; 151ef53b603SJianfeng Tan } 152e9efa4d9SJianfeng Tan 153e9efa4d9SJianfeng Tan if (offset == offsetof(struct virtio_net_config, max_virtqueue_pairs)) 154e9efa4d9SJianfeng Tan *(uint16_t *)dst = dev->max_queue_pairs; 155e9efa4d9SJianfeng Tan } 156e9efa4d9SJianfeng Tan 157e9efa4d9SJianfeng Tan static void 158e9efa4d9SJianfeng Tan virtio_user_write_dev_config(struct virtio_hw *hw, size_t offset, 159e9efa4d9SJianfeng Tan const void *src, int length) 160e9efa4d9SJianfeng Tan { 161e9efa4d9SJianfeng Tan int i; 162e9efa4d9SJianfeng Tan struct virtio_user_dev *dev = virtio_user_get_dev(hw); 163e9efa4d9SJianfeng Tan 164e9efa4d9SJianfeng Tan if ((offset == offsetof(struct virtio_net_config, mac)) && 165e9efa4d9SJianfeng Tan (length == ETHER_ADDR_LEN)) 166e9efa4d9SJianfeng Tan for (i = 0; i < ETHER_ADDR_LEN; ++i) 167e9efa4d9SJianfeng Tan dev->mac_addr[i] = ((const uint8_t *)src)[i]; 168e9efa4d9SJianfeng Tan else 169f2462150SFerruh Yigit PMD_DRV_LOG(ERR, "not supported offset=%zu, len=%d", 170e9efa4d9SJianfeng Tan offset, length); 171e9efa4d9SJianfeng Tan } 172e9efa4d9SJianfeng Tan 173e9efa4d9SJianfeng Tan static void 174c12a26eeSJianfeng Tan virtio_user_reset(struct virtio_hw *hw) 175c12a26eeSJianfeng Tan { 176c12a26eeSJianfeng Tan struct virtio_user_dev *dev = virtio_user_get_dev(hw); 177c12a26eeSJianfeng Tan 178c12a26eeSJianfeng Tan if (dev->status & VIRTIO_CONFIG_STATUS_DRIVER_OK) 179c12a26eeSJianfeng Tan virtio_user_stop_device(dev); 180c12a26eeSJianfeng Tan } 181c12a26eeSJianfeng Tan 182c12a26eeSJianfeng Tan static void 183e9efa4d9SJianfeng Tan virtio_user_set_status(struct virtio_hw *hw, uint8_t status) 184e9efa4d9SJianfeng Tan { 185e9efa4d9SJianfeng Tan struct virtio_user_dev *dev = virtio_user_get_dev(hw); 186e9efa4d9SJianfeng Tan 187e9efa4d9SJianfeng Tan if (status & VIRTIO_CONFIG_STATUS_DRIVER_OK) 188e9efa4d9SJianfeng Tan virtio_user_start_device(dev); 189c12a26eeSJianfeng Tan else if (status == VIRTIO_CONFIG_STATUS_RESET) 190c12a26eeSJianfeng Tan virtio_user_reset(hw); 191e9efa4d9SJianfeng Tan dev->status = status; 192e9efa4d9SJianfeng Tan } 193e9efa4d9SJianfeng Tan 194e9efa4d9SJianfeng Tan static uint8_t 195e9efa4d9SJianfeng Tan virtio_user_get_status(struct virtio_hw *hw) 196e9efa4d9SJianfeng Tan { 197e9efa4d9SJianfeng Tan struct virtio_user_dev *dev = virtio_user_get_dev(hw); 198e9efa4d9SJianfeng Tan 199e9efa4d9SJianfeng Tan return dev->status; 200e9efa4d9SJianfeng Tan } 201e9efa4d9SJianfeng Tan 202e9efa4d9SJianfeng Tan static uint64_t 203e9efa4d9SJianfeng Tan virtio_user_get_features(struct virtio_hw *hw) 204e9efa4d9SJianfeng Tan { 205e9efa4d9SJianfeng Tan struct virtio_user_dev *dev = virtio_user_get_dev(hw); 206e9efa4d9SJianfeng Tan 207142678d4SJianfeng Tan /* unmask feature bits defined in vhost user protocol */ 208142678d4SJianfeng Tan return dev->device_features & VIRTIO_PMD_SUPPORTED_GUEST_FEATURES; 209e9efa4d9SJianfeng Tan } 210e9efa4d9SJianfeng Tan 211e9efa4d9SJianfeng Tan static void 212e9efa4d9SJianfeng Tan virtio_user_set_features(struct virtio_hw *hw, uint64_t features) 213e9efa4d9SJianfeng Tan { 214e9efa4d9SJianfeng Tan struct virtio_user_dev *dev = virtio_user_get_dev(hw); 215e9efa4d9SJianfeng Tan 216142678d4SJianfeng Tan dev->features = features & dev->device_features; 217e9efa4d9SJianfeng Tan } 218e9efa4d9SJianfeng Tan 219e9efa4d9SJianfeng Tan static uint8_t 220e9efa4d9SJianfeng Tan virtio_user_get_isr(struct virtio_hw *hw __rte_unused) 221e9efa4d9SJianfeng Tan { 22235c4f855SJianfeng Tan /* rxq interrupts and config interrupt are separated in virtio-user, 22335c4f855SJianfeng Tan * here we only report config change. 224e9efa4d9SJianfeng Tan */ 22535c4f855SJianfeng Tan return VIRTIO_PCI_ISR_CONFIG; 226e9efa4d9SJianfeng Tan } 227e9efa4d9SJianfeng Tan 228e9efa4d9SJianfeng Tan static uint16_t 229e9efa4d9SJianfeng Tan virtio_user_set_config_irq(struct virtio_hw *hw __rte_unused, 230e9efa4d9SJianfeng Tan uint16_t vec __rte_unused) 231e9efa4d9SJianfeng Tan { 23235c4f855SJianfeng Tan return 0; 233e9efa4d9SJianfeng Tan } 234e9efa4d9SJianfeng Tan 2353d4fb6fdSJianfeng Tan static uint16_t 2363d4fb6fdSJianfeng Tan virtio_user_set_queue_irq(struct virtio_hw *hw __rte_unused, 2373d4fb6fdSJianfeng Tan struct virtqueue *vq __rte_unused, 2383d4fb6fdSJianfeng Tan uint16_t vec) 2393d4fb6fdSJianfeng Tan { 2403d4fb6fdSJianfeng Tan /* pretend we have done that */ 2413d4fb6fdSJianfeng Tan return vec; 2423d4fb6fdSJianfeng Tan } 2433d4fb6fdSJianfeng Tan 244e9efa4d9SJianfeng Tan /* This function is to get the queue size, aka, number of descs, of a specified 245e9efa4d9SJianfeng Tan * queue. Different with the VHOST_USER_GET_QUEUE_NUM, which is used to get the 246e9efa4d9SJianfeng Tan * max supported queues. 247e9efa4d9SJianfeng Tan */ 248e9efa4d9SJianfeng Tan static uint16_t 249e9efa4d9SJianfeng Tan virtio_user_get_queue_num(struct virtio_hw *hw, uint16_t queue_id __rte_unused) 250e9efa4d9SJianfeng Tan { 251e9efa4d9SJianfeng Tan struct virtio_user_dev *dev = virtio_user_get_dev(hw); 252e9efa4d9SJianfeng Tan 253e9efa4d9SJianfeng Tan /* Currently, each queue has same queue size */ 254e9efa4d9SJianfeng Tan return dev->queue_size; 255e9efa4d9SJianfeng Tan } 256e9efa4d9SJianfeng Tan 257e9efa4d9SJianfeng Tan static int 258e9efa4d9SJianfeng Tan virtio_user_setup_queue(struct virtio_hw *hw, struct virtqueue *vq) 259e9efa4d9SJianfeng Tan { 260e9efa4d9SJianfeng Tan struct virtio_user_dev *dev = virtio_user_get_dev(hw); 261e9efa4d9SJianfeng Tan uint16_t queue_idx = vq->vq_queue_index; 262e9efa4d9SJianfeng Tan uint64_t desc_addr, avail_addr, used_addr; 263e9efa4d9SJianfeng Tan 264e9efa4d9SJianfeng Tan desc_addr = (uintptr_t)vq->vq_ring_virt_mem; 265e9efa4d9SJianfeng Tan avail_addr = desc_addr + vq->vq_nentries * sizeof(struct vring_desc); 266e9efa4d9SJianfeng Tan used_addr = RTE_ALIGN_CEIL(avail_addr + offsetof(struct vring_avail, 267e9efa4d9SJianfeng Tan ring[vq->vq_nentries]), 268e9efa4d9SJianfeng Tan VIRTIO_PCI_VRING_ALIGN); 269e9efa4d9SJianfeng Tan 270e9efa4d9SJianfeng Tan dev->vrings[queue_idx].num = vq->vq_nentries; 271e9efa4d9SJianfeng Tan dev->vrings[queue_idx].desc = (void *)(uintptr_t)desc_addr; 272e9efa4d9SJianfeng Tan dev->vrings[queue_idx].avail = (void *)(uintptr_t)avail_addr; 273e9efa4d9SJianfeng Tan dev->vrings[queue_idx].used = (void *)(uintptr_t)used_addr; 274e9efa4d9SJianfeng Tan 275e9efa4d9SJianfeng Tan return 0; 276e9efa4d9SJianfeng Tan } 277e9efa4d9SJianfeng Tan 278e9efa4d9SJianfeng Tan static void 279e9efa4d9SJianfeng Tan virtio_user_del_queue(struct virtio_hw *hw, struct virtqueue *vq) 280e9efa4d9SJianfeng Tan { 281e9efa4d9SJianfeng Tan /* For legacy devices, write 0 to VIRTIO_PCI_QUEUE_PFN port, QEMU 282e9efa4d9SJianfeng Tan * correspondingly stops the ioeventfds, and reset the status of 283e9efa4d9SJianfeng Tan * the device. 284e9efa4d9SJianfeng Tan * For modern devices, set queue desc, avail, used in PCI bar to 0, 285e9efa4d9SJianfeng Tan * not see any more behavior in QEMU. 286e9efa4d9SJianfeng Tan * 287e9efa4d9SJianfeng Tan * Here we just care about what information to deliver to vhost-user 288e9efa4d9SJianfeng Tan * or vhost-kernel. So we just close ioeventfd for now. 289e9efa4d9SJianfeng Tan */ 290e9efa4d9SJianfeng Tan struct virtio_user_dev *dev = virtio_user_get_dev(hw); 291e9efa4d9SJianfeng Tan 292e9efa4d9SJianfeng Tan close(dev->callfds[vq->vq_queue_index]); 293e9efa4d9SJianfeng Tan close(dev->kickfds[vq->vq_queue_index]); 294e9efa4d9SJianfeng Tan } 295e9efa4d9SJianfeng Tan 296e9efa4d9SJianfeng Tan static void 297e9efa4d9SJianfeng Tan virtio_user_notify_queue(struct virtio_hw *hw, struct virtqueue *vq) 298e9efa4d9SJianfeng Tan { 299e9efa4d9SJianfeng Tan uint64_t buf = 1; 300e9efa4d9SJianfeng Tan struct virtio_user_dev *dev = virtio_user_get_dev(hw); 301e9efa4d9SJianfeng Tan 3021b69528eSJianfeng Tan if (hw->cvq && (hw->cvq->vq == vq)) { 3031b69528eSJianfeng Tan virtio_user_handle_cq(dev, vq->vq_queue_index); 3041b69528eSJianfeng Tan return; 3051b69528eSJianfeng Tan } 3061b69528eSJianfeng Tan 307e9efa4d9SJianfeng Tan if (write(dev->kickfds[vq->vq_queue_index], &buf, sizeof(buf)) < 0) 308f2462150SFerruh Yigit PMD_DRV_LOG(ERR, "failed to kick backend: %s", 309e9efa4d9SJianfeng Tan strerror(errno)); 310e9efa4d9SJianfeng Tan } 311e9efa4d9SJianfeng Tan 3126d890f8aSYuanhan Liu const struct virtio_pci_ops virtio_user_ops = { 313e9efa4d9SJianfeng Tan .read_dev_cfg = virtio_user_read_dev_config, 314e9efa4d9SJianfeng Tan .write_dev_cfg = virtio_user_write_dev_config, 315e9efa4d9SJianfeng Tan .reset = virtio_user_reset, 316e9efa4d9SJianfeng Tan .get_status = virtio_user_get_status, 317e9efa4d9SJianfeng Tan .set_status = virtio_user_set_status, 318e9efa4d9SJianfeng Tan .get_features = virtio_user_get_features, 319e9efa4d9SJianfeng Tan .set_features = virtio_user_set_features, 320e9efa4d9SJianfeng Tan .get_isr = virtio_user_get_isr, 321e9efa4d9SJianfeng Tan .set_config_irq = virtio_user_set_config_irq, 3223d4fb6fdSJianfeng Tan .set_queue_irq = virtio_user_set_queue_irq, 323e9efa4d9SJianfeng Tan .get_queue_num = virtio_user_get_queue_num, 324e9efa4d9SJianfeng Tan .setup_queue = virtio_user_setup_queue, 325e9efa4d9SJianfeng Tan .del_queue = virtio_user_del_queue, 326e9efa4d9SJianfeng Tan .notify_queue = virtio_user_notify_queue, 327e9efa4d9SJianfeng Tan }; 328ce2eabddSJianfeng Tan 329ce2eabddSJianfeng Tan static const char *valid_args[] = { 330ce2eabddSJianfeng Tan #define VIRTIO_USER_ARG_QUEUES_NUM "queues" 331ce2eabddSJianfeng Tan VIRTIO_USER_ARG_QUEUES_NUM, 332ce2eabddSJianfeng Tan #define VIRTIO_USER_ARG_CQ_NUM "cq" 333ce2eabddSJianfeng Tan VIRTIO_USER_ARG_CQ_NUM, 334ce2eabddSJianfeng Tan #define VIRTIO_USER_ARG_MAC "mac" 335ce2eabddSJianfeng Tan VIRTIO_USER_ARG_MAC, 336ce2eabddSJianfeng Tan #define VIRTIO_USER_ARG_PATH "path" 337ce2eabddSJianfeng Tan VIRTIO_USER_ARG_PATH, 338ce2eabddSJianfeng Tan #define VIRTIO_USER_ARG_QUEUE_SIZE "queue_size" 339ce2eabddSJianfeng Tan VIRTIO_USER_ARG_QUEUE_SIZE, 3404214a1b4SWenfeng Liu #define VIRTIO_USER_ARG_INTERFACE_NAME "iface" 3414214a1b4SWenfeng Liu VIRTIO_USER_ARG_INTERFACE_NAME, 342*bd8f50a4SZhiyong Yang #define VIRTIO_USER_ARG_SERVER_MODE "server" 343*bd8f50a4SZhiyong Yang VIRTIO_USER_ARG_SERVER_MODE, 344ce2eabddSJianfeng Tan NULL 345ce2eabddSJianfeng Tan }; 346ce2eabddSJianfeng Tan 347ce2eabddSJianfeng Tan #define VIRTIO_USER_DEF_CQ_EN 0 348ce2eabddSJianfeng Tan #define VIRTIO_USER_DEF_Q_NUM 1 349ce2eabddSJianfeng Tan #define VIRTIO_USER_DEF_Q_SZ 256 350*bd8f50a4SZhiyong Yang #define VIRTIO_USER_DEF_SERVER_MODE 0 351ce2eabddSJianfeng Tan 352ce2eabddSJianfeng Tan static int 353ce2eabddSJianfeng Tan get_string_arg(const char *key __rte_unused, 354ce2eabddSJianfeng Tan const char *value, void *extra_args) 355ce2eabddSJianfeng Tan { 356ce2eabddSJianfeng Tan if (!value || !extra_args) 357ce2eabddSJianfeng Tan return -EINVAL; 358ce2eabddSJianfeng Tan 359ce2eabddSJianfeng Tan *(char **)extra_args = strdup(value); 360ce2eabddSJianfeng Tan 3614214a1b4SWenfeng Liu if (!*(char **)extra_args) 3624214a1b4SWenfeng Liu return -ENOMEM; 3634214a1b4SWenfeng Liu 364ce2eabddSJianfeng Tan return 0; 365ce2eabddSJianfeng Tan } 366ce2eabddSJianfeng Tan 367ce2eabddSJianfeng Tan static int 368ce2eabddSJianfeng Tan get_integer_arg(const char *key __rte_unused, 369ce2eabddSJianfeng Tan const char *value, void *extra_args) 370ce2eabddSJianfeng Tan { 371ce2eabddSJianfeng Tan if (!value || !extra_args) 372ce2eabddSJianfeng Tan return -EINVAL; 373ce2eabddSJianfeng Tan 374ce2eabddSJianfeng Tan *(uint64_t *)extra_args = strtoull(value, NULL, 0); 375ce2eabddSJianfeng Tan 376ce2eabddSJianfeng Tan return 0; 377ce2eabddSJianfeng Tan } 378ce2eabddSJianfeng Tan 37973db5badSDavid Marchand static struct rte_vdev_driver virtio_user_driver; 38073db5badSDavid Marchand 381ce2eabddSJianfeng Tan static struct rte_eth_dev * 382050fe6e9SJan Blunck virtio_user_eth_dev_alloc(struct rte_vdev_device *vdev) 383ce2eabddSJianfeng Tan { 384ce2eabddSJianfeng Tan struct rte_eth_dev *eth_dev; 385ce2eabddSJianfeng Tan struct rte_eth_dev_data *data; 386ce2eabddSJianfeng Tan struct virtio_hw *hw; 387ce2eabddSJianfeng Tan struct virtio_user_dev *dev; 388ce2eabddSJianfeng Tan 389050fe6e9SJan Blunck eth_dev = rte_eth_vdev_allocate(vdev, sizeof(*hw)); 390ce2eabddSJianfeng Tan if (!eth_dev) { 391ce2eabddSJianfeng Tan PMD_INIT_LOG(ERR, "cannot alloc rte_eth_dev"); 392ce2eabddSJianfeng Tan return NULL; 393ce2eabddSJianfeng Tan } 394ce2eabddSJianfeng Tan 395ce2eabddSJianfeng Tan data = eth_dev->data; 396050fe6e9SJan Blunck hw = eth_dev->data->dev_private; 397ce2eabddSJianfeng Tan 398ce2eabddSJianfeng Tan dev = rte_zmalloc(NULL, sizeof(*dev), 0); 399ce2eabddSJianfeng Tan if (!dev) { 400ce2eabddSJianfeng Tan PMD_INIT_LOG(ERR, "malloc virtio_user_dev failed"); 401ce2eabddSJianfeng Tan rte_eth_dev_release_port(eth_dev); 402ce2eabddSJianfeng Tan rte_free(hw); 403ce2eabddSJianfeng Tan return NULL; 404ce2eabddSJianfeng Tan } 405ce2eabddSJianfeng Tan 406553f4593SYuanhan Liu hw->port_id = data->port_id; 4073d4fb6fdSJianfeng Tan dev->port_id = data->port_id; 408553f4593SYuanhan Liu virtio_hw_internal[hw->port_id].vtpci_ops = &virtio_user_ops; 409ef53b603SJianfeng Tan /* 410ef53b603SJianfeng Tan * MSIX is required to enable LSC (see virtio_init_device). 411ef53b603SJianfeng Tan * Here just pretend that we support msix. 412ef53b603SJianfeng Tan */ 413ef53b603SJianfeng Tan hw->use_msix = 1; 414ce2eabddSJianfeng Tan hw->modern = 0; 41509649363SOlivier Matz hw->use_simple_rx = 0; 41609649363SOlivier Matz hw->use_simple_tx = 0; 417ce2eabddSJianfeng Tan hw->virtio_user_dev = dev; 418ce2eabddSJianfeng Tan return eth_dev; 419ce2eabddSJianfeng Tan } 420ce2eabddSJianfeng Tan 421ca8326a9SJianfeng Tan static void 422ca8326a9SJianfeng Tan virtio_user_eth_dev_free(struct rte_eth_dev *eth_dev) 423ca8326a9SJianfeng Tan { 424ca8326a9SJianfeng Tan struct rte_eth_dev_data *data = eth_dev->data; 425ca8326a9SJianfeng Tan struct virtio_hw *hw = data->dev_private; 426ca8326a9SJianfeng Tan 427ca8326a9SJianfeng Tan rte_free(hw->virtio_user_dev); 428ca8326a9SJianfeng Tan rte_free(hw); 429ca8326a9SJianfeng Tan rte_eth_dev_release_port(eth_dev); 430ca8326a9SJianfeng Tan } 431ca8326a9SJianfeng Tan 432ce2eabddSJianfeng Tan /* Dev initialization routine. Invoked once for each virtio vdev at 433c3b2fdfeSYong Wang * EAL init time, see rte_bus_probe(). 434ce2eabddSJianfeng Tan * Returns 0 on success. 435ce2eabddSJianfeng Tan */ 436ce2eabddSJianfeng Tan static int 4375d2aa461SJan Blunck virtio_user_pmd_probe(struct rte_vdev_device *dev) 438ce2eabddSJianfeng Tan { 43914f06474SJianfeng Tan struct rte_kvargs *kvlist = NULL; 440ce2eabddSJianfeng Tan struct rte_eth_dev *eth_dev; 441ce2eabddSJianfeng Tan struct virtio_hw *hw; 442ce2eabddSJianfeng Tan uint64_t queues = VIRTIO_USER_DEF_Q_NUM; 443ce2eabddSJianfeng Tan uint64_t cq = VIRTIO_USER_DEF_CQ_EN; 444ce2eabddSJianfeng Tan uint64_t queue_size = VIRTIO_USER_DEF_Q_SZ; 445*bd8f50a4SZhiyong Yang uint64_t server_mode = VIRTIO_USER_DEF_SERVER_MODE; 446ce2eabddSJianfeng Tan char *path = NULL; 4474214a1b4SWenfeng Liu char *ifname = NULL; 448ce2eabddSJianfeng Tan char *mac_addr = NULL; 449ce2eabddSJianfeng Tan int ret = -1; 450ce2eabddSJianfeng Tan 4515d2aa461SJan Blunck kvlist = rte_kvargs_parse(rte_vdev_device_args(dev), valid_args); 452ce2eabddSJianfeng Tan if (!kvlist) { 453ce2eabddSJianfeng Tan PMD_INIT_LOG(ERR, "error when parsing param"); 454ce2eabddSJianfeng Tan goto end; 455ce2eabddSJianfeng Tan } 456ce2eabddSJianfeng Tan 4579cca159eSMaxime Coquelin if (rte_kvargs_count(kvlist, VIRTIO_USER_ARG_PATH) == 1) { 458ca8326a9SJianfeng Tan if (rte_kvargs_process(kvlist, VIRTIO_USER_ARG_PATH, 459ca8326a9SJianfeng Tan &get_string_arg, &path) < 0) { 460404bd6bfSJianfeng Tan PMD_INIT_LOG(ERR, "error to parse %s", 461404bd6bfSJianfeng Tan VIRTIO_USER_ARG_PATH); 462404bd6bfSJianfeng Tan goto end; 463404bd6bfSJianfeng Tan } 4649cca159eSMaxime Coquelin } else { 465f2462150SFerruh Yigit PMD_INIT_LOG(ERR, "arg %s is mandatory for virtio_user", 466ce2eabddSJianfeng Tan VIRTIO_USER_ARG_QUEUE_SIZE); 467ce2eabddSJianfeng Tan goto end; 468ce2eabddSJianfeng Tan } 469ce2eabddSJianfeng Tan 4704214a1b4SWenfeng Liu if (rte_kvargs_count(kvlist, VIRTIO_USER_ARG_INTERFACE_NAME) == 1) { 4714214a1b4SWenfeng Liu if (is_vhost_user_by_type(path)) { 4724214a1b4SWenfeng Liu PMD_INIT_LOG(ERR, 4734214a1b4SWenfeng Liu "arg %s applies only to vhost-kernel backend", 4744214a1b4SWenfeng Liu VIRTIO_USER_ARG_INTERFACE_NAME); 4754214a1b4SWenfeng Liu goto end; 4764214a1b4SWenfeng Liu } 4774214a1b4SWenfeng Liu 4784214a1b4SWenfeng Liu if (rte_kvargs_process(kvlist, VIRTIO_USER_ARG_INTERFACE_NAME, 4794214a1b4SWenfeng Liu &get_string_arg, &ifname) < 0) { 4804214a1b4SWenfeng Liu PMD_INIT_LOG(ERR, "error to parse %s", 4814214a1b4SWenfeng Liu VIRTIO_USER_ARG_INTERFACE_NAME); 4824214a1b4SWenfeng Liu goto end; 4834214a1b4SWenfeng Liu } 4844214a1b4SWenfeng Liu } 4854214a1b4SWenfeng Liu 486404bd6bfSJianfeng Tan if (rte_kvargs_count(kvlist, VIRTIO_USER_ARG_MAC) == 1) { 487ca8326a9SJianfeng Tan if (rte_kvargs_process(kvlist, VIRTIO_USER_ARG_MAC, 488ca8326a9SJianfeng Tan &get_string_arg, &mac_addr) < 0) { 489404bd6bfSJianfeng Tan PMD_INIT_LOG(ERR, "error to parse %s", 490404bd6bfSJianfeng Tan VIRTIO_USER_ARG_MAC); 491404bd6bfSJianfeng Tan goto end; 492404bd6bfSJianfeng Tan } 493404bd6bfSJianfeng Tan } 494ce2eabddSJianfeng Tan 495404bd6bfSJianfeng Tan if (rte_kvargs_count(kvlist, VIRTIO_USER_ARG_QUEUE_SIZE) == 1) { 496ca8326a9SJianfeng Tan if (rte_kvargs_process(kvlist, VIRTIO_USER_ARG_QUEUE_SIZE, 497ca8326a9SJianfeng Tan &get_integer_arg, &queue_size) < 0) { 498404bd6bfSJianfeng Tan PMD_INIT_LOG(ERR, "error to parse %s", 499404bd6bfSJianfeng Tan VIRTIO_USER_ARG_QUEUE_SIZE); 500404bd6bfSJianfeng Tan goto end; 501404bd6bfSJianfeng Tan } 502404bd6bfSJianfeng Tan } 503ce2eabddSJianfeng Tan 504404bd6bfSJianfeng Tan if (rte_kvargs_count(kvlist, VIRTIO_USER_ARG_QUEUES_NUM) == 1) { 505ca8326a9SJianfeng Tan if (rte_kvargs_process(kvlist, VIRTIO_USER_ARG_QUEUES_NUM, 506ca8326a9SJianfeng Tan &get_integer_arg, &queues) < 0) { 507404bd6bfSJianfeng Tan PMD_INIT_LOG(ERR, "error to parse %s", 508404bd6bfSJianfeng Tan VIRTIO_USER_ARG_QUEUES_NUM); 509404bd6bfSJianfeng Tan goto end; 510404bd6bfSJianfeng Tan } 511404bd6bfSJianfeng Tan } 512ce2eabddSJianfeng Tan 513*bd8f50a4SZhiyong Yang if (rte_kvargs_count(kvlist, VIRTIO_USER_ARG_SERVER_MODE) == 1) { 514*bd8f50a4SZhiyong Yang if (rte_kvargs_process(kvlist, VIRTIO_USER_ARG_SERVER_MODE, 515*bd8f50a4SZhiyong Yang &get_integer_arg, &server_mode) < 0) { 516*bd8f50a4SZhiyong Yang PMD_INIT_LOG(ERR, "error to parse %s", 517*bd8f50a4SZhiyong Yang VIRTIO_USER_ARG_SERVER_MODE); 518*bd8f50a4SZhiyong Yang goto end; 519*bd8f50a4SZhiyong Yang } 520*bd8f50a4SZhiyong Yang } 521*bd8f50a4SZhiyong Yang 522404bd6bfSJianfeng Tan if (rte_kvargs_count(kvlist, VIRTIO_USER_ARG_CQ_NUM) == 1) { 523ca8326a9SJianfeng Tan if (rte_kvargs_process(kvlist, VIRTIO_USER_ARG_CQ_NUM, 524ca8326a9SJianfeng Tan &get_integer_arg, &cq) < 0) { 525404bd6bfSJianfeng Tan PMD_INIT_LOG(ERR, "error to parse %s", 526404bd6bfSJianfeng Tan VIRTIO_USER_ARG_CQ_NUM); 527404bd6bfSJianfeng Tan goto end; 528404bd6bfSJianfeng Tan } 529404bd6bfSJianfeng Tan } else if (queues > 1) { 5301b69528eSJianfeng Tan cq = 1; 531404bd6bfSJianfeng Tan } 5321b69528eSJianfeng Tan 5331b69528eSJianfeng Tan if (queues > 1 && cq == 0) { 5341b69528eSJianfeng Tan PMD_INIT_LOG(ERR, "multi-q requires ctrl-q"); 5351b69528eSJianfeng Tan goto end; 5361b69528eSJianfeng Tan } 537ce2eabddSJianfeng Tan 5382269b9aeSWenfeng Liu if (queues > VIRTIO_MAX_VIRTQUEUE_PAIRS) { 5392269b9aeSWenfeng Liu PMD_INIT_LOG(ERR, "arg %s %" PRIu64 " exceeds the limit %u", 5402269b9aeSWenfeng Liu VIRTIO_USER_ARG_QUEUES_NUM, queues, 5412269b9aeSWenfeng Liu VIRTIO_MAX_VIRTQUEUE_PAIRS); 5422269b9aeSWenfeng Liu goto end; 5432269b9aeSWenfeng Liu } 5442269b9aeSWenfeng Liu 545727d83caSAmi Sabo if (rte_eal_process_type() == RTE_PROC_PRIMARY) { 546*bd8f50a4SZhiyong Yang struct virtio_user_dev *vu_dev; 547*bd8f50a4SZhiyong Yang 548050fe6e9SJan Blunck eth_dev = virtio_user_eth_dev_alloc(dev); 549ce2eabddSJianfeng Tan if (!eth_dev) { 550e8df94b8SJianfeng Tan PMD_INIT_LOG(ERR, "virtio_user fails to alloc device"); 551ce2eabddSJianfeng Tan goto end; 552ce2eabddSJianfeng Tan } 553ce2eabddSJianfeng Tan 554ce2eabddSJianfeng Tan hw = eth_dev->data->dev_private; 555*bd8f50a4SZhiyong Yang vu_dev = virtio_user_get_dev(hw); 556*bd8f50a4SZhiyong Yang if (server_mode == 1) 557*bd8f50a4SZhiyong Yang vu_dev->is_server = true; 558*bd8f50a4SZhiyong Yang else 559*bd8f50a4SZhiyong Yang vu_dev->is_server = false; 560ce2eabddSJianfeng Tan if (virtio_user_dev_init(hw->virtio_user_dev, path, queues, cq, 5614214a1b4SWenfeng Liu queue_size, mac_addr, &ifname) < 0) { 562ca8326a9SJianfeng Tan PMD_INIT_LOG(ERR, "virtio_user_dev_init fails"); 563ca8326a9SJianfeng Tan virtio_user_eth_dev_free(eth_dev); 564ce2eabddSJianfeng Tan goto end; 565ca8326a9SJianfeng Tan } 566727d83caSAmi Sabo } else { 5675d2aa461SJan Blunck eth_dev = rte_eth_dev_attach_secondary(rte_vdev_device_name(dev)); 568727d83caSAmi Sabo if (!eth_dev) 569727d83caSAmi Sabo goto end; 570727d83caSAmi Sabo } 571ce2eabddSJianfeng Tan 5723dcfe039SThomas Monjalon /* previously called by rte_pci_probe() for physical dev */ 573ce2eabddSJianfeng Tan if (eth_virtio_dev_init(eth_dev) < 0) { 574ce2eabddSJianfeng Tan PMD_INIT_LOG(ERR, "eth_virtio_dev_init fails"); 575ca8326a9SJianfeng Tan virtio_user_eth_dev_free(eth_dev); 576ce2eabddSJianfeng Tan goto end; 577ce2eabddSJianfeng Tan } 578ce2eabddSJianfeng Tan ret = 0; 579ce2eabddSJianfeng Tan 580ce2eabddSJianfeng Tan end: 58114f06474SJianfeng Tan if (kvlist) 58214f06474SJianfeng Tan rte_kvargs_free(kvlist); 583ce2eabddSJianfeng Tan if (path) 584ce2eabddSJianfeng Tan free(path); 585ce2eabddSJianfeng Tan if (mac_addr) 586ce2eabddSJianfeng Tan free(mac_addr); 5874214a1b4SWenfeng Liu if (ifname) 5884214a1b4SWenfeng Liu free(ifname); 589ce2eabddSJianfeng Tan return ret; 590ce2eabddSJianfeng Tan } 591ce2eabddSJianfeng Tan 592ce2eabddSJianfeng Tan /** Called by rte_eth_dev_detach() */ 593ce2eabddSJianfeng Tan static int 5945d2aa461SJan Blunck virtio_user_pmd_remove(struct rte_vdev_device *vdev) 595ce2eabddSJianfeng Tan { 5965d2aa461SJan Blunck const char *name; 597ce2eabddSJianfeng Tan struct rte_eth_dev *eth_dev; 598ce2eabddSJianfeng Tan struct virtio_hw *hw; 599ce2eabddSJianfeng Tan struct virtio_user_dev *dev; 600ce2eabddSJianfeng Tan 6015d2aa461SJan Blunck if (!vdev) 602ce2eabddSJianfeng Tan return -EINVAL; 603ce2eabddSJianfeng Tan 6045d2aa461SJan Blunck name = rte_vdev_device_name(vdev); 605f2462150SFerruh Yigit PMD_DRV_LOG(INFO, "Un-Initializing %s", name); 606ce2eabddSJianfeng Tan eth_dev = rte_eth_dev_allocated(name); 607ce2eabddSJianfeng Tan if (!eth_dev) 608ce2eabddSJianfeng Tan return -ENODEV; 609ce2eabddSJianfeng Tan 610ce2eabddSJianfeng Tan /* make sure the device is stopped, queues freed */ 611ce2eabddSJianfeng Tan rte_eth_dev_close(eth_dev->data->port_id); 612ce2eabddSJianfeng Tan 613ce2eabddSJianfeng Tan hw = eth_dev->data->dev_private; 614ce2eabddSJianfeng Tan dev = hw->virtio_user_dev; 615ce2eabddSJianfeng Tan virtio_user_dev_uninit(dev); 616ce2eabddSJianfeng Tan 617ce2eabddSJianfeng Tan rte_free(eth_dev->data->dev_private); 618ce2eabddSJianfeng Tan rte_eth_dev_release_port(eth_dev); 619ce2eabddSJianfeng Tan 620ce2eabddSJianfeng Tan return 0; 621ce2eabddSJianfeng Tan } 622ce2eabddSJianfeng Tan 623fe363dd4SJan Viktorin static struct rte_vdev_driver virtio_user_driver = { 62450a3345fSShreyansh Jain .probe = virtio_user_pmd_probe, 62550a3345fSShreyansh Jain .remove = virtio_user_pmd_remove, 626ce2eabddSJianfeng Tan }; 627ce2eabddSJianfeng Tan 62801f19227SShreyansh Jain RTE_PMD_REGISTER_VDEV(net_virtio_user, virtio_user_driver); 6299fa80cb2SJan Blunck RTE_PMD_REGISTER_ALIAS(net_virtio_user, virtio_user); 63001f19227SShreyansh Jain RTE_PMD_REGISTER_PARAM_STRING(net_virtio_user, 63144e32a67SPablo de Lara "path=<path> " 63244e32a67SPablo de Lara "mac=<mac addr> " 63344e32a67SPablo de Lara "cq=<int> " 63444e32a67SPablo de Lara "queue_size=<int> " 6354214a1b4SWenfeng Liu "queues=<int> " 6364214a1b4SWenfeng Liu "iface=<string>"); 637