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(GIOChannel *chan, 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, int 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;
 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    qmp_set_link(name, false, &err);
 239
 240    qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, net_vhost_user_event,
 241                             NULL, opaque, NULL, true);
 242
 243    if (err) {
 244        error_report_err(err);
 245    }
 246}
 247
 248static void net_vhost_user_event(void *opaque, int event)
 249{
 250    const char *name = opaque;
 251    NetClientState *ncs[MAX_QUEUE_NUM];
 252    NetVhostUserState *s;
 253    Chardev *chr;
 254    Error *err = NULL;
 255    int queues;
 256
 257    queues = qemu_find_net_clients_except(name, ncs,
 258                                          NET_CLIENT_DRIVER_NIC,
 259                                          MAX_QUEUE_NUM);
 260    assert(queues < MAX_QUEUE_NUM);
 261
 262    s = DO_UPCAST(NetVhostUserState, nc, ncs[0]);
 263    chr = qemu_chr_fe_get_driver(&s->chr);
 264    trace_vhost_user_event(chr->label, event);
 265    switch (event) {
 266    case CHR_EVENT_OPENED:
 267        if (vhost_user_start(queues, ncs, s->vhost_user) < 0) {
 268            qemu_chr_fe_disconnect(&s->chr);
 269            return;
 270        }
 271        s->watch = qemu_chr_fe_add_watch(&s->chr, G_IO_HUP,
 272                                         net_vhost_user_watch, s);
 273        qmp_set_link(name, true, &err);
 274        s->started = true;
 275        break;
 276    case CHR_EVENT_CLOSED:
 277        /* a close event may happen during a read/write, but vhost
 278         * code assumes the vhost_dev remains setup, so delay the
 279         * stop & clear to idle.
 280         * FIXME: better handle failure in vhost code, remove bh
 281         */
 282        if (s->watch) {
 283            AioContext *ctx = qemu_get_current_aio_context();
 284
 285            g_source_remove(s->watch);
 286            s->watch = 0;
 287            qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, NULL, NULL,
 288                                     NULL, NULL, false);
 289
 290            aio_bh_schedule_oneshot(ctx, chr_closed_bh, opaque);
 291        }
 292        break;
 293    }
 294
 295    if (err) {
 296        error_report_err(err);
 297    }
 298}
 299
 300static int net_vhost_user_init(NetClientState *peer, const char *device,
 301                               const char *name, Chardev *chr,
 302                               int queues)
 303{
 304    Error *err = NULL;
 305    NetClientState *nc, *nc0 = NULL;
 306    NetVhostUserState *s = NULL;
 307    VhostUserState *user;
 308    int i;
 309
 310    assert(name);
 311    assert(queues > 0);
 312
 313    user = g_new0(struct VhostUserState, 1);
 314    for (i = 0; i < queues; i++) {
 315        nc = qemu_new_net_client(&net_vhost_user_info, peer, device, name);
 316        snprintf(nc->info_str, sizeof(nc->info_str), "vhost-user%d to %s",
 317                 i, chr->label);
 318        nc->queue_index = i;
 319        if (!nc0) {
 320            nc0 = nc;
 321            s = DO_UPCAST(NetVhostUserState, nc, nc);
 322            if (!qemu_chr_fe_init(&s->chr, chr, &err) ||
 323                !vhost_user_init(user, &s->chr, &err)) {
 324                error_report_err(err);
 325                goto err;
 326            }
 327        }
 328        s = DO_UPCAST(NetVhostUserState, nc, nc);
 329        s->vhost_user = user;
 330    }
 331
 332    s = DO_UPCAST(NetVhostUserState, nc, nc0);
 333    do {
 334        if (qemu_chr_fe_wait_connected(&s->chr, &err) < 0) {
 335            error_report_err(err);
 336            goto err;
 337        }
 338        qemu_chr_fe_set_handlers(&s->chr, NULL, NULL,
 339                                 net_vhost_user_event, NULL, nc0->name, NULL,
 340                                 true);
 341    } while (!s->started);
 342
 343    assert(s->vhost_net);
 344
 345    return 0;
 346
 347err:
 348    if (user) {
 349        vhost_user_cleanup(user);
 350        g_free(user);
 351        if (s) {
 352            s->vhost_user = NULL;
 353        }
 354    }
 355    if (nc0) {
 356        qemu_del_net_client(nc0);
 357    }
 358
 359    return -1;
 360}
 361
 362static Chardev *net_vhost_claim_chardev(
 363    const NetdevVhostUserOptions *opts, Error **errp)
 364{
 365    Chardev *chr = qemu_chr_find(opts->chardev);
 366
 367    if (chr == NULL) {
 368        error_setg(errp, "chardev \"%s\" not found", opts->chardev);
 369        return NULL;
 370    }
 371
 372    if (!qemu_chr_has_feature(chr, QEMU_CHAR_FEATURE_RECONNECTABLE)) {
 373        error_setg(errp, "chardev \"%s\" is not reconnectable",
 374                   opts->chardev);
 375        return NULL;
 376    }
 377    if (!qemu_chr_has_feature(chr, QEMU_CHAR_FEATURE_FD_PASS)) {
 378        error_setg(errp, "chardev \"%s\" does not support FD passing",
 379                   opts->chardev);
 380        return NULL;
 381    }
 382
 383    return chr;
 384}
 385
 386static int net_vhost_check_net(void *opaque, QemuOpts *opts, Error **errp)
 387{
 388    const char *name = opaque;
 389    const char *driver, *netdev;
 390
 391    driver = qemu_opt_get(opts, "driver");
 392    netdev = qemu_opt_get(opts, "netdev");
 393
 394    if (!driver || !netdev) {
 395        return 0;
 396    }
 397
 398    if (strcmp(netdev, name) == 0 &&
 399        !g_str_has_prefix(driver, "virtio-net-")) {
 400        error_setg(errp, "vhost-user requires frontend driver virtio-net-*");
 401        return -1;
 402    }
 403
 404    return 0;
 405}
 406
 407int net_init_vhost_user(const Netdev *netdev, const char *name,
 408                        NetClientState *peer, Error **errp)
 409{
 410    int queues;
 411    const NetdevVhostUserOptions *vhost_user_opts;
 412    Chardev *chr;
 413
 414    assert(netdev->type == NET_CLIENT_DRIVER_VHOST_USER);
 415    vhost_user_opts = &netdev->u.vhost_user;
 416
 417    chr = net_vhost_claim_chardev(vhost_user_opts, errp);
 418    if (!chr) {
 419        return -1;
 420    }
 421
 422    /* verify net frontend */
 423    if (qemu_opts_foreach(qemu_find_opts("device"), net_vhost_check_net,
 424                          (char *)name, errp)) {
 425        return -1;
 426    }
 427
 428    queues = vhost_user_opts->has_queues ? vhost_user_opts->queues : 1;
 429    if (queues < 1 || queues > MAX_QUEUE_NUM) {
 430        error_setg(errp,
 431                   "vhost-user number of queues must be in range [1, %d]",
 432                   MAX_QUEUE_NUM);
 433        return -1;
 434    }
 435
 436    return net_vhost_user_init(peer, "vhost_user", name, chr, queues);
 437}
 438