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