1*1e0a84eaSCindy Lu /* 2*1e0a84eaSCindy Lu * vhost-vdpa.c 3*1e0a84eaSCindy Lu * 4*1e0a84eaSCindy Lu * Copyright(c) 2017-2018 Intel Corporation. 5*1e0a84eaSCindy Lu * Copyright(c) 2020 Red Hat, Inc. 6*1e0a84eaSCindy Lu * 7*1e0a84eaSCindy Lu * This work is licensed under the terms of the GNU GPL, version 2 or later. 8*1e0a84eaSCindy Lu * See the COPYING file in the top-level directory. 9*1e0a84eaSCindy Lu * 10*1e0a84eaSCindy Lu */ 11*1e0a84eaSCindy Lu 12*1e0a84eaSCindy Lu #include "qemu/osdep.h" 13*1e0a84eaSCindy Lu #include "clients.h" 14*1e0a84eaSCindy Lu #include "net/vhost_net.h" 15*1e0a84eaSCindy Lu #include "net/vhost-vdpa.h" 16*1e0a84eaSCindy Lu #include "hw/virtio/vhost-vdpa.h" 17*1e0a84eaSCindy Lu #include "qemu/config-file.h" 18*1e0a84eaSCindy Lu #include "qemu/error-report.h" 19*1e0a84eaSCindy Lu #include "qemu/option.h" 20*1e0a84eaSCindy Lu #include "qapi/error.h" 21*1e0a84eaSCindy Lu #include <sys/ioctl.h> 22*1e0a84eaSCindy Lu #include <err.h> 23*1e0a84eaSCindy Lu #include "standard-headers/linux/virtio_net.h" 24*1e0a84eaSCindy Lu #include "monitor/monitor.h" 25*1e0a84eaSCindy Lu #include "hw/virtio/vhost.h" 26*1e0a84eaSCindy Lu 27*1e0a84eaSCindy Lu /* Todo:need to add the multiqueue support here */ 28*1e0a84eaSCindy Lu typedef struct VhostVDPAState { 29*1e0a84eaSCindy Lu NetClientState nc; 30*1e0a84eaSCindy Lu struct vhost_vdpa vhost_vdpa; 31*1e0a84eaSCindy Lu VHostNetState *vhost_net; 32*1e0a84eaSCindy Lu uint64_t acked_features; 33*1e0a84eaSCindy Lu bool started; 34*1e0a84eaSCindy Lu } VhostVDPAState; 35*1e0a84eaSCindy Lu 36*1e0a84eaSCindy Lu const int vdpa_feature_bits[] = { 37*1e0a84eaSCindy Lu VIRTIO_F_NOTIFY_ON_EMPTY, 38*1e0a84eaSCindy Lu VIRTIO_RING_F_INDIRECT_DESC, 39*1e0a84eaSCindy Lu VIRTIO_RING_F_EVENT_IDX, 40*1e0a84eaSCindy Lu VIRTIO_F_ANY_LAYOUT, 41*1e0a84eaSCindy Lu VIRTIO_F_VERSION_1, 42*1e0a84eaSCindy Lu VIRTIO_NET_F_CSUM, 43*1e0a84eaSCindy Lu VIRTIO_NET_F_GUEST_CSUM, 44*1e0a84eaSCindy Lu VIRTIO_NET_F_GSO, 45*1e0a84eaSCindy Lu VIRTIO_NET_F_GUEST_TSO4, 46*1e0a84eaSCindy Lu VIRTIO_NET_F_GUEST_TSO6, 47*1e0a84eaSCindy Lu VIRTIO_NET_F_GUEST_ECN, 48*1e0a84eaSCindy Lu VIRTIO_NET_F_GUEST_UFO, 49*1e0a84eaSCindy Lu VIRTIO_NET_F_HOST_TSO4, 50*1e0a84eaSCindy Lu VIRTIO_NET_F_HOST_TSO6, 51*1e0a84eaSCindy Lu VIRTIO_NET_F_HOST_ECN, 52*1e0a84eaSCindy Lu VIRTIO_NET_F_HOST_UFO, 53*1e0a84eaSCindy Lu VIRTIO_NET_F_MRG_RXBUF, 54*1e0a84eaSCindy Lu VIRTIO_NET_F_MTU, 55*1e0a84eaSCindy Lu VIRTIO_F_IOMMU_PLATFORM, 56*1e0a84eaSCindy Lu VIRTIO_F_RING_PACKED, 57*1e0a84eaSCindy Lu VIRTIO_NET_F_GUEST_ANNOUNCE, 58*1e0a84eaSCindy Lu VHOST_INVALID_FEATURE_BIT 59*1e0a84eaSCindy Lu }; 60*1e0a84eaSCindy Lu 61*1e0a84eaSCindy Lu VHostNetState *vhost_vdpa_get_vhost_net(NetClientState *nc) 62*1e0a84eaSCindy Lu { 63*1e0a84eaSCindy Lu VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); 64*1e0a84eaSCindy Lu assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); 65*1e0a84eaSCindy Lu return s->vhost_net; 66*1e0a84eaSCindy Lu } 67*1e0a84eaSCindy Lu 68*1e0a84eaSCindy Lu uint64_t vhost_vdpa_get_acked_features(NetClientState *nc) 69*1e0a84eaSCindy Lu { 70*1e0a84eaSCindy Lu VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); 71*1e0a84eaSCindy Lu assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); 72*1e0a84eaSCindy Lu s->acked_features = vhost_net_get_acked_features(s->vhost_net); 73*1e0a84eaSCindy Lu 74*1e0a84eaSCindy Lu return s->acked_features; 75*1e0a84eaSCindy Lu } 76*1e0a84eaSCindy Lu 77*1e0a84eaSCindy Lu static int vhost_vdpa_net_check_device_id(struct vhost_net *net) 78*1e0a84eaSCindy Lu { 79*1e0a84eaSCindy Lu uint32_t device_id; 80*1e0a84eaSCindy Lu int ret; 81*1e0a84eaSCindy Lu struct vhost_dev *hdev; 82*1e0a84eaSCindy Lu 83*1e0a84eaSCindy Lu hdev = (struct vhost_dev *)&net->dev; 84*1e0a84eaSCindy Lu ret = hdev->vhost_ops->vhost_get_device_id(hdev, &device_id); 85*1e0a84eaSCindy Lu if (device_id != VIRTIO_ID_NET) { 86*1e0a84eaSCindy Lu return -ENOTSUP; 87*1e0a84eaSCindy Lu } 88*1e0a84eaSCindy Lu return ret; 89*1e0a84eaSCindy Lu } 90*1e0a84eaSCindy Lu 91*1e0a84eaSCindy Lu static void vhost_vdpa_del(NetClientState *ncs) 92*1e0a84eaSCindy Lu { 93*1e0a84eaSCindy Lu VhostVDPAState *s; 94*1e0a84eaSCindy Lu assert(ncs->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); 95*1e0a84eaSCindy Lu s = DO_UPCAST(VhostVDPAState, nc, ncs); 96*1e0a84eaSCindy Lu if (s->vhost_net) { 97*1e0a84eaSCindy Lu vhost_net_cleanup(s->vhost_net); 98*1e0a84eaSCindy Lu } 99*1e0a84eaSCindy Lu } 100*1e0a84eaSCindy Lu 101*1e0a84eaSCindy Lu static int vhost_vdpa_add(NetClientState *ncs, void *be) 102*1e0a84eaSCindy Lu { 103*1e0a84eaSCindy Lu VhostNetOptions options; 104*1e0a84eaSCindy Lu struct vhost_net *net = NULL; 105*1e0a84eaSCindy Lu VhostVDPAState *s; 106*1e0a84eaSCindy Lu int ret; 107*1e0a84eaSCindy Lu 108*1e0a84eaSCindy Lu options.backend_type = VHOST_BACKEND_TYPE_VDPA; 109*1e0a84eaSCindy Lu assert(ncs->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); 110*1e0a84eaSCindy Lu s = DO_UPCAST(VhostVDPAState, nc, ncs); 111*1e0a84eaSCindy Lu options.net_backend = ncs; 112*1e0a84eaSCindy Lu options.opaque = be; 113*1e0a84eaSCindy Lu options.busyloop_timeout = 0; 114*1e0a84eaSCindy Lu 115*1e0a84eaSCindy Lu net = vhost_net_init(&options); 116*1e0a84eaSCindy Lu if (!net) { 117*1e0a84eaSCindy Lu error_report("failed to init vhost_net for queue"); 118*1e0a84eaSCindy Lu goto err; 119*1e0a84eaSCindy Lu } 120*1e0a84eaSCindy Lu if (s->vhost_net) { 121*1e0a84eaSCindy Lu vhost_net_cleanup(s->vhost_net); 122*1e0a84eaSCindy Lu g_free(s->vhost_net); 123*1e0a84eaSCindy Lu } 124*1e0a84eaSCindy Lu s->vhost_net = net; 125*1e0a84eaSCindy Lu ret = vhost_vdpa_net_check_device_id(net); 126*1e0a84eaSCindy Lu if (ret) { 127*1e0a84eaSCindy Lu goto err; 128*1e0a84eaSCindy Lu } 129*1e0a84eaSCindy Lu return 0; 130*1e0a84eaSCindy Lu err: 131*1e0a84eaSCindy Lu if (net) { 132*1e0a84eaSCindy Lu vhost_net_cleanup(net); 133*1e0a84eaSCindy Lu } 134*1e0a84eaSCindy Lu vhost_vdpa_del(ncs); 135*1e0a84eaSCindy Lu return -1; 136*1e0a84eaSCindy Lu } 137*1e0a84eaSCindy Lu 138*1e0a84eaSCindy Lu static void vhost_vdpa_cleanup(NetClientState *nc) 139*1e0a84eaSCindy Lu { 140*1e0a84eaSCindy Lu VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); 141*1e0a84eaSCindy Lu 142*1e0a84eaSCindy Lu if (s->vhost_net) { 143*1e0a84eaSCindy Lu vhost_net_cleanup(s->vhost_net); 144*1e0a84eaSCindy Lu g_free(s->vhost_net); 145*1e0a84eaSCindy Lu s->vhost_net = NULL; 146*1e0a84eaSCindy Lu } 147*1e0a84eaSCindy Lu } 148*1e0a84eaSCindy Lu 149*1e0a84eaSCindy Lu static bool vhost_vdpa_has_vnet_hdr(NetClientState *nc) 150*1e0a84eaSCindy Lu { 151*1e0a84eaSCindy Lu assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); 152*1e0a84eaSCindy Lu 153*1e0a84eaSCindy Lu return true; 154*1e0a84eaSCindy Lu } 155*1e0a84eaSCindy Lu 156*1e0a84eaSCindy Lu static bool vhost_vdpa_has_ufo(NetClientState *nc) 157*1e0a84eaSCindy Lu { 158*1e0a84eaSCindy Lu assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); 159*1e0a84eaSCindy Lu VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); 160*1e0a84eaSCindy Lu uint64_t features = 0; 161*1e0a84eaSCindy Lu features |= (1ULL << VIRTIO_NET_F_HOST_UFO); 162*1e0a84eaSCindy Lu features = vhost_net_get_features(s->vhost_net, features); 163*1e0a84eaSCindy Lu return !!(features & (1ULL << VIRTIO_NET_F_HOST_UFO)); 164*1e0a84eaSCindy Lu 165*1e0a84eaSCindy Lu } 166*1e0a84eaSCindy Lu 167*1e0a84eaSCindy Lu static NetClientInfo net_vhost_vdpa_info = { 168*1e0a84eaSCindy Lu .type = NET_CLIENT_DRIVER_VHOST_VDPA, 169*1e0a84eaSCindy Lu .size = sizeof(VhostVDPAState), 170*1e0a84eaSCindy Lu .cleanup = vhost_vdpa_cleanup, 171*1e0a84eaSCindy Lu .has_vnet_hdr = vhost_vdpa_has_vnet_hdr, 172*1e0a84eaSCindy Lu .has_ufo = vhost_vdpa_has_ufo, 173*1e0a84eaSCindy Lu }; 174*1e0a84eaSCindy Lu 175*1e0a84eaSCindy Lu static int net_vhost_vdpa_init(NetClientState *peer, const char *device, 176*1e0a84eaSCindy Lu const char *name, const char *vhostdev) 177*1e0a84eaSCindy Lu { 178*1e0a84eaSCindy Lu NetClientState *nc = NULL; 179*1e0a84eaSCindy Lu VhostVDPAState *s; 180*1e0a84eaSCindy Lu int vdpa_device_fd = -1; 181*1e0a84eaSCindy Lu int ret = 0; 182*1e0a84eaSCindy Lu assert(name); 183*1e0a84eaSCindy Lu nc = qemu_new_net_client(&net_vhost_vdpa_info, peer, device, name); 184*1e0a84eaSCindy Lu snprintf(nc->info_str, sizeof(nc->info_str), TYPE_VHOST_VDPA); 185*1e0a84eaSCindy Lu nc->queue_index = 0; 186*1e0a84eaSCindy Lu s = DO_UPCAST(VhostVDPAState, nc, nc); 187*1e0a84eaSCindy Lu vdpa_device_fd = qemu_open(vhostdev, O_RDWR); 188*1e0a84eaSCindy Lu if (vdpa_device_fd == -1) { 189*1e0a84eaSCindy Lu return -errno; 190*1e0a84eaSCindy Lu } 191*1e0a84eaSCindy Lu s->vhost_vdpa.device_fd = vdpa_device_fd; 192*1e0a84eaSCindy Lu ret = vhost_vdpa_add(nc, (void *)&s->vhost_vdpa); 193*1e0a84eaSCindy Lu assert(s->vhost_net); 194*1e0a84eaSCindy Lu return ret; 195*1e0a84eaSCindy Lu } 196*1e0a84eaSCindy Lu 197*1e0a84eaSCindy Lu static int net_vhost_check_net(void *opaque, QemuOpts *opts, Error **errp) 198*1e0a84eaSCindy Lu { 199*1e0a84eaSCindy Lu const char *name = opaque; 200*1e0a84eaSCindy Lu const char *driver, *netdev; 201*1e0a84eaSCindy Lu 202*1e0a84eaSCindy Lu driver = qemu_opt_get(opts, "driver"); 203*1e0a84eaSCindy Lu netdev = qemu_opt_get(opts, "netdev"); 204*1e0a84eaSCindy Lu if (!driver || !netdev) { 205*1e0a84eaSCindy Lu return 0; 206*1e0a84eaSCindy Lu } 207*1e0a84eaSCindy Lu if (strcmp(netdev, name) == 0 && 208*1e0a84eaSCindy Lu !g_str_has_prefix(driver, "virtio-net-")) { 209*1e0a84eaSCindy Lu error_setg(errp, "vhost-vdpa requires frontend driver virtio-net-*"); 210*1e0a84eaSCindy Lu return -1; 211*1e0a84eaSCindy Lu } 212*1e0a84eaSCindy Lu return 0; 213*1e0a84eaSCindy Lu } 214*1e0a84eaSCindy Lu 215*1e0a84eaSCindy Lu int net_init_vhost_vdpa(const Netdev *netdev, const char *name, 216*1e0a84eaSCindy Lu NetClientState *peer, Error **errp) 217*1e0a84eaSCindy Lu { 218*1e0a84eaSCindy Lu const NetdevVhostVDPAOptions *opts; 219*1e0a84eaSCindy Lu 220*1e0a84eaSCindy Lu assert(netdev->type == NET_CLIENT_DRIVER_VHOST_VDPA); 221*1e0a84eaSCindy Lu opts = &netdev->u.vhost_vdpa; 222*1e0a84eaSCindy Lu /* verify net frontend */ 223*1e0a84eaSCindy Lu if (qemu_opts_foreach(qemu_find_opts("device"), net_vhost_check_net, 224*1e0a84eaSCindy Lu (char *)name, errp)) { 225*1e0a84eaSCindy Lu return -1; 226*1e0a84eaSCindy Lu } 227*1e0a84eaSCindy Lu return net_vhost_vdpa_init(peer, TYPE_VHOST_VDPA, name, opts->vhostdev); 228*1e0a84eaSCindy Lu } 229