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 uint64_t acked_features; 33 bool started; 34 } VhostVDPAState; 35 36 const int vdpa_feature_bits[] = { 37 VIRTIO_F_NOTIFY_ON_EMPTY, 38 VIRTIO_RING_F_INDIRECT_DESC, 39 VIRTIO_RING_F_EVENT_IDX, 40 VIRTIO_F_ANY_LAYOUT, 41 VIRTIO_F_VERSION_1, 42 VIRTIO_NET_F_CSUM, 43 VIRTIO_NET_F_GUEST_CSUM, 44 VIRTIO_NET_F_GSO, 45 VIRTIO_NET_F_GUEST_TSO4, 46 VIRTIO_NET_F_GUEST_TSO6, 47 VIRTIO_NET_F_GUEST_ECN, 48 VIRTIO_NET_F_GUEST_UFO, 49 VIRTIO_NET_F_HOST_TSO4, 50 VIRTIO_NET_F_HOST_TSO6, 51 VIRTIO_NET_F_HOST_ECN, 52 VIRTIO_NET_F_HOST_UFO, 53 VIRTIO_NET_F_MRG_RXBUF, 54 VIRTIO_NET_F_MTU, 55 VIRTIO_F_IOMMU_PLATFORM, 56 VIRTIO_F_RING_PACKED, 57 VIRTIO_NET_F_GUEST_ANNOUNCE, 58 VIRTIO_NET_F_STATUS, 59 VHOST_INVALID_FEATURE_BIT 60 }; 61 62 VHostNetState *vhost_vdpa_get_vhost_net(NetClientState *nc) 63 { 64 VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); 65 assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); 66 return s->vhost_net; 67 } 68 69 uint64_t vhost_vdpa_get_acked_features(NetClientState *nc) 70 { 71 VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); 72 assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); 73 s->acked_features = vhost_net_get_acked_features(s->vhost_net); 74 75 return s->acked_features; 76 } 77 78 static int vhost_vdpa_net_check_device_id(struct vhost_net *net) 79 { 80 uint32_t device_id; 81 int ret; 82 struct vhost_dev *hdev; 83 84 hdev = (struct vhost_dev *)&net->dev; 85 ret = hdev->vhost_ops->vhost_get_device_id(hdev, &device_id); 86 if (device_id != VIRTIO_ID_NET) { 87 return -ENOTSUP; 88 } 89 return ret; 90 } 91 92 static void vhost_vdpa_del(NetClientState *ncs) 93 { 94 VhostVDPAState *s; 95 assert(ncs->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); 96 s = DO_UPCAST(VhostVDPAState, nc, ncs); 97 if (s->vhost_net) { 98 vhost_net_cleanup(s->vhost_net); 99 } 100 } 101 102 static int vhost_vdpa_add(NetClientState *ncs, void *be) 103 { 104 VhostNetOptions options; 105 struct vhost_net *net = NULL; 106 VhostVDPAState *s; 107 int ret; 108 109 options.backend_type = VHOST_BACKEND_TYPE_VDPA; 110 assert(ncs->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); 111 s = DO_UPCAST(VhostVDPAState, nc, ncs); 112 options.net_backend = ncs; 113 options.opaque = be; 114 options.busyloop_timeout = 0; 115 116 net = vhost_net_init(&options); 117 if (!net) { 118 error_report("failed to init vhost_net for queue"); 119 goto err; 120 } 121 if (s->vhost_net) { 122 vhost_net_cleanup(s->vhost_net); 123 g_free(s->vhost_net); 124 } 125 s->vhost_net = net; 126 ret = vhost_vdpa_net_check_device_id(net); 127 if (ret) { 128 goto err; 129 } 130 return 0; 131 err: 132 if (net) { 133 vhost_net_cleanup(net); 134 } 135 vhost_vdpa_del(ncs); 136 return -1; 137 } 138 139 static void vhost_vdpa_cleanup(NetClientState *nc) 140 { 141 VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); 142 143 if (s->vhost_net) { 144 vhost_net_cleanup(s->vhost_net); 145 g_free(s->vhost_net); 146 s->vhost_net = NULL; 147 } 148 if (s->vhost_vdpa.device_fd >= 0) { 149 qemu_close(s->vhost_vdpa.device_fd); 150 s->vhost_vdpa.device_fd = -1; 151 } 152 } 153 154 static bool vhost_vdpa_has_vnet_hdr(NetClientState *nc) 155 { 156 assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); 157 158 return true; 159 } 160 161 static bool vhost_vdpa_has_ufo(NetClientState *nc) 162 { 163 assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); 164 VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); 165 uint64_t features = 0; 166 features |= (1ULL << VIRTIO_NET_F_HOST_UFO); 167 features = vhost_net_get_features(s->vhost_net, features); 168 return !!(features & (1ULL << VIRTIO_NET_F_HOST_UFO)); 169 170 } 171 172 static NetClientInfo net_vhost_vdpa_info = { 173 .type = NET_CLIENT_DRIVER_VHOST_VDPA, 174 .size = sizeof(VhostVDPAState), 175 .cleanup = vhost_vdpa_cleanup, 176 .has_vnet_hdr = vhost_vdpa_has_vnet_hdr, 177 .has_ufo = vhost_vdpa_has_ufo, 178 }; 179 180 static int net_vhost_vdpa_init(NetClientState *peer, const char *device, 181 const char *name, const char *vhostdev) 182 { 183 NetClientState *nc = NULL; 184 VhostVDPAState *s; 185 int vdpa_device_fd = -1; 186 int ret = 0; 187 NetdevVhostVDPAOptions *stored; 188 189 assert(name); 190 nc = qemu_new_net_client(&net_vhost_vdpa_info, peer, device, name); 191 192 /* Store startup parameters */ 193 nc->stored_config = g_new0(NetdevInfo, 1); 194 nc->stored_config->type = NET_BACKEND_VHOST_VDPA; 195 stored = &nc->stored_config->u.vhost_vdpa; 196 197 stored->has_vhostdev = true; 198 stored->vhostdev = g_strdup(vhostdev); 199 200 stored->has_queues = true; 201 stored->queues = 1; /* TODO: change when support multiqueue */ 202 203 nc->queue_index = 0; 204 s = DO_UPCAST(VhostVDPAState, nc, nc); 205 vdpa_device_fd = qemu_open_old(vhostdev, O_RDWR); 206 if (vdpa_device_fd == -1) { 207 return -errno; 208 } 209 s->vhost_vdpa.device_fd = vdpa_device_fd; 210 ret = vhost_vdpa_add(nc, (void *)&s->vhost_vdpa); 211 assert(s->vhost_net); 212 return ret; 213 } 214 215 static int net_vhost_check_net(void *opaque, QemuOpts *opts, Error **errp) 216 { 217 const char *name = opaque; 218 const char *driver, *netdev; 219 220 driver = qemu_opt_get(opts, "driver"); 221 netdev = qemu_opt_get(opts, "netdev"); 222 if (!driver || !netdev) { 223 return 0; 224 } 225 if (strcmp(netdev, name) == 0 && 226 !g_str_has_prefix(driver, "virtio-net-")) { 227 error_setg(errp, "vhost-vdpa requires frontend driver virtio-net-*"); 228 return -1; 229 } 230 return 0; 231 } 232 233 int net_init_vhost_vdpa(const Netdev *netdev, const char *name, 234 NetClientState *peer, Error **errp) 235 { 236 const NetdevVhostVDPAOptions *opts; 237 238 assert(netdev->type == NET_CLIENT_DRIVER_VHOST_VDPA); 239 opts = &netdev->u.vhost_vdpa; 240 /* verify net frontend */ 241 if (qemu_opts_foreach(qemu_find_opts("device"), net_vhost_check_net, 242 (char *)name, errp)) { 243 return -1; 244 } 245 return net_vhost_vdpa_init(peer, TYPE_VHOST_VDPA, name, opts->vhostdev); 246 } 247