qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

virtio-net-test.c (9730B)


      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 
     31 static 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 
     68 static 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 
     97 static 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 
    146 static 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 
    158 static 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 static 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 
    187 static 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 
    263 static 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 
    273 static 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 
    289 static 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 
    314 static 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 
    320 static 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 
    340 libqos_init(register_virtio_net_test);