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 24 struct QIOChannelTest { 25 QIOChannel *src; 26 QIOChannel *dst; 27 bool blocking; 28 size_t len; 29 size_t niov; 30 char *input; 31 struct iovec *inputv; 32 char *output; 33 struct iovec *outputv; 34 Error *writeerr; 35 Error *readerr; 36 }; 37 38 39 static void test_skip_iovec(struct iovec **iov, 40 size_t *niov, 41 size_t skip, 42 struct iovec *old) 43 { 44 size_t offset = 0; 45 size_t i; 46 47 for (i = 0; i < *niov; i++) { 48 if (skip < (*iov)[i].iov_len) { 49 old->iov_len = (*iov)[i].iov_len; 50 old->iov_base = (*iov)[i].iov_base; 51 52 (*iov)[i].iov_len -= skip; 53 (*iov)[i].iov_base += skip; 54 break; 55 } else { 56 skip -= (*iov)[i].iov_len; 57 58 if (i == 0 && old->iov_base) { 59 (*iov)[i].iov_len = old->iov_len; 60 (*iov)[i].iov_base = old->iov_base; 61 old->iov_len = 0; 62 old->iov_base = NULL; 63 } 64 65 offset++; 66 } 67 } 68 69 *iov = *iov + offset; 70 *niov -= offset; 71 } 72 73 74 /* This thread sends all data using iovecs */ 75 static gpointer test_io_thread_writer(gpointer opaque) 76 { 77 QIOChannelTest *data = opaque; 78 struct iovec *iov = data->inputv; 79 size_t niov = data->niov; 80 struct iovec old = { 0 }; 81 82 qio_channel_set_blocking(data->src, data->blocking, NULL); 83 84 while (niov) { 85 ssize_t ret; 86 ret = qio_channel_writev(data->src, 87 iov, 88 niov, 89 &data->writeerr); 90 if (ret == QIO_CHANNEL_ERR_BLOCK) { 91 if (data->blocking) { 92 error_setg(&data->writeerr, 93 "Unexpected I/O blocking"); 94 break; 95 } else { 96 qio_channel_wait(data->src, 97 G_IO_OUT); 98 continue; 99 } 100 } else if (ret < 0) { 101 break; 102 } else if (ret == 0) { 103 error_setg(&data->writeerr, 104 "Unexpected zero length write"); 105 break; 106 } 107 108 test_skip_iovec(&iov, &niov, ret, &old); 109 } 110 111 return NULL; 112 } 113 114 115 /* This thread receives all data using iovecs */ 116 static gpointer test_io_thread_reader(gpointer opaque) 117 { 118 QIOChannelTest *data = opaque; 119 struct iovec *iov = data->outputv; 120 size_t niov = data->niov; 121 struct iovec old = { 0 }; 122 123 qio_channel_set_blocking(data->dst, data->blocking, NULL); 124 125 while (niov) { 126 ssize_t ret; 127 128 ret = qio_channel_readv(data->dst, 129 iov, 130 niov, 131 &data->readerr); 132 133 if (ret == QIO_CHANNEL_ERR_BLOCK) { 134 if (data->blocking) { 135 error_setg(&data->readerr, 136 "Unexpected I/O blocking"); 137 break; 138 } else { 139 qio_channel_wait(data->dst, 140 G_IO_IN); 141 continue; 142 } 143 } else if (ret < 0) { 144 break; 145 } else if (ret == 0) { 146 break; 147 } 148 149 test_skip_iovec(&iov, &niov, ret, &old); 150 } 151 152 return NULL; 153 } 154 155 156 QIOChannelTest *qio_channel_test_new(void) 157 { 158 QIOChannelTest *data = g_new0(QIOChannelTest, 1); 159 size_t i; 160 size_t offset; 161 162 163 /* We'll send 1 MB of data */ 164 #define CHUNK_COUNT 250 165 #define CHUNK_LEN 4194 166 167 data->len = CHUNK_COUNT * CHUNK_LEN; 168 data->input = g_new0(char, data->len); 169 data->output = g_new0(gchar, data->len); 170 171 /* Fill input with a pattern */ 172 for (i = 0; i < data->len; i += CHUNK_LEN) { 173 memset(data->input + i, (i / CHUNK_LEN), CHUNK_LEN); 174 } 175 176 /* We'll split the data across a bunch of IO vecs */ 177 data->niov = CHUNK_COUNT; 178 data->inputv = g_new0(struct iovec, data->niov); 179 data->outputv = g_new0(struct iovec, data->niov); 180 181 for (i = 0, offset = 0; i < data->niov; i++, offset += CHUNK_LEN) { 182 data->inputv[i].iov_base = data->input + offset; 183 data->outputv[i].iov_base = data->output + offset; 184 data->inputv[i].iov_len = CHUNK_LEN; 185 data->outputv[i].iov_len = CHUNK_LEN; 186 } 187 188 return data; 189 } 190 191 void qio_channel_test_run_threads(QIOChannelTest *test, 192 bool blocking, 193 QIOChannel *src, 194 QIOChannel *dst) 195 { 196 GThread *reader, *writer; 197 198 test->src = src; 199 test->dst = dst; 200 test->blocking = blocking; 201 202 reader = g_thread_new("reader", 203 test_io_thread_reader, 204 test); 205 writer = g_thread_new("writer", 206 test_io_thread_writer, 207 test); 208 209 g_thread_join(reader); 210 g_thread_join(writer); 211 212 test->dst = test->src = NULL; 213 } 214 215 216 void qio_channel_test_run_writer(QIOChannelTest *test, 217 QIOChannel *src) 218 { 219 test->src = src; 220 test_io_thread_writer(test); 221 test->src = NULL; 222 } 223 224 225 void qio_channel_test_run_reader(QIOChannelTest *test, 226 QIOChannel *dst) 227 { 228 test->dst = dst; 229 test_io_thread_reader(test); 230 test->dst = NULL; 231 } 232 233 234 void qio_channel_test_validate(QIOChannelTest *test) 235 { 236 g_assert(test->readerr == NULL); 237 g_assert(test->writeerr == NULL); 238 g_assert_cmpint(memcmp(test->input, 239 test->output, 240 test->len), ==, 0); 241 242 g_free(test->inputv); 243 g_free(test->outputv); 244 g_free(test->input); 245 g_free(test->output); 246 g_free(test); 247 } 248