15566a3e3SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause 25566a3e3SBruce Richardson * Copyright(c) 2010-2016 Intel Corporation 337a7eb2aSJianfeng Tan */ 437a7eb2aSJianfeng Tan 537a7eb2aSJianfeng Tan #include <stdint.h> 637a7eb2aSJianfeng Tan #include <stdio.h> 737a7eb2aSJianfeng Tan #include <fcntl.h> 837a7eb2aSJianfeng Tan #include <string.h> 937a7eb2aSJianfeng Tan #include <errno.h> 1037a7eb2aSJianfeng Tan #include <sys/mman.h> 1137a7eb2aSJianfeng Tan #include <unistd.h> 1237a7eb2aSJianfeng Tan #include <sys/eventfd.h> 1333d24d65SJianfeng Tan #include <sys/types.h> 1433d24d65SJianfeng Tan #include <sys/stat.h> 1537a7eb2aSJianfeng Tan 166723c0fcSBruce Richardson #include <rte_string_fns.h> 177ff26957STiwei Bie #include <rte_eal_memconfig.h> 187ff26957STiwei Bie 1937a7eb2aSJianfeng Tan #include "vhost.h" 2037a7eb2aSJianfeng Tan #include "virtio_user_dev.h" 2137a7eb2aSJianfeng Tan #include "../virtio_ethdev.h" 2237a7eb2aSJianfeng Tan 2312ecb2f6SMaxime Coquelin #define VIRTIO_USER_MEM_EVENT_CLB_NAME "virtio_user_mem_event_clb" 2412ecb2f6SMaxime Coquelin 25b0395dc8SAdrian Moreno const char * const virtio_user_backend_strings[] = { 26b0395dc8SAdrian Moreno [VIRTIO_USER_BACKEND_UNKNOWN] = "VIRTIO_USER_BACKEND_UNKNOWN", 27b0395dc8SAdrian Moreno [VIRTIO_USER_BACKEND_VHOST_USER] = "VHOST_USER", 28b0395dc8SAdrian Moreno [VIRTIO_USER_BACKEND_VHOST_KERNEL] = "VHOST_NET", 29b0395dc8SAdrian Moreno [VIRTIO_USER_BACKEND_VHOST_VDPA] = "VHOST_VDPA", 30b0395dc8SAdrian Moreno }; 31b0395dc8SAdrian Moreno 3237a7eb2aSJianfeng Tan static int 3357ae79a7SJianfeng Tan virtio_user_create_queue(struct virtio_user_dev *dev, uint32_t queue_sel) 3457ae79a7SJianfeng Tan { 3557ae79a7SJianfeng Tan /* Of all per virtqueue MSGs, make sure VHOST_SET_VRING_CALL come 3657ae79a7SJianfeng Tan * firstly because vhost depends on this msg to allocate virtqueue 3757ae79a7SJianfeng Tan * pair. 3857ae79a7SJianfeng Tan */ 3957ae79a7SJianfeng Tan struct vhost_vring_file file; 40a3fb6b1dSMaxime Coquelin int ret; 4157ae79a7SJianfeng Tan 4257ae79a7SJianfeng Tan file.index = queue_sel; 43e6e7ad8bSJianfeng Tan file.fd = dev->callfds[queue_sel]; 44a3fb6b1dSMaxime Coquelin ret = dev->ops->set_vring_call(dev, &file); 45a3fb6b1dSMaxime Coquelin if (ret < 0) { 46a3fb6b1dSMaxime Coquelin PMD_INIT_LOG(ERR, "(%s) Failed to create queue %u\n", dev->path, queue_sel); 47a3fb6b1dSMaxime Coquelin return -1; 48a3fb6b1dSMaxime Coquelin } 4957ae79a7SJianfeng Tan 5057ae79a7SJianfeng Tan return 0; 5157ae79a7SJianfeng Tan } 5257ae79a7SJianfeng Tan 5357ae79a7SJianfeng Tan static int 5437a7eb2aSJianfeng Tan virtio_user_kick_queue(struct virtio_user_dev *dev, uint32_t queue_sel) 5537a7eb2aSJianfeng Tan { 56a3fb6b1dSMaxime Coquelin int ret; 5737a7eb2aSJianfeng Tan struct vhost_vring_file file; 5837a7eb2aSJianfeng Tan struct vhost_vring_state state; 5937a7eb2aSJianfeng Tan struct vring *vring = &dev->vrings[queue_sel]; 6048a44640SJens Freimann struct vring_packed *pq_vring = &dev->packed_vrings[queue_sel]; 6137a7eb2aSJianfeng Tan struct vhost_vring_addr addr = { 6237a7eb2aSJianfeng Tan .index = queue_sel, 6337a7eb2aSJianfeng Tan .log_guest_addr = 0, 6437a7eb2aSJianfeng Tan .flags = 0, /* disable log */ 6537a7eb2aSJianfeng Tan }; 6637a7eb2aSJianfeng Tan 6748a44640SJens Freimann if (dev->features & (1ULL << VIRTIO_F_RING_PACKED)) { 6848a44640SJens Freimann addr.desc_user_addr = 694cdc4d98STiwei Bie (uint64_t)(uintptr_t)pq_vring->desc; 7048a44640SJens Freimann addr.avail_user_addr = 714cdc4d98STiwei Bie (uint64_t)(uintptr_t)pq_vring->driver; 7248a44640SJens Freimann addr.used_user_addr = 734cdc4d98STiwei Bie (uint64_t)(uintptr_t)pq_vring->device; 7448a44640SJens Freimann } else { 7548a44640SJens Freimann addr.desc_user_addr = (uint64_t)(uintptr_t)vring->desc; 7648a44640SJens Freimann addr.avail_user_addr = (uint64_t)(uintptr_t)vring->avail; 7748a44640SJens Freimann addr.used_user_addr = (uint64_t)(uintptr_t)vring->used; 7848a44640SJens Freimann } 7948a44640SJens Freimann 8037a7eb2aSJianfeng Tan state.index = queue_sel; 8137a7eb2aSJianfeng Tan state.num = vring->num; 82a3fb6b1dSMaxime Coquelin ret = dev->ops->set_vring_num(dev, &state); 83a3fb6b1dSMaxime Coquelin if (ret < 0) 84a3fb6b1dSMaxime Coquelin goto err; 8537a7eb2aSJianfeng Tan 86be7a4707SJianfeng Tan state.index = queue_sel; 8737a7eb2aSJianfeng Tan state.num = 0; /* no reservation */ 8834f3966cSYuanhan Liu if (dev->features & (1ULL << VIRTIO_F_RING_PACKED)) 8934f3966cSYuanhan Liu state.num |= (1 << 15); 90a3fb6b1dSMaxime Coquelin ret = dev->ops->set_vring_base(dev, &state); 91a3fb6b1dSMaxime Coquelin if (ret < 0) 92a3fb6b1dSMaxime Coquelin goto err; 9337a7eb2aSJianfeng Tan 94a3fb6b1dSMaxime Coquelin ret = dev->ops->set_vring_addr(dev, &addr); 95a3fb6b1dSMaxime Coquelin if (ret < 0) 96a3fb6b1dSMaxime Coquelin goto err; 9737a7eb2aSJianfeng Tan 9837a7eb2aSJianfeng Tan /* Of all per virtqueue MSGs, make sure VHOST_USER_SET_VRING_KICK comes 9937a7eb2aSJianfeng Tan * lastly because vhost depends on this msg to judge if 10037a7eb2aSJianfeng Tan * virtio is ready. 10137a7eb2aSJianfeng Tan */ 10257ae79a7SJianfeng Tan file.index = queue_sel; 103e6e7ad8bSJianfeng Tan file.fd = dev->kickfds[queue_sel]; 104a3fb6b1dSMaxime Coquelin ret = dev->ops->set_vring_kick(dev, &file); 105a3fb6b1dSMaxime Coquelin if (ret < 0) 106a3fb6b1dSMaxime Coquelin goto err; 10737a7eb2aSJianfeng Tan 10837a7eb2aSJianfeng Tan return 0; 109a3fb6b1dSMaxime Coquelin err: 110a3fb6b1dSMaxime Coquelin PMD_INIT_LOG(ERR, "(%s) Failed to kick queue %u\n", dev->path, queue_sel); 111a3fb6b1dSMaxime Coquelin 112a3fb6b1dSMaxime Coquelin return -1; 11337a7eb2aSJianfeng Tan } 11437a7eb2aSJianfeng Tan 11557ae79a7SJianfeng Tan static int 11657ae79a7SJianfeng Tan virtio_user_queue_setup(struct virtio_user_dev *dev, 11757ae79a7SJianfeng Tan int (*fn)(struct virtio_user_dev *, uint32_t)) 11857ae79a7SJianfeng Tan { 11957ae79a7SJianfeng Tan uint32_t i, queue_sel; 12057ae79a7SJianfeng Tan 12157ae79a7SJianfeng Tan for (i = 0; i < dev->max_queue_pairs; ++i) { 12257ae79a7SJianfeng Tan queue_sel = 2 * i + VTNET_SQ_RQ_QUEUE_IDX; 12357ae79a7SJianfeng Tan if (fn(dev, queue_sel) < 0) { 124a3fb6b1dSMaxime Coquelin PMD_DRV_LOG(ERR, "(%s) setup rx vq %u failed", dev->path, i); 12557ae79a7SJianfeng Tan return -1; 12657ae79a7SJianfeng Tan } 12757ae79a7SJianfeng Tan } 12857ae79a7SJianfeng Tan for (i = 0; i < dev->max_queue_pairs; ++i) { 12957ae79a7SJianfeng Tan queue_sel = 2 * i + VTNET_SQ_TQ_QUEUE_IDX; 13057ae79a7SJianfeng Tan if (fn(dev, queue_sel) < 0) { 131a3fb6b1dSMaxime Coquelin PMD_DRV_LOG(INFO, "(%s) setup tx vq %u failed", dev->path, i); 13257ae79a7SJianfeng Tan return -1; 13357ae79a7SJianfeng Tan } 13457ae79a7SJianfeng Tan } 13557ae79a7SJianfeng Tan 13657ae79a7SJianfeng Tan return 0; 13757ae79a7SJianfeng Tan } 13857ae79a7SJianfeng Tan 13937a7eb2aSJianfeng Tan int 140844e4683SMaxime Coquelin virtio_user_dev_set_features(struct virtio_user_dev *dev) 14137a7eb2aSJianfeng Tan { 14237a7eb2aSJianfeng Tan uint64_t features; 143844e4683SMaxime Coquelin int ret = -1; 144844e4683SMaxime Coquelin 145844e4683SMaxime Coquelin pthread_mutex_lock(&dev->mutex); 146844e4683SMaxime Coquelin 147844e4683SMaxime Coquelin /* Step 0: tell vhost to create queues */ 148844e4683SMaxime Coquelin if (virtio_user_queue_setup(dev, virtio_user_create_queue) < 0) 149844e4683SMaxime Coquelin goto error; 150844e4683SMaxime Coquelin 151844e4683SMaxime Coquelin features = dev->features; 152844e4683SMaxime Coquelin 153844e4683SMaxime Coquelin /* Strip VIRTIO_NET_F_MAC, as MAC address is handled in vdev init */ 154844e4683SMaxime Coquelin features &= ~(1ull << VIRTIO_NET_F_MAC); 155844e4683SMaxime Coquelin /* Strip VIRTIO_NET_F_CTRL_VQ, as devices do not really need to know */ 156844e4683SMaxime Coquelin features &= ~(1ull << VIRTIO_NET_F_CTRL_VQ); 157844e4683SMaxime Coquelin features &= ~(1ull << VIRTIO_NET_F_STATUS); 158cc0151b3SMaxime Coquelin ret = dev->ops->set_features(dev, features); 159844e4683SMaxime Coquelin if (ret < 0) 160844e4683SMaxime Coquelin goto error; 161a3fb6b1dSMaxime Coquelin PMD_DRV_LOG(INFO, "(%s) set features: 0x%" PRIx64, dev->path, features); 162844e4683SMaxime Coquelin error: 163844e4683SMaxime Coquelin pthread_mutex_unlock(&dev->mutex); 164844e4683SMaxime Coquelin 165844e4683SMaxime Coquelin return ret; 166844e4683SMaxime Coquelin } 167844e4683SMaxime Coquelin 168844e4683SMaxime Coquelin int 169844e4683SMaxime Coquelin virtio_user_start_device(struct virtio_user_dev *dev) 170844e4683SMaxime Coquelin { 17137a7eb2aSJianfeng Tan int ret; 17237a7eb2aSJianfeng Tan 1737ff26957STiwei Bie /* 1747ff26957STiwei Bie * XXX workaround! 1757ff26957STiwei Bie * 1767ff26957STiwei Bie * We need to make sure that the locks will be 1777ff26957STiwei Bie * taken in the correct order to avoid deadlocks. 1787ff26957STiwei Bie * 1797ff26957STiwei Bie * Before releasing this lock, this thread should 1807ff26957STiwei Bie * not trigger any memory hotplug events. 1817ff26957STiwei Bie * 1827ff26957STiwei Bie * This is a temporary workaround, and should be 1837ff26957STiwei Bie * replaced when we get proper supports from the 1847ff26957STiwei Bie * memory subsystem in the future. 1857ff26957STiwei Bie */ 18676f80881SAnatoly Burakov rte_mcfg_mem_read_lock(); 18712ecb2f6SMaxime Coquelin pthread_mutex_lock(&dev->mutex); 18812ecb2f6SMaxime Coquelin 18957ae79a7SJianfeng Tan /* Step 2: share memory regions */ 190539d910cSMaxime Coquelin ret = dev->ops->set_memory_table(dev); 19157ae79a7SJianfeng Tan if (ret < 0) 19257ae79a7SJianfeng Tan goto error; 19357ae79a7SJianfeng Tan 19457ae79a7SJianfeng Tan /* Step 3: kick queues */ 195a3fb6b1dSMaxime Coquelin ret = virtio_user_queue_setup(dev, virtio_user_kick_queue); 196a3fb6b1dSMaxime Coquelin if (ret < 0) 19757ae79a7SJianfeng Tan goto error; 19857ae79a7SJianfeng Tan 19957ae79a7SJianfeng Tan /* Step 4: enable queues 20057ae79a7SJianfeng Tan * we enable the 1st queue pair by default. 20157ae79a7SJianfeng Tan */ 202a3fb6b1dSMaxime Coquelin ret = dev->ops->enable_qp(dev, 0, 1); 203a3fb6b1dSMaxime Coquelin if (ret < 0) 204a3fb6b1dSMaxime Coquelin goto error; 20557ae79a7SJianfeng Tan 20612ecb2f6SMaxime Coquelin dev->started = true; 2079af79db2SMaxime Coquelin 20812ecb2f6SMaxime Coquelin pthread_mutex_unlock(&dev->mutex); 20976f80881SAnatoly Burakov rte_mcfg_mem_read_unlock(); 21012ecb2f6SMaxime Coquelin 21137a7eb2aSJianfeng Tan return 0; 21237a7eb2aSJianfeng Tan error: 21312ecb2f6SMaxime Coquelin pthread_mutex_unlock(&dev->mutex); 21476f80881SAnatoly Burakov rte_mcfg_mem_read_unlock(); 215a3fb6b1dSMaxime Coquelin 216a3fb6b1dSMaxime Coquelin PMD_INIT_LOG(ERR, "(%s) Failed to start device\n", dev->path); 217a3fb6b1dSMaxime Coquelin 21837a7eb2aSJianfeng Tan /* TODO: free resource here or caller to check */ 21937a7eb2aSJianfeng Tan return -1; 22037a7eb2aSJianfeng Tan } 22137a7eb2aSJianfeng Tan 22237a7eb2aSJianfeng Tan int virtio_user_stop_device(struct virtio_user_dev *dev) 22337a7eb2aSJianfeng Tan { 22474dc6746STiwei Bie struct vhost_vring_state state; 225c12a26eeSJianfeng Tan uint32_t i; 226a3fb6b1dSMaxime Coquelin int ret; 227c12a26eeSJianfeng Tan 22812ecb2f6SMaxime Coquelin pthread_mutex_lock(&dev->mutex); 229f457e900STiwei Bie if (!dev->started) 230f457e900STiwei Bie goto out; 231f457e900STiwei Bie 232a3fb6b1dSMaxime Coquelin for (i = 0; i < dev->max_queue_pairs; ++i) { 233a3fb6b1dSMaxime Coquelin ret = dev->ops->enable_qp(dev, i, 0); 234a3fb6b1dSMaxime Coquelin if (ret < 0) 235a3fb6b1dSMaxime Coquelin goto err; 236a3fb6b1dSMaxime Coquelin } 237c12a26eeSJianfeng Tan 23874dc6746STiwei Bie /* Stop the backend. */ 23974dc6746STiwei Bie for (i = 0; i < dev->max_queue_pairs * 2; ++i) { 24074dc6746STiwei Bie state.index = i; 241a3fb6b1dSMaxime Coquelin ret = dev->ops->get_vring_base(dev, &state); 242a3fb6b1dSMaxime Coquelin if (ret < 0) { 243a3fb6b1dSMaxime Coquelin PMD_DRV_LOG(ERR, "(%s) get_vring_base failed, index=%u", dev->path, i); 244a3fb6b1dSMaxime Coquelin goto err; 2450d6a8752SJianfeng Tan } 24674dc6746STiwei Bie } 24774dc6746STiwei Bie 24812ecb2f6SMaxime Coquelin dev->started = false; 249a3fb6b1dSMaxime Coquelin 250f457e900STiwei Bie out: 25112ecb2f6SMaxime Coquelin pthread_mutex_unlock(&dev->mutex); 2520d6a8752SJianfeng Tan 253a3fb6b1dSMaxime Coquelin return 0; 254a3fb6b1dSMaxime Coquelin err: 255a3fb6b1dSMaxime Coquelin pthread_mutex_unlock(&dev->mutex); 256a3fb6b1dSMaxime Coquelin 257a3fb6b1dSMaxime Coquelin PMD_INIT_LOG(ERR, "(%s) Failed to stop device\n", dev->path); 258a3fb6b1dSMaxime Coquelin 259a3fb6b1dSMaxime Coquelin return -1; 26037a7eb2aSJianfeng Tan } 26137a7eb2aSJianfeng Tan 26237a7eb2aSJianfeng Tan static inline void 26337a7eb2aSJianfeng Tan parse_mac(struct virtio_user_dev *dev, const char *mac) 26437a7eb2aSJianfeng Tan { 2658eb49a18SStephen Hemminger struct rte_ether_addr tmp; 26637a7eb2aSJianfeng Tan 26737a7eb2aSJianfeng Tan if (!mac) 26837a7eb2aSJianfeng Tan return; 26937a7eb2aSJianfeng Tan 2708eb49a18SStephen Hemminger if (rte_ether_unformat_addr(mac, &tmp) == 0) { 2718eb49a18SStephen Hemminger memcpy(dev->mac_addr, &tmp, RTE_ETHER_ADDR_LEN); 27237a7eb2aSJianfeng Tan dev->mac_specified = 1; 27337a7eb2aSJianfeng Tan } else { 27437a7eb2aSJianfeng Tan /* ignore the wrong mac, use random mac */ 27537a7eb2aSJianfeng Tan PMD_DRV_LOG(ERR, "wrong format of mac: %s", mac); 27637a7eb2aSJianfeng Tan } 27737a7eb2aSJianfeng Tan } 27837a7eb2aSJianfeng Tan 27933d24d65SJianfeng Tan static int 280e6e7ad8bSJianfeng Tan virtio_user_dev_init_notify(struct virtio_user_dev *dev) 28133d24d65SJianfeng Tan { 282e6e7ad8bSJianfeng Tan uint32_t i, j; 283e6e7ad8bSJianfeng Tan int callfd; 284e6e7ad8bSJianfeng Tan int kickfd; 28533d24d65SJianfeng Tan 286*2e4c1b50SMaxime Coquelin for (i = 0; i < dev->max_queue_pairs * 2; i++) { 287e6e7ad8bSJianfeng Tan /* May use invalid flag, but some backend uses kickfd and 288e6e7ad8bSJianfeng Tan * callfd as criteria to judge if dev is alive. so finally we 289e6e7ad8bSJianfeng Tan * use real event_fd. 290e6e7ad8bSJianfeng Tan */ 291e6e7ad8bSJianfeng Tan callfd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); 292e6e7ad8bSJianfeng Tan if (callfd < 0) { 293a3fb6b1dSMaxime Coquelin PMD_DRV_LOG(ERR, "(%s) callfd error, %s", dev->path, strerror(errno)); 294*2e4c1b50SMaxime Coquelin goto err; 295e6e7ad8bSJianfeng Tan } 296e6e7ad8bSJianfeng Tan kickfd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); 297e6e7ad8bSJianfeng Tan if (kickfd < 0) { 29897ed740cSJiawei Zhu close(callfd); 299a3fb6b1dSMaxime Coquelin PMD_DRV_LOG(ERR, "(%s) kickfd error, %s", dev->path, strerror(errno)); 300*2e4c1b50SMaxime Coquelin goto err; 301e6e7ad8bSJianfeng Tan } 302e6e7ad8bSJianfeng Tan dev->callfds[i] = callfd; 303e6e7ad8bSJianfeng Tan dev->kickfds[i] = kickfd; 304e6e7ad8bSJianfeng Tan } 305e6e7ad8bSJianfeng Tan 306*2e4c1b50SMaxime Coquelin return 0; 307*2e4c1b50SMaxime Coquelin err: 308*2e4c1b50SMaxime Coquelin for (j = 0; j < i; j++) { 309*2e4c1b50SMaxime Coquelin if (dev->kickfds[j] >= 0) { 310e6e7ad8bSJianfeng Tan close(dev->kickfds[j]); 311*2e4c1b50SMaxime Coquelin dev->kickfds[j] = -1; 312*2e4c1b50SMaxime Coquelin } 313*2e4c1b50SMaxime Coquelin if (dev->callfds[j] >= 0) { 314*2e4c1b50SMaxime Coquelin close(dev->callfds[j]); 315*2e4c1b50SMaxime Coquelin dev->callfds[j] = -1; 316*2e4c1b50SMaxime Coquelin } 317e6e7ad8bSJianfeng Tan } 318e6e7ad8bSJianfeng Tan 319e6e7ad8bSJianfeng Tan return -1; 320e6e7ad8bSJianfeng Tan } 321e6e7ad8bSJianfeng Tan 322*2e4c1b50SMaxime Coquelin static void 323*2e4c1b50SMaxime Coquelin virtio_user_dev_uninit_notify(struct virtio_user_dev *dev) 324*2e4c1b50SMaxime Coquelin { 325*2e4c1b50SMaxime Coquelin uint32_t i; 326*2e4c1b50SMaxime Coquelin 327*2e4c1b50SMaxime Coquelin for (i = 0; i < dev->max_queue_pairs * 2; ++i) { 328*2e4c1b50SMaxime Coquelin if (dev->kickfds[i] >= 0) { 329*2e4c1b50SMaxime Coquelin close(dev->kickfds[i]); 330*2e4c1b50SMaxime Coquelin dev->kickfds[i] = -1; 331*2e4c1b50SMaxime Coquelin } 332*2e4c1b50SMaxime Coquelin if (dev->callfds[i] >= 0) { 333*2e4c1b50SMaxime Coquelin close(dev->callfds[i]); 334*2e4c1b50SMaxime Coquelin dev->callfds[i] = -1; 335*2e4c1b50SMaxime Coquelin } 336*2e4c1b50SMaxime Coquelin } 337e6e7ad8bSJianfeng Tan } 338e6e7ad8bSJianfeng Tan 339e6e7ad8bSJianfeng Tan static int 3403d4fb6fdSJianfeng Tan virtio_user_fill_intr_handle(struct virtio_user_dev *dev) 3413d4fb6fdSJianfeng Tan { 3423d4fb6fdSJianfeng Tan uint32_t i; 3433d4fb6fdSJianfeng Tan struct rte_eth_dev *eth_dev = &rte_eth_devices[dev->port_id]; 3443d4fb6fdSJianfeng Tan 3453d4fb6fdSJianfeng Tan if (!eth_dev->intr_handle) { 3463d4fb6fdSJianfeng Tan eth_dev->intr_handle = malloc(sizeof(*eth_dev->intr_handle)); 3473d4fb6fdSJianfeng Tan if (!eth_dev->intr_handle) { 348a3fb6b1dSMaxime Coquelin PMD_DRV_LOG(ERR, "(%s) failed to allocate intr_handle", dev->path); 3493d4fb6fdSJianfeng Tan return -1; 3503d4fb6fdSJianfeng Tan } 3513d4fb6fdSJianfeng Tan memset(eth_dev->intr_handle, 0, sizeof(*eth_dev->intr_handle)); 3523d4fb6fdSJianfeng Tan } 3533d4fb6fdSJianfeng Tan 3543d4fb6fdSJianfeng Tan for (i = 0; i < dev->max_queue_pairs; ++i) 3553d4fb6fdSJianfeng Tan eth_dev->intr_handle->efds[i] = dev->callfds[i]; 3563d4fb6fdSJianfeng Tan eth_dev->intr_handle->nb_efd = dev->max_queue_pairs; 3573d4fb6fdSJianfeng Tan eth_dev->intr_handle->max_intr = dev->max_queue_pairs + 1; 3583d4fb6fdSJianfeng Tan eth_dev->intr_handle->type = RTE_INTR_HANDLE_VDEV; 35929906b97SJingjing Wu /* For virtio vdev, no need to read counter for clean */ 36029906b97SJingjing Wu eth_dev->intr_handle->efd_counter_size = 0; 36194973531SMaxime Coquelin eth_dev->intr_handle->fd = dev->ops->get_intr_fd(dev); 3623d4fb6fdSJianfeng Tan 3633d4fb6fdSJianfeng Tan return 0; 3643d4fb6fdSJianfeng Tan } 3653d4fb6fdSJianfeng Tan 36612ecb2f6SMaxime Coquelin static void 36712ecb2f6SMaxime Coquelin virtio_user_mem_event_cb(enum rte_mem_event type __rte_unused, 3682286291dSTiwei Bie const void *addr, 36912ecb2f6SMaxime Coquelin size_t len __rte_unused, 37012ecb2f6SMaxime Coquelin void *arg) 37112ecb2f6SMaxime Coquelin { 37212ecb2f6SMaxime Coquelin struct virtio_user_dev *dev = arg; 373f32c7c9dSAnatoly Burakov struct rte_memseg_list *msl; 37412ecb2f6SMaxime Coquelin uint16_t i; 375a3fb6b1dSMaxime Coquelin int ret = 0; 37612ecb2f6SMaxime Coquelin 377f32c7c9dSAnatoly Burakov /* ignore externally allocated memory */ 378f32c7c9dSAnatoly Burakov msl = rte_mem_virt2memseg_list(addr); 379f32c7c9dSAnatoly Burakov if (msl->external) 380f32c7c9dSAnatoly Burakov return; 381f32c7c9dSAnatoly Burakov 38212ecb2f6SMaxime Coquelin pthread_mutex_lock(&dev->mutex); 38312ecb2f6SMaxime Coquelin 38412ecb2f6SMaxime Coquelin if (dev->started == false) 38512ecb2f6SMaxime Coquelin goto exit; 38612ecb2f6SMaxime Coquelin 38712ecb2f6SMaxime Coquelin /* Step 1: pause the active queues */ 388a3fb6b1dSMaxime Coquelin for (i = 0; i < dev->queue_pairs; i++) { 389a3fb6b1dSMaxime Coquelin ret = dev->ops->enable_qp(dev, i, 0); 390a3fb6b1dSMaxime Coquelin if (ret < 0) 391a3fb6b1dSMaxime Coquelin goto exit; 392a3fb6b1dSMaxime Coquelin } 39312ecb2f6SMaxime Coquelin 39412ecb2f6SMaxime Coquelin /* Step 2: update memory regions */ 395a3fb6b1dSMaxime Coquelin ret = dev->ops->set_memory_table(dev); 396a3fb6b1dSMaxime Coquelin if (ret < 0) 397a3fb6b1dSMaxime Coquelin goto exit; 39812ecb2f6SMaxime Coquelin 39912ecb2f6SMaxime Coquelin /* Step 3: resume the active queues */ 400a3fb6b1dSMaxime Coquelin for (i = 0; i < dev->queue_pairs; i++) { 401a3fb6b1dSMaxime Coquelin ret = dev->ops->enable_qp(dev, i, 1); 402a3fb6b1dSMaxime Coquelin if (ret < 0) 403a3fb6b1dSMaxime Coquelin goto exit; 404a3fb6b1dSMaxime Coquelin } 40512ecb2f6SMaxime Coquelin 40612ecb2f6SMaxime Coquelin exit: 40712ecb2f6SMaxime Coquelin pthread_mutex_unlock(&dev->mutex); 408a3fb6b1dSMaxime Coquelin 409a3fb6b1dSMaxime Coquelin if (ret < 0) 410a3fb6b1dSMaxime Coquelin PMD_DRV_LOG(ERR, "(%s) Failed to update memory table\n", dev->path); 41112ecb2f6SMaxime Coquelin } 41212ecb2f6SMaxime Coquelin 4133d4fb6fdSJianfeng Tan static int 414e6e7ad8bSJianfeng Tan virtio_user_dev_setup(struct virtio_user_dev *dev) 415e6e7ad8bSJianfeng Tan { 416bd8f50a4SZhiyong Yang if (dev->is_server) { 417f908b22eSAdrian Moreno if (dev->backend_type != VIRTIO_USER_BACKEND_VHOST_USER) { 418f908b22eSAdrian Moreno PMD_DRV_LOG(ERR, "Server mode only supports vhost-user!"); 419bd8f50a4SZhiyong Yang return -1; 420bd8f50a4SZhiyong Yang } 4219af79db2SMaxime Coquelin } 4229af79db2SMaxime Coquelin 42386388a3aSMaxime Coquelin switch (dev->backend_type) { 42486388a3aSMaxime Coquelin case VIRTIO_USER_BACKEND_VHOST_USER: 425520dd992SFerruh Yigit dev->ops = &virtio_ops_user; 42686388a3aSMaxime Coquelin break; 42786388a3aSMaxime Coquelin case VIRTIO_USER_BACKEND_VHOST_KERNEL: 428520dd992SFerruh Yigit dev->ops = &virtio_ops_kernel; 42986388a3aSMaxime Coquelin break; 43086388a3aSMaxime Coquelin case VIRTIO_USER_BACKEND_VHOST_VDPA: 4316b901437SMaxime Coquelin dev->ops = &virtio_ops_vdpa; 43286388a3aSMaxime Coquelin break; 43386388a3aSMaxime Coquelin default: 434a3fb6b1dSMaxime Coquelin PMD_DRV_LOG(ERR, "(%s) Unknown backend type", dev->path); 4356b901437SMaxime Coquelin return -1; 436e3b43481SJianfeng Tan } 4379af79db2SMaxime Coquelin 438a3fb6b1dSMaxime Coquelin if (dev->ops->setup(dev) < 0) { 439a3fb6b1dSMaxime Coquelin PMD_INIT_LOG(ERR, "(%s) Failed to setup backend\n", dev->path); 440cc4690e9SJianfeng Tan return -1; 441a3fb6b1dSMaxime Coquelin } 442cc4690e9SJianfeng Tan 443a3fb6b1dSMaxime Coquelin if (virtio_user_dev_init_notify(dev) < 0) { 444a3fb6b1dSMaxime Coquelin PMD_INIT_LOG(ERR, "(%s) Failed to init notifiers\n", dev->path); 445*2e4c1b50SMaxime Coquelin goto destroy; 446a3fb6b1dSMaxime Coquelin } 447cc4690e9SJianfeng Tan 448a3fb6b1dSMaxime Coquelin if (virtio_user_fill_intr_handle(dev) < 0) { 449a3fb6b1dSMaxime Coquelin PMD_INIT_LOG(ERR, "(%s) Failed to init interrupt handler\n", dev->path); 450*2e4c1b50SMaxime Coquelin goto uninit; 451a3fb6b1dSMaxime Coquelin } 452cc4690e9SJianfeng Tan 453cc4690e9SJianfeng Tan return 0; 454*2e4c1b50SMaxime Coquelin 455*2e4c1b50SMaxime Coquelin uninit: 456*2e4c1b50SMaxime Coquelin virtio_user_dev_uninit_notify(dev); 457*2e4c1b50SMaxime Coquelin destroy: 458*2e4c1b50SMaxime Coquelin dev->ops->destroy(dev); 459*2e4c1b50SMaxime Coquelin 460*2e4c1b50SMaxime Coquelin return -1; 46133d24d65SJianfeng Tan } 46233d24d65SJianfeng Tan 463bed3b24cSJianfeng Tan /* Use below macro to filter features from vhost backend */ 464bed3b24cSJianfeng Tan #define VIRTIO_USER_SUPPORTED_FEATURES \ 465bed3b24cSJianfeng Tan (1ULL << VIRTIO_NET_F_MAC | \ 466bed3b24cSJianfeng Tan 1ULL << VIRTIO_NET_F_STATUS | \ 467bed3b24cSJianfeng Tan 1ULL << VIRTIO_NET_F_MQ | \ 468bed3b24cSJianfeng Tan 1ULL << VIRTIO_NET_F_CTRL_MAC_ADDR | \ 469bed3b24cSJianfeng Tan 1ULL << VIRTIO_NET_F_CTRL_VQ | \ 470bed3b24cSJianfeng Tan 1ULL << VIRTIO_NET_F_CTRL_RX | \ 471bed3b24cSJianfeng Tan 1ULL << VIRTIO_NET_F_CTRL_VLAN | \ 472bed3b24cSJianfeng Tan 1ULL << VIRTIO_NET_F_CSUM | \ 473bed3b24cSJianfeng Tan 1ULL << VIRTIO_NET_F_HOST_TSO4 | \ 474bed3b24cSJianfeng Tan 1ULL << VIRTIO_NET_F_HOST_TSO6 | \ 475bed3b24cSJianfeng Tan 1ULL << VIRTIO_NET_F_MRG_RXBUF | \ 476bed3b24cSJianfeng Tan 1ULL << VIRTIO_RING_F_INDIRECT_DESC | \ 477bed3b24cSJianfeng Tan 1ULL << VIRTIO_NET_F_GUEST_CSUM | \ 478bed3b24cSJianfeng Tan 1ULL << VIRTIO_NET_F_GUEST_TSO4 | \ 479bed3b24cSJianfeng Tan 1ULL << VIRTIO_NET_F_GUEST_TSO6 | \ 48041e45c90SMarvin Liu 1ULL << VIRTIO_F_IN_ORDER | \ 48134f3966cSYuanhan Liu 1ULL << VIRTIO_F_VERSION_1 | \ 4825b75b63cSMaxime Coquelin 1ULL << VIRTIO_F_RING_PACKED) 4838e756105SMaxime Coquelin 48437a7eb2aSJianfeng Tan int 48537a7eb2aSJianfeng Tan virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues, 486488ed97aSMarvin Liu int cq, int queue_size, const char *mac, char **ifname, 487f908b22eSAdrian Moreno int server, int mrg_rxbuf, int in_order, int packed_vq, 488f908b22eSAdrian Moreno enum virtio_user_backend_type backend_type) 48937a7eb2aSJianfeng Tan { 4905b75b63cSMaxime Coquelin uint64_t backend_features; 491*2e4c1b50SMaxime Coquelin int i; 4928e756105SMaxime Coquelin 49312ecb2f6SMaxime Coquelin pthread_mutex_init(&dev->mutex, NULL); 4946723c0fcSBruce Richardson strlcpy(dev->path, path, PATH_MAX); 495*2e4c1b50SMaxime Coquelin 496*2e4c1b50SMaxime Coquelin for (i = 0; i < VIRTIO_MAX_VIRTQUEUES; i++) { 497*2e4c1b50SMaxime Coquelin dev->kickfds[i] = -1; 498*2e4c1b50SMaxime Coquelin dev->callfds[i] = -1; 499*2e4c1b50SMaxime Coquelin } 500*2e4c1b50SMaxime Coquelin 50112ecb2f6SMaxime Coquelin dev->started = 0; 50237a7eb2aSJianfeng Tan dev->max_queue_pairs = queues; 50337a7eb2aSJianfeng Tan dev->queue_pairs = 1; /* mq disabled by default */ 50437a7eb2aSJianfeng Tan dev->queue_size = queue_size; 5051c8489daSTiwei Bie dev->is_server = server; 50637a7eb2aSJianfeng Tan dev->mac_specified = 0; 507bb97d2ddSTiwei Bie dev->frontend_features = 0; 5085b75b63cSMaxime Coquelin dev->unsupported_features = 0; 509f908b22eSAdrian Moreno dev->backend_type = backend_type; 510f908b22eSAdrian Moreno 51137a7eb2aSJianfeng Tan parse_mac(dev, mac); 51237a7eb2aSJianfeng Tan 5134214a1b4SWenfeng Liu if (*ifname) { 5144214a1b4SWenfeng Liu dev->ifname = *ifname; 5154214a1b4SWenfeng Liu *ifname = NULL; 5164214a1b4SWenfeng Liu } 5174214a1b4SWenfeng Liu 51833d24d65SJianfeng Tan if (virtio_user_dev_setup(dev) < 0) { 519a3fb6b1dSMaxime Coquelin PMD_INIT_LOG(ERR, "(%s) backend set up fails", dev->path); 52037a7eb2aSJianfeng Tan return -1; 52137a7eb2aSJianfeng Tan } 522bce7e905SJianfeng Tan 52306856cabSMaxime Coquelin if (dev->ops->set_owner(dev) < 0) { 524a3fb6b1dSMaxime Coquelin PMD_INIT_LOG(ERR, "(%s) Failed to set backend owner", dev->path); 52537a7eb2aSJianfeng Tan return -1; 52637a7eb2aSJianfeng Tan } 52737a7eb2aSJianfeng Tan 5285b75b63cSMaxime Coquelin if (dev->ops->get_backend_features(&backend_features) < 0) { 529a3fb6b1dSMaxime Coquelin PMD_INIT_LOG(ERR, "(%s) Failed to get backend features", dev->path); 53037a7eb2aSJianfeng Tan return -1; 53137a7eb2aSJianfeng Tan } 5328e756105SMaxime Coquelin 5335b75b63cSMaxime Coquelin dev->unsupported_features = ~(VIRTIO_USER_SUPPORTED_FEATURES | backend_features); 5345b75b63cSMaxime Coquelin 5355b75b63cSMaxime Coquelin if (dev->ops->get_features(dev, &dev->device_features) < 0) { 5365b75b63cSMaxime Coquelin PMD_INIT_LOG(ERR, "(%s) Failed to get device features", dev->path); 5378e756105SMaxime Coquelin return -1; 538a3fb6b1dSMaxime Coquelin } 5398e756105SMaxime Coquelin 540bd9568f3STiwei Bie if (!mrg_rxbuf) 541488ed97aSMarvin Liu dev->unsupported_features |= (1ull << VIRTIO_NET_F_MRG_RXBUF); 542488ed97aSMarvin Liu 543bd9568f3STiwei Bie if (!in_order) 544488ed97aSMarvin Liu dev->unsupported_features |= (1ull << VIRTIO_F_IN_ORDER); 545488ed97aSMarvin Liu 54648a44640SJens Freimann if (!packed_vq) 5479070f88bSTiwei Bie dev->unsupported_features |= (1ull << VIRTIO_F_RING_PACKED); 54834f3966cSYuanhan Liu 5499070f88bSTiwei Bie if (dev->mac_specified) 5509070f88bSTiwei Bie dev->frontend_features |= (1ull << VIRTIO_NET_F_MAC); 5519070f88bSTiwei Bie else 5527c66ff61SMarvin Liu dev->unsupported_features |= (1ull << VIRTIO_NET_F_MAC); 553f9b9d1a5SJianfeng Tan 554142678d4SJianfeng Tan if (cq) { 555142678d4SJianfeng Tan /* device does not really need to know anything about CQ, 556142678d4SJianfeng Tan * so if necessary, we just claim to support CQ 557f9b9d1a5SJianfeng Tan */ 558bb97d2ddSTiwei Bie dev->frontend_features |= (1ull << VIRTIO_NET_F_CTRL_VQ); 559142678d4SJianfeng Tan } else { 5607c66ff61SMarvin Liu dev->unsupported_features |= (1ull << VIRTIO_NET_F_CTRL_VQ); 561bd9568f3STiwei Bie /* Also disable features that depend on VIRTIO_NET_F_CTRL_VQ */ 5627c66ff61SMarvin Liu dev->unsupported_features |= (1ull << VIRTIO_NET_F_CTRL_RX); 5637c66ff61SMarvin Liu dev->unsupported_features |= (1ull << VIRTIO_NET_F_CTRL_VLAN); 5647c66ff61SMarvin Liu dev->unsupported_features |= 5657c66ff61SMarvin Liu (1ull << VIRTIO_NET_F_GUEST_ANNOUNCE); 5667c66ff61SMarvin Liu dev->unsupported_features |= (1ull << VIRTIO_NET_F_MQ); 5677c66ff61SMarvin Liu dev->unsupported_features |= 5687c66ff61SMarvin Liu (1ull << VIRTIO_NET_F_CTRL_MAC_ADDR); 569f9b9d1a5SJianfeng Tan } 570f9b9d1a5SJianfeng Tan 57135c4f855SJianfeng Tan /* The backend will not report this feature, we add it explicitly */ 572f908b22eSAdrian Moreno if (dev->backend_type == VIRTIO_USER_BACKEND_VHOST_USER) 573bb97d2ddSTiwei Bie dev->frontend_features |= (1ull << VIRTIO_NET_F_STATUS); 57435c4f855SJianfeng Tan 575bd9568f3STiwei Bie /* 576bd9568f3STiwei Bie * Device features = 577bd9568f3STiwei Bie * (frontend_features | backend_features) & ~unsupported_features; 578bd9568f3STiwei Bie */ 579bb97d2ddSTiwei Bie dev->device_features |= dev->frontend_features; 580bd9568f3STiwei Bie dev->device_features &= ~dev->unsupported_features; 581bed3b24cSJianfeng Tan 58212ecb2f6SMaxime Coquelin if (rte_mem_event_callback_register(VIRTIO_USER_MEM_EVENT_CLB_NAME, 58312ecb2f6SMaxime Coquelin virtio_user_mem_event_cb, dev)) { 58488e5469fSXiao Wang if (rte_errno != ENOTSUP) { 585a3fb6b1dSMaxime Coquelin PMD_INIT_LOG(ERR, "(%s) Failed to register mem event callback\n", 586a3fb6b1dSMaxime Coquelin dev->path); 58712ecb2f6SMaxime Coquelin return -1; 58812ecb2f6SMaxime Coquelin } 58988e5469fSXiao Wang } 59012ecb2f6SMaxime Coquelin 59137a7eb2aSJianfeng Tan return 0; 59237a7eb2aSJianfeng Tan } 59337a7eb2aSJianfeng Tan 59437a7eb2aSJianfeng Tan void 59537a7eb2aSJianfeng Tan virtio_user_dev_uninit(struct virtio_user_dev *dev) 59637a7eb2aSJianfeng Tan { 597e3b43481SJianfeng Tan virtio_user_stop_device(dev); 598e3b43481SJianfeng Tan 59912ecb2f6SMaxime Coquelin rte_mem_event_callback_unregister(VIRTIO_USER_MEM_EVENT_CLB_NAME, dev); 60012ecb2f6SMaxime Coquelin 601*2e4c1b50SMaxime Coquelin virtio_user_dev_uninit_notify(dev); 6024214a1b4SWenfeng Liu 6034214a1b4SWenfeng Liu free(dev->ifname); 604bd8f50a4SZhiyong Yang 605bd8f50a4SZhiyong Yang if (dev->is_server) 606bd8f50a4SZhiyong Yang unlink(dev->path); 607748e5ea5SMaxime Coquelin 608748e5ea5SMaxime Coquelin dev->ops->destroy(dev); 60937a7eb2aSJianfeng Tan } 610f9b9d1a5SJianfeng Tan 611201a4165SZhiyong Yang uint8_t 612f9b9d1a5SJianfeng Tan virtio_user_handle_mq(struct virtio_user_dev *dev, uint16_t q_pairs) 613f9b9d1a5SJianfeng Tan { 614f9b9d1a5SJianfeng Tan uint16_t i; 615f9b9d1a5SJianfeng Tan uint8_t ret = 0; 616f9b9d1a5SJianfeng Tan 617f9b9d1a5SJianfeng Tan if (q_pairs > dev->max_queue_pairs) { 618a3fb6b1dSMaxime Coquelin PMD_INIT_LOG(ERR, "(%s) multi-q config %u, but only %u supported", 619a3fb6b1dSMaxime Coquelin dev->path, q_pairs, dev->max_queue_pairs); 620f9b9d1a5SJianfeng Tan return -1; 621f9b9d1a5SJianfeng Tan } 622f9b9d1a5SJianfeng Tan 623f9b9d1a5SJianfeng Tan for (i = 0; i < q_pairs; ++i) 62433d24d65SJianfeng Tan ret |= dev->ops->enable_qp(dev, i, 1); 625f9b9d1a5SJianfeng Tan for (i = q_pairs; i < dev->max_queue_pairs; ++i) 62633d24d65SJianfeng Tan ret |= dev->ops->enable_qp(dev, i, 0); 62794973531SMaxime Coquelin 628f9b9d1a5SJianfeng Tan dev->queue_pairs = q_pairs; 629f9b9d1a5SJianfeng Tan 630f9b9d1a5SJianfeng Tan return ret; 631f9b9d1a5SJianfeng Tan } 632f9b9d1a5SJianfeng Tan 633f9b9d1a5SJianfeng Tan static uint32_t 634f9b9d1a5SJianfeng Tan virtio_user_handle_ctrl_msg(struct virtio_user_dev *dev, struct vring *vring, 635f9b9d1a5SJianfeng Tan uint16_t idx_hdr) 636f9b9d1a5SJianfeng Tan { 637f9b9d1a5SJianfeng Tan struct virtio_net_ctrl_hdr *hdr; 638f9b9d1a5SJianfeng Tan virtio_net_ctrl_ack status = ~0; 639f9b9d1a5SJianfeng Tan uint16_t i, idx_data, idx_status; 640f9b9d1a5SJianfeng Tan uint32_t n_descs = 0; 641f9b9d1a5SJianfeng Tan 642f9b9d1a5SJianfeng Tan /* locate desc for header, data, and status */ 643f9b9d1a5SJianfeng Tan idx_data = vring->desc[idx_hdr].next; 644f9b9d1a5SJianfeng Tan n_descs++; 645f9b9d1a5SJianfeng Tan 646f9b9d1a5SJianfeng Tan i = idx_data; 647f9b9d1a5SJianfeng Tan while (vring->desc[i].flags == VRING_DESC_F_NEXT) { 648f9b9d1a5SJianfeng Tan i = vring->desc[i].next; 649f9b9d1a5SJianfeng Tan n_descs++; 650f9b9d1a5SJianfeng Tan } 651f9b9d1a5SJianfeng Tan 652f9b9d1a5SJianfeng Tan /* locate desc for status */ 653f9b9d1a5SJianfeng Tan idx_status = i; 654f9b9d1a5SJianfeng Tan n_descs++; 655f9b9d1a5SJianfeng Tan 656f9b9d1a5SJianfeng Tan hdr = (void *)(uintptr_t)vring->desc[idx_hdr].addr; 657f9b9d1a5SJianfeng Tan if (hdr->class == VIRTIO_NET_CTRL_MQ && 658f9b9d1a5SJianfeng Tan hdr->cmd == VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) { 659f9b9d1a5SJianfeng Tan uint16_t queues; 660f9b9d1a5SJianfeng Tan 661f9b9d1a5SJianfeng Tan queues = *(uint16_t *)(uintptr_t)vring->desc[idx_data].addr; 662f9b9d1a5SJianfeng Tan status = virtio_user_handle_mq(dev, queues); 663a76552d4SMarvin Liu } else if (hdr->class == VIRTIO_NET_CTRL_RX || 664a76552d4SMarvin Liu hdr->class == VIRTIO_NET_CTRL_MAC || 665a76552d4SMarvin Liu hdr->class == VIRTIO_NET_CTRL_VLAN) { 666a76552d4SMarvin Liu status = 0; 667f9b9d1a5SJianfeng Tan } 668f9b9d1a5SJianfeng Tan 669f9b9d1a5SJianfeng Tan /* Update status */ 670f9b9d1a5SJianfeng Tan *(virtio_net_ctrl_ack *)(uintptr_t)vring->desc[idx_status].addr = status; 671f9b9d1a5SJianfeng Tan 672f9b9d1a5SJianfeng Tan return n_descs; 673f9b9d1a5SJianfeng Tan } 674f9b9d1a5SJianfeng Tan 67548a44640SJens Freimann static inline int 67648a44640SJens Freimann desc_is_avail(struct vring_packed_desc *desc, bool wrap_counter) 67748a44640SJens Freimann { 6786094557dSJoyce Kong uint16_t flags = __atomic_load_n(&desc->flags, __ATOMIC_ACQUIRE); 67912e9e70cSTiwei Bie 68012e9e70cSTiwei Bie return wrap_counter == !!(flags & VRING_PACKED_DESC_F_AVAIL) && 68112e9e70cSTiwei Bie wrap_counter != !!(flags & VRING_PACKED_DESC_F_USED); 68248a44640SJens Freimann } 68348a44640SJens Freimann 68448a44640SJens Freimann static uint32_t 68545c224e7STiwei Bie virtio_user_handle_ctrl_msg_packed(struct virtio_user_dev *dev, 68648a44640SJens Freimann struct vring_packed *vring, 68748a44640SJens Freimann uint16_t idx_hdr) 68848a44640SJens Freimann { 68948a44640SJens Freimann struct virtio_net_ctrl_hdr *hdr; 69048a44640SJens Freimann virtio_net_ctrl_ack status = ~0; 69148a44640SJens Freimann uint16_t idx_data, idx_status; 69248a44640SJens Freimann /* initialize to one, header is first */ 69348a44640SJens Freimann uint32_t n_descs = 1; 69448a44640SJens Freimann 69548a44640SJens Freimann /* locate desc for header, data, and status */ 69648a44640SJens Freimann idx_data = idx_hdr + 1; 69748a44640SJens Freimann if (idx_data >= dev->queue_size) 69848a44640SJens Freimann idx_data -= dev->queue_size; 69948a44640SJens Freimann 70048a44640SJens Freimann n_descs++; 70148a44640SJens Freimann 70248a44640SJens Freimann idx_status = idx_data; 7034cdc4d98STiwei Bie while (vring->desc[idx_status].flags & VRING_DESC_F_NEXT) { 70448a44640SJens Freimann idx_status++; 70548a44640SJens Freimann if (idx_status >= dev->queue_size) 70648a44640SJens Freimann idx_status -= dev->queue_size; 70748a44640SJens Freimann n_descs++; 70848a44640SJens Freimann } 70948a44640SJens Freimann 7104cdc4d98STiwei Bie hdr = (void *)(uintptr_t)vring->desc[idx_hdr].addr; 71148a44640SJens Freimann if (hdr->class == VIRTIO_NET_CTRL_MQ && 71248a44640SJens Freimann hdr->cmd == VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) { 71348a44640SJens Freimann uint16_t queues; 71448a44640SJens Freimann 71548a44640SJens Freimann queues = *(uint16_t *)(uintptr_t) 7164cdc4d98STiwei Bie vring->desc[idx_data].addr; 71748a44640SJens Freimann status = virtio_user_handle_mq(dev, queues); 718a76552d4SMarvin Liu } else if (hdr->class == VIRTIO_NET_CTRL_RX || 719a76552d4SMarvin Liu hdr->class == VIRTIO_NET_CTRL_MAC || 720a76552d4SMarvin Liu hdr->class == VIRTIO_NET_CTRL_VLAN) { 721a76552d4SMarvin Liu status = 0; 72248a44640SJens Freimann } 72348a44640SJens Freimann 72448a44640SJens Freimann /* Update status */ 72548a44640SJens Freimann *(virtio_net_ctrl_ack *)(uintptr_t) 7264cdc4d98STiwei Bie vring->desc[idx_status].addr = status; 72748a44640SJens Freimann 72845c224e7STiwei Bie /* Update used descriptor */ 7294cdc4d98STiwei Bie vring->desc[idx_hdr].id = vring->desc[idx_status].id; 7304cdc4d98STiwei Bie vring->desc[idx_hdr].len = sizeof(status); 73145c224e7STiwei Bie 73248a44640SJens Freimann return n_descs; 73348a44640SJens Freimann } 73448a44640SJens Freimann 73548a44640SJens Freimann void 73648a44640SJens Freimann virtio_user_handle_cq_packed(struct virtio_user_dev *dev, uint16_t queue_idx) 73748a44640SJens Freimann { 73848a44640SJens Freimann struct virtio_user_queue *vq = &dev->packed_queues[queue_idx]; 73948a44640SJens Freimann struct vring_packed *vring = &dev->packed_vrings[queue_idx]; 74012e9e70cSTiwei Bie uint16_t n_descs, flags; 74148a44640SJens Freimann 7426094557dSJoyce Kong /* Perform a load-acquire barrier in desc_is_avail to 7436094557dSJoyce Kong * enforce the ordering between desc flags and desc 7446094557dSJoyce Kong * content. 7456094557dSJoyce Kong */ 7464cdc4d98STiwei Bie while (desc_is_avail(&vring->desc[vq->used_idx], 74748a44640SJens Freimann vq->used_wrap_counter)) { 74848a44640SJens Freimann 74945c224e7STiwei Bie n_descs = virtio_user_handle_ctrl_msg_packed(dev, vring, 75045c224e7STiwei Bie vq->used_idx); 75148a44640SJens Freimann 75212e9e70cSTiwei Bie flags = VRING_DESC_F_WRITE; 75312e9e70cSTiwei Bie if (vq->used_wrap_counter) 75412e9e70cSTiwei Bie flags |= VRING_PACKED_DESC_F_AVAIL_USED; 75512e9e70cSTiwei Bie 7562c661d41SJoyce Kong __atomic_store_n(&vring->desc[vq->used_idx].flags, flags, 7572c661d41SJoyce Kong __ATOMIC_RELEASE); 75845c224e7STiwei Bie 75945c224e7STiwei Bie vq->used_idx += n_descs; 76045c224e7STiwei Bie if (vq->used_idx >= dev->queue_size) { 76148a44640SJens Freimann vq->used_idx -= dev->queue_size; 76248a44640SJens Freimann vq->used_wrap_counter ^= 1; 76348a44640SJens Freimann } 76448a44640SJens Freimann } 76548a44640SJens Freimann } 76648a44640SJens Freimann 767f9b9d1a5SJianfeng Tan void 768f9b9d1a5SJianfeng Tan virtio_user_handle_cq(struct virtio_user_dev *dev, uint16_t queue_idx) 769f9b9d1a5SJianfeng Tan { 770f9b9d1a5SJianfeng Tan uint16_t avail_idx, desc_idx; 771f9b9d1a5SJianfeng Tan struct vring_used_elem *uep; 772f9b9d1a5SJianfeng Tan uint32_t n_descs; 773f9b9d1a5SJianfeng Tan struct vring *vring = &dev->vrings[queue_idx]; 774f9b9d1a5SJianfeng Tan 775f9b9d1a5SJianfeng Tan /* Consume avail ring, using used ring idx as first one */ 776ea5207c1SJoyce Kong while (__atomic_load_n(&vring->used->idx, __ATOMIC_RELAXED) 777ea5207c1SJoyce Kong != vring->avail->idx) { 778ea5207c1SJoyce Kong avail_idx = __atomic_load_n(&vring->used->idx, __ATOMIC_RELAXED) 779ea5207c1SJoyce Kong & (vring->num - 1); 780f9b9d1a5SJianfeng Tan desc_idx = vring->avail->ring[avail_idx]; 781f9b9d1a5SJianfeng Tan 782f9b9d1a5SJianfeng Tan n_descs = virtio_user_handle_ctrl_msg(dev, vring, desc_idx); 783f9b9d1a5SJianfeng Tan 784f9b9d1a5SJianfeng Tan /* Update used ring */ 785f9b9d1a5SJianfeng Tan uep = &vring->used->ring[avail_idx]; 7860403e37aSTiwei Bie uep->id = desc_idx; 787f9b9d1a5SJianfeng Tan uep->len = n_descs; 788f9b9d1a5SJianfeng Tan 789ea5207c1SJoyce Kong __atomic_add_fetch(&vring->used->idx, 1, __ATOMIC_RELAXED); 790f9b9d1a5SJianfeng Tan } 791f9b9d1a5SJianfeng Tan } 79257912824SMaxime Coquelin 79357912824SMaxime Coquelin int 794d7e10ea9SAdrian Moreno virtio_user_dev_set_status(struct virtio_user_dev *dev, uint8_t status) 79557912824SMaxime Coquelin { 79657912824SMaxime Coquelin int ret; 79757912824SMaxime Coquelin 798d7e10ea9SAdrian Moreno pthread_mutex_lock(&dev->mutex); 799d7e10ea9SAdrian Moreno dev->status = status; 8008723c894SMaxime Coquelin ret = dev->ops->set_status(dev, status); 801a3fb6b1dSMaxime Coquelin if (ret && ret != -ENOTSUP) 802a3fb6b1dSMaxime Coquelin PMD_INIT_LOG(ERR, "(%s) Failed to set backend status\n", dev->path); 803d7e10ea9SAdrian Moreno 804d7e10ea9SAdrian Moreno pthread_mutex_unlock(&dev->mutex); 8055043a060SAdrian Moreno return ret; 80657912824SMaxime Coquelin } 8070b0dc66cSAdrian Moreno 8080b0dc66cSAdrian Moreno int 809d7e10ea9SAdrian Moreno virtio_user_dev_update_status(struct virtio_user_dev *dev) 8100b0dc66cSAdrian Moreno { 8118723c894SMaxime Coquelin int ret; 8127784e977SMaxime Coquelin uint8_t status; 8130b0dc66cSAdrian Moreno 814d7e10ea9SAdrian Moreno pthread_mutex_lock(&dev->mutex); 8157784e977SMaxime Coquelin 8168723c894SMaxime Coquelin ret = dev->ops->get_status(dev, &status); 8178723c894SMaxime Coquelin if (!ret) { 8187784e977SMaxime Coquelin dev->status = status; 8190b0dc66cSAdrian Moreno PMD_INIT_LOG(DEBUG, "Updated Device Status(0x%08x):\n" 8200b0dc66cSAdrian Moreno "\t-RESET: %u\n" 8210b0dc66cSAdrian Moreno "\t-ACKNOWLEDGE: %u\n" 8220b0dc66cSAdrian Moreno "\t-DRIVER: %u\n" 8230b0dc66cSAdrian Moreno "\t-DRIVER_OK: %u\n" 8240b0dc66cSAdrian Moreno "\t-FEATURES_OK: %u\n" 8250b0dc66cSAdrian Moreno "\t-DEVICE_NEED_RESET: %u\n" 8260b0dc66cSAdrian Moreno "\t-FAILED: %u\n", 8270b0dc66cSAdrian Moreno dev->status, 8280b0dc66cSAdrian Moreno (dev->status == VIRTIO_CONFIG_STATUS_RESET), 8290b0dc66cSAdrian Moreno !!(dev->status & VIRTIO_CONFIG_STATUS_ACK), 8300b0dc66cSAdrian Moreno !!(dev->status & VIRTIO_CONFIG_STATUS_DRIVER), 8310b0dc66cSAdrian Moreno !!(dev->status & VIRTIO_CONFIG_STATUS_DRIVER_OK), 8320b0dc66cSAdrian Moreno !!(dev->status & VIRTIO_CONFIG_STATUS_FEATURES_OK), 8330b0dc66cSAdrian Moreno !!(dev->status & VIRTIO_CONFIG_STATUS_DEV_NEED_RESET), 8340b0dc66cSAdrian Moreno !!(dev->status & VIRTIO_CONFIG_STATUS_FAILED)); 8358723c894SMaxime Coquelin } else if (ret != -ENOTSUP) { 836a3fb6b1dSMaxime Coquelin PMD_INIT_LOG(ERR, "(%s) Failed to get backend status\n", dev->path); 8375043a060SAdrian Moreno } 8385043a060SAdrian Moreno 839d7e10ea9SAdrian Moreno pthread_mutex_unlock(&dev->mutex); 8408723c894SMaxime Coquelin return ret; 8410b0dc66cSAdrian Moreno } 84294973531SMaxime Coquelin 84394973531SMaxime Coquelin int 84494973531SMaxime Coquelin virtio_user_dev_update_link_state(struct virtio_user_dev *dev) 84594973531SMaxime Coquelin { 84694973531SMaxime Coquelin if (dev->ops->update_link_state) 84794973531SMaxime Coquelin return dev->ops->update_link_state(dev); 84894973531SMaxime Coquelin 84994973531SMaxime Coquelin return 0; 85094973531SMaxime Coquelin } 85194973531SMaxime Coquelin 85294973531SMaxime Coquelin static void 85394973531SMaxime Coquelin virtio_user_dev_reset_queues_packed(struct rte_eth_dev *eth_dev) 85494973531SMaxime Coquelin { 85594973531SMaxime Coquelin struct virtio_user_dev *dev = eth_dev->data->dev_private; 85694973531SMaxime Coquelin struct virtio_hw *hw = &dev->hw; 85794973531SMaxime Coquelin struct virtnet_rx *rxvq; 85894973531SMaxime Coquelin struct virtnet_tx *txvq; 85994973531SMaxime Coquelin uint16_t i; 86094973531SMaxime Coquelin 86194973531SMaxime Coquelin /* Add lock to avoid queue contention. */ 86294973531SMaxime Coquelin rte_spinlock_lock(&hw->state_lock); 86394973531SMaxime Coquelin hw->started = 0; 86494973531SMaxime Coquelin 86594973531SMaxime Coquelin /* 86694973531SMaxime Coquelin * Waiting for datapath to complete before resetting queues. 86794973531SMaxime Coquelin * 1 ms should be enough for the ongoing Tx/Rx function to finish. 86894973531SMaxime Coquelin */ 86994973531SMaxime Coquelin rte_delay_ms(1); 87094973531SMaxime Coquelin 87194973531SMaxime Coquelin /* Vring reset for each Tx queue and Rx queue. */ 87294973531SMaxime Coquelin for (i = 0; i < eth_dev->data->nb_rx_queues; i++) { 87394973531SMaxime Coquelin rxvq = eth_dev->data->rx_queues[i]; 87494973531SMaxime Coquelin virtqueue_rxvq_reset_packed(rxvq->vq); 87594973531SMaxime Coquelin virtio_dev_rx_queue_setup_finish(eth_dev, i); 87694973531SMaxime Coquelin } 87794973531SMaxime Coquelin 87894973531SMaxime Coquelin for (i = 0; i < eth_dev->data->nb_tx_queues; i++) { 87994973531SMaxime Coquelin txvq = eth_dev->data->tx_queues[i]; 88094973531SMaxime Coquelin virtqueue_txvq_reset_packed(txvq->vq); 88194973531SMaxime Coquelin } 88294973531SMaxime Coquelin 88394973531SMaxime Coquelin hw->started = 1; 88494973531SMaxime Coquelin rte_spinlock_unlock(&hw->state_lock); 88594973531SMaxime Coquelin } 88694973531SMaxime Coquelin 88794973531SMaxime Coquelin void 88894973531SMaxime Coquelin virtio_user_dev_delayed_handler(void *param) 88994973531SMaxime Coquelin { 89094973531SMaxime Coquelin struct virtio_user_dev *dev = param; 89194973531SMaxime Coquelin struct rte_eth_dev *eth_dev = &rte_eth_devices[dev->port_id]; 89294973531SMaxime Coquelin 89394973531SMaxime Coquelin if (rte_intr_disable(eth_dev->intr_handle) < 0) { 89494973531SMaxime Coquelin PMD_DRV_LOG(ERR, "interrupt disable failed"); 89594973531SMaxime Coquelin return; 89694973531SMaxime Coquelin } 89794973531SMaxime Coquelin rte_intr_callback_unregister(eth_dev->intr_handle, 89894973531SMaxime Coquelin virtio_interrupt_handler, eth_dev); 89994973531SMaxime Coquelin if (dev->is_server) { 90094973531SMaxime Coquelin if (dev->ops->server_disconnect) 90194973531SMaxime Coquelin dev->ops->server_disconnect(dev); 90294973531SMaxime Coquelin eth_dev->intr_handle->fd = dev->ops->get_intr_fd(dev); 90394973531SMaxime Coquelin rte_intr_callback_register(eth_dev->intr_handle, 90494973531SMaxime Coquelin virtio_interrupt_handler, eth_dev); 90594973531SMaxime Coquelin if (rte_intr_enable(eth_dev->intr_handle) < 0) { 90694973531SMaxime Coquelin PMD_DRV_LOG(ERR, "interrupt enable failed"); 90794973531SMaxime Coquelin return; 90894973531SMaxime Coquelin } 90994973531SMaxime Coquelin } 91094973531SMaxime Coquelin } 91194973531SMaxime Coquelin 91294973531SMaxime Coquelin int 91394973531SMaxime Coquelin virtio_user_dev_server_reconnect(struct virtio_user_dev *dev) 91494973531SMaxime Coquelin { 91594973531SMaxime Coquelin int ret, old_status; 91694973531SMaxime Coquelin struct rte_eth_dev *eth_dev = &rte_eth_devices[dev->port_id]; 91794973531SMaxime Coquelin struct virtio_hw *hw = &dev->hw; 91894973531SMaxime Coquelin 91994973531SMaxime Coquelin if (!dev->ops->server_reconnect) { 92094973531SMaxime Coquelin PMD_DRV_LOG(ERR, "(%s) Missing server reconnect callback", dev->path); 92194973531SMaxime Coquelin return -1; 92294973531SMaxime Coquelin } 92394973531SMaxime Coquelin 92494973531SMaxime Coquelin if (dev->ops->server_reconnect(dev)) { 92594973531SMaxime Coquelin PMD_DRV_LOG(ERR, "(%s) Reconnect callback call failed", dev->path); 92694973531SMaxime Coquelin return -1; 92794973531SMaxime Coquelin } 92894973531SMaxime Coquelin 92994973531SMaxime Coquelin old_status = dev->status; 93094973531SMaxime Coquelin 93194973531SMaxime Coquelin virtio_reset(hw); 93294973531SMaxime Coquelin 93394973531SMaxime Coquelin virtio_set_status(hw, VIRTIO_CONFIG_STATUS_ACK); 93494973531SMaxime Coquelin 93594973531SMaxime Coquelin virtio_set_status(hw, VIRTIO_CONFIG_STATUS_DRIVER); 93694973531SMaxime Coquelin 93794973531SMaxime Coquelin if (dev->ops->get_features(dev, &dev->device_features) < 0) { 93894973531SMaxime Coquelin PMD_INIT_LOG(ERR, "get_features failed: %s", 93994973531SMaxime Coquelin strerror(errno)); 94094973531SMaxime Coquelin return -1; 94194973531SMaxime Coquelin } 94294973531SMaxime Coquelin 94394973531SMaxime Coquelin dev->device_features |= dev->frontend_features; 94494973531SMaxime Coquelin 94594973531SMaxime Coquelin /* unmask vhost-user unsupported features */ 94694973531SMaxime Coquelin dev->device_features &= ~(dev->unsupported_features); 94794973531SMaxime Coquelin 94894973531SMaxime Coquelin dev->features &= dev->device_features; 94994973531SMaxime Coquelin 95094973531SMaxime Coquelin /* For packed ring, resetting queues is required in reconnection. */ 95194973531SMaxime Coquelin if (virtio_with_packed_queue(hw) && 95294973531SMaxime Coquelin (old_status & VIRTIO_CONFIG_STATUS_DRIVER_OK)) { 95394973531SMaxime Coquelin PMD_INIT_LOG(NOTICE, "Packets on the fly will be dropped" 95494973531SMaxime Coquelin " when packed ring reconnecting."); 95594973531SMaxime Coquelin virtio_user_dev_reset_queues_packed(eth_dev); 95694973531SMaxime Coquelin } 95794973531SMaxime Coquelin 95894973531SMaxime Coquelin virtio_set_status(hw, VIRTIO_CONFIG_STATUS_FEATURES_OK); 95994973531SMaxime Coquelin 96094973531SMaxime Coquelin /* Start the device */ 96194973531SMaxime Coquelin virtio_set_status(hw, VIRTIO_CONFIG_STATUS_DRIVER_OK); 96294973531SMaxime Coquelin if (!dev->started) 96394973531SMaxime Coquelin return -1; 96494973531SMaxime Coquelin 96594973531SMaxime Coquelin if (dev->queue_pairs > 1) { 96694973531SMaxime Coquelin ret = virtio_user_handle_mq(dev, dev->queue_pairs); 96794973531SMaxime Coquelin if (ret != 0) { 96894973531SMaxime Coquelin PMD_INIT_LOG(ERR, "Fails to enable multi-queue pairs!"); 96994973531SMaxime Coquelin return -1; 97094973531SMaxime Coquelin } 97194973531SMaxime Coquelin } 97294973531SMaxime Coquelin if (eth_dev->data->dev_flags & RTE_ETH_DEV_INTR_LSC) { 97394973531SMaxime Coquelin if (rte_intr_disable(eth_dev->intr_handle) < 0) { 97494973531SMaxime Coquelin PMD_DRV_LOG(ERR, "interrupt disable failed"); 97594973531SMaxime Coquelin return -1; 97694973531SMaxime Coquelin } 97794973531SMaxime Coquelin rte_intr_callback_unregister(eth_dev->intr_handle, 97894973531SMaxime Coquelin virtio_interrupt_handler, 97994973531SMaxime Coquelin eth_dev); 98094973531SMaxime Coquelin 98194973531SMaxime Coquelin eth_dev->intr_handle->fd = dev->ops->get_intr_fd(dev); 98294973531SMaxime Coquelin rte_intr_callback_register(eth_dev->intr_handle, 98394973531SMaxime Coquelin virtio_interrupt_handler, eth_dev); 98494973531SMaxime Coquelin 98594973531SMaxime Coquelin if (rte_intr_enable(eth_dev->intr_handle) < 0) { 98694973531SMaxime Coquelin PMD_DRV_LOG(ERR, "interrupt enable failed"); 98794973531SMaxime Coquelin return -1; 98894973531SMaxime Coquelin } 98994973531SMaxime Coquelin } 99094973531SMaxime Coquelin PMD_INIT_LOG(NOTICE, "server mode virtio-user reconnection succeeds!"); 99194973531SMaxime Coquelin return 0; 99294973531SMaxime Coquelin } 993