cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

vhost-user.c (12443B)


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