1559607eaSDaniel P. Berrange /* 2559607eaSDaniel P. Berrange * QEMU I/O channel test helpers 3559607eaSDaniel P. Berrange * 4559607eaSDaniel P. Berrange * Copyright (c) 2015 Red Hat, Inc. 5559607eaSDaniel P. Berrange * 6559607eaSDaniel P. Berrange * This library is free software; you can redistribute it and/or 7559607eaSDaniel P. Berrange * modify it under the terms of the GNU Lesser General Public 8559607eaSDaniel P. Berrange * License as published by the Free Software Foundation; either 9559607eaSDaniel P. Berrange * version 2 of the License, or (at your option) any later version. 10559607eaSDaniel P. Berrange * 11559607eaSDaniel P. Berrange * This library is distributed in the hope that it will be useful, 12559607eaSDaniel P. Berrange * but WITHOUT ANY WARRANTY; without even the implied warranty of 13559607eaSDaniel P. Berrange * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14559607eaSDaniel P. Berrange * Lesser General Public License for more details. 15559607eaSDaniel P. Berrange * 16559607eaSDaniel P. Berrange * You should have received a copy of the GNU Lesser General Public 17559607eaSDaniel P. Berrange * License along with this library; if not, see <http://www.gnu.org/licenses/>. 18559607eaSDaniel P. Berrange * 19559607eaSDaniel P. Berrange */ 20559607eaSDaniel P. Berrange 21*681c28a3SPeter Maydell #include "qemu/osdep.h" 22559607eaSDaniel P. Berrange #include "io-channel-helpers.h" 23559607eaSDaniel P. Berrange 24559607eaSDaniel P. Berrange struct QIOChannelTest { 25559607eaSDaniel P. Berrange QIOChannel *src; 26559607eaSDaniel P. Berrange QIOChannel *dst; 27559607eaSDaniel P. Berrange bool blocking; 28559607eaSDaniel P. Berrange size_t len; 29559607eaSDaniel P. Berrange size_t niov; 30559607eaSDaniel P. Berrange char *input; 31559607eaSDaniel P. Berrange struct iovec *inputv; 32559607eaSDaniel P. Berrange char *output; 33559607eaSDaniel P. Berrange struct iovec *outputv; 34559607eaSDaniel P. Berrange Error *writeerr; 35559607eaSDaniel P. Berrange Error *readerr; 36559607eaSDaniel P. Berrange }; 37559607eaSDaniel P. Berrange 38559607eaSDaniel P. Berrange 39559607eaSDaniel P. Berrange static void test_skip_iovec(struct iovec **iov, 40559607eaSDaniel P. Berrange size_t *niov, 41559607eaSDaniel P. Berrange size_t skip, 42559607eaSDaniel P. Berrange struct iovec *old) 43559607eaSDaniel P. Berrange { 44559607eaSDaniel P. Berrange size_t offset = 0; 45559607eaSDaniel P. Berrange size_t i; 46559607eaSDaniel P. Berrange 47559607eaSDaniel P. Berrange for (i = 0; i < *niov; i++) { 48559607eaSDaniel P. Berrange if (skip < (*iov)[i].iov_len) { 49559607eaSDaniel P. Berrange old->iov_len = (*iov)[i].iov_len; 50559607eaSDaniel P. Berrange old->iov_base = (*iov)[i].iov_base; 51559607eaSDaniel P. Berrange 52559607eaSDaniel P. Berrange (*iov)[i].iov_len -= skip; 53559607eaSDaniel P. Berrange (*iov)[i].iov_base += skip; 54559607eaSDaniel P. Berrange break; 55559607eaSDaniel P. Berrange } else { 56559607eaSDaniel P. Berrange skip -= (*iov)[i].iov_len; 57559607eaSDaniel P. Berrange 58559607eaSDaniel P. Berrange if (i == 0 && old->iov_base) { 59559607eaSDaniel P. Berrange (*iov)[i].iov_len = old->iov_len; 60559607eaSDaniel P. Berrange (*iov)[i].iov_base = old->iov_base; 61559607eaSDaniel P. Berrange old->iov_len = 0; 62559607eaSDaniel P. Berrange old->iov_base = NULL; 63559607eaSDaniel P. Berrange } 64559607eaSDaniel P. Berrange 65559607eaSDaniel P. Berrange offset++; 66559607eaSDaniel P. Berrange } 67559607eaSDaniel P. Berrange } 68559607eaSDaniel P. Berrange 69559607eaSDaniel P. Berrange *iov = *iov + offset; 70559607eaSDaniel P. Berrange *niov -= offset; 71559607eaSDaniel P. Berrange } 72559607eaSDaniel P. Berrange 73559607eaSDaniel P. Berrange 74559607eaSDaniel P. Berrange /* This thread sends all data using iovecs */ 75559607eaSDaniel P. Berrange static gpointer test_io_thread_writer(gpointer opaque) 76559607eaSDaniel P. Berrange { 77559607eaSDaniel P. Berrange QIOChannelTest *data = opaque; 78559607eaSDaniel P. Berrange struct iovec *iov = data->inputv; 79559607eaSDaniel P. Berrange size_t niov = data->niov; 80559607eaSDaniel P. Berrange struct iovec old = { 0 }; 81559607eaSDaniel P. Berrange 82559607eaSDaniel P. Berrange qio_channel_set_blocking(data->src, data->blocking, NULL); 83559607eaSDaniel P. Berrange 84559607eaSDaniel P. Berrange while (niov) { 85559607eaSDaniel P. Berrange ssize_t ret; 86559607eaSDaniel P. Berrange ret = qio_channel_writev(data->src, 87559607eaSDaniel P. Berrange iov, 88559607eaSDaniel P. Berrange niov, 89559607eaSDaniel P. Berrange &data->writeerr); 90559607eaSDaniel P. Berrange if (ret == QIO_CHANNEL_ERR_BLOCK) { 91559607eaSDaniel P. Berrange if (data->blocking) { 92559607eaSDaniel P. Berrange error_setg(&data->writeerr, 93559607eaSDaniel P. Berrange "Unexpected I/O blocking"); 94559607eaSDaniel P. Berrange break; 95559607eaSDaniel P. Berrange } else { 96559607eaSDaniel P. Berrange qio_channel_wait(data->src, 97559607eaSDaniel P. Berrange G_IO_OUT); 98559607eaSDaniel P. Berrange continue; 99559607eaSDaniel P. Berrange } 100559607eaSDaniel P. Berrange } else if (ret < 0) { 101559607eaSDaniel P. Berrange break; 102559607eaSDaniel P. Berrange } else if (ret == 0) { 103559607eaSDaniel P. Berrange error_setg(&data->writeerr, 104559607eaSDaniel P. Berrange "Unexpected zero length write"); 105559607eaSDaniel P. Berrange break; 106559607eaSDaniel P. Berrange } 107559607eaSDaniel P. Berrange 108559607eaSDaniel P. Berrange test_skip_iovec(&iov, &niov, ret, &old); 109559607eaSDaniel P. Berrange } 110559607eaSDaniel P. Berrange 111559607eaSDaniel P. Berrange return NULL; 112559607eaSDaniel P. Berrange } 113559607eaSDaniel P. Berrange 114559607eaSDaniel P. Berrange 115559607eaSDaniel P. Berrange /* This thread receives all data using iovecs */ 116559607eaSDaniel P. Berrange static gpointer test_io_thread_reader(gpointer opaque) 117559607eaSDaniel P. Berrange { 118559607eaSDaniel P. Berrange QIOChannelTest *data = opaque; 119559607eaSDaniel P. Berrange struct iovec *iov = data->outputv; 120559607eaSDaniel P. Berrange size_t niov = data->niov; 121559607eaSDaniel P. Berrange struct iovec old = { 0 }; 122559607eaSDaniel P. Berrange 123559607eaSDaniel P. Berrange qio_channel_set_blocking(data->dst, data->blocking, NULL); 124559607eaSDaniel P. Berrange 125559607eaSDaniel P. Berrange while (niov) { 126559607eaSDaniel P. Berrange ssize_t ret; 127559607eaSDaniel P. Berrange 128559607eaSDaniel P. Berrange ret = qio_channel_readv(data->dst, 129559607eaSDaniel P. Berrange iov, 130559607eaSDaniel P. Berrange niov, 131559607eaSDaniel P. Berrange &data->readerr); 132559607eaSDaniel P. Berrange 133559607eaSDaniel P. Berrange if (ret == QIO_CHANNEL_ERR_BLOCK) { 134559607eaSDaniel P. Berrange if (data->blocking) { 135559607eaSDaniel P. Berrange error_setg(&data->writeerr, 136559607eaSDaniel P. Berrange "Unexpected I/O blocking"); 137559607eaSDaniel P. Berrange break; 138559607eaSDaniel P. Berrange } else { 139559607eaSDaniel P. Berrange qio_channel_wait(data->dst, 140559607eaSDaniel P. Berrange G_IO_IN); 141559607eaSDaniel P. Berrange continue; 142559607eaSDaniel P. Berrange } 143559607eaSDaniel P. Berrange } else if (ret < 0) { 144559607eaSDaniel P. Berrange break; 145559607eaSDaniel P. Berrange } else if (ret == 0) { 146559607eaSDaniel P. Berrange break; 147559607eaSDaniel P. Berrange } 148559607eaSDaniel P. Berrange 149559607eaSDaniel P. Berrange test_skip_iovec(&iov, &niov, ret, &old); 150559607eaSDaniel P. Berrange } 151559607eaSDaniel P. Berrange 152559607eaSDaniel P. Berrange return NULL; 153559607eaSDaniel P. Berrange } 154559607eaSDaniel P. Berrange 155559607eaSDaniel P. Berrange 156559607eaSDaniel P. Berrange QIOChannelTest *qio_channel_test_new(void) 157559607eaSDaniel P. Berrange { 158559607eaSDaniel P. Berrange QIOChannelTest *data = g_new0(QIOChannelTest, 1); 159559607eaSDaniel P. Berrange size_t i; 160559607eaSDaniel P. Berrange size_t offset; 161559607eaSDaniel P. Berrange 162559607eaSDaniel P. Berrange 163559607eaSDaniel P. Berrange /* We'll send 1 MB of data */ 164559607eaSDaniel P. Berrange #define CHUNK_COUNT 250 165559607eaSDaniel P. Berrange #define CHUNK_LEN 4194 166559607eaSDaniel P. Berrange 167559607eaSDaniel P. Berrange data->len = CHUNK_COUNT * CHUNK_LEN; 168559607eaSDaniel P. Berrange data->input = g_new0(char, data->len); 169559607eaSDaniel P. Berrange data->output = g_new0(gchar, data->len); 170559607eaSDaniel P. Berrange 171559607eaSDaniel P. Berrange /* Fill input with a pattern */ 172559607eaSDaniel P. Berrange for (i = 0; i < data->len; i += CHUNK_LEN) { 173559607eaSDaniel P. Berrange memset(data->input + i, (i / CHUNK_LEN), CHUNK_LEN); 174559607eaSDaniel P. Berrange } 175559607eaSDaniel P. Berrange 176559607eaSDaniel P. Berrange /* We'll split the data across a bunch of IO vecs */ 177559607eaSDaniel P. Berrange data->niov = CHUNK_COUNT; 178559607eaSDaniel P. Berrange data->inputv = g_new0(struct iovec, data->niov); 179559607eaSDaniel P. Berrange data->outputv = g_new0(struct iovec, data->niov); 180559607eaSDaniel P. Berrange 181559607eaSDaniel P. Berrange for (i = 0, offset = 0; i < data->niov; i++, offset += CHUNK_LEN) { 182559607eaSDaniel P. Berrange data->inputv[i].iov_base = data->input + offset; 183559607eaSDaniel P. Berrange data->outputv[i].iov_base = data->output + offset; 184559607eaSDaniel P. Berrange data->inputv[i].iov_len = CHUNK_LEN; 185559607eaSDaniel P. Berrange data->outputv[i].iov_len = CHUNK_LEN; 186559607eaSDaniel P. Berrange } 187559607eaSDaniel P. Berrange 188559607eaSDaniel P. Berrange return data; 189559607eaSDaniel P. Berrange } 190559607eaSDaniel P. Berrange 191559607eaSDaniel P. Berrange void qio_channel_test_run_threads(QIOChannelTest *test, 192559607eaSDaniel P. Berrange bool blocking, 193559607eaSDaniel P. Berrange QIOChannel *src, 194559607eaSDaniel P. Berrange QIOChannel *dst) 195559607eaSDaniel P. Berrange { 196559607eaSDaniel P. Berrange GThread *reader, *writer; 197559607eaSDaniel P. Berrange 198559607eaSDaniel P. Berrange test->src = src; 199559607eaSDaniel P. Berrange test->dst = dst; 200559607eaSDaniel P. Berrange test->blocking = blocking; 201559607eaSDaniel P. Berrange 202559607eaSDaniel P. Berrange reader = g_thread_new("reader", 203559607eaSDaniel P. Berrange test_io_thread_reader, 204559607eaSDaniel P. Berrange test); 205559607eaSDaniel P. Berrange writer = g_thread_new("writer", 206559607eaSDaniel P. Berrange test_io_thread_writer, 207559607eaSDaniel P. Berrange test); 208559607eaSDaniel P. Berrange 209559607eaSDaniel P. Berrange g_thread_join(reader); 210559607eaSDaniel P. Berrange g_thread_join(writer); 211559607eaSDaniel P. Berrange 212559607eaSDaniel P. Berrange test->dst = test->src = NULL; 213559607eaSDaniel P. Berrange } 214559607eaSDaniel P. Berrange 215559607eaSDaniel P. Berrange 216559607eaSDaniel P. Berrange void qio_channel_test_run_writer(QIOChannelTest *test, 217559607eaSDaniel P. Berrange QIOChannel *src) 218559607eaSDaniel P. Berrange { 219559607eaSDaniel P. Berrange test->src = src; 220559607eaSDaniel P. Berrange test_io_thread_writer(test); 221559607eaSDaniel P. Berrange test->src = NULL; 222559607eaSDaniel P. Berrange } 223559607eaSDaniel P. Berrange 224559607eaSDaniel P. Berrange 225559607eaSDaniel P. Berrange void qio_channel_test_run_reader(QIOChannelTest *test, 226559607eaSDaniel P. Berrange QIOChannel *dst) 227559607eaSDaniel P. Berrange { 228559607eaSDaniel P. Berrange test->dst = dst; 229559607eaSDaniel P. Berrange test_io_thread_reader(test); 230559607eaSDaniel P. Berrange test->dst = NULL; 231559607eaSDaniel P. Berrange } 232559607eaSDaniel P. Berrange 233559607eaSDaniel P. Berrange 234559607eaSDaniel P. Berrange void qio_channel_test_validate(QIOChannelTest *test) 235559607eaSDaniel P. Berrange { 236559607eaSDaniel P. Berrange g_assert_cmpint(memcmp(test->input, 237559607eaSDaniel P. Berrange test->output, 238559607eaSDaniel P. Berrange test->len), ==, 0); 239559607eaSDaniel P. Berrange g_assert(test->readerr == NULL); 240559607eaSDaniel P. Berrange g_assert(test->writeerr == NULL); 241559607eaSDaniel P. Berrange 242559607eaSDaniel P. Berrange g_free(test->inputv); 243559607eaSDaniel P. Berrange g_free(test->outputv); 244559607eaSDaniel P. Berrange g_free(test->input); 245559607eaSDaniel P. Berrange g_free(test->output); 246559607eaSDaniel P. Berrange g_free(test); 247559607eaSDaniel P. Berrange } 248