qemu/tests/io-channel-helpers.c
<<
>>
Prefs
   1/*
   2 * QEMU I/O channel test helpers
   3 *
   4 * Copyright (c) 2015 Red Hat, Inc.
   5 *
   6 * This library is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU Lesser General Public
   8 * License as published by the Free Software Foundation; either
   9 * version 2 of the License, or (at your option) any later version.
  10 *
  11 * This library is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14 * Lesser General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU Lesser General Public
  17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  18 *
  19 */
  20
  21#include "qemu/osdep.h"
  22#include "io-channel-helpers.h"
  23#include "qapi/error.h"
  24
  25struct QIOChannelTest {
  26    QIOChannel *src;
  27    QIOChannel *dst;
  28    bool blocking;
  29    size_t len;
  30    size_t niov;
  31    char *input;
  32    struct iovec *inputv;
  33    char *output;
  34    struct iovec *outputv;
  35    Error *writeerr;
  36    Error *readerr;
  37};
  38
  39
  40static void test_skip_iovec(struct iovec **iov,
  41                            size_t *niov,
  42                            size_t skip,
  43                            struct iovec *old)
  44{
  45    size_t offset = 0;
  46    size_t i;
  47
  48    for (i = 0; i < *niov; i++) {
  49        if (skip < (*iov)[i].iov_len) {
  50            old->iov_len = (*iov)[i].iov_len;
  51            old->iov_base = (*iov)[i].iov_base;
  52
  53            (*iov)[i].iov_len -= skip;
  54            (*iov)[i].iov_base += skip;
  55            break;
  56        } else {
  57            skip -= (*iov)[i].iov_len;
  58
  59            if (i == 0 && old->iov_base) {
  60                (*iov)[i].iov_len = old->iov_len;
  61                (*iov)[i].iov_base = old->iov_base;
  62                old->iov_len = 0;
  63                old->iov_base = NULL;
  64            }
  65
  66            offset++;
  67        }
  68    }
  69
  70    *iov = *iov + offset;
  71    *niov -= offset;
  72}
  73
  74
  75/* This thread sends all data using iovecs */
  76static gpointer test_io_thread_writer(gpointer opaque)
  77{
  78    QIOChannelTest *data = opaque;
  79    struct iovec *iov = data->inputv;
  80    size_t niov = data->niov;
  81    struct iovec old = { 0 };
  82
  83    qio_channel_set_blocking(data->src, data->blocking, NULL);
  84
  85    while (niov) {
  86        ssize_t ret;
  87        ret = qio_channel_writev(data->src,
  88                                 iov,
  89                                 niov,
  90                                 &data->writeerr);
  91        if (ret == QIO_CHANNEL_ERR_BLOCK) {
  92            if (data->blocking) {
  93                error_setg(&data->writeerr,
  94                           "Unexpected I/O blocking");
  95                break;
  96            } else {
  97                qio_channel_wait(data->src,
  98                                 G_IO_OUT);
  99                continue;
 100            }
 101        } else if (ret < 0) {
 102            break;
 103        } else if (ret == 0) {
 104            error_setg(&data->writeerr,
 105                       "Unexpected zero length write");
 106            break;
 107        }
 108
 109        test_skip_iovec(&iov, &niov, ret, &old);
 110    }
 111
 112    return NULL;
 113}
 114
 115
 116/* This thread receives all data using iovecs */
 117static gpointer test_io_thread_reader(gpointer opaque)
 118{
 119    QIOChannelTest *data = opaque;
 120    struct iovec *iov = data->outputv;
 121    size_t niov = data->niov;
 122    struct iovec old = { 0 };
 123
 124    qio_channel_set_blocking(data->dst, data->blocking, NULL);
 125
 126    while (niov) {
 127        ssize_t ret;
 128
 129        ret = qio_channel_readv(data->dst,
 130                                iov,
 131                                niov,
 132                                &data->readerr);
 133
 134        if (ret == QIO_CHANNEL_ERR_BLOCK) {
 135            if (data->blocking) {
 136                error_setg(&data->readerr,
 137                           "Unexpected I/O blocking");
 138                break;
 139            } else {
 140                qio_channel_wait(data->dst,
 141                                 G_IO_IN);
 142                continue;
 143            }
 144        } else if (ret < 0) {
 145            break;
 146        } else if (ret == 0) {
 147            break;
 148        }
 149
 150        test_skip_iovec(&iov, &niov, ret, &old);
 151    }
 152
 153    return NULL;
 154}
 155
 156
 157QIOChannelTest *qio_channel_test_new(void)
 158{
 159    QIOChannelTest *data = g_new0(QIOChannelTest, 1);
 160    size_t i;
 161    size_t offset;
 162
 163
 164    /* We'll send 1 MB of data */
 165#define CHUNK_COUNT 250
 166#define CHUNK_LEN 4194
 167
 168    data->len = CHUNK_COUNT * CHUNK_LEN;
 169    data->input = g_new0(char, data->len);
 170    data->output = g_new0(gchar, data->len);
 171
 172    /* Fill input with a pattern */
 173    for (i = 0; i < data->len; i += CHUNK_LEN) {
 174        memset(data->input + i, (i / CHUNK_LEN), CHUNK_LEN);
 175    }
 176
 177    /* We'll split the data across a bunch of IO vecs */
 178    data->niov = CHUNK_COUNT;
 179    data->inputv = g_new0(struct iovec, data->niov);
 180    data->outputv = g_new0(struct iovec, data->niov);
 181
 182    for (i = 0, offset = 0; i < data->niov; i++, offset += CHUNK_LEN) {
 183        data->inputv[i].iov_base = data->input + offset;
 184        data->outputv[i].iov_base = data->output + offset;
 185        data->inputv[i].iov_len = CHUNK_LEN;
 186        data->outputv[i].iov_len = CHUNK_LEN;
 187    }
 188
 189    return data;
 190}
 191
 192void qio_channel_test_run_threads(QIOChannelTest *test,
 193                                  bool blocking,
 194                                  QIOChannel *src,
 195                                  QIOChannel *dst)
 196{
 197    GThread *reader, *writer;
 198
 199    test->src = src;
 200    test->dst = dst;
 201    test->blocking = blocking;
 202
 203    reader = g_thread_new("reader",
 204                          test_io_thread_reader,
 205                          test);
 206    writer = g_thread_new("writer",
 207                          test_io_thread_writer,
 208                          test);
 209
 210    g_thread_join(reader);
 211    g_thread_join(writer);
 212
 213    test->dst = test->src = NULL;
 214}
 215
 216
 217void qio_channel_test_run_writer(QIOChannelTest *test,
 218                                 QIOChannel *src)
 219{
 220    test->src = src;
 221    test_io_thread_writer(test);
 222    test->src = NULL;
 223}
 224
 225
 226void qio_channel_test_run_reader(QIOChannelTest *test,
 227                                 QIOChannel *dst)
 228{
 229    test->dst = dst;
 230    test_io_thread_reader(test);
 231    test->dst = NULL;
 232}
 233
 234
 235void qio_channel_test_validate(QIOChannelTest *test)
 236{
 237    g_assert(test->readerr == NULL);
 238    g_assert(test->writeerr == NULL);
 239    g_assert_cmpint(memcmp(test->input,
 240                           test->output,
 241                           test->len), ==, 0);
 242
 243    g_free(test->inputv);
 244    g_free(test->outputv);
 245    g_free(test->input);
 246    g_free(test->output);
 247    g_free(test);
 248}
 249