These changes are the raw update to qemu-2.6.
[kvmfornfv.git] / qemu / tests / io-channel-helpers.c
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
25 struct 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
40 static 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 */
76 static 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 */
117 static 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
157 QIOChannelTest *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
192 void 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
217 void 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
226 void 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
235 void 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 }