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 uint64_t acked_features; 331e0a84eaSCindy Lu bool started; 341e0a84eaSCindy Lu } VhostVDPAState; 351e0a84eaSCindy Lu 361e0a84eaSCindy Lu const int vdpa_feature_bits[] = { 371e0a84eaSCindy Lu VIRTIO_F_NOTIFY_ON_EMPTY, 381e0a84eaSCindy Lu VIRTIO_RING_F_INDIRECT_DESC, 391e0a84eaSCindy Lu VIRTIO_RING_F_EVENT_IDX, 401e0a84eaSCindy Lu VIRTIO_F_ANY_LAYOUT, 411e0a84eaSCindy Lu VIRTIO_F_VERSION_1, 421e0a84eaSCindy Lu VIRTIO_NET_F_CSUM, 431e0a84eaSCindy Lu VIRTIO_NET_F_GUEST_CSUM, 441e0a84eaSCindy Lu VIRTIO_NET_F_GSO, 451e0a84eaSCindy Lu VIRTIO_NET_F_GUEST_TSO4, 461e0a84eaSCindy Lu VIRTIO_NET_F_GUEST_TSO6, 471e0a84eaSCindy Lu VIRTIO_NET_F_GUEST_ECN, 481e0a84eaSCindy Lu VIRTIO_NET_F_GUEST_UFO, 491e0a84eaSCindy Lu VIRTIO_NET_F_HOST_TSO4, 501e0a84eaSCindy Lu VIRTIO_NET_F_HOST_TSO6, 511e0a84eaSCindy Lu VIRTIO_NET_F_HOST_ECN, 521e0a84eaSCindy Lu VIRTIO_NET_F_HOST_UFO, 531e0a84eaSCindy Lu VIRTIO_NET_F_MRG_RXBUF, 541e0a84eaSCindy Lu VIRTIO_NET_F_MTU, 551e0a84eaSCindy Lu VIRTIO_F_IOMMU_PLATFORM, 561e0a84eaSCindy Lu VIRTIO_F_RING_PACKED, 571e0a84eaSCindy Lu VIRTIO_NET_F_GUEST_ANNOUNCE, 581e0a84eaSCindy Lu VHOST_INVALID_FEATURE_BIT 591e0a84eaSCindy Lu }; 601e0a84eaSCindy Lu 611e0a84eaSCindy Lu VHostNetState *vhost_vdpa_get_vhost_net(NetClientState *nc) 621e0a84eaSCindy Lu { 631e0a84eaSCindy Lu VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); 641e0a84eaSCindy Lu assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); 651e0a84eaSCindy Lu return s->vhost_net; 661e0a84eaSCindy Lu } 671e0a84eaSCindy Lu 681e0a84eaSCindy Lu uint64_t vhost_vdpa_get_acked_features(NetClientState *nc) 691e0a84eaSCindy Lu { 701e0a84eaSCindy Lu VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); 711e0a84eaSCindy Lu assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); 721e0a84eaSCindy Lu s->acked_features = vhost_net_get_acked_features(s->vhost_net); 731e0a84eaSCindy Lu 741e0a84eaSCindy Lu return s->acked_features; 751e0a84eaSCindy Lu } 761e0a84eaSCindy Lu 771e0a84eaSCindy Lu static int vhost_vdpa_net_check_device_id(struct vhost_net *net) 781e0a84eaSCindy Lu { 791e0a84eaSCindy Lu uint32_t device_id; 801e0a84eaSCindy Lu int ret; 811e0a84eaSCindy Lu struct vhost_dev *hdev; 821e0a84eaSCindy Lu 831e0a84eaSCindy Lu hdev = (struct vhost_dev *)&net->dev; 841e0a84eaSCindy Lu ret = hdev->vhost_ops->vhost_get_device_id(hdev, &device_id); 851e0a84eaSCindy Lu if (device_id != VIRTIO_ID_NET) { 861e0a84eaSCindy Lu return -ENOTSUP; 871e0a84eaSCindy Lu } 881e0a84eaSCindy Lu return ret; 891e0a84eaSCindy Lu } 901e0a84eaSCindy Lu 911e0a84eaSCindy Lu static void vhost_vdpa_del(NetClientState *ncs) 921e0a84eaSCindy Lu { 931e0a84eaSCindy Lu VhostVDPAState *s; 941e0a84eaSCindy Lu assert(ncs->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); 951e0a84eaSCindy Lu s = DO_UPCAST(VhostVDPAState, nc, ncs); 961e0a84eaSCindy Lu if (s->vhost_net) { 971e0a84eaSCindy Lu vhost_net_cleanup(s->vhost_net); 981e0a84eaSCindy Lu } 991e0a84eaSCindy Lu } 1001e0a84eaSCindy Lu 1011e0a84eaSCindy Lu static int vhost_vdpa_add(NetClientState *ncs, void *be) 1021e0a84eaSCindy Lu { 1031e0a84eaSCindy Lu VhostNetOptions options; 1041e0a84eaSCindy Lu struct vhost_net *net = NULL; 1051e0a84eaSCindy Lu VhostVDPAState *s; 1061e0a84eaSCindy Lu int ret; 1071e0a84eaSCindy Lu 1081e0a84eaSCindy Lu options.backend_type = VHOST_BACKEND_TYPE_VDPA; 1091e0a84eaSCindy Lu assert(ncs->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); 1101e0a84eaSCindy Lu s = DO_UPCAST(VhostVDPAState, nc, ncs); 1111e0a84eaSCindy Lu options.net_backend = ncs; 1121e0a84eaSCindy Lu options.opaque = be; 1131e0a84eaSCindy Lu options.busyloop_timeout = 0; 1141e0a84eaSCindy Lu 1151e0a84eaSCindy Lu net = vhost_net_init(&options); 1161e0a84eaSCindy Lu if (!net) { 1171e0a84eaSCindy Lu error_report("failed to init vhost_net for queue"); 1181e0a84eaSCindy Lu goto err; 1191e0a84eaSCindy Lu } 1201e0a84eaSCindy Lu if (s->vhost_net) { 1211e0a84eaSCindy Lu vhost_net_cleanup(s->vhost_net); 1221e0a84eaSCindy Lu g_free(s->vhost_net); 1231e0a84eaSCindy Lu } 1241e0a84eaSCindy Lu s->vhost_net = net; 1251e0a84eaSCindy Lu ret = vhost_vdpa_net_check_device_id(net); 1261e0a84eaSCindy Lu if (ret) { 1271e0a84eaSCindy Lu goto err; 1281e0a84eaSCindy Lu } 1291e0a84eaSCindy Lu return 0; 1301e0a84eaSCindy Lu err: 1311e0a84eaSCindy Lu if (net) { 1321e0a84eaSCindy Lu vhost_net_cleanup(net); 1331e0a84eaSCindy Lu } 1341e0a84eaSCindy Lu vhost_vdpa_del(ncs); 1351e0a84eaSCindy Lu return -1; 1361e0a84eaSCindy Lu } 1371e0a84eaSCindy Lu 1381e0a84eaSCindy Lu static void vhost_vdpa_cleanup(NetClientState *nc) 1391e0a84eaSCindy Lu { 1401e0a84eaSCindy Lu VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); 1411e0a84eaSCindy Lu 1421e0a84eaSCindy Lu if (s->vhost_net) { 1431e0a84eaSCindy Lu vhost_net_cleanup(s->vhost_net); 1441e0a84eaSCindy Lu g_free(s->vhost_net); 1451e0a84eaSCindy Lu s->vhost_net = NULL; 1461e0a84eaSCindy Lu } 1471e0a84eaSCindy Lu } 1481e0a84eaSCindy Lu 1491e0a84eaSCindy Lu static bool vhost_vdpa_has_vnet_hdr(NetClientState *nc) 1501e0a84eaSCindy Lu { 1511e0a84eaSCindy Lu assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); 1521e0a84eaSCindy Lu 1531e0a84eaSCindy Lu return true; 1541e0a84eaSCindy Lu } 1551e0a84eaSCindy Lu 1561e0a84eaSCindy Lu static bool vhost_vdpa_has_ufo(NetClientState *nc) 1571e0a84eaSCindy Lu { 1581e0a84eaSCindy Lu assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); 1591e0a84eaSCindy Lu VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); 1601e0a84eaSCindy Lu uint64_t features = 0; 1611e0a84eaSCindy Lu features |= (1ULL << VIRTIO_NET_F_HOST_UFO); 1621e0a84eaSCindy Lu features = vhost_net_get_features(s->vhost_net, features); 1631e0a84eaSCindy Lu return !!(features & (1ULL << VIRTIO_NET_F_HOST_UFO)); 1641e0a84eaSCindy Lu 1651e0a84eaSCindy Lu } 1661e0a84eaSCindy Lu 1671e0a84eaSCindy Lu static NetClientInfo net_vhost_vdpa_info = { 1681e0a84eaSCindy Lu .type = NET_CLIENT_DRIVER_VHOST_VDPA, 1691e0a84eaSCindy Lu .size = sizeof(VhostVDPAState), 1701e0a84eaSCindy Lu .cleanup = vhost_vdpa_cleanup, 1711e0a84eaSCindy Lu .has_vnet_hdr = vhost_vdpa_has_vnet_hdr, 1721e0a84eaSCindy Lu .has_ufo = vhost_vdpa_has_ufo, 1731e0a84eaSCindy Lu }; 1741e0a84eaSCindy Lu 1751e0a84eaSCindy Lu static int net_vhost_vdpa_init(NetClientState *peer, const char *device, 1761e0a84eaSCindy Lu const char *name, const char *vhostdev) 1771e0a84eaSCindy Lu { 1781e0a84eaSCindy Lu NetClientState *nc = NULL; 1791e0a84eaSCindy Lu VhostVDPAState *s; 1801e0a84eaSCindy Lu int vdpa_device_fd = -1; 1811e0a84eaSCindy Lu int ret = 0; 1821e0a84eaSCindy Lu assert(name); 1831e0a84eaSCindy Lu nc = qemu_new_net_client(&net_vhost_vdpa_info, peer, device, name); 1841e0a84eaSCindy Lu snprintf(nc->info_str, sizeof(nc->info_str), TYPE_VHOST_VDPA); 1851e0a84eaSCindy Lu nc->queue_index = 0; 1861e0a84eaSCindy Lu s = DO_UPCAST(VhostVDPAState, nc, nc); 187*448058aaSDaniel P. Berrangé vdpa_device_fd = qemu_open_old(vhostdev, O_RDWR); 1881e0a84eaSCindy Lu if (vdpa_device_fd == -1) { 1891e0a84eaSCindy Lu return -errno; 1901e0a84eaSCindy Lu } 1911e0a84eaSCindy Lu s->vhost_vdpa.device_fd = vdpa_device_fd; 1921e0a84eaSCindy Lu ret = vhost_vdpa_add(nc, (void *)&s->vhost_vdpa); 1931e0a84eaSCindy Lu assert(s->vhost_net); 1941e0a84eaSCindy Lu return ret; 1951e0a84eaSCindy Lu } 1961e0a84eaSCindy Lu 1971e0a84eaSCindy Lu static int net_vhost_check_net(void *opaque, QemuOpts *opts, Error **errp) 1981e0a84eaSCindy Lu { 1991e0a84eaSCindy Lu const char *name = opaque; 2001e0a84eaSCindy Lu const char *driver, *netdev; 2011e0a84eaSCindy Lu 2021e0a84eaSCindy Lu driver = qemu_opt_get(opts, "driver"); 2031e0a84eaSCindy Lu netdev = qemu_opt_get(opts, "netdev"); 2041e0a84eaSCindy Lu if (!driver || !netdev) { 2051e0a84eaSCindy Lu return 0; 2061e0a84eaSCindy Lu } 2071e0a84eaSCindy Lu if (strcmp(netdev, name) == 0 && 2081e0a84eaSCindy Lu !g_str_has_prefix(driver, "virtio-net-")) { 2091e0a84eaSCindy Lu error_setg(errp, "vhost-vdpa requires frontend driver virtio-net-*"); 2101e0a84eaSCindy Lu return -1; 2111e0a84eaSCindy Lu } 2121e0a84eaSCindy Lu return 0; 2131e0a84eaSCindy Lu } 2141e0a84eaSCindy Lu 2151e0a84eaSCindy Lu int net_init_vhost_vdpa(const Netdev *netdev, const char *name, 2161e0a84eaSCindy Lu NetClientState *peer, Error **errp) 2171e0a84eaSCindy Lu { 2181e0a84eaSCindy Lu const NetdevVhostVDPAOptions *opts; 2191e0a84eaSCindy Lu 2201e0a84eaSCindy Lu assert(netdev->type == NET_CLIENT_DRIVER_VHOST_VDPA); 2211e0a84eaSCindy Lu opts = &netdev->u.vhost_vdpa; 2221e0a84eaSCindy Lu /* verify net frontend */ 2231e0a84eaSCindy Lu if (qemu_opts_foreach(qemu_find_opts("device"), net_vhost_check_net, 2241e0a84eaSCindy Lu (char *)name, errp)) { 2251e0a84eaSCindy Lu return -1; 2261e0a84eaSCindy Lu } 2271e0a84eaSCindy Lu return net_vhost_vdpa_init(peer, TYPE_VHOST_VDPA, name, opts->vhostdev); 2281e0a84eaSCindy Lu } 229