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