xref: /qemu/tests/unit/io-channel-helpers.c (revision 559607ea173a0003efda7f884bec73b242f923fb)
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