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
 168#endif
 169
 170static void hotplug(void *obj, void *data, QGuestAllocator *t_alloc)
 171{
 172    QVirtioPCIDevice *dev = obj;
 173    QTestState *qts = dev->pdev->bus->qts;
 174    const char *arch = qtest_get_arch();
 175
 176    if (dev->pdev->bus->not_hotpluggable) {
 177        g_test_skip("pci bus does not support hotplug");
 178        return;
 179    }
 180
 181    qtest_qmp_device_add(qts, "virtio-net-pci", "net1",
 182                         "{'addr': %s}", stringify(PCI_SLOT_HP));
 183
 184    if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
 185        qpci_unplug_acpi_device_test(qts, "net1", PCI_SLOT_HP);
 186    }
 187}
 188
 189static void announce_self(void *obj, void *data, QGuestAllocator *t_alloc)
 190{
 191    int *sv = data;
 192    char buffer[60];
 193    int len;
 194    QDict *rsp;
 195    int ret;
 196    uint16_t *proto = (uint16_t *)&buffer[12];
 197    size_t total_received = 0;
 198    uint64_t start, now, last_rxt, deadline;
 199
 200    /* Send a set of packets over a few second period */
 201    rsp = qmp("{ 'execute' : 'announce-self', "
 202                  " 'arguments': {"
 203                      " 'initial': 20, 'max': 100,"
 204                      " 'rounds': 300, 'step': 10, 'id': 'bob' } }");
 205    assert(!qdict_haskey(rsp, "error"));
 206    qobject_unref(rsp);
 207
 208    /* Catch the first packet and make sure it's a RARP */
 209    ret = recv(sv[0], &len, sizeof(len), 0);
 210    g_assert_cmpint(ret, ==,  sizeof(len));
 211    len = ntohl(len);
 212
 213    ret = recv(sv[0], buffer, len, 0);
 214    g_assert_cmpint(*proto, ==, htons(ETH_P_RARP));
 215
 216    /*
 217     * Stop the announcment by settings rounds to 0 on the
 218     * existing timer.
 219     */
 220    rsp = qmp("{ 'execute' : 'announce-self', "
 221                  " 'arguments': {"
 222                      " 'initial': 20, 'max': 100,"
 223                      " 'rounds': 0, 'step': 10, 'id': 'bob' } }");
 224    assert(!qdict_haskey(rsp, "error"));
 225    qobject_unref(rsp);
 226
 227    /* Now make sure the packets stop */
 228
 229    /* Times are in us */
 230    start = g_get_monotonic_time();
 231    /* 30 packets, max gap 100ms, * 4 for wiggle */
 232    deadline = start + 1000 * (100 * 30 * 4);
 233    last_rxt = start;
 234
 235    while (true) {
 236        int saved_err;
 237        ret = recv(sv[0], buffer, 60, MSG_DONTWAIT);
 238        saved_err = errno;
 239        now = g_get_monotonic_time();
 240        g_assert_cmpint(now, <, deadline);
 241
 242        if (ret >= 0) {
 243            if (ret) {
 244                last_rxt = now;
 245            }
 246            total_received += ret;
 247
 248            /* Check it's not spewing loads */
 249            g_assert_cmpint(total_received, <, 60 * 30 * 2);
 250        } else {
 251            g_assert_cmpint(saved_err, ==, EAGAIN);
 252
 253            /* 400ms, i.e. 4 worst case gaps */
 254            if ((now - last_rxt) > (1000 * 100 * 4)) {
 255                /* Nothings arrived for a while - must have stopped */
 256                break;
 257            };
 258
 259            /* 100ms */
 260            g_usleep(1000 * 100);
 261        }
 262    };
 263}
 264
 265static void virtio_net_test_cleanup(void *sockets)
 266{
 267    int *sv = sockets;
 268
 269    close(sv[0]);
 270    qos_invalidate_command_line();
 271    close(sv[1]);
 272    g_free(sv);
 273}
 274
 275static void *virtio_net_test_setup(GString *cmd_line, void *arg)
 276{
 277    int ret;
 278    int *sv = g_new(int, 2);
 279
 280    ret = socketpair(PF_UNIX, SOCK_STREAM, 0, sv);
 281    g_assert_cmpint(ret, !=, -1);
 282
 283    g_string_append_printf(cmd_line, " -netdev socket,fd=%d,id=hs0 ", sv[1]);
 284
 285    g_test_queue_destroy(virtio_net_test_cleanup, sv);
 286    return sv;
 287}
 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 = {
 323        .before = virtio_net_test_setup,
 324    };
 325
 326    qos_add_test("hotplug", "virtio-net-pci", hotplug, &opts);
 327#ifndef _WIN32
 328    qos_add_test("basic", "virtio-net", send_recv_test, &opts);
 329    qos_add_test("rx_stop_cont", "virtio-net", stop_cont_test, &opts);
 330#endif
 331    qos_add_test("announce-self", "virtio-net", announce_self, &opts);
 332
 333    /* These tests do not need a loopback backend.  */
 334    opts.before = virtio_net_test_setup_nosocket;
 335    opts.arg = (gpointer)UINT_MAX;
 336    qos_add_test("large_tx/uint_max", "virtio-net", large_tx, &opts);
 337    opts.arg = (gpointer)NET_BUFSIZE;
 338    qos_add_test("large_tx/net_bufsize", "virtio-net", large_tx, &opts);
 339}
 340
 341libqos_init(register_virtio_net_test);
 342