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" 141e0a84eaSCindy Lu #include "net/vhost_net.h" 151e0a84eaSCindy Lu #include "net/vhost-vdpa.h" 161e0a84eaSCindy Lu #include "hw/virtio/vhost-vdpa.h" 171e0a84eaSCindy Lu #include "qemu/config-file.h" 181e0a84eaSCindy Lu #include "qemu/error-report.h" 191e0a84eaSCindy Lu #include "qemu/option.h" 201e0a84eaSCindy Lu #include "qapi/error.h" 211e0a84eaSCindy Lu #include <sys/ioctl.h> 221e0a84eaSCindy Lu #include <err.h> 231e0a84eaSCindy Lu #include "standard-headers/linux/virtio_net.h" 241e0a84eaSCindy Lu #include "monitor/monitor.h" 251e0a84eaSCindy Lu #include "hw/virtio/vhost.h" 261e0a84eaSCindy Lu 271e0a84eaSCindy Lu /* Todo:need to add the multiqueue support here */ 281e0a84eaSCindy Lu typedef struct VhostVDPAState { 291e0a84eaSCindy Lu NetClientState nc; 301e0a84eaSCindy Lu struct vhost_vdpa vhost_vdpa; 311e0a84eaSCindy Lu VHostNetState *vhost_net; 321e0a84eaSCindy Lu bool started; 331e0a84eaSCindy Lu } VhostVDPAState; 341e0a84eaSCindy Lu 351e0a84eaSCindy Lu const int vdpa_feature_bits[] = { 361e0a84eaSCindy Lu VIRTIO_F_NOTIFY_ON_EMPTY, 371e0a84eaSCindy Lu VIRTIO_RING_F_INDIRECT_DESC, 381e0a84eaSCindy Lu VIRTIO_RING_F_EVENT_IDX, 391e0a84eaSCindy Lu VIRTIO_F_ANY_LAYOUT, 401e0a84eaSCindy Lu VIRTIO_F_VERSION_1, 411e0a84eaSCindy Lu VIRTIO_NET_F_CSUM, 421e0a84eaSCindy Lu VIRTIO_NET_F_GUEST_CSUM, 431e0a84eaSCindy Lu VIRTIO_NET_F_GSO, 441e0a84eaSCindy Lu VIRTIO_NET_F_GUEST_TSO4, 451e0a84eaSCindy Lu VIRTIO_NET_F_GUEST_TSO6, 461e0a84eaSCindy Lu VIRTIO_NET_F_GUEST_ECN, 471e0a84eaSCindy Lu VIRTIO_NET_F_GUEST_UFO, 481e0a84eaSCindy Lu VIRTIO_NET_F_HOST_TSO4, 491e0a84eaSCindy Lu VIRTIO_NET_F_HOST_TSO6, 501e0a84eaSCindy Lu VIRTIO_NET_F_HOST_ECN, 511e0a84eaSCindy Lu VIRTIO_NET_F_HOST_UFO, 521e0a84eaSCindy Lu VIRTIO_NET_F_MRG_RXBUF, 531e0a84eaSCindy Lu VIRTIO_NET_F_MTU, 541e0a84eaSCindy Lu VIRTIO_F_IOMMU_PLATFORM, 551e0a84eaSCindy Lu VIRTIO_F_RING_PACKED, 560145c393SAndrew Melnychenko VIRTIO_NET_F_RSS, 570145c393SAndrew Melnychenko VIRTIO_NET_F_HASH_REPORT, 581e0a84eaSCindy Lu VIRTIO_NET_F_GUEST_ANNOUNCE, 599aa47eddSSi-Wei Liu VIRTIO_NET_F_STATUS, 601e0a84eaSCindy Lu VHOST_INVALID_FEATURE_BIT 611e0a84eaSCindy Lu }; 621e0a84eaSCindy Lu 631e0a84eaSCindy Lu VHostNetState *vhost_vdpa_get_vhost_net(NetClientState *nc) 641e0a84eaSCindy Lu { 651e0a84eaSCindy Lu VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); 661e0a84eaSCindy Lu assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); 671e0a84eaSCindy Lu return s->vhost_net; 681e0a84eaSCindy Lu } 691e0a84eaSCindy Lu 701e0a84eaSCindy Lu static int vhost_vdpa_net_check_device_id(struct vhost_net *net) 711e0a84eaSCindy Lu { 721e0a84eaSCindy Lu uint32_t device_id; 731e0a84eaSCindy Lu int ret; 741e0a84eaSCindy Lu struct vhost_dev *hdev; 751e0a84eaSCindy Lu 761e0a84eaSCindy Lu hdev = (struct vhost_dev *)&net->dev; 771e0a84eaSCindy Lu ret = hdev->vhost_ops->vhost_get_device_id(hdev, &device_id); 781e0a84eaSCindy Lu if (device_id != VIRTIO_ID_NET) { 791e0a84eaSCindy Lu return -ENOTSUP; 801e0a84eaSCindy Lu } 811e0a84eaSCindy Lu return ret; 821e0a84eaSCindy Lu } 831e0a84eaSCindy Lu 841e0a84eaSCindy Lu static int vhost_vdpa_add(NetClientState *ncs, void *be) 851e0a84eaSCindy Lu { 861e0a84eaSCindy Lu VhostNetOptions options; 871e0a84eaSCindy Lu struct vhost_net *net = NULL; 881e0a84eaSCindy Lu VhostVDPAState *s; 891e0a84eaSCindy Lu int ret; 901e0a84eaSCindy Lu 911e0a84eaSCindy Lu options.backend_type = VHOST_BACKEND_TYPE_VDPA; 921e0a84eaSCindy Lu assert(ncs->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); 931e0a84eaSCindy Lu s = DO_UPCAST(VhostVDPAState, nc, ncs); 941e0a84eaSCindy Lu options.net_backend = ncs; 951e0a84eaSCindy Lu options.opaque = be; 961e0a84eaSCindy Lu options.busyloop_timeout = 0; 976a756d14SJason Wang options.nvqs = 2; 981e0a84eaSCindy Lu 991e0a84eaSCindy Lu net = vhost_net_init(&options); 1001e0a84eaSCindy Lu if (!net) { 1011e0a84eaSCindy Lu error_report("failed to init vhost_net for queue"); 102a97ef87aSJason Wang goto err_init; 1031e0a84eaSCindy Lu } 1041e0a84eaSCindy Lu s->vhost_net = net; 1051e0a84eaSCindy Lu ret = vhost_vdpa_net_check_device_id(net); 1061e0a84eaSCindy Lu if (ret) { 107a97ef87aSJason Wang goto err_check; 1081e0a84eaSCindy Lu } 1091e0a84eaSCindy Lu return 0; 110a97ef87aSJason Wang err_check: 1111e0a84eaSCindy Lu vhost_net_cleanup(net); 112ab36edcfSJason Wang g_free(net); 113a97ef87aSJason Wang err_init: 1141e0a84eaSCindy Lu return -1; 1151e0a84eaSCindy Lu } 1161e0a84eaSCindy Lu 1171e0a84eaSCindy Lu static void vhost_vdpa_cleanup(NetClientState *nc) 1181e0a84eaSCindy Lu { 1191e0a84eaSCindy Lu VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); 1201e0a84eaSCindy Lu 1211e0a84eaSCindy Lu if (s->vhost_net) { 1221e0a84eaSCindy Lu vhost_net_cleanup(s->vhost_net); 1231e0a84eaSCindy Lu g_free(s->vhost_net); 1241e0a84eaSCindy Lu s->vhost_net = NULL; 1251e0a84eaSCindy Lu } 12657b3a7d8SCindy Lu if (s->vhost_vdpa.device_fd >= 0) { 12757b3a7d8SCindy Lu qemu_close(s->vhost_vdpa.device_fd); 12857b3a7d8SCindy Lu s->vhost_vdpa.device_fd = -1; 12957b3a7d8SCindy Lu } 1301e0a84eaSCindy Lu } 1311e0a84eaSCindy Lu 1321e0a84eaSCindy Lu static bool vhost_vdpa_has_vnet_hdr(NetClientState *nc) 1331e0a84eaSCindy Lu { 1341e0a84eaSCindy Lu assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); 1351e0a84eaSCindy Lu 1361e0a84eaSCindy Lu return true; 1371e0a84eaSCindy Lu } 1381e0a84eaSCindy Lu 1391e0a84eaSCindy Lu static bool vhost_vdpa_has_ufo(NetClientState *nc) 1401e0a84eaSCindy Lu { 1411e0a84eaSCindy Lu assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); 1421e0a84eaSCindy Lu VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); 1431e0a84eaSCindy Lu uint64_t features = 0; 1441e0a84eaSCindy Lu features |= (1ULL << VIRTIO_NET_F_HOST_UFO); 1451e0a84eaSCindy Lu features = vhost_net_get_features(s->vhost_net, features); 1461e0a84eaSCindy Lu return !!(features & (1ULL << VIRTIO_NET_F_HOST_UFO)); 1471e0a84eaSCindy Lu 1481e0a84eaSCindy Lu } 1491e0a84eaSCindy Lu 1501e0a84eaSCindy Lu static NetClientInfo net_vhost_vdpa_info = { 1511e0a84eaSCindy Lu .type = NET_CLIENT_DRIVER_VHOST_VDPA, 1521e0a84eaSCindy Lu .size = sizeof(VhostVDPAState), 1531e0a84eaSCindy Lu .cleanup = vhost_vdpa_cleanup, 1541e0a84eaSCindy Lu .has_vnet_hdr = vhost_vdpa_has_vnet_hdr, 1551e0a84eaSCindy Lu .has_ufo = vhost_vdpa_has_ufo, 1561e0a84eaSCindy Lu }; 1571e0a84eaSCindy Lu 1581e0a84eaSCindy Lu static int net_vhost_vdpa_init(NetClientState *peer, const char *device, 1591e0a84eaSCindy Lu const char *name, const char *vhostdev) 1601e0a84eaSCindy Lu { 1611e0a84eaSCindy Lu NetClientState *nc = NULL; 1621e0a84eaSCindy Lu VhostVDPAState *s; 1631e0a84eaSCindy Lu int vdpa_device_fd = -1; 1641e0a84eaSCindy Lu int ret = 0; 1651e0a84eaSCindy Lu assert(name); 1661e0a84eaSCindy Lu nc = qemu_new_net_client(&net_vhost_vdpa_info, peer, device, name); 16756e6f594SJason Wang snprintf(nc->info_str, sizeof(nc->info_str), TYPE_VHOST_VDPA); 1681e0a84eaSCindy Lu nc->queue_index = 0; 1691e0a84eaSCindy Lu s = DO_UPCAST(VhostVDPAState, nc, nc); 170448058aaSDaniel P. Berrangé vdpa_device_fd = qemu_open_old(vhostdev, O_RDWR); 1711e0a84eaSCindy Lu if (vdpa_device_fd == -1) { 1721e0a84eaSCindy Lu return -errno; 1731e0a84eaSCindy Lu } 1741e0a84eaSCindy Lu s->vhost_vdpa.device_fd = vdpa_device_fd; 1751e0a84eaSCindy Lu ret = vhost_vdpa_add(nc, (void *)&s->vhost_vdpa); 176*74af5eecSJason Wang if (ret) { 177*74af5eecSJason Wang qemu_close(vdpa_device_fd); 178*74af5eecSJason Wang qemu_del_net_client(nc); 179*74af5eecSJason Wang } 1801e0a84eaSCindy Lu return ret; 1811e0a84eaSCindy Lu } 1821e0a84eaSCindy Lu 1831e0a84eaSCindy Lu static int net_vhost_check_net(void *opaque, QemuOpts *opts, Error **errp) 1841e0a84eaSCindy Lu { 1851e0a84eaSCindy Lu const char *name = opaque; 1861e0a84eaSCindy Lu const char *driver, *netdev; 1871e0a84eaSCindy Lu 1881e0a84eaSCindy Lu driver = qemu_opt_get(opts, "driver"); 1891e0a84eaSCindy Lu netdev = qemu_opt_get(opts, "netdev"); 1901e0a84eaSCindy Lu if (!driver || !netdev) { 1911e0a84eaSCindy Lu return 0; 1921e0a84eaSCindy Lu } 1931e0a84eaSCindy Lu if (strcmp(netdev, name) == 0 && 1941e0a84eaSCindy Lu !g_str_has_prefix(driver, "virtio-net-")) { 1951e0a84eaSCindy Lu error_setg(errp, "vhost-vdpa requires frontend driver virtio-net-*"); 1961e0a84eaSCindy Lu return -1; 1971e0a84eaSCindy Lu } 1981e0a84eaSCindy Lu return 0; 1991e0a84eaSCindy Lu } 2001e0a84eaSCindy Lu 2011e0a84eaSCindy Lu int net_init_vhost_vdpa(const Netdev *netdev, const char *name, 2021e0a84eaSCindy Lu NetClientState *peer, Error **errp) 2031e0a84eaSCindy Lu { 2041e0a84eaSCindy Lu const NetdevVhostVDPAOptions *opts; 2051e0a84eaSCindy Lu 2061e0a84eaSCindy Lu assert(netdev->type == NET_CLIENT_DRIVER_VHOST_VDPA); 2071e0a84eaSCindy Lu opts = &netdev->u.vhost_vdpa; 2081e0a84eaSCindy Lu /* verify net frontend */ 2091e0a84eaSCindy Lu if (qemu_opts_foreach(qemu_find_opts("device"), net_vhost_check_net, 2101e0a84eaSCindy Lu (char *)name, errp)) { 2111e0a84eaSCindy Lu return -1; 2121e0a84eaSCindy Lu } 2131e0a84eaSCindy Lu return net_vhost_vdpa_init(peer, TYPE_VHOST_VDPA, name, opts->vhostdev); 2141e0a84eaSCindy Lu } 215