qemu/tests/qtest/virtio-net-test.c
<<
>>
Prefs
   1/*
   2 * QTest testcase for VirtIO NIC
   3 *
   4 * Copyright (c) 2014 SUSE LINUX Products GmbH
   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#include "qemu/osdep.h"
  11#include "libqtest-single.h"
  12#include "qemu/iov.h"
  13#include "qemu/module.h"
  14#include "qapi/qmp/qdict.h"
  15#include "hw/virtio/virtio-net.h"
  16#include "libqos/qgraph.h"
  17#include "libqos/virtio-net.h"
  18
  19#ifndef ETH_P_RARP
  20#define ETH_P_RARP 0x8035
  21#endif
  22
  23#define PCI_SLOT_HP             0x06
  24#define PCI_SLOT                0x04
  25
  26#define QVIRTIO_NET_TIMEOUT_US (30 * 1000 * 1000)
  27#define VNET_HDR_SIZE sizeof(struct virtio_net_hdr_mrg_rxbuf)
  28
  29#ifndef _WIN32
  30
  31static void rx_test(QVirtioDevice *dev,
  32                    QGuestAllocator *alloc, QVirtQueue *vq,
  33                    int socket)
  34{
  35    QTestState *qts = global_qtest;
  36    uint64_t req_addr;
  37    uint32_t free_head;
  38    char test[] = "TEST";
  39    char buffer[64];
  40    int len = htonl(sizeof(test));
  41    struct iovec iov[] = {
  42        {
  43            .iov_base = &len,
  44            .iov_len = sizeof(len),
  45        }, {
  46            .iov_base = test,
  47            .iov_len = sizeof(test),
  48        },
  49    };
  50    int ret;
  51
  52    req_addr = guest_alloc(alloc, 64);
  53
  54    free_head = qvirtqueue_add(qts, vq, req_addr, 64, true, false);
  55    qvirtqueue_kick(qts, dev, vq, free_head);
  56
  57    ret = iov_send(socket, iov, 2, 0, sizeof(len) + sizeof(test));
  58    g_assert_cmpint(ret, ==, sizeof(test) + sizeof(len));
  59
  60    qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
  61                           QVIRTIO_NET_TIMEOUT_US);
  62    memread(req_addr + VNET_HDR_SIZE, buffer, sizeof(test));
  63    g_assert_cmpstr(buffer, ==, "TEST");
  64
  65    guest_free(alloc, req_addr);
  66}
  67
  68static void tx_test(QVirtioDevice *dev,
  69                    QGuestAllocator *alloc, QVirtQueue *vq,
  70                    int socket)
  71{
  72    QTestState *qts = global_qtest;
  73    uint64_t req_addr;
  74    uint32_t free_head;
  75    uint32_t len;
  76    char buffer[64];
  77    int ret;
  78
  79    req_addr = guest_alloc(alloc, 64);
  80    memwrite(req_addr + VNET_HDR_SIZE, "TEST", 4);
  81
  82    free_head = qvirtqueue_add(qts, vq, req_addr, 64, false, false);
  83    qvirtqueue_kick(qts, dev, vq, free_head);
  84
  85    qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
  86                           QVIRTIO_NET_TIMEOUT_US);
  87    guest_free(alloc, req_addr);
  88
  89    ret = recv(socket, &len, sizeof(len), 0);
  90    g_assert_cmpint(ret, ==, sizeof(len));
  91    len = ntohl(len);
  92
  93    ret = recv(socket, buffer, len, 0);
  94    g_assert_cmpstr(buffer, ==, "TEST");
  95}
  96
  97static void rx_stop_cont_test(QVirtioDevice *dev,
  98                              QGuestAllocator *alloc, QVirtQueue *vq,
  99                              int socket)
 100{
 101    QTestState *qts = global_qtest;
 102    uint64_t req_addr;
 103    uint32_t free_head;
 104    char test[] = "TEST";
 105    char buffer[64];
 106    int len = htonl(sizeof(test));
 107    QDict *rsp;
 108    struct iovec iov[] = {
 109        {
 110            .iov_base = &len,
 111            .iov_len = sizeof(len),
 112        }, {
 113            .iov_base = test,
 114            .iov_len = sizeof(test),
 115        },
 116    };
 117    int ret;
 118
 119    req_addr = guest_alloc(alloc, 64);
 120
 121    free_head = qvirtqueue_add(qts, vq, req_addr, 64, true, false);
 122    qvirtqueue_kick(qts, dev, vq, free_head);
 123
 124    rsp = qmp("{ 'execute' : 'stop'}");
 125    qobject_unref(rsp);
 126
 127    ret = iov_send(socket, iov, 2, 0, sizeof(len) + sizeof(test));
 128    g_assert_cmpint(ret, ==, sizeof(test) + sizeof(len));
 129
 130    /* We could check the status, but this command is more importantly to
 131     * ensure the packet data gets queued in QEMU, before we do 'cont'.
 132     */
 133    rsp = qmp("{ 'execute' : 'query-status'}");
 134    qobject_unref(rsp);
 135    rsp = qmp("{ 'execute' : 'cont'}");
 136    qobject_unref(rsp);
 137
 138    qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
 139                           QVIRTIO_NET_TIMEOUT_US);
 140    memread(req_addr + VNET_HDR_SIZE, buffer, sizeof(test));
 141    g_assert_cmpstr(buffer, ==, "TEST");
 142
 143    guest_free(alloc, req_addr);
 144}
 145
 146static void send_recv_test(void *obj, void *data, QGuestAllocator *t_alloc)
 147{
 148    QVirtioNet *net_if = obj;
 149    QVirtioDevice *dev = net_if->vdev;
 150    QVirtQueue *rx = net_if->queues[0];
 151    QVirtQueue *tx = net_if->queues[1];
 152    int *sv = data;
 153
 154    rx_test(dev, t_alloc, rx, sv[0]);
 155    tx_test(dev, t_alloc, tx, sv[0]);
 156}
 157
 158static void stop_cont_test(void *obj, void *data, QGuestAllocator *t_alloc)
 159{
 160    QVirtioNet *net_if = obj;
 161    QVirtioDevice *dev = net_if->vdev;
 162    QVirtQueue *rx = net_if->queues[0];
 163    int *sv = data;
 164
 165    rx_stop_cont_test(dev, t_alloc, rx, sv[0]);
 166}
 167
 168static void hotplug(void *obj, void *data, QGuestAllocator *t_alloc)
 169{
 170    QVirtioPCIDevice *dev = obj;
 171    QTestState *qts = dev->pdev->bus->qts;
 172    const char *arch = qtest_get_arch();
 173
 174    if (dev->pdev->bus->not_hotpluggable) {
 175        g_test_skip("pci bus does not support hotplug");
 176        return;
 177    }
 178
 179    qtest_qmp_device_add(qts, "virtio-net-pci", "net1",
 180                         "{'addr': %s}", stringify(PCI_SLOT_HP));
 181
 182    if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
 183        qpci_unplug_acpi_device_test(qts, "net1", PCI_SLOT_HP);
 184    }
 185}
 186
 187static void announce_self(void *obj, void *data, QGuestAllocator *t_alloc)
 188{
 189    int *sv = data;
 190    char buffer[60];
 191    int len;
 192    QDict *rsp;
 193    int ret;
 194    uint16_t *proto = (uint16_t *)&buffer[12];
 195    size_t total_received = 0;
 196    uint64_t start, now, last_rxt, deadline;
 197
 198    /* Send a set of packets over a few second period */
 199    rsp = qmp("{ 'execute' : 'announce-self', "
 200                  " 'arguments': {"
 201                      " 'initial': 20, 'max': 100,"
 202                      " 'rounds': 300, 'step': 10, 'id': 'bob' } }");
 203    assert(!qdict_haskey(rsp, "error"));
 204    qobject_unref(rsp);
 205
 206    /* Catch the first packet and make sure it's a RARP */
 207    ret = recv(sv[0], &len, sizeof(len), 0);
 208    g_assert_cmpint(ret, ==,  sizeof(len));
 209    len = ntohl(len);
 210
 211    ret = recv(sv[0], buffer, len, 0);
 212    g_assert_cmpint(*proto, ==, htons(ETH_P_RARP));
 213
 214    /*
 215     * Stop the announcment by settings rounds to 0 on the
 216     * existing timer.
 217     */
 218    rsp = qmp("{ 'execute' : 'announce-self', "
 219                  " 'arguments': {"
 220                      " 'initial': 20, 'max': 100,"
 221                      " 'rounds': 0, 'step': 10, 'id': 'bob' } }");
 222    assert(!qdict_haskey(rsp, "error"));
 223    qobject_unref(rsp);
 224
 225    /* Now make sure the packets stop */
 226
 227    /* Times are in us */
 228    start = g_get_monotonic_time();
 229    /* 30 packets, max gap 100ms, * 4 for wiggle */
 230    deadline = start + 1000 * (100 * 30 * 4);
 231    last_rxt = start;
 232
 233    while (true) {
 234        int saved_err;
 235        ret = recv(sv[0], buffer, 60, MSG_DONTWAIT);
 236        saved_err = errno;
 237        now = g_get_monotonic_time();
 238        g_assert_cmpint(now, <, deadline);
 239
 240        if (ret >= 0) {
 241            if (ret) {
 242                last_rxt = now;
 243            }
 244            total_received += ret;
 245
 246            /* Check it's not spewing loads */
 247            g_assert_cmpint(total_received, <, 60 * 30 * 2);
 248        } else {
 249            g_assert_cmpint(saved_err, ==, EAGAIN);
 250
 251            /* 400ms, i.e. 4 worst case gaps */
 252            if ((now - last_rxt) > (1000 * 100 * 4)) {
 253                /* Nothings arrived for a while - must have stopped */
 254                break;
 255            };
 256
 257            /* 100ms */
 258            g_usleep(1000 * 100);
 259        }
 260    };
 261}
 262
 263static void virtio_net_test_cleanup(void *sockets)
 264{
 265    int *sv = sockets;
 266
 267    close(sv[0]);
 268    qos_invalidate_command_line();
 269    close(sv[1]);
 270    g_free(sv);
 271}
 272
 273static void *virtio_net_test_setup(GString *cmd_line, void *arg)
 274{
 275    int ret;
 276    int *sv = g_new(int, 2);
 277
 278    ret = socketpair(PF_UNIX, SOCK_STREAM, 0, sv);
 279    g_assert_cmpint(ret, !=, -1);
 280
 281    g_string_append_printf(cmd_line, " -netdev socket,fd=%d,id=hs0 ", sv[1]);
 282
 283    g_test_queue_destroy(virtio_net_test_cleanup, sv);
 284    return sv;
 285}
 286
 287#endif /* _WIN32 */
 288
 289static void large_tx(void *obj, void *data, QGuestAllocator *t_alloc)
 290{
 291    QVirtioNet *dev = obj;
 292    QVirtQueue *vq = dev->queues[1];
 293    uint64_t req_addr;
 294    uint32_t free_head;
 295    size_t alloc_size = (size_t)data / 64;
 296    QTestState *qts = global_qtest;
 297    int i;
 298
 299    /* Bypass the limitation by pointing several descriptors to a single
 300     * smaller area */
 301    req_addr = guest_alloc(t_alloc, alloc_size);
 302    free_head = qvirtqueue_add(qts, vq, req_addr, alloc_size, false, true);
 303
 304    for (i = 0; i < 64; i++) {
 305        qvirtqueue_add(qts, vq, req_addr, alloc_size, false, i != 63);
 306    }
 307    qvirtqueue_kick(qts, dev->vdev, vq, free_head);
 308
 309    qvirtio_wait_used_elem(qts, dev->vdev, vq, free_head, NULL,
 310                           QVIRTIO_NET_TIMEOUT_US);
 311    guest_free(t_alloc, req_addr);
 312}
 313
 314static void *virtio_net_test_setup_nosocket(GString *cmd_line, void *arg)
 315{
 316    g_string_append(cmd_line, " -netdev hubport,hubid=0,id=hs0 ");
 317    return arg;
 318}
 319
 320static void register_virtio_net_test(void)
 321{
 322    QOSGraphTestOptions opts = { 0 };
 323
 324#ifndef _WIN32
 325    opts.before = virtio_net_test_setup;
 326    qos_add_test("hotplug", "virtio-net-pci", hotplug, &opts);
 327    qos_add_test("basic", "virtio-net", send_recv_test, &opts);
 328    qos_add_test("rx_stop_cont", "virtio-net", stop_cont_test, &opts);
 329    qos_add_test("announce-self", "virtio-net", announce_self, &opts);
 330#endif
 331
 332    /* These tests do not need a loopback backend.  */
 333    opts.before = virtio_net_test_setup_nosocket;
 334    opts.arg = (gpointer)UINT_MAX;
 335    qos_add_test("large_tx/uint_max", "virtio-net", large_tx, &opts);
 336    opts.arg = (gpointer)NET_BUFSIZE;
 337    qos_add_test("large_tx/net_bufsize", "virtio-net", large_tx, &opts);
 338}
 339
 340libqos_init(register_virtio_net_test);
 341