qemu/tests/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.h"
  12#include "qemu-common.h"
  13#include "qemu/sockets.h"
  14#include "qemu/iov.h"
  15#include "libqos/libqos-pc.h"
  16#include "libqos/libqos-spapr.h"
  17#include "libqos/virtio.h"
  18#include "libqos/virtio-pci.h"
  19#include "qapi/qmp/qdict.h"
  20#include "qemu/bswap.h"
  21#include "hw/virtio/virtio-net.h"
  22#include "standard-headers/linux/virtio_ids.h"
  23#include "standard-headers/linux/virtio_ring.h"
  24
  25#define PCI_SLOT_HP             0x06
  26#define PCI_SLOT                0x04
  27
  28#define QVIRTIO_NET_TIMEOUT_US (30 * 1000 * 1000)
  29#define VNET_HDR_SIZE sizeof(struct virtio_net_hdr_mrg_rxbuf)
  30
  31static void test_end(void)
  32{
  33    qtest_end();
  34}
  35
  36#ifndef _WIN32
  37
  38static QVirtioPCIDevice *virtio_net_pci_init(QPCIBus *bus, int slot)
  39{
  40    QVirtioPCIDevice *dev;
  41
  42    dev = qvirtio_pci_device_find(bus, VIRTIO_ID_NET);
  43    g_assert(dev != NULL);
  44    g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_NET);
  45
  46    qvirtio_pci_device_enable(dev);
  47    qvirtio_reset(&dev->vdev);
  48    qvirtio_set_acknowledge(&dev->vdev);
  49    qvirtio_set_driver(&dev->vdev);
  50
  51    return dev;
  52}
  53
  54GCC_FMT_ATTR(1, 2)
  55static QOSState *pci_test_start(const char *cmd, ...)
  56{
  57    QOSState *qs;
  58    va_list ap;
  59    const char *arch = qtest_get_arch();
  60
  61    if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
  62        va_start(ap, cmd);
  63        qs = qtest_pc_vboot(cmd, ap);
  64        va_end(ap);
  65    } else if (strcmp(arch, "ppc64") == 0) {
  66        va_start(ap, cmd);
  67        qs = qtest_spapr_vboot(cmd, ap);
  68        va_end(ap);
  69    } else {
  70        g_printerr("virtio-net tests are only available on x86 or ppc64\n");
  71        exit(EXIT_FAILURE);
  72    }
  73    global_qtest = qs->qts;
  74    return qs;
  75}
  76
  77static void driver_init(QVirtioDevice *dev)
  78{
  79    uint32_t features;
  80
  81    features = qvirtio_get_features(dev);
  82    features = features & ~(QVIRTIO_F_BAD_FEATURE |
  83                            (1u << VIRTIO_RING_F_INDIRECT_DESC) |
  84                            (1u << VIRTIO_RING_F_EVENT_IDX));
  85    qvirtio_set_features(dev, features);
  86
  87    qvirtio_set_driver_ok(dev);
  88}
  89
  90static void rx_test(QVirtioDevice *dev,
  91                    QGuestAllocator *alloc, QVirtQueue *vq,
  92                    int socket)
  93{
  94    uint64_t req_addr;
  95    uint32_t free_head;
  96    char test[] = "TEST";
  97    char buffer[64];
  98    int len = htonl(sizeof(test));
  99    struct iovec iov[] = {
 100        {
 101            .iov_base = &len,
 102            .iov_len = sizeof(len),
 103        }, {
 104            .iov_base = test,
 105            .iov_len = sizeof(test),
 106        },
 107    };
 108    int ret;
 109
 110    req_addr = guest_alloc(alloc, 64);
 111
 112    free_head = qvirtqueue_add(vq, req_addr, 64, true, false);
 113    qvirtqueue_kick(dev, vq, free_head);
 114
 115    ret = iov_send(socket, iov, 2, 0, sizeof(len) + sizeof(test));
 116    g_assert_cmpint(ret, ==, sizeof(test) + sizeof(len));
 117
 118    qvirtio_wait_used_elem(dev, vq, free_head, NULL, QVIRTIO_NET_TIMEOUT_US);
 119    memread(req_addr + VNET_HDR_SIZE, buffer, sizeof(test));
 120    g_assert_cmpstr(buffer, ==, "TEST");
 121
 122    guest_free(alloc, req_addr);
 123}
 124
 125static void tx_test(QVirtioDevice *dev,
 126                    QGuestAllocator *alloc, QVirtQueue *vq,
 127                    int socket)
 128{
 129    uint64_t req_addr;
 130    uint32_t free_head;
 131    uint32_t len;
 132    char buffer[64];
 133    int ret;
 134
 135    req_addr = guest_alloc(alloc, 64);
 136    memwrite(req_addr + VNET_HDR_SIZE, "TEST", 4);
 137
 138    free_head = qvirtqueue_add(vq, req_addr, 64, false, false);
 139    qvirtqueue_kick(dev, vq, free_head);
 140
 141    qvirtio_wait_used_elem(dev, vq, free_head, NULL, QVIRTIO_NET_TIMEOUT_US);
 142    guest_free(alloc, req_addr);
 143
 144    ret = qemu_recv(socket, &len, sizeof(len), 0);
 145    g_assert_cmpint(ret, ==, sizeof(len));
 146    len = ntohl(len);
 147
 148    ret = qemu_recv(socket, buffer, len, 0);
 149    g_assert_cmpstr(buffer, ==, "TEST");
 150}
 151
 152static void rx_stop_cont_test(QVirtioDevice *dev,
 153                              QGuestAllocator *alloc, QVirtQueue *vq,
 154                              int socket)
 155{
 156    uint64_t req_addr;
 157    uint32_t free_head;
 158    char test[] = "TEST";
 159    char buffer[64];
 160    int len = htonl(sizeof(test));
 161    QDict *rsp;
 162    struct iovec iov[] = {
 163        {
 164            .iov_base = &len,
 165            .iov_len = sizeof(len),
 166        }, {
 167            .iov_base = test,
 168            .iov_len = sizeof(test),
 169        },
 170    };
 171    int ret;
 172
 173    req_addr = guest_alloc(alloc, 64);
 174
 175    free_head = qvirtqueue_add(vq, req_addr, 64, true, false);
 176    qvirtqueue_kick(dev, vq, free_head);
 177
 178    rsp = qmp("{ 'execute' : 'stop'}");
 179    qobject_unref(rsp);
 180
 181    ret = iov_send(socket, iov, 2, 0, sizeof(len) + sizeof(test));
 182    g_assert_cmpint(ret, ==, sizeof(test) + sizeof(len));
 183
 184    /* We could check the status, but this command is more importantly to
 185     * ensure the packet data gets queued in QEMU, before we do 'cont'.
 186     */
 187    rsp = qmp("{ 'execute' : 'query-status'}");
 188    qobject_unref(rsp);
 189    rsp = qmp("{ 'execute' : 'cont'}");
 190    qobject_unref(rsp);
 191
 192    qvirtio_wait_used_elem(dev, vq, free_head, NULL, QVIRTIO_NET_TIMEOUT_US);
 193    memread(req_addr + VNET_HDR_SIZE, buffer, sizeof(test));
 194    g_assert_cmpstr(buffer, ==, "TEST");
 195
 196    guest_free(alloc, req_addr);
 197}
 198
 199static void send_recv_test(QVirtioDevice *dev,
 200                           QGuestAllocator *alloc, QVirtQueue *rvq,
 201                           QVirtQueue *tvq, int socket)
 202{
 203    rx_test(dev, alloc, rvq, socket);
 204    tx_test(dev, alloc, tvq, socket);
 205}
 206
 207static void stop_cont_test(QVirtioDevice *dev,
 208                           QGuestAllocator *alloc, QVirtQueue *rvq,
 209                           QVirtQueue *tvq, int socket)
 210{
 211    rx_stop_cont_test(dev, alloc, rvq, socket);
 212}
 213
 214static void pci_basic(gconstpointer data)
 215{
 216    QVirtioPCIDevice *dev;
 217    QOSState *qs;
 218    QVirtQueuePCI *tx, *rx;
 219    void (*func) (QVirtioDevice *dev,
 220                  QGuestAllocator *alloc,
 221                  QVirtQueue *rvq,
 222                  QVirtQueue *tvq,
 223                  int socket) = data;
 224    int sv[2], ret;
 225
 226    ret = socketpair(PF_UNIX, SOCK_STREAM, 0, sv);
 227    g_assert_cmpint(ret, !=, -1);
 228
 229    qs = pci_test_start("-netdev socket,fd=%d,id=hs0 -device "
 230                        "virtio-net-pci,netdev=hs0", sv[1]);
 231    dev = virtio_net_pci_init(qs->pcibus, PCI_SLOT);
 232
 233    rx = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0);
 234    tx = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 1);
 235
 236    driver_init(&dev->vdev);
 237    func(&dev->vdev, qs->alloc, &rx->vq, &tx->vq, sv[0]);
 238
 239    /* End test */
 240    close(sv[0]);
 241    qvirtqueue_cleanup(dev->vdev.bus, &tx->vq, qs->alloc);
 242    qvirtqueue_cleanup(dev->vdev.bus, &rx->vq, qs->alloc);
 243    qvirtio_pci_device_disable(dev);
 244    g_free(dev->pdev);
 245    g_free(dev);
 246    qtest_shutdown(qs);
 247}
 248
 249static void large_tx(gconstpointer data)
 250{
 251    QVirtioPCIDevice *dev;
 252    QOSState *qs;
 253    QVirtQueuePCI *tx, *rx;
 254    QVirtQueue *vq;
 255    uint64_t req_addr;
 256    uint32_t free_head;
 257    size_t alloc_size = (size_t)data / 64;
 258    int i;
 259
 260    qs = pci_test_start("-netdev hubport,id=hp0,hubid=0 "
 261                        "-device virtio-net-pci,netdev=hp0");
 262    dev = virtio_net_pci_init(qs->pcibus, PCI_SLOT);
 263
 264    rx = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0);
 265    tx = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 1);
 266
 267    driver_init(&dev->vdev);
 268    vq = &tx->vq;
 269
 270    /* Bypass the limitation by pointing several descriptors to a single
 271     * smaller area */
 272    req_addr = guest_alloc(qs->alloc, alloc_size);
 273    free_head = qvirtqueue_add(vq, req_addr, alloc_size, false, true);
 274
 275    for (i = 0; i < 64; i++) {
 276        qvirtqueue_add(vq, req_addr, alloc_size, false, i != 63);
 277    }
 278    qvirtqueue_kick(&dev->vdev, vq, free_head);
 279
 280    qvirtio_wait_used_elem(&dev->vdev, vq, free_head, NULL,
 281                           QVIRTIO_NET_TIMEOUT_US);
 282
 283    qvirtqueue_cleanup(dev->vdev.bus, &tx->vq, qs->alloc);
 284    qvirtqueue_cleanup(dev->vdev.bus, &rx->vq, qs->alloc);
 285    qvirtio_pci_device_disable(dev);
 286    g_free(dev->pdev);
 287    g_free(dev);
 288    qtest_shutdown(qs);
 289}
 290#endif
 291
 292static void hotplug(void)
 293{
 294    const char *arch = qtest_get_arch();
 295
 296    qtest_start("-device virtio-net-pci");
 297
 298    qtest_qmp_device_add("virtio-net-pci", "net1",
 299                         "{'addr': %s}", stringify(PCI_SLOT_HP));
 300
 301    if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
 302        qpci_unplug_acpi_device_test("net1", PCI_SLOT_HP);
 303    }
 304
 305    test_end();
 306}
 307
 308int main(int argc, char **argv)
 309{
 310    g_test_init(&argc, &argv, NULL);
 311#ifndef _WIN32
 312    qtest_add_data_func("/virtio/net/pci/basic", send_recv_test, pci_basic);
 313    qtest_add_data_func("/virtio/net/pci/rx_stop_cont",
 314                        stop_cont_test, pci_basic);
 315    qtest_add_data_func("/virtio/net/pci/large_tx_uint_max",
 316                        (gconstpointer)UINT_MAX, large_tx);
 317    qtest_add_data_func("/virtio/net/pci/large_tx_net_bufsize",
 318                        (gconstpointer)NET_BUFSIZE, large_tx);
 319#endif
 320    qtest_add_func("/virtio/net/pci/hotplug", hotplug);
 321
 322    return g_test_run();
 323}
 324