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