xref: /openbmc/qemu/net/vhost-vdpa.c (revision 53b85d95)
11e0a84eaSCindy Lu /*
21e0a84eaSCindy Lu  * vhost-vdpa.c
31e0a84eaSCindy Lu  *
41e0a84eaSCindy Lu  * Copyright(c) 2017-2018 Intel Corporation.
51e0a84eaSCindy Lu  * Copyright(c) 2020 Red Hat, Inc.
61e0a84eaSCindy Lu  *
71e0a84eaSCindy Lu  * This work is licensed under the terms of the GNU GPL, version 2 or later.
81e0a84eaSCindy Lu  * See the COPYING file in the top-level directory.
91e0a84eaSCindy Lu  *
101e0a84eaSCindy Lu  */
111e0a84eaSCindy Lu 
121e0a84eaSCindy Lu #include "qemu/osdep.h"
131e0a84eaSCindy Lu #include "clients.h"
14bd907ae4SEugenio Pérez #include "hw/virtio/virtio-net.h"
151e0a84eaSCindy Lu #include "net/vhost_net.h"
161e0a84eaSCindy Lu #include "net/vhost-vdpa.h"
171e0a84eaSCindy Lu #include "hw/virtio/vhost-vdpa.h"
181e0a84eaSCindy Lu #include "qemu/config-file.h"
191e0a84eaSCindy Lu #include "qemu/error-report.h"
20bd907ae4SEugenio Pérez #include "qemu/log.h"
21bd907ae4SEugenio Pérez #include "qemu/memalign.h"
221e0a84eaSCindy Lu #include "qemu/option.h"
231e0a84eaSCindy Lu #include "qapi/error.h"
2440237840SJason Wang #include <linux/vhost.h>
251e0a84eaSCindy Lu #include <sys/ioctl.h>
261e0a84eaSCindy Lu #include <err.h>
271e0a84eaSCindy Lu #include "standard-headers/linux/virtio_net.h"
281e0a84eaSCindy Lu #include "monitor/monitor.h"
291e0a84eaSCindy Lu #include "hw/virtio/vhost.h"
301e0a84eaSCindy Lu 
311e0a84eaSCindy Lu /* Todo:need to add the multiqueue support here */
321e0a84eaSCindy Lu typedef struct VhostVDPAState {
331e0a84eaSCindy Lu     NetClientState nc;
341e0a84eaSCindy Lu     struct vhost_vdpa vhost_vdpa;
351e0a84eaSCindy Lu     VHostNetState *vhost_net;
362df4dd31SEugenio Pérez 
372df4dd31SEugenio Pérez     /* Control commands shadow buffers */
3817fb889fSEugenio Pérez     void *cvq_cmd_out_buffer;
3917fb889fSEugenio Pérez     virtio_net_ctrl_ack *status;
4017fb889fSEugenio Pérez 
411e0a84eaSCindy Lu     bool started;
421e0a84eaSCindy Lu } VhostVDPAState;
431e0a84eaSCindy Lu 
441e0a84eaSCindy Lu const int vdpa_feature_bits[] = {
451e0a84eaSCindy Lu     VIRTIO_F_NOTIFY_ON_EMPTY,
461e0a84eaSCindy Lu     VIRTIO_RING_F_INDIRECT_DESC,
471e0a84eaSCindy Lu     VIRTIO_RING_F_EVENT_IDX,
481e0a84eaSCindy Lu     VIRTIO_F_ANY_LAYOUT,
491e0a84eaSCindy Lu     VIRTIO_F_VERSION_1,
501e0a84eaSCindy Lu     VIRTIO_NET_F_CSUM,
511e0a84eaSCindy Lu     VIRTIO_NET_F_GUEST_CSUM,
521e0a84eaSCindy Lu     VIRTIO_NET_F_GSO,
531e0a84eaSCindy Lu     VIRTIO_NET_F_GUEST_TSO4,
541e0a84eaSCindy Lu     VIRTIO_NET_F_GUEST_TSO6,
551e0a84eaSCindy Lu     VIRTIO_NET_F_GUEST_ECN,
561e0a84eaSCindy Lu     VIRTIO_NET_F_GUEST_UFO,
571e0a84eaSCindy Lu     VIRTIO_NET_F_HOST_TSO4,
581e0a84eaSCindy Lu     VIRTIO_NET_F_HOST_TSO6,
591e0a84eaSCindy Lu     VIRTIO_NET_F_HOST_ECN,
601e0a84eaSCindy Lu     VIRTIO_NET_F_HOST_UFO,
611e0a84eaSCindy Lu     VIRTIO_NET_F_MRG_RXBUF,
621e0a84eaSCindy Lu     VIRTIO_NET_F_MTU,
6340237840SJason Wang     VIRTIO_NET_F_CTRL_RX,
6440237840SJason Wang     VIRTIO_NET_F_CTRL_RX_EXTRA,
6540237840SJason Wang     VIRTIO_NET_F_CTRL_VLAN,
6640237840SJason Wang     VIRTIO_NET_F_CTRL_MAC_ADDR,
6740237840SJason Wang     VIRTIO_NET_F_RSS,
6840237840SJason Wang     VIRTIO_NET_F_MQ,
6940237840SJason Wang     VIRTIO_NET_F_CTRL_VQ,
701e0a84eaSCindy Lu     VIRTIO_F_IOMMU_PLATFORM,
711e0a84eaSCindy Lu     VIRTIO_F_RING_PACKED,
720145c393SAndrew Melnychenko     VIRTIO_NET_F_RSS,
730145c393SAndrew Melnychenko     VIRTIO_NET_F_HASH_REPORT,
741e0a84eaSCindy Lu     VIRTIO_NET_F_GUEST_ANNOUNCE,
759aa47eddSSi-Wei Liu     VIRTIO_NET_F_STATUS,
761e0a84eaSCindy Lu     VHOST_INVALID_FEATURE_BIT
771e0a84eaSCindy Lu };
781e0a84eaSCindy Lu 
791576dbb5SEugenio Pérez /** Supported device specific feature bits with SVQ */
801576dbb5SEugenio Pérez static const uint64_t vdpa_svq_device_features =
811576dbb5SEugenio Pérez     BIT_ULL(VIRTIO_NET_F_CSUM) |
821576dbb5SEugenio Pérez     BIT_ULL(VIRTIO_NET_F_GUEST_CSUM) |
831576dbb5SEugenio Pérez     BIT_ULL(VIRTIO_NET_F_MTU) |
841576dbb5SEugenio Pérez     BIT_ULL(VIRTIO_NET_F_MAC) |
851576dbb5SEugenio Pérez     BIT_ULL(VIRTIO_NET_F_GUEST_TSO4) |
861576dbb5SEugenio Pérez     BIT_ULL(VIRTIO_NET_F_GUEST_TSO6) |
871576dbb5SEugenio Pérez     BIT_ULL(VIRTIO_NET_F_GUEST_ECN) |
881576dbb5SEugenio Pérez     BIT_ULL(VIRTIO_NET_F_GUEST_UFO) |
891576dbb5SEugenio Pérez     BIT_ULL(VIRTIO_NET_F_HOST_TSO4) |
901576dbb5SEugenio Pérez     BIT_ULL(VIRTIO_NET_F_HOST_TSO6) |
911576dbb5SEugenio Pérez     BIT_ULL(VIRTIO_NET_F_HOST_ECN) |
921576dbb5SEugenio Pérez     BIT_ULL(VIRTIO_NET_F_HOST_UFO) |
931576dbb5SEugenio Pérez     BIT_ULL(VIRTIO_NET_F_MRG_RXBUF) |
941576dbb5SEugenio Pérez     BIT_ULL(VIRTIO_NET_F_STATUS) |
951576dbb5SEugenio Pérez     BIT_ULL(VIRTIO_NET_F_CTRL_VQ) |
9672b99a87SEugenio Pérez     BIT_ULL(VIRTIO_NET_F_MQ) |
971576dbb5SEugenio Pérez     BIT_ULL(VIRTIO_F_ANY_LAYOUT) |
981576dbb5SEugenio Pérez     BIT_ULL(VIRTIO_NET_F_CTRL_MAC_ADDR) |
991576dbb5SEugenio Pérez     BIT_ULL(VIRTIO_NET_F_RSC_EXT) |
1001576dbb5SEugenio Pérez     BIT_ULL(VIRTIO_NET_F_STANDBY);
1011576dbb5SEugenio Pérez 
1021e0a84eaSCindy Lu VHostNetState *vhost_vdpa_get_vhost_net(NetClientState *nc)
1031e0a84eaSCindy Lu {
1041e0a84eaSCindy Lu     VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc);
1051e0a84eaSCindy Lu     assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA);
1061e0a84eaSCindy Lu     return s->vhost_net;
1071e0a84eaSCindy Lu }
1081e0a84eaSCindy Lu 
1091e0a84eaSCindy Lu static int vhost_vdpa_net_check_device_id(struct vhost_net *net)
1101e0a84eaSCindy Lu {
1111e0a84eaSCindy Lu     uint32_t device_id;
1121e0a84eaSCindy Lu     int ret;
1131e0a84eaSCindy Lu     struct vhost_dev *hdev;
1141e0a84eaSCindy Lu 
1151e0a84eaSCindy Lu     hdev = (struct vhost_dev *)&net->dev;
1161e0a84eaSCindy Lu     ret = hdev->vhost_ops->vhost_get_device_id(hdev, &device_id);
1171e0a84eaSCindy Lu     if (device_id != VIRTIO_ID_NET) {
1181e0a84eaSCindy Lu         return -ENOTSUP;
1191e0a84eaSCindy Lu     }
1201e0a84eaSCindy Lu     return ret;
1211e0a84eaSCindy Lu }
1221e0a84eaSCindy Lu 
12340237840SJason Wang static int vhost_vdpa_add(NetClientState *ncs, void *be,
12440237840SJason Wang                           int queue_pair_index, int nvqs)
1251e0a84eaSCindy Lu {
1261e0a84eaSCindy Lu     VhostNetOptions options;
1271e0a84eaSCindy Lu     struct vhost_net *net = NULL;
1281e0a84eaSCindy Lu     VhostVDPAState *s;
1291e0a84eaSCindy Lu     int ret;
1301e0a84eaSCindy Lu 
1311e0a84eaSCindy Lu     options.backend_type = VHOST_BACKEND_TYPE_VDPA;
1321e0a84eaSCindy Lu     assert(ncs->info->type == NET_CLIENT_DRIVER_VHOST_VDPA);
1331e0a84eaSCindy Lu     s = DO_UPCAST(VhostVDPAState, nc, ncs);
1341e0a84eaSCindy Lu     options.net_backend = ncs;
1351e0a84eaSCindy Lu     options.opaque      = be;
1361e0a84eaSCindy Lu     options.busyloop_timeout = 0;
13740237840SJason Wang     options.nvqs = nvqs;
1381e0a84eaSCindy Lu 
1391e0a84eaSCindy Lu     net = vhost_net_init(&options);
1401e0a84eaSCindy Lu     if (!net) {
1411e0a84eaSCindy Lu         error_report("failed to init vhost_net for queue");
142a97ef87aSJason Wang         goto err_init;
1431e0a84eaSCindy Lu     }
1441e0a84eaSCindy Lu     s->vhost_net = net;
1451e0a84eaSCindy Lu     ret = vhost_vdpa_net_check_device_id(net);
1461e0a84eaSCindy Lu     if (ret) {
147a97ef87aSJason Wang         goto err_check;
1481e0a84eaSCindy Lu     }
1491e0a84eaSCindy Lu     return 0;
150a97ef87aSJason Wang err_check:
1511e0a84eaSCindy Lu     vhost_net_cleanup(net);
152ab36edcfSJason Wang     g_free(net);
153a97ef87aSJason Wang err_init:
1541e0a84eaSCindy Lu     return -1;
1551e0a84eaSCindy Lu }
1561e0a84eaSCindy Lu 
1571e0a84eaSCindy Lu static void vhost_vdpa_cleanup(NetClientState *nc)
1581e0a84eaSCindy Lu {
1591e0a84eaSCindy Lu     VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc);
1601576dbb5SEugenio Pérez     struct vhost_dev *dev = &s->vhost_net->dev;
1611e0a84eaSCindy Lu 
1622df4dd31SEugenio Pérez     qemu_vfree(s->cvq_cmd_out_buffer);
16317fb889fSEugenio Pérez     qemu_vfree(s->status);
1641576dbb5SEugenio Pérez     if (dev->vq_index + dev->nvqs == dev->vq_index_end) {
1651576dbb5SEugenio Pérez         g_clear_pointer(&s->vhost_vdpa.iova_tree, vhost_iova_tree_delete);
1661576dbb5SEugenio Pérez     }
1671e0a84eaSCindy Lu     if (s->vhost_net) {
1681e0a84eaSCindy Lu         vhost_net_cleanup(s->vhost_net);
1691e0a84eaSCindy Lu         g_free(s->vhost_net);
1701e0a84eaSCindy Lu         s->vhost_net = NULL;
1711e0a84eaSCindy Lu     }
17257b3a7d8SCindy Lu      if (s->vhost_vdpa.device_fd >= 0) {
17357b3a7d8SCindy Lu         qemu_close(s->vhost_vdpa.device_fd);
17457b3a7d8SCindy Lu         s->vhost_vdpa.device_fd = -1;
17557b3a7d8SCindy Lu     }
1761e0a84eaSCindy Lu }
1771e0a84eaSCindy Lu 
1781e0a84eaSCindy Lu static bool vhost_vdpa_has_vnet_hdr(NetClientState *nc)
1791e0a84eaSCindy Lu {
1801e0a84eaSCindy Lu     assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA);
1811e0a84eaSCindy Lu 
1821e0a84eaSCindy Lu     return true;
1831e0a84eaSCindy Lu }
1841e0a84eaSCindy Lu 
1851e0a84eaSCindy Lu static bool vhost_vdpa_has_ufo(NetClientState *nc)
1861e0a84eaSCindy Lu {
1871e0a84eaSCindy Lu     assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA);
1881e0a84eaSCindy Lu     VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc);
1891e0a84eaSCindy Lu     uint64_t features = 0;
1901e0a84eaSCindy Lu     features |= (1ULL << VIRTIO_NET_F_HOST_UFO);
1911e0a84eaSCindy Lu     features = vhost_net_get_features(s->vhost_net, features);
1921e0a84eaSCindy Lu     return !!(features & (1ULL << VIRTIO_NET_F_HOST_UFO));
1931e0a84eaSCindy Lu 
1941e0a84eaSCindy Lu }
1951e0a84eaSCindy Lu 
196ee8a1c63SKevin Wolf static bool vhost_vdpa_check_peer_type(NetClientState *nc, ObjectClass *oc,
197ee8a1c63SKevin Wolf                                        Error **errp)
198ee8a1c63SKevin Wolf {
199ee8a1c63SKevin Wolf     const char *driver = object_class_get_name(oc);
200ee8a1c63SKevin Wolf 
201ee8a1c63SKevin Wolf     if (!g_str_has_prefix(driver, "virtio-net-")) {
202ee8a1c63SKevin Wolf         error_setg(errp, "vhost-vdpa requires frontend driver virtio-net-*");
203ee8a1c63SKevin Wolf         return false;
204ee8a1c63SKevin Wolf     }
205ee8a1c63SKevin Wolf 
206ee8a1c63SKevin Wolf     return true;
207ee8a1c63SKevin Wolf }
208ee8a1c63SKevin Wolf 
209846a1e85SEugenio Pérez /** Dummy receive in case qemu falls back to userland tap networking */
210846a1e85SEugenio Pérez static ssize_t vhost_vdpa_receive(NetClientState *nc, const uint8_t *buf,
211846a1e85SEugenio Pérez                                   size_t size)
212846a1e85SEugenio Pérez {
213846a1e85SEugenio Pérez     return 0;
214846a1e85SEugenio Pérez }
215846a1e85SEugenio Pérez 
2161e0a84eaSCindy Lu static NetClientInfo net_vhost_vdpa_info = {
2171e0a84eaSCindy Lu         .type = NET_CLIENT_DRIVER_VHOST_VDPA,
2181e0a84eaSCindy Lu         .size = sizeof(VhostVDPAState),
219846a1e85SEugenio Pérez         .receive = vhost_vdpa_receive,
2201e0a84eaSCindy Lu         .cleanup = vhost_vdpa_cleanup,
2211e0a84eaSCindy Lu         .has_vnet_hdr = vhost_vdpa_has_vnet_hdr,
2221e0a84eaSCindy Lu         .has_ufo = vhost_vdpa_has_ufo,
223ee8a1c63SKevin Wolf         .check_peer_type = vhost_vdpa_check_peer_type,
2241e0a84eaSCindy Lu };
2251e0a84eaSCindy Lu 
2262df4dd31SEugenio Pérez static void vhost_vdpa_cvq_unmap_buf(struct vhost_vdpa *v, void *addr)
2272df4dd31SEugenio Pérez {
2282df4dd31SEugenio Pérez     VhostIOVATree *tree = v->iova_tree;
2292df4dd31SEugenio Pérez     DMAMap needle = {
2302df4dd31SEugenio Pérez         /*
2312df4dd31SEugenio Pérez          * No need to specify size or to look for more translations since
2322df4dd31SEugenio Pérez          * this contiguous chunk was allocated by us.
2332df4dd31SEugenio Pérez          */
2342df4dd31SEugenio Pérez         .translated_addr = (hwaddr)(uintptr_t)addr,
2352df4dd31SEugenio Pérez     };
2362df4dd31SEugenio Pérez     const DMAMap *map = vhost_iova_tree_find_iova(tree, &needle);
2372df4dd31SEugenio Pérez     int r;
2382df4dd31SEugenio Pérez 
2392df4dd31SEugenio Pérez     if (unlikely(!map)) {
2402df4dd31SEugenio Pérez         error_report("Cannot locate expected map");
2412df4dd31SEugenio Pérez         return;
2422df4dd31SEugenio Pérez     }
2432df4dd31SEugenio Pérez 
2442df4dd31SEugenio Pérez     r = vhost_vdpa_dma_unmap(v, map->iova, map->size + 1);
2452df4dd31SEugenio Pérez     if (unlikely(r != 0)) {
2462df4dd31SEugenio Pérez         error_report("Device cannot unmap: %s(%d)", g_strerror(r), r);
2472df4dd31SEugenio Pérez     }
2482df4dd31SEugenio Pérez 
24969292a8eSEugenio Pérez     vhost_iova_tree_remove(tree, *map);
2502df4dd31SEugenio Pérez }
2512df4dd31SEugenio Pérez 
2522df4dd31SEugenio Pérez static size_t vhost_vdpa_net_cvq_cmd_len(void)
2532df4dd31SEugenio Pérez {
2542df4dd31SEugenio Pérez     /*
2552df4dd31SEugenio Pérez      * MAC_TABLE_SET is the ctrl command that produces the longer out buffer.
2562df4dd31SEugenio Pérez      * In buffer is always 1 byte, so it should fit here
2572df4dd31SEugenio Pérez      */
2582df4dd31SEugenio Pérez     return sizeof(struct virtio_net_ctrl_hdr) +
2592df4dd31SEugenio Pérez            2 * sizeof(struct virtio_net_ctrl_mac) +
2602df4dd31SEugenio Pérez            MAC_TABLE_ENTRIES * ETH_ALEN;
2612df4dd31SEugenio Pérez }
2622df4dd31SEugenio Pérez 
2632df4dd31SEugenio Pérez static size_t vhost_vdpa_net_cvq_cmd_page_len(void)
2642df4dd31SEugenio Pérez {
2652df4dd31SEugenio Pérez     return ROUND_UP(vhost_vdpa_net_cvq_cmd_len(), qemu_real_host_page_size());
2662df4dd31SEugenio Pérez }
2672df4dd31SEugenio Pérez 
2687a7f87e9SEugenio Pérez /** Map CVQ buffer. */
2697a7f87e9SEugenio Pérez static int vhost_vdpa_cvq_map_buf(struct vhost_vdpa *v, void *buf, size_t size,
2707a7f87e9SEugenio Pérez                                   bool write)
2712df4dd31SEugenio Pérez {
2722df4dd31SEugenio Pérez     DMAMap map = {};
2732df4dd31SEugenio Pérez     int r;
2742df4dd31SEugenio Pérez 
2752df4dd31SEugenio Pérez     map.translated_addr = (hwaddr)(uintptr_t)buf;
2767a7f87e9SEugenio Pérez     map.size = size - 1;
2772df4dd31SEugenio Pérez     map.perm = write ? IOMMU_RW : IOMMU_RO,
2782df4dd31SEugenio Pérez     r = vhost_iova_tree_map_alloc(v->iova_tree, &map);
2792df4dd31SEugenio Pérez     if (unlikely(r != IOVA_OK)) {
2802df4dd31SEugenio Pérez         error_report("Cannot map injected element");
2817a7f87e9SEugenio Pérez         return r;
2822df4dd31SEugenio Pérez     }
2832df4dd31SEugenio Pérez 
2842df4dd31SEugenio Pérez     r = vhost_vdpa_dma_map(v, map.iova, vhost_vdpa_net_cvq_cmd_page_len(), buf,
2852df4dd31SEugenio Pérez                            !write);
2862df4dd31SEugenio Pérez     if (unlikely(r < 0)) {
2872df4dd31SEugenio Pérez         goto dma_map_err;
2882df4dd31SEugenio Pérez     }
2892df4dd31SEugenio Pérez 
2907a7f87e9SEugenio Pérez     return 0;
2912df4dd31SEugenio Pérez 
2922df4dd31SEugenio Pérez dma_map_err:
29369292a8eSEugenio Pérez     vhost_iova_tree_remove(v->iova_tree, map);
2947a7f87e9SEugenio Pérez     return r;
2952df4dd31SEugenio Pérez }
2962df4dd31SEugenio Pérez 
2977a7f87e9SEugenio Pérez static int vhost_vdpa_net_cvq_start(NetClientState *nc)
2982df4dd31SEugenio Pérez {
2997a7f87e9SEugenio Pérez     VhostVDPAState *s;
3007a7f87e9SEugenio Pérez     int r;
3012df4dd31SEugenio Pérez 
3027a7f87e9SEugenio Pérez     assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA);
3037a7f87e9SEugenio Pérez 
3047a7f87e9SEugenio Pérez     s = DO_UPCAST(VhostVDPAState, nc, nc);
3057a7f87e9SEugenio Pérez     if (!s->vhost_vdpa.shadow_vqs_enabled) {
3067a7f87e9SEugenio Pérez         return 0;
3072df4dd31SEugenio Pérez     }
3082df4dd31SEugenio Pérez 
3097a7f87e9SEugenio Pérez     r = vhost_vdpa_cvq_map_buf(&s->vhost_vdpa, s->cvq_cmd_out_buffer,
3107a7f87e9SEugenio Pérez                                vhost_vdpa_net_cvq_cmd_page_len(), false);
3117a7f87e9SEugenio Pérez     if (unlikely(r < 0)) {
3127a7f87e9SEugenio Pérez         return r;
3137a7f87e9SEugenio Pérez     }
3147a7f87e9SEugenio Pérez 
31517fb889fSEugenio Pérez     r = vhost_vdpa_cvq_map_buf(&s->vhost_vdpa, s->status,
3167a7f87e9SEugenio Pérez                                vhost_vdpa_net_cvq_cmd_page_len(), true);
3177a7f87e9SEugenio Pérez     if (unlikely(r < 0)) {
3182df4dd31SEugenio Pérez         vhost_vdpa_cvq_unmap_buf(&s->vhost_vdpa, s->cvq_cmd_out_buffer);
3192df4dd31SEugenio Pérez     }
3202df4dd31SEugenio Pérez 
3217a7f87e9SEugenio Pérez     return r;
3227a7f87e9SEugenio Pérez }
3237a7f87e9SEugenio Pérez 
3247a7f87e9SEugenio Pérez static void vhost_vdpa_net_cvq_stop(NetClientState *nc)
3257a7f87e9SEugenio Pérez {
3267a7f87e9SEugenio Pérez     VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc);
3277a7f87e9SEugenio Pérez 
3287a7f87e9SEugenio Pérez     assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA);
3297a7f87e9SEugenio Pérez 
3307a7f87e9SEugenio Pérez     if (s->vhost_vdpa.shadow_vqs_enabled) {
3317a7f87e9SEugenio Pérez         vhost_vdpa_cvq_unmap_buf(&s->vhost_vdpa, s->cvq_cmd_out_buffer);
33217fb889fSEugenio Pérez         vhost_vdpa_cvq_unmap_buf(&s->vhost_vdpa, s->status);
3337a7f87e9SEugenio Pérez     }
3342df4dd31SEugenio Pérez }
3352df4dd31SEugenio Pérez 
336be4278b6SEugenio Pérez static ssize_t vhost_vdpa_net_cvq_add(VhostVDPAState *s, size_t out_len,
337be4278b6SEugenio Pérez                                       size_t in_len)
338be4278b6SEugenio Pérez {
339be4278b6SEugenio Pérez     /* Buffers for the device */
340be4278b6SEugenio Pérez     const struct iovec out = {
341be4278b6SEugenio Pérez         .iov_base = s->cvq_cmd_out_buffer,
342be4278b6SEugenio Pérez         .iov_len = out_len,
343be4278b6SEugenio Pérez     };
344be4278b6SEugenio Pérez     const struct iovec in = {
34517fb889fSEugenio Pérez         .iov_base = s->status,
346be4278b6SEugenio Pérez         .iov_len = sizeof(virtio_net_ctrl_ack),
347be4278b6SEugenio Pérez     };
348be4278b6SEugenio Pérez     VhostShadowVirtqueue *svq = g_ptr_array_index(s->vhost_vdpa.shadow_vqs, 0);
349be4278b6SEugenio Pérez     int r;
350be4278b6SEugenio Pérez 
351be4278b6SEugenio Pérez     r = vhost_svq_add(svq, &out, 1, &in, 1, NULL);
352be4278b6SEugenio Pérez     if (unlikely(r != 0)) {
353be4278b6SEugenio Pérez         if (unlikely(r == -ENOSPC)) {
354be4278b6SEugenio Pérez             qemu_log_mask(LOG_GUEST_ERROR, "%s: No space on device queue\n",
355be4278b6SEugenio Pérez                           __func__);
356be4278b6SEugenio Pérez         }
357be4278b6SEugenio Pérez         return r;
358be4278b6SEugenio Pérez     }
359be4278b6SEugenio Pérez 
360be4278b6SEugenio Pérez     /*
361be4278b6SEugenio Pérez      * We can poll here since we've had BQL from the time we sent the
362be4278b6SEugenio Pérez      * descriptor. Also, we need to take the answer before SVQ pulls by itself,
363be4278b6SEugenio Pérez      * when BQL is released
364be4278b6SEugenio Pérez      */
365be4278b6SEugenio Pérez     return vhost_svq_poll(svq);
366be4278b6SEugenio Pérez }
367be4278b6SEugenio Pérez 
368f73c0c43SEugenio Pérez static ssize_t vhost_vdpa_net_load_cmd(VhostVDPAState *s, uint8_t class,
369f73c0c43SEugenio Pérez                                        uint8_t cmd, const void *data,
370f73c0c43SEugenio Pérez                                        size_t data_size)
371f73c0c43SEugenio Pérez {
372f73c0c43SEugenio Pérez     const struct virtio_net_ctrl_hdr ctrl = {
373f73c0c43SEugenio Pérez         .class = class,
374f73c0c43SEugenio Pérez         .cmd = cmd,
375f73c0c43SEugenio Pérez     };
376f73c0c43SEugenio Pérez 
377f73c0c43SEugenio Pérez     assert(data_size < vhost_vdpa_net_cvq_cmd_page_len() - sizeof(ctrl));
378f73c0c43SEugenio Pérez 
379f73c0c43SEugenio Pérez     memcpy(s->cvq_cmd_out_buffer, &ctrl, sizeof(ctrl));
380f73c0c43SEugenio Pérez     memcpy(s->cvq_cmd_out_buffer + sizeof(ctrl), data, data_size);
381f73c0c43SEugenio Pérez 
382f73c0c43SEugenio Pérez     return vhost_vdpa_net_cvq_add(s, sizeof(ctrl) + data_size,
383f73c0c43SEugenio Pérez                                   sizeof(virtio_net_ctrl_ack));
384f73c0c43SEugenio Pérez }
385f73c0c43SEugenio Pérez 
386f73c0c43SEugenio Pérez static int vhost_vdpa_net_load_mac(VhostVDPAState *s, const VirtIONet *n)
387f73c0c43SEugenio Pérez {
388f73c0c43SEugenio Pérez     uint64_t features = n->parent_obj.guest_features;
389f73c0c43SEugenio Pérez     if (features & BIT_ULL(VIRTIO_NET_F_CTRL_MAC_ADDR)) {
390f73c0c43SEugenio Pérez         ssize_t dev_written = vhost_vdpa_net_load_cmd(s, VIRTIO_NET_CTRL_MAC,
391f73c0c43SEugenio Pérez                                                   VIRTIO_NET_CTRL_MAC_ADDR_SET,
392f73c0c43SEugenio Pérez                                                   n->mac, sizeof(n->mac));
393f73c0c43SEugenio Pérez         if (unlikely(dev_written < 0)) {
394f73c0c43SEugenio Pérez             return dev_written;
395f73c0c43SEugenio Pérez         }
396f73c0c43SEugenio Pérez 
397f73c0c43SEugenio Pérez         return *s->status != VIRTIO_NET_OK;
398f73c0c43SEugenio Pérez     }
399f73c0c43SEugenio Pérez 
400f73c0c43SEugenio Pérez     return 0;
401f73c0c43SEugenio Pérez }
402f73c0c43SEugenio Pérez 
403f64c7cdaSEugenio Pérez static int vhost_vdpa_net_load_mq(VhostVDPAState *s,
404f64c7cdaSEugenio Pérez                                   const VirtIONet *n)
405f64c7cdaSEugenio Pérez {
406f64c7cdaSEugenio Pérez     struct virtio_net_ctrl_mq mq;
407f64c7cdaSEugenio Pérez     uint64_t features = n->parent_obj.guest_features;
408f64c7cdaSEugenio Pérez     ssize_t dev_written;
409f64c7cdaSEugenio Pérez 
410f64c7cdaSEugenio Pérez     if (!(features & BIT_ULL(VIRTIO_NET_F_MQ))) {
411f64c7cdaSEugenio Pérez         return 0;
412f64c7cdaSEugenio Pérez     }
413f64c7cdaSEugenio Pérez 
414f64c7cdaSEugenio Pérez     mq.virtqueue_pairs = cpu_to_le16(n->curr_queue_pairs);
415f64c7cdaSEugenio Pérez     dev_written = vhost_vdpa_net_load_cmd(s, VIRTIO_NET_CTRL_MQ,
416f64c7cdaSEugenio Pérez                                           VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, &mq,
417f64c7cdaSEugenio Pérez                                           sizeof(mq));
418f64c7cdaSEugenio Pérez     if (unlikely(dev_written < 0)) {
419f64c7cdaSEugenio Pérez         return dev_written;
420f64c7cdaSEugenio Pérez     }
421f64c7cdaSEugenio Pérez 
422f64c7cdaSEugenio Pérez     return *s->status != VIRTIO_NET_OK;
423f64c7cdaSEugenio Pérez }
424f64c7cdaSEugenio Pérez 
425dd036d8dSEugenio Pérez static int vhost_vdpa_net_load(NetClientState *nc)
426dd036d8dSEugenio Pérez {
427dd036d8dSEugenio Pérez     VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc);
428f73c0c43SEugenio Pérez     struct vhost_vdpa *v = &s->vhost_vdpa;
429dd036d8dSEugenio Pérez     const VirtIONet *n;
430f73c0c43SEugenio Pérez     int r;
431dd036d8dSEugenio Pérez 
432dd036d8dSEugenio Pérez     assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA);
433dd036d8dSEugenio Pérez 
434dd036d8dSEugenio Pérez     if (!v->shadow_vqs_enabled) {
435dd036d8dSEugenio Pérez         return 0;
436dd036d8dSEugenio Pérez     }
437dd036d8dSEugenio Pérez 
438dd036d8dSEugenio Pérez     n = VIRTIO_NET(v->dev->vdev);
439f73c0c43SEugenio Pérez     r = vhost_vdpa_net_load_mac(s, n);
440f73c0c43SEugenio Pérez     if (unlikely(r < 0)) {
441f73c0c43SEugenio Pérez         return r;
442dd036d8dSEugenio Pérez     }
443f64c7cdaSEugenio Pérez     r = vhost_vdpa_net_load_mq(s, n);
444f64c7cdaSEugenio Pérez     if (unlikely(r)) {
445f64c7cdaSEugenio Pérez         return r;
446f64c7cdaSEugenio Pérez     }
447dd036d8dSEugenio Pérez 
448dd036d8dSEugenio Pérez     return 0;
449dd036d8dSEugenio Pérez }
450dd036d8dSEugenio Pérez 
451f8972b56SEugenio Pérez static NetClientInfo net_vhost_vdpa_cvq_info = {
452f8972b56SEugenio Pérez     .type = NET_CLIENT_DRIVER_VHOST_VDPA,
453f8972b56SEugenio Pérez     .size = sizeof(VhostVDPAState),
454f8972b56SEugenio Pérez     .receive = vhost_vdpa_receive,
4557a7f87e9SEugenio Pérez     .start = vhost_vdpa_net_cvq_start,
456dd036d8dSEugenio Pérez     .load = vhost_vdpa_net_load,
4577a7f87e9SEugenio Pérez     .stop = vhost_vdpa_net_cvq_stop,
458f8972b56SEugenio Pérez     .cleanup = vhost_vdpa_cleanup,
459f8972b56SEugenio Pérez     .has_vnet_hdr = vhost_vdpa_has_vnet_hdr,
460f8972b56SEugenio Pérez     .has_ufo = vhost_vdpa_has_ufo,
461f8972b56SEugenio Pérez     .check_peer_type = vhost_vdpa_check_peer_type,
462f8972b56SEugenio Pérez };
463f8972b56SEugenio Pérez 
4642df4dd31SEugenio Pérez /**
4652df4dd31SEugenio Pérez  * Validate and copy control virtqueue commands.
4662df4dd31SEugenio Pérez  *
4672df4dd31SEugenio Pérez  * Following QEMU guidelines, we offer a copy of the buffers to the device to
4682df4dd31SEugenio Pérez  * prevent TOCTOU bugs.
469bd907ae4SEugenio Pérez  */
470bd907ae4SEugenio Pérez static int vhost_vdpa_net_handle_ctrl_avail(VhostShadowVirtqueue *svq,
471bd907ae4SEugenio Pérez                                             VirtQueueElement *elem,
472bd907ae4SEugenio Pérez                                             void *opaque)
473bd907ae4SEugenio Pérez {
4742df4dd31SEugenio Pérez     VhostVDPAState *s = opaque;
475be4278b6SEugenio Pérez     size_t in_len;
476bd907ae4SEugenio Pérez     virtio_net_ctrl_ack status = VIRTIO_NET_ERR;
4777a7f87e9SEugenio Pérez     /* Out buffer sent to both the vdpa device and the device model */
4787a7f87e9SEugenio Pérez     struct iovec out = {
4797a7f87e9SEugenio Pérez         .iov_base = s->cvq_cmd_out_buffer,
4807a7f87e9SEugenio Pérez     };
4812df4dd31SEugenio Pérez     /* in buffer used for device model */
4822df4dd31SEugenio Pérez     const struct iovec in = {
4832df4dd31SEugenio Pérez         .iov_base = &status,
4842df4dd31SEugenio Pérez         .iov_len = sizeof(status),
4852df4dd31SEugenio Pérez     };
486be4278b6SEugenio Pérez     ssize_t dev_written = -EINVAL;
487bd907ae4SEugenio Pérez 
4887a7f87e9SEugenio Pérez     out.iov_len = iov_to_buf(elem->out_sg, elem->out_num, 0,
4897a7f87e9SEugenio Pérez                              s->cvq_cmd_out_buffer,
4907a7f87e9SEugenio Pérez                              vhost_vdpa_net_cvq_cmd_len());
491be4278b6SEugenio Pérez     dev_written = vhost_vdpa_net_cvq_add(s, out.iov_len, sizeof(status));
492be4278b6SEugenio Pérez     if (unlikely(dev_written < 0)) {
493bd907ae4SEugenio Pérez         goto out;
494bd907ae4SEugenio Pérez     }
495bd907ae4SEugenio Pérez 
496bd907ae4SEugenio Pérez     if (unlikely(dev_written < sizeof(status))) {
497bd907ae4SEugenio Pérez         error_report("Insufficient written data (%zu)", dev_written);
4982df4dd31SEugenio Pérez         goto out;
4992df4dd31SEugenio Pérez     }
5002df4dd31SEugenio Pérez 
50117fb889fSEugenio Pérez     if (*s->status != VIRTIO_NET_OK) {
502be4278b6SEugenio Pérez         return VIRTIO_NET_ERR;
5032df4dd31SEugenio Pérez     }
5042df4dd31SEugenio Pérez 
5052df4dd31SEugenio Pérez     status = VIRTIO_NET_ERR;
5067a7f87e9SEugenio Pérez     virtio_net_handle_ctrl_iov(svq->vdev, &in, 1, &out, 1);
5072df4dd31SEugenio Pérez     if (status != VIRTIO_NET_OK) {
5082df4dd31SEugenio Pérez         error_report("Bad CVQ processing in model");
509bd907ae4SEugenio Pérez     }
510bd907ae4SEugenio Pérez 
511bd907ae4SEugenio Pérez out:
512bd907ae4SEugenio Pérez     in_len = iov_from_buf(elem->in_sg, elem->in_num, 0, &status,
513bd907ae4SEugenio Pérez                           sizeof(status));
514bd907ae4SEugenio Pérez     if (unlikely(in_len < sizeof(status))) {
515bd907ae4SEugenio Pérez         error_report("Bad device CVQ written length");
516bd907ae4SEugenio Pérez     }
517bd907ae4SEugenio Pérez     vhost_svq_push_elem(svq, elem, MIN(in_len, sizeof(status)));
518bd907ae4SEugenio Pérez     g_free(elem);
519be4278b6SEugenio Pérez     return dev_written < 0 ? dev_written : 0;
520bd907ae4SEugenio Pérez }
521bd907ae4SEugenio Pérez 
522bd907ae4SEugenio Pérez static const VhostShadowVirtqueueOps vhost_vdpa_net_svq_ops = {
523bd907ae4SEugenio Pérez     .avail_handler = vhost_vdpa_net_handle_ctrl_avail,
524bd907ae4SEugenio Pérez };
525bd907ae4SEugenio Pérez 
526654790b6SJason Wang static NetClientState *net_vhost_vdpa_init(NetClientState *peer,
527654790b6SJason Wang                                            const char *device,
528654790b6SJason Wang                                            const char *name,
52940237840SJason Wang                                            int vdpa_device_fd,
53040237840SJason Wang                                            int queue_pair_index,
53140237840SJason Wang                                            int nvqs,
5321576dbb5SEugenio Pérez                                            bool is_datapath,
5331576dbb5SEugenio Pérez                                            bool svq,
5341576dbb5SEugenio Pérez                                            VhostIOVATree *iova_tree)
5351e0a84eaSCindy Lu {
5361e0a84eaSCindy Lu     NetClientState *nc = NULL;
5371e0a84eaSCindy Lu     VhostVDPAState *s;
5381e0a84eaSCindy Lu     int ret = 0;
5391e0a84eaSCindy Lu     assert(name);
54040237840SJason Wang     if (is_datapath) {
54140237840SJason Wang         nc = qemu_new_net_client(&net_vhost_vdpa_info, peer, device,
54240237840SJason Wang                                  name);
54340237840SJason Wang     } else {
544f8972b56SEugenio Pérez         nc = qemu_new_net_control_client(&net_vhost_vdpa_cvq_info, peer,
54540237840SJason Wang                                          device, name);
54640237840SJason Wang     }
547*53b85d95SLaurent Vivier     qemu_set_info_str(nc, TYPE_VHOST_VDPA);
5481e0a84eaSCindy Lu     s = DO_UPCAST(VhostVDPAState, nc, nc);
5497327813dSJason Wang 
5501e0a84eaSCindy Lu     s->vhost_vdpa.device_fd = vdpa_device_fd;
55140237840SJason Wang     s->vhost_vdpa.index = queue_pair_index;
5521576dbb5SEugenio Pérez     s->vhost_vdpa.shadow_vqs_enabled = svq;
5531576dbb5SEugenio Pérez     s->vhost_vdpa.iova_tree = iova_tree;
554bd907ae4SEugenio Pérez     if (!is_datapath) {
5552df4dd31SEugenio Pérez         s->cvq_cmd_out_buffer = qemu_memalign(qemu_real_host_page_size(),
5562df4dd31SEugenio Pérez                                             vhost_vdpa_net_cvq_cmd_page_len());
5572df4dd31SEugenio Pérez         memset(s->cvq_cmd_out_buffer, 0, vhost_vdpa_net_cvq_cmd_page_len());
55817fb889fSEugenio Pérez         s->status = qemu_memalign(qemu_real_host_page_size(),
5592df4dd31SEugenio Pérez                                   vhost_vdpa_net_cvq_cmd_page_len());
56017fb889fSEugenio Pérez         memset(s->status, 0, vhost_vdpa_net_cvq_cmd_page_len());
5612df4dd31SEugenio Pérez 
562bd907ae4SEugenio Pérez         s->vhost_vdpa.shadow_vq_ops = &vhost_vdpa_net_svq_ops;
563bd907ae4SEugenio Pérez         s->vhost_vdpa.shadow_vq_ops_opaque = s;
564bd907ae4SEugenio Pérez     }
56540237840SJason Wang     ret = vhost_vdpa_add(nc, (void *)&s->vhost_vdpa, queue_pair_index, nvqs);
56674af5eecSJason Wang     if (ret) {
56774af5eecSJason Wang         qemu_del_net_client(nc);
568654790b6SJason Wang         return NULL;
56974af5eecSJason Wang     }
570654790b6SJason Wang     return nc;
5711e0a84eaSCindy Lu }
5721e0a84eaSCindy Lu 
5731576dbb5SEugenio Pérez static int vhost_vdpa_get_iova_range(int fd,
5741576dbb5SEugenio Pérez                                      struct vhost_vdpa_iova_range *iova_range)
5751576dbb5SEugenio Pérez {
5761576dbb5SEugenio Pérez     int ret = ioctl(fd, VHOST_VDPA_GET_IOVA_RANGE, iova_range);
5771576dbb5SEugenio Pérez 
5781576dbb5SEugenio Pérez     return ret < 0 ? -errno : 0;
5791576dbb5SEugenio Pérez }
5801576dbb5SEugenio Pérez 
5818170ab3fSEugenio Pérez static int vhost_vdpa_get_features(int fd, uint64_t *features, Error **errp)
5828170ab3fSEugenio Pérez {
5838170ab3fSEugenio Pérez     int ret = ioctl(fd, VHOST_GET_FEATURES, features);
5848170ab3fSEugenio Pérez     if (unlikely(ret < 0)) {
5858170ab3fSEugenio Pérez         error_setg_errno(errp, errno,
5868170ab3fSEugenio Pérez                          "Fail to query features from vhost-vDPA device");
5878170ab3fSEugenio Pérez     }
5888170ab3fSEugenio Pérez     return ret;
5898170ab3fSEugenio Pérez }
5908170ab3fSEugenio Pérez 
5918170ab3fSEugenio Pérez static int vhost_vdpa_get_max_queue_pairs(int fd, uint64_t features,
5928170ab3fSEugenio Pérez                                           int *has_cvq, Error **errp)
59340237840SJason Wang {
59440237840SJason Wang     unsigned long config_size = offsetof(struct vhost_vdpa_config, buf);
595cd523a41SStefano Garzarella     g_autofree struct vhost_vdpa_config *config = NULL;
59640237840SJason Wang     __virtio16 *max_queue_pairs;
59740237840SJason Wang     int ret;
59840237840SJason Wang 
59940237840SJason Wang     if (features & (1 << VIRTIO_NET_F_CTRL_VQ)) {
60040237840SJason Wang         *has_cvq = 1;
60140237840SJason Wang     } else {
60240237840SJason Wang         *has_cvq = 0;
60340237840SJason Wang     }
60440237840SJason Wang 
60540237840SJason Wang     if (features & (1 << VIRTIO_NET_F_MQ)) {
60640237840SJason Wang         config = g_malloc0(config_size + sizeof(*max_queue_pairs));
60740237840SJason Wang         config->off = offsetof(struct virtio_net_config, max_virtqueue_pairs);
60840237840SJason Wang         config->len = sizeof(*max_queue_pairs);
60940237840SJason Wang 
61040237840SJason Wang         ret = ioctl(fd, VHOST_VDPA_GET_CONFIG, config);
61140237840SJason Wang         if (ret) {
61240237840SJason Wang             error_setg(errp, "Fail to get config from vhost-vDPA device");
61340237840SJason Wang             return -ret;
61440237840SJason Wang         }
61540237840SJason Wang 
61640237840SJason Wang         max_queue_pairs = (__virtio16 *)&config->buf;
61740237840SJason Wang 
61840237840SJason Wang         return lduw_le_p(max_queue_pairs);
61940237840SJason Wang     }
62040237840SJason Wang 
62140237840SJason Wang     return 1;
62240237840SJason Wang }
62340237840SJason Wang 
6241e0a84eaSCindy Lu int net_init_vhost_vdpa(const Netdev *netdev, const char *name,
6251e0a84eaSCindy Lu                         NetClientState *peer, Error **errp)
6261e0a84eaSCindy Lu {
6271e0a84eaSCindy Lu     const NetdevVhostVDPAOptions *opts;
6288170ab3fSEugenio Pérez     uint64_t features;
629654790b6SJason Wang     int vdpa_device_fd;
630eb3cb751SEugenio Pérez     g_autofree NetClientState **ncs = NULL;
6311576dbb5SEugenio Pérez     g_autoptr(VhostIOVATree) iova_tree = NULL;
632eb3cb751SEugenio Pérez     NetClientState *nc;
633aed5da45SEugenio Pérez     int queue_pairs, r, i = 0, has_cvq = 0;
6341e0a84eaSCindy Lu 
6351e0a84eaSCindy Lu     assert(netdev->type == NET_CLIENT_DRIVER_VHOST_VDPA);
6361e0a84eaSCindy Lu     opts = &netdev->u.vhost_vdpa;
6378801ccd0SSi-Wei Liu     if (!opts->has_vhostdev && !opts->has_vhostfd) {
6388801ccd0SSi-Wei Liu         error_setg(errp,
6398801ccd0SSi-Wei Liu                    "vhost-vdpa: neither vhostdev= nor vhostfd= was specified");
640c8295404SEugenio Pérez         return -1;
641c8295404SEugenio Pérez     }
6427327813dSJason Wang 
6438801ccd0SSi-Wei Liu     if (opts->has_vhostdev && opts->has_vhostfd) {
6448801ccd0SSi-Wei Liu         error_setg(errp,
6458801ccd0SSi-Wei Liu                    "vhost-vdpa: vhostdev= and vhostfd= are mutually exclusive");
6468801ccd0SSi-Wei Liu         return -1;
6478801ccd0SSi-Wei Liu     }
6488801ccd0SSi-Wei Liu 
6498801ccd0SSi-Wei Liu     if (opts->has_vhostdev) {
6500351152bSEugenio Pérez         vdpa_device_fd = qemu_open(opts->vhostdev, O_RDWR, errp);
6517327813dSJason Wang         if (vdpa_device_fd == -1) {
6527327813dSJason Wang             return -errno;
6537327813dSJason Wang         }
6548801ccd0SSi-Wei Liu     } else if (opts->has_vhostfd) {
6558801ccd0SSi-Wei Liu         vdpa_device_fd = monitor_fd_param(monitor_cur(), opts->vhostfd, errp);
6568801ccd0SSi-Wei Liu         if (vdpa_device_fd == -1) {
6578801ccd0SSi-Wei Liu             error_prepend(errp, "vhost-vdpa: unable to parse vhostfd: ");
6588801ccd0SSi-Wei Liu             return -1;
6598801ccd0SSi-Wei Liu         }
6608801ccd0SSi-Wei Liu     }
6617327813dSJason Wang 
6628170ab3fSEugenio Pérez     r = vhost_vdpa_get_features(vdpa_device_fd, &features, errp);
6638170ab3fSEugenio Pérez     if (unlikely(r < 0)) {
664aed5da45SEugenio Pérez         goto err;
6658170ab3fSEugenio Pérez     }
6668170ab3fSEugenio Pérez 
6678170ab3fSEugenio Pérez     queue_pairs = vhost_vdpa_get_max_queue_pairs(vdpa_device_fd, features,
66840237840SJason Wang                                                  &has_cvq, errp);
66940237840SJason Wang     if (queue_pairs < 0) {
6707327813dSJason Wang         qemu_close(vdpa_device_fd);
67140237840SJason Wang         return queue_pairs;
6727327813dSJason Wang     }
6737327813dSJason Wang 
6741576dbb5SEugenio Pérez     if (opts->x_svq) {
6751576dbb5SEugenio Pérez         struct vhost_vdpa_iova_range iova_range;
6761576dbb5SEugenio Pérez 
6771576dbb5SEugenio Pérez         uint64_t invalid_dev_features =
6781576dbb5SEugenio Pérez             features & ~vdpa_svq_device_features &
6791576dbb5SEugenio Pérez             /* Transport are all accepted at this point */
6801576dbb5SEugenio Pérez             ~MAKE_64BIT_MASK(VIRTIO_TRANSPORT_F_START,
6811576dbb5SEugenio Pérez                              VIRTIO_TRANSPORT_F_END - VIRTIO_TRANSPORT_F_START);
6821576dbb5SEugenio Pérez 
6831576dbb5SEugenio Pérez         if (invalid_dev_features) {
6841576dbb5SEugenio Pérez             error_setg(errp, "vdpa svq does not work with features 0x%" PRIx64,
6851576dbb5SEugenio Pérez                        invalid_dev_features);
6861576dbb5SEugenio Pérez             goto err_svq;
6871576dbb5SEugenio Pérez         }
6881576dbb5SEugenio Pérez 
6891576dbb5SEugenio Pérez         vhost_vdpa_get_iova_range(vdpa_device_fd, &iova_range);
6901576dbb5SEugenio Pérez         iova_tree = vhost_iova_tree_new(iova_range.first, iova_range.last);
6911576dbb5SEugenio Pérez     }
6921576dbb5SEugenio Pérez 
69340237840SJason Wang     ncs = g_malloc0(sizeof(*ncs) * queue_pairs);
69440237840SJason Wang 
69540237840SJason Wang     for (i = 0; i < queue_pairs; i++) {
69640237840SJason Wang         ncs[i] = net_vhost_vdpa_init(peer, TYPE_VHOST_VDPA, name,
6971576dbb5SEugenio Pérez                                      vdpa_device_fd, i, 2, true, opts->x_svq,
6981576dbb5SEugenio Pérez                                      iova_tree);
69940237840SJason Wang         if (!ncs[i])
70040237840SJason Wang             goto err;
70140237840SJason Wang     }
70240237840SJason Wang 
70340237840SJason Wang     if (has_cvq) {
70440237840SJason Wang         nc = net_vhost_vdpa_init(peer, TYPE_VHOST_VDPA, name,
7051576dbb5SEugenio Pérez                                  vdpa_device_fd, i, 1, false,
7061576dbb5SEugenio Pérez                                  opts->x_svq, iova_tree);
70740237840SJason Wang         if (!nc)
70840237840SJason Wang             goto err;
70940237840SJason Wang     }
71040237840SJason Wang 
7111576dbb5SEugenio Pérez     /* iova_tree ownership belongs to last NetClientState */
7121576dbb5SEugenio Pérez     g_steal_pointer(&iova_tree);
713654790b6SJason Wang     return 0;
71440237840SJason Wang 
71540237840SJason Wang err:
71640237840SJason Wang     if (i) {
7179bd05507SSi-Wei Liu         for (i--; i >= 0; i--) {
7189bd05507SSi-Wei Liu             qemu_del_net_client(ncs[i]);
7199bd05507SSi-Wei Liu         }
72040237840SJason Wang     }
7211576dbb5SEugenio Pérez 
7221576dbb5SEugenio Pérez err_svq:
72340237840SJason Wang     qemu_close(vdpa_device_fd);
72440237840SJason Wang 
72540237840SJason Wang     return -1;
7261e0a84eaSCindy Lu }
727