Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (C) 2023 Intel Corporation. All rights reserved.
3 : : */
4 : :
5 : : #include "spdk_internal/cunit.h"
6 : :
7 : : #include "common/lib/ut_multithread.c"
8 : : #include "unit/lib/json_mock.c"
9 : :
10 : : #include "spdk/config.h"
11 : : #include "spdk/thread.h"
12 : :
13 : : #include "thread/iobuf.c"
14 : :
15 : : struct ut_iobuf_entry {
16 : : struct spdk_iobuf_channel *ioch;
17 : : struct spdk_iobuf_entry iobuf;
18 : : void *buf;
19 : : void *buf2;
20 : : uint32_t thread_id;
21 : : const char *module;
22 : : };
23 : :
24 : : static void
25 : 15 : ut_iobuf_finish_cb(void *ctx)
26 : : {
27 : 15 : *(int *)ctx = 1;
28 : 15 : }
29 : :
30 : : static void
31 : 60 : ut_iobuf_get_buf_cb(struct spdk_iobuf_entry *entry, void *buf)
32 : : {
33 : 60 : struct ut_iobuf_entry *ut_entry = SPDK_CONTAINEROF(entry, struct ut_iobuf_entry, iobuf);
34 : :
35 : 60 : ut_entry->buf = buf;
36 : 60 : }
37 : :
38 : : static int
39 : 40 : ut_iobuf_foreach_cb(struct spdk_iobuf_channel *ch, struct spdk_iobuf_entry *entry, void *cb_arg)
40 : : {
41 : 40 : struct ut_iobuf_entry *ut_entry = SPDK_CONTAINEROF(entry, struct ut_iobuf_entry, iobuf);
42 : :
43 : 40 : ut_entry->buf = cb_arg;
44 : :
45 : 40 : return 0;
46 : : }
47 : :
48 : : #define SMALL_BUFSIZE 4096
49 : : #define LARGE_BUFSIZE 8192
50 : :
51 : : static void
52 : 5 : iobuf(void)
53 : : {
54 : 5 : struct spdk_iobuf_opts opts = {
55 : : .small_pool_count = 2,
56 : : .large_pool_count = 2,
57 : : .small_bufsize = SMALL_BUFSIZE,
58 : : .large_bufsize = LARGE_BUFSIZE,
59 : : };
60 : : struct ut_iobuf_entry *entry;
61 : 4 : struct spdk_iobuf_channel mod0_ch[2], mod1_ch[2];
62 : 5 : struct ut_iobuf_entry mod0_entries[] = {
63 : : { .thread_id = 0, .module = "ut_module0", },
64 : : { .thread_id = 0, .module = "ut_module0", },
65 : : { .thread_id = 0, .module = "ut_module0", },
66 : : { .thread_id = 0, .module = "ut_module0", },
67 : : { .thread_id = 1, .module = "ut_module0", },
68 : : { .thread_id = 1, .module = "ut_module0", },
69 : : { .thread_id = 1, .module = "ut_module0", },
70 : : { .thread_id = 1, .module = "ut_module0", },
71 : : };
72 : 5 : struct ut_iobuf_entry mod1_entries[] = {
73 : : { .thread_id = 0, .module = "ut_module1", },
74 : : { .thread_id = 0, .module = "ut_module1", },
75 : : { .thread_id = 0, .module = "ut_module1", },
76 : : { .thread_id = 0, .module = "ut_module1", },
77 : : { .thread_id = 1, .module = "ut_module1", },
78 : : { .thread_id = 1, .module = "ut_module1", },
79 : : { .thread_id = 1, .module = "ut_module1", },
80 : : { .thread_id = 1, .module = "ut_module1", },
81 : : };
82 : 5 : int rc, finish = 0;
83 : : uint32_t i;
84 : :
85 : 5 : allocate_cores(2);
86 : 5 : allocate_threads(2);
87 : :
88 : 5 : set_thread(0);
89 : :
90 : : /* We cannot use spdk_iobuf_set_opts(), as it won't allow us to use such small pools */
91 : 5 : g_iobuf.opts = opts;
92 : 5 : rc = spdk_iobuf_initialize();
93 : 5 : CU_ASSERT_EQUAL(rc, 0);
94 : :
95 : 5 : rc = spdk_iobuf_register_module("ut_module0");
96 : 5 : CU_ASSERT_EQUAL(rc, 0);
97 : :
98 : 5 : rc = spdk_iobuf_register_module("ut_module1");
99 : 5 : CU_ASSERT_EQUAL(rc, 0);
100 : :
101 : 5 : set_thread(0);
102 : 5 : rc = spdk_iobuf_channel_init(&mod0_ch[0], "ut_module0", 0, 0);
103 : 5 : CU_ASSERT_EQUAL(rc, 0);
104 : 5 : set_thread(1);
105 : 5 : rc = spdk_iobuf_channel_init(&mod0_ch[1], "ut_module0", 0, 0);
106 : 5 : CU_ASSERT_EQUAL(rc, 0);
107 [ + + ]: 45 : for (i = 0; i < SPDK_COUNTOF(mod0_entries); ++i) {
108 : 40 : mod0_entries[i].ioch = &mod0_ch[mod0_entries[i].thread_id];
109 : : }
110 : 5 : set_thread(0);
111 : 5 : rc = spdk_iobuf_channel_init(&mod1_ch[0], "ut_module1", 0, 0);
112 : 5 : CU_ASSERT_EQUAL(rc, 0);
113 : 5 : set_thread(1);
114 : 5 : rc = spdk_iobuf_channel_init(&mod1_ch[1], "ut_module1", 0, 0);
115 : 5 : CU_ASSERT_EQUAL(rc, 0);
116 [ + + ]: 45 : for (i = 0; i < SPDK_COUNTOF(mod1_entries); ++i) {
117 : 40 : mod1_entries[i].ioch = &mod1_ch[mod1_entries[i].thread_id];
118 : : }
119 : :
120 : : /* First check that it's possible to retrieve the whole pools from a single module */
121 : 5 : set_thread(0);
122 : 5 : entry = &mod0_entries[0];
123 : 5 : entry->buf = spdk_iobuf_get(entry->ioch, LARGE_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
124 : 5 : CU_ASSERT_PTR_NOT_NULL(entry->buf);
125 : 5 : entry = &mod0_entries[1];
126 : 5 : entry->buf = spdk_iobuf_get(entry->ioch, LARGE_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
127 : 5 : CU_ASSERT_PTR_NOT_NULL(entry->buf);
128 : : /* The next two should be put onto the large buf wait queue */
129 : 5 : entry = &mod0_entries[2];
130 : 5 : entry->buf = spdk_iobuf_get(entry->ioch, LARGE_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
131 : 5 : CU_ASSERT_PTR_NULL(entry->buf);
132 : 5 : entry = &mod0_entries[3];
133 : 5 : entry->buf = spdk_iobuf_get(entry->ioch, LARGE_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
134 : 5 : CU_ASSERT_PTR_NULL(entry->buf);
135 : : /* Pick the two next buffers from the small pool */
136 : 5 : set_thread(1);
137 : 5 : entry = &mod0_entries[4];
138 : 5 : entry->buf = spdk_iobuf_get(entry->ioch, SMALL_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
139 : 5 : CU_ASSERT_PTR_NOT_NULL(entry->buf);
140 : 5 : entry = &mod0_entries[5];
141 : 5 : entry->buf = spdk_iobuf_get(entry->ioch, SMALL_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
142 : 5 : CU_ASSERT_PTR_NOT_NULL(entry->buf);
143 : : /* The next two should be put onto the small buf wait queue */
144 : 5 : entry = &mod0_entries[6];
145 : 5 : entry->buf = spdk_iobuf_get(entry->ioch, SMALL_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
146 : 5 : CU_ASSERT_PTR_NULL(entry->buf);
147 : 5 : entry = &mod0_entries[7];
148 : 5 : entry->buf = spdk_iobuf_get(entry->ioch, SMALL_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
149 : 5 : CU_ASSERT_PTR_NULL(entry->buf);
150 : :
151 : : /* Now return one of the large buffers to the pool and verify that the first request's
152 : : * (entry 2) callback was executed and it was removed from the wait queue.
153 : : */
154 : 5 : set_thread(0);
155 : 5 : entry = &mod0_entries[0];
156 : 5 : spdk_iobuf_put(entry->ioch, entry->buf, LARGE_BUFSIZE);
157 : 5 : entry = &mod0_entries[2];
158 : 5 : CU_ASSERT_PTR_NOT_NULL(entry->buf);
159 : 5 : entry = &mod0_entries[3];
160 : 5 : CU_ASSERT_PTR_NULL(entry->buf);
161 : :
162 : : /* Return the second buffer and check that the other request is satisfied */
163 : 5 : entry = &mod0_entries[1];
164 : 5 : spdk_iobuf_put(entry->ioch, entry->buf, LARGE_BUFSIZE);
165 : 5 : entry = &mod0_entries[3];
166 : 5 : CU_ASSERT_PTR_NOT_NULL(entry->buf);
167 : :
168 : : /* Return the remaining two buffers */
169 : 5 : entry = &mod0_entries[2];
170 : 5 : spdk_iobuf_put(entry->ioch, entry->buf, LARGE_BUFSIZE);
171 : 5 : entry = &mod0_entries[3];
172 : 5 : spdk_iobuf_put(entry->ioch, entry->buf, LARGE_BUFSIZE);
173 : :
174 : : /* Check that it didn't change the requests waiting for the small buffers */
175 : 5 : entry = &mod0_entries[6];
176 : 5 : CU_ASSERT_PTR_NULL(entry->buf);
177 : 5 : entry = &mod0_entries[7];
178 : 5 : CU_ASSERT_PTR_NULL(entry->buf);
179 : :
180 : : /* Do the same test as above, this time using the small pool */
181 : 5 : set_thread(1);
182 : 5 : entry = &mod0_entries[4];
183 : 5 : spdk_iobuf_put(entry->ioch, entry->buf, SMALL_BUFSIZE);
184 : 5 : entry = &mod0_entries[6];
185 : 5 : CU_ASSERT_PTR_NOT_NULL(entry->buf);
186 : 5 : entry = &mod0_entries[7];
187 : 5 : CU_ASSERT_PTR_NULL(entry->buf);
188 : :
189 : : /* Return the second buffer and check that the other request is satisfied */
190 : 5 : entry = &mod0_entries[5];
191 : 5 : spdk_iobuf_put(entry->ioch, entry->buf, SMALL_BUFSIZE);
192 : 5 : entry = &mod0_entries[7];
193 : 5 : CU_ASSERT_PTR_NOT_NULL(entry->buf);
194 : :
195 : : /* Return the remaining two buffers */
196 : 5 : entry = &mod0_entries[6];
197 : 5 : spdk_iobuf_put(entry->ioch, entry->buf, SMALL_BUFSIZE);
198 : 5 : entry = &mod0_entries[7];
199 : 5 : spdk_iobuf_put(entry->ioch, entry->buf, SMALL_BUFSIZE);
200 : :
201 : : /* Now check requesting buffers from different modules - first request all of them from one
202 : : * module, starting from the large pool
203 : : */
204 : 5 : set_thread(0);
205 : 5 : entry = &mod0_entries[0];
206 : 5 : entry->buf = spdk_iobuf_get(entry->ioch, LARGE_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
207 : 5 : CU_ASSERT_PTR_NOT_NULL(entry->buf);
208 : 5 : entry = &mod0_entries[1];
209 : 5 : entry->buf = spdk_iobuf_get(entry->ioch, LARGE_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
210 : 5 : CU_ASSERT_PTR_NOT_NULL(entry->buf);
211 : : /* Request all of them from the small one */
212 : 5 : set_thread(1);
213 : 5 : entry = &mod0_entries[4];
214 : 5 : entry->buf = spdk_iobuf_get(entry->ioch, SMALL_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
215 : 5 : CU_ASSERT_PTR_NOT_NULL(entry->buf);
216 : 5 : entry = &mod0_entries[5];
217 : 5 : entry->buf = spdk_iobuf_get(entry->ioch, SMALL_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
218 : 5 : CU_ASSERT_PTR_NOT_NULL(entry->buf);
219 : :
220 : : /* Request one buffer per module from each pool */
221 : 5 : set_thread(0);
222 : 5 : entry = &mod1_entries[0];
223 : 5 : entry->buf = spdk_iobuf_get(entry->ioch, LARGE_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
224 : 5 : CU_ASSERT_PTR_NULL(entry->buf);
225 : 5 : entry = &mod0_entries[3];
226 : 5 : entry->buf = spdk_iobuf_get(entry->ioch, LARGE_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
227 : 5 : CU_ASSERT_PTR_NULL(entry->buf);
228 : : /* Change the order from the small pool and request a buffer from mod0 first */
229 : 5 : set_thread(1);
230 : 5 : entry = &mod0_entries[6];
231 : 5 : entry->buf = spdk_iobuf_get(entry->ioch, SMALL_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
232 : 5 : CU_ASSERT_PTR_NULL(entry->buf);
233 : 5 : entry = &mod1_entries[4];
234 : 5 : entry->buf = spdk_iobuf_get(entry->ioch, SMALL_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
235 : 5 : CU_ASSERT_PTR_NULL(entry->buf);
236 : :
237 : : /* Now return one buffer to the large pool */
238 : 5 : set_thread(0);
239 : 5 : entry = &mod0_entries[0];
240 : 5 : spdk_iobuf_put(entry->ioch, entry->buf, LARGE_BUFSIZE);
241 : :
242 : : /* Make sure the request from mod1 got the buffer, as it was the first to request it */
243 : 5 : entry = &mod1_entries[0];
244 : 5 : CU_ASSERT_PTR_NOT_NULL(entry->buf);
245 : 5 : entry = &mod0_entries[3];
246 : 5 : CU_ASSERT_PTR_NULL(entry->buf);
247 : :
248 : : /* Return second buffer to the large pool and check the outstanding mod0 request */
249 : 5 : entry = &mod0_entries[1];
250 : 5 : spdk_iobuf_put(entry->ioch, entry->buf, LARGE_BUFSIZE);
251 : 5 : entry = &mod0_entries[3];
252 : 5 : CU_ASSERT_PTR_NOT_NULL(entry->buf);
253 : :
254 : : /* Return the remaining two buffers */
255 : 5 : entry = &mod1_entries[0];
256 : 5 : spdk_iobuf_put(entry->ioch, entry->buf, LARGE_BUFSIZE);
257 : 5 : entry = &mod0_entries[3];
258 : 5 : spdk_iobuf_put(entry->ioch, entry->buf, LARGE_BUFSIZE);
259 : :
260 : : /* Check the same for the small pool, but this time the order of the request is reversed
261 : : * (mod0 before mod1)
262 : : */
263 : 5 : set_thread(1);
264 : 5 : entry = &mod0_entries[4];
265 : 5 : spdk_iobuf_put(entry->ioch, entry->buf, SMALL_BUFSIZE);
266 : 5 : entry = &mod0_entries[6];
267 : 5 : CU_ASSERT_PTR_NOT_NULL(entry->buf);
268 : : /* mod1 request was second in this case, so it still needs to wait */
269 : 5 : entry = &mod1_entries[4];
270 : 5 : CU_ASSERT_PTR_NULL(entry->buf);
271 : :
272 : : /* Return the second requested buffer */
273 : 5 : entry = &mod0_entries[5];
274 : 5 : spdk_iobuf_put(entry->ioch, entry->buf, SMALL_BUFSIZE);
275 : 5 : entry = &mod1_entries[4];
276 : 5 : CU_ASSERT_PTR_NOT_NULL(entry->buf);
277 : :
278 : : /* Return the remaining two buffers */
279 : 5 : entry = &mod0_entries[6];
280 : 5 : spdk_iobuf_put(entry->ioch, entry->buf, SMALL_BUFSIZE);
281 : 5 : entry = &mod1_entries[4];
282 : 5 : spdk_iobuf_put(entry->ioch, entry->buf, SMALL_BUFSIZE);
283 : :
284 : : /* Request buffers to make the pools empty */
285 : 5 : set_thread(0);
286 : 5 : entry = &mod0_entries[0];
287 : 5 : entry->buf = spdk_iobuf_get(entry->ioch, LARGE_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
288 : 5 : CU_ASSERT_PTR_NOT_NULL(entry->buf);
289 : 5 : entry = &mod1_entries[0];
290 : 5 : entry->buf = spdk_iobuf_get(entry->ioch, LARGE_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
291 : 5 : CU_ASSERT_PTR_NOT_NULL(entry->buf);
292 : 5 : entry = &mod0_entries[1];
293 : 5 : entry->buf = spdk_iobuf_get(entry->ioch, SMALL_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
294 : 5 : CU_ASSERT_PTR_NOT_NULL(entry->buf);
295 : 5 : entry = &mod1_entries[1];
296 : 5 : entry->buf = spdk_iobuf_get(entry->ioch, SMALL_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
297 : 5 : CU_ASSERT_PTR_NOT_NULL(entry->buf);
298 : :
299 : : /* Queue more requests from both modules */
300 : 5 : entry = &mod0_entries[2];
301 : 5 : entry->buf = spdk_iobuf_get(entry->ioch, LARGE_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
302 : 5 : CU_ASSERT_PTR_NULL(entry->buf);
303 : 5 : entry = &mod1_entries[2];
304 : 5 : entry->buf = spdk_iobuf_get(entry->ioch, LARGE_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
305 : 5 : CU_ASSERT_PTR_NULL(entry->buf);
306 : 5 : entry = &mod1_entries[3];
307 : 5 : entry->buf = spdk_iobuf_get(entry->ioch, SMALL_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
308 : 5 : CU_ASSERT_PTR_NULL(entry->buf);
309 : 5 : entry = &mod0_entries[3];
310 : 5 : entry->buf = spdk_iobuf_get(entry->ioch, SMALL_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
311 : 5 : CU_ASSERT_PTR_NULL(entry->buf);
312 : :
313 : : /* Check that abort correctly remove an entry from the queue */
314 : 5 : entry = &mod0_entries[2];
315 : 5 : spdk_iobuf_entry_abort(entry->ioch, &entry->iobuf, LARGE_BUFSIZE);
316 : 5 : entry = &mod1_entries[3];
317 : 5 : spdk_iobuf_entry_abort(entry->ioch, &entry->iobuf, SMALL_BUFSIZE);
318 : :
319 : 5 : entry = &mod0_entries[0];
320 : 5 : spdk_iobuf_put(entry->ioch, entry->buf, LARGE_BUFSIZE);
321 : 5 : CU_ASSERT_PTR_NOT_NULL(mod1_entries[2].buf);
322 : 5 : entry = &mod0_entries[1];
323 : 5 : spdk_iobuf_put(entry->ioch, entry->buf, SMALL_BUFSIZE);
324 : 5 : CU_ASSERT_PTR_NOT_NULL(mod0_entries[3].buf);
325 : :
326 : : /* Clean up */
327 : 5 : entry = &mod1_entries[0];
328 : 5 : spdk_iobuf_put(entry->ioch, entry->buf, LARGE_BUFSIZE);
329 : 5 : entry = &mod1_entries[2];
330 : 5 : spdk_iobuf_put(entry->ioch, entry->buf, LARGE_BUFSIZE);
331 : 5 : entry = &mod1_entries[1];
332 : 5 : spdk_iobuf_put(entry->ioch, entry->buf, SMALL_BUFSIZE);
333 : 5 : entry = &mod0_entries[3];
334 : 5 : spdk_iobuf_put(entry->ioch, entry->buf, SMALL_BUFSIZE);
335 : :
336 : : /* Request buffers to make the pools empty */
337 : 5 : set_thread(0);
338 : 5 : entry = &mod0_entries[0];
339 : 5 : entry->buf = spdk_iobuf_get(entry->ioch, LARGE_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
340 : 5 : CU_ASSERT_PTR_NOT_NULL(entry->buf);
341 : 5 : entry = &mod1_entries[0];
342 : 5 : entry->buf = spdk_iobuf_get(entry->ioch, LARGE_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
343 : 5 : CU_ASSERT_PTR_NOT_NULL(entry->buf);
344 : 5 : entry = &mod0_entries[1];
345 : 5 : entry->buf = spdk_iobuf_get(entry->ioch, SMALL_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
346 : 5 : CU_ASSERT_PTR_NOT_NULL(entry->buf);
347 : 5 : entry = &mod1_entries[1];
348 : 5 : entry->buf = spdk_iobuf_get(entry->ioch, SMALL_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
349 : 5 : CU_ASSERT_PTR_NOT_NULL(entry->buf);
350 : :
351 : : /* Request a buffer from each queue and each module on thread 0 */
352 : 5 : set_thread(0);
353 : 5 : entry = &mod0_entries[2];
354 : 5 : entry->buf = spdk_iobuf_get(entry->ioch, LARGE_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
355 : 5 : CU_ASSERT_PTR_NULL(entry->buf);
356 : 5 : entry = &mod1_entries[2];
357 : 5 : entry->buf = spdk_iobuf_get(entry->ioch, LARGE_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
358 : 5 : CU_ASSERT_PTR_NULL(entry->buf);
359 : 5 : entry = &mod0_entries[3];
360 : 5 : entry->buf = spdk_iobuf_get(entry->ioch, SMALL_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
361 : 5 : CU_ASSERT_PTR_NULL(entry->buf);
362 : 5 : entry = &mod1_entries[3];
363 : 5 : entry->buf = spdk_iobuf_get(entry->ioch, SMALL_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
364 : 5 : CU_ASSERT_PTR_NULL(entry->buf);
365 : :
366 : : /* Do the same on thread 1 */
367 : 5 : set_thread(1);
368 : 5 : entry = &mod0_entries[6];
369 : 5 : entry->buf = spdk_iobuf_get(entry->ioch, LARGE_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
370 : 5 : CU_ASSERT_PTR_NULL(entry->buf);
371 : 5 : entry = &mod1_entries[6];
372 : 5 : entry->buf = spdk_iobuf_get(entry->ioch, LARGE_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
373 : 5 : CU_ASSERT_PTR_NULL(entry->buf);
374 : 5 : entry = &mod0_entries[7];
375 : 5 : entry->buf = spdk_iobuf_get(entry->ioch, SMALL_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
376 : 5 : CU_ASSERT_PTR_NULL(entry->buf);
377 : 5 : entry = &mod1_entries[7];
378 : 5 : entry->buf = spdk_iobuf_get(entry->ioch, SMALL_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
379 : 5 : CU_ASSERT_PTR_NULL(entry->buf);
380 : :
381 : : /* Now do the foreach and check that correct entries are iterated over by assigning their
382 : : * ->buf pointers to different values.
383 : : */
384 : 5 : set_thread(0);
385 : 5 : rc = spdk_iobuf_for_each_entry(&mod0_ch[0], &mod0_ch[0].large,
386 : : ut_iobuf_foreach_cb, (void *)0xdeadbeef);
387 : 5 : CU_ASSERT_EQUAL(rc, 0);
388 : 5 : rc = spdk_iobuf_for_each_entry(&mod0_ch[0], &mod0_ch[0].small,
389 : : ut_iobuf_foreach_cb, (void *)0xbeefdead);
390 : 5 : CU_ASSERT_EQUAL(rc, 0);
391 : 5 : rc = spdk_iobuf_for_each_entry(&mod1_ch[0], &mod1_ch[0].large,
392 : : ut_iobuf_foreach_cb, (void *)0xfeedbeef);
393 : 5 : CU_ASSERT_EQUAL(rc, 0);
394 : 5 : rc = spdk_iobuf_for_each_entry(&mod1_ch[0], &mod1_ch[0].small,
395 : : ut_iobuf_foreach_cb, (void *)0xbeeffeed);
396 : 5 : CU_ASSERT_EQUAL(rc, 0);
397 : 5 : set_thread(1);
398 : 5 : rc = spdk_iobuf_for_each_entry(&mod0_ch[1], &mod0_ch[1].large,
399 : : ut_iobuf_foreach_cb, (void *)0xcafebabe);
400 : 5 : CU_ASSERT_EQUAL(rc, 0);
401 : 5 : rc = spdk_iobuf_for_each_entry(&mod0_ch[1], &mod0_ch[1].small,
402 : : ut_iobuf_foreach_cb, (void *)0xbabecafe);
403 : 5 : CU_ASSERT_EQUAL(rc, 0);
404 : 5 : rc = spdk_iobuf_for_each_entry(&mod1_ch[1], &mod1_ch[1].large,
405 : : ut_iobuf_foreach_cb, (void *)0xbeefcafe);
406 : 5 : CU_ASSERT_EQUAL(rc, 0);
407 : 5 : rc = spdk_iobuf_for_each_entry(&mod1_ch[1], &mod1_ch[1].small,
408 : : ut_iobuf_foreach_cb, (void *)0xcafebeef);
409 : 5 : CU_ASSERT_EQUAL(rc, 0);
410 : :
411 : : /* thread 0 */
412 : 5 : CU_ASSERT_PTR_EQUAL(mod0_entries[2].buf, (void *)0xdeadbeef);
413 : 5 : CU_ASSERT_PTR_EQUAL(mod0_entries[3].buf, (void *)0xbeefdead);
414 : 5 : CU_ASSERT_PTR_EQUAL(mod1_entries[2].buf, (void *)0xfeedbeef);
415 : 5 : CU_ASSERT_PTR_EQUAL(mod1_entries[3].buf, (void *)0xbeeffeed);
416 : : /* thread 1 */
417 : 5 : CU_ASSERT_PTR_EQUAL(mod0_entries[6].buf, (void *)0xcafebabe);
418 : 5 : CU_ASSERT_PTR_EQUAL(mod0_entries[7].buf, (void *)0xbabecafe);
419 : 5 : CU_ASSERT_PTR_EQUAL(mod1_entries[6].buf, (void *)0xbeefcafe);
420 : 5 : CU_ASSERT_PTR_EQUAL(mod1_entries[7].buf, (void *)0xcafebeef);
421 : :
422 : : /* Clean everything up */
423 : 5 : set_thread(0);
424 : 5 : entry = &mod0_entries[2];
425 : 5 : spdk_iobuf_entry_abort(entry->ioch, &entry->iobuf, LARGE_BUFSIZE);
426 : 5 : entry = &mod0_entries[3];
427 : 5 : spdk_iobuf_entry_abort(entry->ioch, &entry->iobuf, SMALL_BUFSIZE);
428 : 5 : entry = &mod1_entries[2];
429 : 5 : spdk_iobuf_entry_abort(entry->ioch, &entry->iobuf, LARGE_BUFSIZE);
430 : 5 : entry = &mod1_entries[3];
431 : 5 : spdk_iobuf_entry_abort(entry->ioch, &entry->iobuf, SMALL_BUFSIZE);
432 : :
433 : 5 : entry = &mod0_entries[0];
434 : 5 : spdk_iobuf_put(entry->ioch, entry->buf, LARGE_BUFSIZE);
435 : 5 : entry = &mod1_entries[0];
436 : 5 : spdk_iobuf_put(entry->ioch, entry->buf, LARGE_BUFSIZE);
437 : 5 : entry = &mod0_entries[1];
438 : 5 : spdk_iobuf_put(entry->ioch, entry->buf, SMALL_BUFSIZE);
439 : 5 : entry = &mod1_entries[1];
440 : 5 : spdk_iobuf_put(entry->ioch, entry->buf, SMALL_BUFSIZE);
441 : :
442 : 5 : set_thread(1);
443 : 5 : entry = &mod0_entries[6];
444 : 5 : spdk_iobuf_entry_abort(entry->ioch, &entry->iobuf, LARGE_BUFSIZE);
445 : 5 : entry = &mod0_entries[7];
446 : 5 : spdk_iobuf_entry_abort(entry->ioch, &entry->iobuf, SMALL_BUFSIZE);
447 : 5 : entry = &mod1_entries[6];
448 : 5 : spdk_iobuf_entry_abort(entry->ioch, &entry->iobuf, LARGE_BUFSIZE);
449 : 5 : entry = &mod1_entries[7];
450 : 5 : spdk_iobuf_entry_abort(entry->ioch, &entry->iobuf, SMALL_BUFSIZE);
451 : :
452 : 5 : set_thread(0);
453 : 5 : spdk_iobuf_channel_fini(&mod0_ch[0]);
454 : 5 : poll_threads();
455 : 5 : spdk_iobuf_channel_fini(&mod1_ch[0]);
456 : 5 : poll_threads();
457 : 5 : set_thread(1);
458 : 5 : spdk_iobuf_channel_fini(&mod0_ch[1]);
459 : 5 : poll_threads();
460 : 5 : spdk_iobuf_channel_fini(&mod1_ch[1]);
461 : 5 : poll_threads();
462 : :
463 : 5 : spdk_iobuf_finish(ut_iobuf_finish_cb, &finish);
464 : 5 : poll_threads();
465 : :
466 : 5 : CU_ASSERT_EQUAL(finish, 1);
467 : :
468 : 5 : free_threads();
469 : 5 : free_cores();
470 : 5 : }
471 : :
472 : : static void
473 : 5 : iobuf_cache(void)
474 : : {
475 : 5 : struct spdk_iobuf_opts opts = {
476 : : .small_pool_count = 4,
477 : : .large_pool_count = 4,
478 : : .small_bufsize = SMALL_BUFSIZE,
479 : : .large_bufsize = LARGE_BUFSIZE,
480 : : };
481 : 4 : struct spdk_iobuf_channel iobuf_ch[2];
482 : : struct ut_iobuf_entry *entry;
483 : 5 : struct ut_iobuf_entry mod0_entries[] = {
484 : : { .thread_id = 0, .module = "ut_module0", },
485 : : { .thread_id = 0, .module = "ut_module0", },
486 : : { .thread_id = 0, .module = "ut_module0", },
487 : : { .thread_id = 0, .module = "ut_module0", },
488 : : };
489 : 5 : struct ut_iobuf_entry mod1_entries[] = {
490 : : { .thread_id = 0, .module = "ut_module1", },
491 : : { .thread_id = 0, .module = "ut_module1", },
492 : : };
493 : 5 : int rc, finish = 0;
494 : : uint32_t i, j, bufsize;
495 : :
496 : 5 : allocate_cores(1);
497 : 5 : allocate_threads(1);
498 : :
499 : 5 : set_thread(0);
500 : :
501 : : /* We cannot use spdk_iobuf_set_opts(), as it won't allow us to use such small pools */
502 : 5 : g_iobuf.opts = opts;
503 : 5 : rc = spdk_iobuf_initialize();
504 : 5 : CU_ASSERT_EQUAL(rc, 0);
505 : :
506 : 5 : rc = spdk_iobuf_register_module("ut_module0");
507 : 5 : CU_ASSERT_EQUAL(rc, 0);
508 : :
509 : 5 : rc = spdk_iobuf_register_module("ut_module1");
510 : 5 : CU_ASSERT_EQUAL(rc, 0);
511 : :
512 : : /* First check that channel initialization fails when it's not possible to fill in the cache
513 : : * from the pool.
514 : : */
515 : 5 : rc = spdk_iobuf_channel_init(&iobuf_ch[0], "ut_module0", 5, 1);
516 : 5 : CU_ASSERT_EQUAL(rc, -ENOMEM);
517 : 5 : rc = spdk_iobuf_channel_init(&iobuf_ch[0], "ut_module0", 1, 5);
518 : 5 : CU_ASSERT_EQUAL(rc, -ENOMEM);
519 : :
520 : 5 : rc = spdk_iobuf_channel_init(&iobuf_ch[0], "ut_module0", 4, 4);
521 : 5 : CU_ASSERT_EQUAL(rc, 0);
522 : 5 : rc = spdk_iobuf_channel_init(&iobuf_ch[1], "ut_module1", 4, 4);
523 : 5 : CU_ASSERT_EQUAL(rc, -ENOMEM);
524 : :
525 : 5 : spdk_iobuf_channel_fini(&iobuf_ch[0]);
526 : 5 : poll_threads();
527 : :
528 : : /* Initialize one channel with cache, acquire buffers, and check that a second one can be
529 : : * created once the buffers acquired from the first one are returned to the pool
530 : : */
531 : 5 : rc = spdk_iobuf_channel_init(&iobuf_ch[0], "ut_module0", 2, 2);
532 : 5 : CU_ASSERT_EQUAL(rc, 0);
533 : :
534 [ + + ]: 20 : for (i = 0; i < 3; ++i) {
535 : 15 : mod0_entries[i].buf = spdk_iobuf_get(&iobuf_ch[0], LARGE_BUFSIZE, &mod0_entries[i].iobuf,
536 : : ut_iobuf_get_buf_cb);
537 : 15 : CU_ASSERT_PTR_NOT_NULL(mod0_entries[i].buf);
538 : : }
539 : :
540 : : /* The channels can be temporarily greedy, holding more buffers than their configured cache
541 : : * size. We can only guarantee that we can create a channel if all outstanding buffers
542 : : * have been returned. */
543 [ + + ]: 20 : for (i = 0; i < 3; ++i) {
544 : 15 : spdk_iobuf_put(&iobuf_ch[0], mod0_entries[i].buf, LARGE_BUFSIZE);
545 : : }
546 : :
547 : : /* The last buffer should be released back to the pool, so we should be able to create a new
548 : : * channel
549 : : */
550 : 5 : rc = spdk_iobuf_channel_init(&iobuf_ch[1], "ut_module1", 2, 2);
551 : 5 : CU_ASSERT_EQUAL(rc, 0);
552 : :
553 : 5 : spdk_iobuf_channel_fini(&iobuf_ch[0]);
554 : 5 : spdk_iobuf_channel_fini(&iobuf_ch[1]);
555 : 5 : poll_threads();
556 : :
557 : : /* Check that the pool is only used when the cache is empty and that the cache guarantees a
558 : : * certain set of buffers
559 : : */
560 : 5 : rc = spdk_iobuf_channel_init(&iobuf_ch[0], "ut_module0", 2, 2);
561 : 5 : CU_ASSERT_EQUAL(rc, 0);
562 : 5 : rc = spdk_iobuf_channel_init(&iobuf_ch[1], "ut_module1", 1, 1);
563 : 5 : CU_ASSERT_EQUAL(rc, 0);
564 : :
565 : 5 : uint32_t buffer_sizes[] = { SMALL_BUFSIZE, LARGE_BUFSIZE };
566 [ + + ]: 15 : for (i = 0; i < SPDK_COUNTOF(buffer_sizes); ++i) {
567 : 10 : bufsize = buffer_sizes[i];
568 : :
569 [ + + ]: 40 : for (j = 0; j < 3; ++j) {
570 : 30 : entry = &mod0_entries[j];
571 : 30 : entry->buf = spdk_iobuf_get(&iobuf_ch[0], bufsize, &entry->iobuf,
572 : : ut_iobuf_get_buf_cb);
573 : 30 : CU_ASSERT_PTR_NOT_NULL(entry->buf);
574 : : }
575 : :
576 : 10 : mod1_entries[0].buf = spdk_iobuf_get(&iobuf_ch[1], bufsize, &mod1_entries[0].iobuf,
577 : : ut_iobuf_get_buf_cb);
578 : 10 : CU_ASSERT_PTR_NOT_NULL(mod1_entries[0].buf);
579 : :
580 : : /* The whole pool is exhausted now */
581 : 10 : mod1_entries[1].buf = spdk_iobuf_get(&iobuf_ch[1], bufsize, &mod1_entries[1].iobuf,
582 : : ut_iobuf_get_buf_cb);
583 : 10 : CU_ASSERT_PTR_NULL(mod1_entries[1].buf);
584 : 10 : mod0_entries[3].buf = spdk_iobuf_get(&iobuf_ch[0], bufsize, &mod0_entries[3].iobuf,
585 : : ut_iobuf_get_buf_cb);
586 : 10 : CU_ASSERT_PTR_NULL(mod0_entries[3].buf);
587 : :
588 : : /* If there are outstanding requests waiting for a buffer, they should have priority
589 : : * over filling in the cache, even if they're from different modules.
590 : : */
591 : 10 : spdk_iobuf_put(&iobuf_ch[0], mod0_entries[2].buf, bufsize);
592 : : /* Also make sure the queue is FIFO and doesn't care about which module requested
593 : : * and which module released the buffer.
594 : : */
595 : 10 : CU_ASSERT_PTR_NOT_NULL(mod1_entries[1].buf);
596 : 10 : CU_ASSERT_PTR_NULL(mod0_entries[3].buf);
597 : :
598 : : /* Return the buffers back */
599 : 10 : spdk_iobuf_entry_abort(&iobuf_ch[0], &mod0_entries[3].iobuf, bufsize);
600 [ + + ]: 30 : for (j = 0; j < 2; ++j) {
601 : 20 : spdk_iobuf_put(&iobuf_ch[0], mod0_entries[j].buf, bufsize);
602 : 20 : spdk_iobuf_put(&iobuf_ch[1], mod1_entries[j].buf, bufsize);
603 : : }
604 : : }
605 : :
606 : 5 : spdk_iobuf_channel_fini(&iobuf_ch[0]);
607 : 5 : spdk_iobuf_channel_fini(&iobuf_ch[1]);
608 : 5 : poll_threads();
609 : :
610 : 5 : spdk_iobuf_finish(ut_iobuf_finish_cb, &finish);
611 : 5 : poll_threads();
612 : :
613 : 5 : CU_ASSERT_EQUAL(finish, 1);
614 : :
615 : 5 : free_threads();
616 : 5 : free_cores();
617 : 5 : }
618 : :
619 : : static void
620 : 10 : ut_iobuf_get_buf2_cb(struct spdk_iobuf_entry *entry, void *buf)
621 : : {
622 : 10 : struct ut_iobuf_entry *ut_entry = SPDK_CONTAINEROF(entry, struct ut_iobuf_entry, iobuf);
623 : :
624 : 10 : CU_ASSERT_PTR_NOT_NULL(ut_entry->buf);
625 : 10 : CU_ASSERT_PTR_NULL(ut_entry->buf2);
626 : :
627 : 10 : ut_entry->buf2 = buf;
628 : 10 : }
629 : :
630 : : static void
631 : 10 : ut_iobuf_get_buf1_cb(struct spdk_iobuf_entry *entry, void *buf)
632 : : {
633 : 10 : struct ut_iobuf_entry *ut_entry = SPDK_CONTAINEROF(entry, struct ut_iobuf_entry, iobuf);
634 : : void *buf2;
635 : :
636 : 10 : CU_ASSERT_PTR_NULL(ut_entry->buf);
637 : 10 : CU_ASSERT_PTR_NULL(ut_entry->buf2);
638 : 10 : ut_entry->buf = buf;
639 : :
640 : 10 : buf2 = spdk_iobuf_get(ut_entry->ioch, SMALL_BUFSIZE, &ut_entry->iobuf,
641 : : ut_iobuf_get_buf2_cb);
642 : 10 : CU_ASSERT_PTR_NULL(buf2);
643 : 10 : }
644 : :
645 : : static void
646 : 5 : iobuf_priority(void)
647 : : {
648 : 5 : struct spdk_iobuf_opts opts = {
649 : : .small_pool_count = 2,
650 : : .large_pool_count = 2,
651 : : .small_bufsize = SMALL_BUFSIZE,
652 : : .large_bufsize = LARGE_BUFSIZE,
653 : : };
654 : 5 : struct ut_iobuf_entry entries[4] = {};
655 : 4 : struct spdk_iobuf_channel iobuf_ch;
656 : 5 : int rc, finish = 0;
657 : : uint32_t i;
658 : :
659 : 5 : allocate_cores(1);
660 : 5 : allocate_threads(1);
661 : :
662 : 5 : set_thread(0);
663 : :
664 : : /* We cannot use spdk_iobuf_set_opts(), as it won't allow us to use such small pools */
665 : 5 : g_iobuf.opts = opts;
666 : 5 : rc = spdk_iobuf_initialize();
667 : 5 : CU_ASSERT_EQUAL(rc, 0);
668 : :
669 : 5 : rc = spdk_iobuf_register_module("ut_module");
670 : 5 : CU_ASSERT_EQUAL(rc, 0);
671 : 5 : rc = spdk_iobuf_channel_init(&iobuf_ch, "ut_module", 0, 0);
672 : 5 : CU_ASSERT_EQUAL(rc, 0);
673 : :
674 [ + + ]: 25 : for (i = 0; i < SPDK_COUNTOF(entries); ++i) {
675 : 20 : entries[i].ioch = &iobuf_ch;
676 : : }
677 : :
678 : : /* Check that requests for an iobuf called from within the iobuf_get_cb are prioritized */
679 : 5 : entries[0].buf = spdk_iobuf_get(&iobuf_ch, SMALL_BUFSIZE, NULL, NULL);
680 : 5 : CU_ASSERT_PTR_NOT_NULL(entries[0].buf);
681 : 5 : entries[1].buf = spdk_iobuf_get(&iobuf_ch, SMALL_BUFSIZE, NULL, NULL);
682 : 5 : CU_ASSERT_PTR_NOT_NULL(entries[1].buf);
683 : :
684 : : /* Try to acquire two iobufs twice */
685 : 5 : entries[2].buf = spdk_iobuf_get(&iobuf_ch, SMALL_BUFSIZE, &entries[2].iobuf,
686 : : ut_iobuf_get_buf1_cb);
687 : 5 : CU_ASSERT_PTR_NULL(entries[2].buf);
688 : 5 : entries[3].buf = spdk_iobuf_get(&iobuf_ch, SMALL_BUFSIZE, &entries[3].iobuf,
689 : : ut_iobuf_get_buf1_cb);
690 : 5 : CU_ASSERT_PTR_NULL(entries[3].buf);
691 : :
692 : : /* Return one of the iobufs - the first entry on the wait queue should get it */
693 : 5 : spdk_iobuf_put(&iobuf_ch, entries[0].buf, SMALL_BUFSIZE);
694 : 5 : CU_ASSERT_PTR_NOT_NULL(entries[2].buf);
695 : 5 : CU_ASSERT_PTR_NULL(entries[3].buf);
696 : :
697 : : /* Return the second one, this time the same entry should get it, because it requested
698 : : * inside its iobuf_get_cb */
699 : 5 : spdk_iobuf_put(&iobuf_ch, entries[1].buf, SMALL_BUFSIZE);
700 : 5 : CU_ASSERT_PTR_NOT_NULL(entries[2].buf2);
701 : 5 : CU_ASSERT_PTR_NULL(entries[3].buf);
702 : :
703 : : /* Release it again, now the last entry should finally get it */
704 : 5 : spdk_iobuf_put(&iobuf_ch, entries[2].buf, SMALL_BUFSIZE);
705 : 5 : spdk_iobuf_put(&iobuf_ch, entries[2].buf2, SMALL_BUFSIZE);
706 : 5 : CU_ASSERT_PTR_NOT_NULL(entries[3].buf);
707 : 5 : CU_ASSERT_PTR_NOT_NULL(entries[3].buf2);
708 : 5 : spdk_iobuf_put(&iobuf_ch, entries[3].buf, SMALL_BUFSIZE);
709 : 5 : spdk_iobuf_put(&iobuf_ch, entries[3].buf2, SMALL_BUFSIZE);
710 : :
711 : 5 : spdk_iobuf_channel_fini(&iobuf_ch);
712 : 5 : poll_threads();
713 : :
714 : 5 : spdk_iobuf_finish(ut_iobuf_finish_cb, &finish);
715 : 5 : poll_threads();
716 : :
717 : 5 : CU_ASSERT_EQUAL(finish, 1);
718 : :
719 : 5 : free_threads();
720 : 5 : free_cores();
721 : 5 : }
722 : :
723 : : int
724 : 5 : main(int argc, char **argv)
725 : : {
726 : 5 : CU_pSuite suite = NULL;
727 : : unsigned int num_failures;
728 : :
729 : 5 : CU_initialize_registry();
730 : :
731 : 5 : suite = CU_add_suite("io_channel", NULL, NULL);
732 : 5 : CU_ADD_TEST(suite, iobuf);
733 : 5 : CU_ADD_TEST(suite, iobuf_cache);
734 : 5 : CU_ADD_TEST(suite, iobuf_priority);
735 : :
736 : 5 : num_failures = spdk_ut_run_tests(argc, argv, NULL);
737 : 5 : CU_cleanup_registry();
738 : 5 : return num_failures;
739 : : }
|