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