qemu/net/vhost-user.c
<<
>>
Prefs
   1/*
   2 * vhost-user.c
   3 *
   4 * Copyright (c) 2013 Virtual Open Systems Sarl.
   5 *
   6 * This work is licensed under the terms of the GNU GPL, version 2 or later.
   7 * See the COPYING file in the top-level directory.
   8 *
   9 */
  10
  11#include "qemu/osdep.h"
  12#include "clients.h"
  13#include "net/vhost_net.h"
  14#include "net/vhost-user.h"
  15#include "hw/virtio/vhost-user.h"
  16#include "chardev/char-fe.h"
  17#include "qapi/error.h"
  18#include "qapi/qapi-commands-net.h"
  19#include "qemu/config-file.h"
  20#include "qemu/error-report.h"
  21#include "qemu/option.h"
  22#include "trace.h"
  23
  24typedef struct NetVhostUserState {
  25    NetClientState nc;
  26    CharBackend chr; /* only queue index 0 */
  27    VhostUserState *vhost_user;
  28    VHostNetState *vhost_net;
  29    guint watch;
  30    uint64_t acked_features;
  31    bool started;
  32} NetVhostUserState;
  33
  34VHostNetState *vhost_user_get_vhost_net(NetClientState *nc)
  35{
  36    NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc);
  37    assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER);
  38    return s->vhost_net;
  39}
  40
  41uint64_t vhost_user_get_acked_features(NetClientState *nc)
  42{
  43    NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc);
  44    assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER);
  45    return s->acked_features;
  46}
  47
  48void vhost_user_save_acked_features(NetClientState *nc)
  49{
  50    NetVhostUserState *s;
  51
  52    s = DO_UPCAST(NetVhostUserState, nc, nc);
  53    if (s->vhost_net) {
  54        uint64_t features = vhost_net_get_acked_features(s->vhost_net);
  55        if (features) {
  56            s->acked_features = features;
  57        }
  58    }
  59}
  60
  61static void vhost_user_stop(int queues, NetClientState *ncs[])
  62{
  63    int i;
  64    NetVhostUserState *s;
  65
  66    for (i = 0; i < queues; i++) {
  67        assert(ncs[i]->info->type == NET_CLIENT_DRIVER_VHOST_USER);
  68
  69        s = DO_UPCAST(NetVhostUserState, nc, ncs[i]);
  70
  71        if (s->vhost_net) {
  72            vhost_user_save_acked_features(ncs[i]);
  73            vhost_net_cleanup(s->vhost_net);
  74        }
  75    }
  76}
  77
  78static int vhost_user_start(int queues, NetClientState *ncs[],
  79                            VhostUserState *be)
  80{
  81    VhostNetOptions options;
  82    struct vhost_net *net = NULL;
  83    NetVhostUserState *s;
  84    int max_queues;
  85    int i;
  86
  87    options.backend_type = VHOST_BACKEND_TYPE_USER;
  88
  89    for (i = 0; i < queues; i++) {
  90        assert(ncs[i]->info->type == NET_CLIENT_DRIVER_VHOST_USER);
  91
  92        s = DO_UPCAST(NetVhostUserState, nc, ncs[i]);
  93
  94        options.net_backend = ncs[i];
  95        options.opaque      = be;
  96        options.busyloop_timeout = 0;
  97        options.nvqs = 2;
  98        net = vhost_net_init(&options);
  99        if (!net) {
 100            error_report("failed to init vhost_net for queue %d", i);
 101            goto err;
 102        }
 103
 104        if (i == 0) {
 105            max_queues = vhost_net_get_max_queues(net);
 106            if (queues > max_queues) {
 107                error_report("you are asking more queues than supported: %d",
 108                             max_queues);
 109                goto err;
 110            }
 111        }
 112
 113        if (s->vhost_net) {
 114            vhost_net_cleanup(s->vhost_net);
 115            g_free(s->vhost_net);
 116        }
 117        s->vhost_net = net;
 118    }
 119
 120    return 0;
 121
 122err:
 123    if (net) {
 124        vhost_net_cleanup(net);
 125        g_free(net);
 126    }
 127    vhost_user_stop(i, ncs);
 128    return -1;
 129}
 130
 131static ssize_t vhost_user_receive(NetClientState *nc, const uint8_t *buf,
 132                                  size_t size)
 133{
 134    /* In case of RARP (message size is 60) notify backup to send a fake RARP.
 135       This fake RARP will be sent by backend only for guest
 136       without GUEST_ANNOUNCE capability.
 137     */
 138    if (size == 60) {
 139        NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc);
 140        int r;
 141        static int display_rarp_failure = 1;
 142        char mac_addr[6];
 143
 144        /* extract guest mac address from the RARP message */
 145        memcpy(mac_addr, &buf[6], 6);
 146
 147        r = vhost_net_notify_migration_done(s->vhost_net, mac_addr);
 148
 149        if ((r != 0) && (display_rarp_failure)) {
 150            fprintf(stderr,
 151                    "Vhost user backend fails to broadcast fake RARP\n");
 152            fflush(stderr);
 153            display_rarp_failure = 0;
 154        }
 155    }
 156
 157    return size;
 158}
 159
 160static void net_vhost_user_cleanup(NetClientState *nc)
 161{
 162    NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc);
 163
 164    if (s->vhost_net) {
 165        vhost_net_cleanup(s->vhost_net);
 166        g_free(s->vhost_net);
 167        s->vhost_net = NULL;
 168    }
 169    if (nc->queue_index == 0) {
 170        if (s->watch) {
 171            g_source_remove(s->watch);
 172            s->watch = 0;
 173        }
 174        qemu_chr_fe_deinit(&s->chr, true);
 175        if (s->vhost_user) {
 176            vhost_user_cleanup(s->vhost_user);
 177            g_free(s->vhost_user);
 178            s->vhost_user = NULL;
 179        }
 180    }
 181
 182    qemu_purge_queued_packets(nc);
 183}
 184
 185static int vhost_user_set_vnet_endianness(NetClientState *nc,
 186                                          bool enable)
 187{
 188    /* Nothing to do.  If the server supports
 189     * VHOST_USER_PROTOCOL_F_CROSS_ENDIAN, it will get the
 190     * vnet header endianness from there.  If it doesn't, negotiation
 191     * fails.
 192     */
 193    return 0;
 194}
 195
 196static bool vhost_user_has_vnet_hdr(NetClientState *nc)
 197{
 198    assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER);
 199
 200    return true;
 201}
 202
 203static bool vhost_user_has_ufo(NetClientState *nc)
 204{
 205    assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER);
 206
 207    return true;
 208}
 209
 210static bool vhost_user_check_peer_type(NetClientState *nc, ObjectClass *oc,
 211                                       Error **errp)
 212{
 213    const char *driver = object_class_get_name(oc);
 214
 215    if (!g_str_has_prefix(driver, "virtio-net-")) {
 216        error_setg(errp, "vhost-user requires frontend driver virtio-net-*");
 217        return false;
 218    }
 219
 220    return true;
 221}
 222
 223static NetClientInfo net_vhost_user_info = {
 224        .type = NET_CLIENT_DRIVER_VHOST_USER,
 225        .size = sizeof(NetVhostUserState),
 226        .receive = vhost_user_receive,
 227        .cleanup = net_vhost_user_cleanup,
 228        .has_vnet_hdr = vhost_user_has_vnet_hdr,
 229        .has_ufo = vhost_user_has_ufo,
 230        .set_vnet_be = vhost_user_set_vnet_endianness,
 231        .set_vnet_le = vhost_user_set_vnet_endianness,
 232        .check_peer_type = vhost_user_check_peer_type,
 233};
 234
 235static gboolean net_vhost_user_watch(void *do_not_use, GIOCondition cond,
 236                                     void *opaque)
 237{
 238    NetVhostUserState *s = opaque;
 239
 240    qemu_chr_fe_disconnect(&s->chr);
 241
 242    return TRUE;
 243}
 244
 245static void net_vhost_user_event(void *opaque, QEMUChrEvent event);
 246
 247static void chr_closed_bh(void *opaque)
 248{
 249    const char *name = opaque;
 250    NetClientState *ncs[MAX_QUEUE_NUM];
 251    NetVhostUserState *s;
 252    Error *err = NULL;
 253    int queues, i;
 254
 255    queues = qemu_find_net_clients_except(name, ncs,
 256                                          NET_CLIENT_DRIVER_NIC,
 257                                          MAX_QUEUE_NUM);
 258    assert(queues < MAX_QUEUE_NUM);
 259
 260    s = DO_UPCAST(NetVhostUserState, nc, ncs[0]);
 261
 262    for (i = queues -1; i >= 0; i--) {
 263        vhost_user_save_acked_features(ncs[i]);
 264    }
 265
 266    qmp_set_link(name, false, &err);
 267
 268    qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, net_vhost_user_event,
 269                             NULL, opaque, NULL, true);
 270
 271    if (err) {
 272        error_report_err(err);
 273    }
 274}
 275
 276static void net_vhost_user_event(void *opaque, QEMUChrEvent event)
 277{
 278    const char *name = opaque;
 279    NetClientState *ncs[MAX_QUEUE_NUM];
 280    NetVhostUserState *s;
 281    Chardev *chr;
 282    Error *err = NULL;
 283    int queues;
 284
 285    queues = qemu_find_net_clients_except(name, ncs,
 286                                          NET_CLIENT_DRIVER_NIC,
 287                                          MAX_QUEUE_NUM);
 288    assert(queues < MAX_QUEUE_NUM);
 289
 290    s = DO_UPCAST(NetVhostUserState, nc, ncs[0]);
 291    chr = qemu_chr_fe_get_driver(&s->chr);
 292    trace_vhost_user_event(chr->label, event);
 293    switch (event) {
 294    case CHR_EVENT_OPENED:
 295        if (vhost_user_start(queues, ncs, s->vhost_user) < 0) {
 296            qemu_chr_fe_disconnect(&s->chr);
 297            return;
 298        }
 299        s->watch = qemu_chr_fe_add_watch(&s->chr, G_IO_HUP,
 300                                         net_vhost_user_watch, s);
 301        qmp_set_link(name, true, &err);
 302        s->started = true;
 303        break;
 304    case CHR_EVENT_CLOSED:
 305        /* a close event may happen during a read/write, but vhost
 306         * code assumes the vhost_dev remains setup, so delay the
 307         * stop & clear to idle.
 308         * FIXME: better handle failure in vhost code, remove bh
 309         */
 310        if (s->watch) {
 311            AioContext *ctx = qemu_get_current_aio_context();
 312
 313            g_source_remove(s->watch);
 314            s->watch = 0;
 315            qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, NULL, NULL,
 316                                     NULL, NULL, false);
 317
 318            aio_bh_schedule_oneshot(ctx, chr_closed_bh, opaque);
 319        }
 320        break;
 321    case CHR_EVENT_BREAK:
 322    case CHR_EVENT_MUX_IN:
 323    case CHR_EVENT_MUX_OUT:
 324        /* Ignore */
 325        break;
 326    }
 327
 328    if (err) {
 329        error_report_err(err);
 330    }
 331}
 332
 333static int net_vhost_user_init(NetClientState *peer, const char *device,
 334                               const char *name, Chardev *chr,
 335                               int queues)
 336{
 337    Error *err = NULL;
 338    NetClientState *nc, *nc0 = NULL;
 339    NetVhostUserState *s = NULL;
 340    VhostUserState *user;
 341    int i;
 342
 343    assert(name);
 344    assert(queues > 0);
 345
 346    user = g_new0(struct VhostUserState, 1);
 347    for (i = 0; i < queues; i++) {
 348        nc = qemu_new_net_client(&net_vhost_user_info, peer, device, name);
 349        qemu_set_info_str(nc, "vhost-user%d to %s", i, chr->label);
 350        nc->queue_index = i;
 351        if (!nc0) {
 352            nc0 = nc;
 353            s = DO_UPCAST(NetVhostUserState, nc, nc);
 354            if (!qemu_chr_fe_init(&s->chr, chr, &err) ||
 355                !vhost_user_init(user, &s->chr, &err)) {
 356                error_report_err(err);
 357                goto err;
 358            }
 359        }
 360        s = DO_UPCAST(NetVhostUserState, nc, nc);
 361        s->vhost_user = user;
 362    }
 363
 364    s = DO_UPCAST(NetVhostUserState, nc, nc0);
 365    do {
 366        if (qemu_chr_fe_wait_connected(&s->chr, &err) < 0) {
 367            error_report_err(err);
 368            goto err;
 369        }
 370        qemu_chr_fe_set_handlers(&s->chr, NULL, NULL,
 371                                 net_vhost_user_event, NULL, nc0->name, NULL,
 372                                 true);
 373    } while (!s->started);
 374
 375    assert(s->vhost_net);
 376
 377    return 0;
 378
 379err:
 380    if (user) {
 381        vhost_user_cleanup(user);
 382        g_free(user);
 383        if (s) {
 384            s->vhost_user = NULL;
 385        }
 386    }
 387    if (nc0) {
 388        qemu_del_net_client(nc0);
 389    }
 390
 391    return -1;
 392}
 393
 394static Chardev *net_vhost_claim_chardev(
 395    const NetdevVhostUserOptions *opts, Error **errp)
 396{
 397    Chardev *chr = qemu_chr_find(opts->chardev);
 398
 399    if (chr == NULL) {
 400        error_setg(errp, "chardev \"%s\" not found", opts->chardev);
 401        return NULL;
 402    }
 403
 404    if (!qemu_chr_has_feature(chr, QEMU_CHAR_FEATURE_RECONNECTABLE)) {
 405        error_setg(errp, "chardev \"%s\" is not reconnectable",
 406                   opts->chardev);
 407        return NULL;
 408    }
 409    if (!qemu_chr_has_feature(chr, QEMU_CHAR_FEATURE_FD_PASS)) {
 410        error_setg(errp, "chardev \"%s\" does not support FD passing",
 411                   opts->chardev);
 412        return NULL;
 413    }
 414
 415    return chr;
 416}
 417
 418int net_init_vhost_user(const Netdev *netdev, const char *name,
 419                        NetClientState *peer, Error **errp)
 420{
 421    int queues;
 422    const NetdevVhostUserOptions *vhost_user_opts;
 423    Chardev *chr;
 424
 425    assert(netdev->type == NET_CLIENT_DRIVER_VHOST_USER);
 426    vhost_user_opts = &netdev->u.vhost_user;
 427
 428    chr = net_vhost_claim_chardev(vhost_user_opts, errp);
 429    if (!chr) {
 430        return -1;
 431    }
 432
 433    queues = vhost_user_opts->has_queues ? vhost_user_opts->queues : 1;
 434    if (queues < 1 || queues > MAX_QUEUE_NUM) {
 435        error_setg(errp,
 436                   "vhost-user number of queues must be in range [1, %d]",
 437                   MAX_QUEUE_NUM);
 438        return -1;
 439    }
 440
 441    return net_vhost_user_init(peer, "vhost_user", name, chr, queues);
 442}
 443