qemu/net/vhost-vdpa.c
<<
>>
Prefs
   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 <linux/vhost.h>
  22#include <sys/ioctl.h>
  23#include <err.h>
  24#include "standard-headers/linux/virtio_net.h"
  25#include "monitor/monitor.h"
  26#include "hw/virtio/vhost.h"
  27
  28/* Todo:need to add the multiqueue support here */
  29typedef struct VhostVDPAState {
  30    NetClientState nc;
  31    struct vhost_vdpa vhost_vdpa;
  32    VHostNetState *vhost_net;
  33    bool started;
  34} VhostVDPAState;
  35
  36const 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_NET_F_CTRL_RX,
  56    VIRTIO_NET_F_CTRL_RX_EXTRA,
  57    VIRTIO_NET_F_CTRL_VLAN,
  58    VIRTIO_NET_F_GUEST_ANNOUNCE,
  59    VIRTIO_NET_F_CTRL_MAC_ADDR,
  60    VIRTIO_NET_F_RSS,
  61    VIRTIO_NET_F_MQ,
  62    VIRTIO_NET_F_CTRL_VQ,
  63    VIRTIO_F_IOMMU_PLATFORM,
  64    VIRTIO_F_RING_PACKED,
  65    VIRTIO_NET_F_RSS,
  66    VIRTIO_NET_F_HASH_REPORT,
  67    VIRTIO_NET_F_GUEST_ANNOUNCE,
  68    VIRTIO_NET_F_STATUS,
  69    VHOST_INVALID_FEATURE_BIT
  70};
  71
  72VHostNetState *vhost_vdpa_get_vhost_net(NetClientState *nc)
  73{
  74    VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc);
  75    assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA);
  76    return s->vhost_net;
  77}
  78
  79static int vhost_vdpa_net_check_device_id(struct vhost_net *net)
  80{
  81    uint32_t device_id;
  82    int ret;
  83    struct vhost_dev *hdev;
  84
  85    hdev = (struct vhost_dev *)&net->dev;
  86    ret = hdev->vhost_ops->vhost_get_device_id(hdev, &device_id);
  87    if (device_id != VIRTIO_ID_NET) {
  88        return -ENOTSUP;
  89    }
  90    return ret;
  91}
  92
  93static int vhost_vdpa_add(NetClientState *ncs, void *be,
  94                          int queue_pair_index, int nvqs)
  95{
  96    VhostNetOptions options;
  97    struct vhost_net *net = NULL;
  98    VhostVDPAState *s;
  99    int ret;
 100
 101    options.backend_type = VHOST_BACKEND_TYPE_VDPA;
 102    assert(ncs->info->type == NET_CLIENT_DRIVER_VHOST_VDPA);
 103    s = DO_UPCAST(VhostVDPAState, nc, ncs);
 104    options.net_backend = ncs;
 105    options.opaque      = be;
 106    options.busyloop_timeout = 0;
 107    options.nvqs = nvqs;
 108
 109    net = vhost_net_init(&options);
 110    if (!net) {
 111        error_report("failed to init vhost_net for queue");
 112        goto err_init;
 113    }
 114    s->vhost_net = net;
 115    ret = vhost_vdpa_net_check_device_id(net);
 116    if (ret) {
 117        goto err_check;
 118    }
 119    return 0;
 120err_check:
 121    vhost_net_cleanup(net);
 122    g_free(net);
 123err_init:
 124    return -1;
 125}
 126
 127static void vhost_vdpa_cleanup(NetClientState *nc)
 128{
 129    VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc);
 130
 131    if (s->vhost_net) {
 132        vhost_net_cleanup(s->vhost_net);
 133        g_free(s->vhost_net);
 134        s->vhost_net = NULL;
 135    }
 136     if (s->vhost_vdpa.device_fd >= 0) {
 137        qemu_close(s->vhost_vdpa.device_fd);
 138        s->vhost_vdpa.device_fd = -1;
 139    }
 140}
 141
 142static bool vhost_vdpa_has_vnet_hdr(NetClientState *nc)
 143{
 144    assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA);
 145
 146    return true;
 147}
 148
 149static bool vhost_vdpa_has_ufo(NetClientState *nc)
 150{
 151    assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA);
 152    VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc);
 153    uint64_t features = 0;
 154    features |= (1ULL << VIRTIO_NET_F_HOST_UFO);
 155    features = vhost_net_get_features(s->vhost_net, features);
 156    return !!(features & (1ULL << VIRTIO_NET_F_HOST_UFO));
 157
 158}
 159
 160static bool vhost_vdpa_check_peer_type(NetClientState *nc, ObjectClass *oc,
 161                                       Error **errp)
 162{
 163    const char *driver = object_class_get_name(oc);
 164
 165    if (!g_str_has_prefix(driver, "virtio-net-")) {
 166        error_setg(errp, "vhost-vdpa requires frontend driver virtio-net-*");
 167        return false;
 168    }
 169
 170    return true;
 171}
 172
 173/** Dummy receive in case qemu falls back to userland tap networking */
 174static ssize_t vhost_vdpa_receive(NetClientState *nc, const uint8_t *buf,
 175                                  size_t size)
 176{
 177    return 0;
 178}
 179
 180static NetClientInfo net_vhost_vdpa_info = {
 181        .type = NET_CLIENT_DRIVER_VHOST_VDPA,
 182        .size = sizeof(VhostVDPAState),
 183        .receive = vhost_vdpa_receive,
 184        .cleanup = vhost_vdpa_cleanup,
 185        .has_vnet_hdr = vhost_vdpa_has_vnet_hdr,
 186        .has_ufo = vhost_vdpa_has_ufo,
 187        .check_peer_type = vhost_vdpa_check_peer_type,
 188};
 189
 190static NetClientState *net_vhost_vdpa_init(NetClientState *peer,
 191                                           const char *device,
 192                                           const char *name,
 193                                           int vdpa_device_fd,
 194                                           int queue_pair_index,
 195                                           int nvqs,
 196                                           bool is_datapath)
 197{
 198    NetClientState *nc = NULL;
 199    VhostVDPAState *s;
 200    int ret = 0;
 201    assert(name);
 202    if (is_datapath) {
 203        nc = qemu_new_net_client(&net_vhost_vdpa_info, peer, device,
 204                                 name);
 205    } else {
 206        nc = qemu_new_net_control_client(&net_vhost_vdpa_info, peer,
 207                                         device, name);
 208    }
 209    snprintf(nc->info_str, sizeof(nc->info_str), TYPE_VHOST_VDPA);
 210    s = DO_UPCAST(VhostVDPAState, nc, nc);
 211
 212    s->vhost_vdpa.device_fd = vdpa_device_fd;
 213    s->vhost_vdpa.index = queue_pair_index;
 214    ret = vhost_vdpa_add(nc, (void *)&s->vhost_vdpa, queue_pair_index, nvqs);
 215    if (ret) {
 216        qemu_del_net_client(nc);
 217        return NULL;
 218    }
 219    return nc;
 220}
 221
 222static int vhost_vdpa_get_max_queue_pairs(int fd, int *has_cvq, Error **errp)
 223{
 224    unsigned long config_size = offsetof(struct vhost_vdpa_config, buf);
 225    g_autofree struct vhost_vdpa_config *config = NULL;
 226    __virtio16 *max_queue_pairs;
 227    uint64_t features;
 228    int ret;
 229
 230    ret = ioctl(fd, VHOST_GET_FEATURES, &features);
 231    if (ret) {
 232        error_setg(errp, "Fail to query features from vhost-vDPA device");
 233        return ret;
 234    }
 235
 236    if (features & (1 << VIRTIO_NET_F_CTRL_VQ)) {
 237        *has_cvq = 1;
 238    } else {
 239        *has_cvq = 0;
 240    }
 241
 242    if (features & (1 << VIRTIO_NET_F_MQ)) {
 243        config = g_malloc0(config_size + sizeof(*max_queue_pairs));
 244        config->off = offsetof(struct virtio_net_config, max_virtqueue_pairs);
 245        config->len = sizeof(*max_queue_pairs);
 246
 247        ret = ioctl(fd, VHOST_VDPA_GET_CONFIG, config);
 248        if (ret) {
 249            error_setg(errp, "Fail to get config from vhost-vDPA device");
 250            return -ret;
 251        }
 252
 253        max_queue_pairs = (__virtio16 *)&config->buf;
 254
 255        return lduw_le_p(max_queue_pairs);
 256    }
 257
 258    return 1;
 259}
 260
 261int net_init_vhost_vdpa(const Netdev *netdev, const char *name,
 262                        NetClientState *peer, Error **errp)
 263{
 264    const NetdevVhostVDPAOptions *opts;
 265    int vdpa_device_fd;
 266    NetClientState **ncs, *nc;
 267    int queue_pairs, i, has_cvq = 0;
 268
 269    assert(netdev->type == NET_CLIENT_DRIVER_VHOST_VDPA);
 270    opts = &netdev->u.vhost_vdpa;
 271    if (!opts->vhostdev) {
 272        error_setg(errp, "vdpa character device not specified with vhostdev");
 273        return -1;
 274    }
 275
 276    vdpa_device_fd = qemu_open(opts->vhostdev, O_RDWR, errp);
 277    if (vdpa_device_fd == -1) {
 278        return -errno;
 279    }
 280
 281    queue_pairs = vhost_vdpa_get_max_queue_pairs(vdpa_device_fd,
 282                                                 &has_cvq, errp);
 283    if (queue_pairs < 0) {
 284        qemu_close(vdpa_device_fd);
 285        return queue_pairs;
 286    }
 287
 288    ncs = g_malloc0(sizeof(*ncs) * queue_pairs);
 289
 290    for (i = 0; i < queue_pairs; i++) {
 291        ncs[i] = net_vhost_vdpa_init(peer, TYPE_VHOST_VDPA, name,
 292                                     vdpa_device_fd, i, 2, true);
 293        if (!ncs[i])
 294            goto err;
 295    }
 296
 297    if (has_cvq) {
 298        nc = net_vhost_vdpa_init(peer, TYPE_VHOST_VDPA, name,
 299                                 vdpa_device_fd, i, 1, false);
 300        if (!nc)
 301            goto err;
 302    }
 303
 304    g_free(ncs);
 305    return 0;
 306
 307err:
 308    if (i) {
 309        qemu_del_net_client(ncs[0]);
 310    }
 311    qemu_close(vdpa_device_fd);
 312    g_free(ncs);
 313
 314    return -1;
 315}
 316