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