Spdk/test/unit/lib/thread/iobuf.c/iobuf_ut.c
Ben Walker 9aa709e25a thread: Move iobuf code to a separate compilation unit.
This makes it much easier to mock this code in unit tests without having
to mock up the entire thread library.

Change-Id: Ic3d9cb826ae71af780a06f88669c37cef2c9a4ae
Signed-off-by: Ben Walker <benjamin.walker@intel.com>
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/16173
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Community-CI: Mellanox Build Bot
Reviewed-by: Aleksey Marchuk <alexeymar@nvidia.com>
2023-05-09 17:58:11 +08:00

648 lines
22 KiB
C

/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (C) 2023 Intel Corporation. All rights reserved.
*/
#include "spdk_cunit.h"
#include "common/lib/ut_multithread.c"
#include "unit/lib/json_mock.c"
#include "spdk/config.h"
#include "spdk/thread.h"
#include "thread/iobuf.c"
struct ut_iobuf_entry {
struct spdk_iobuf_channel *ioch;
struct spdk_iobuf_entry iobuf;
void *buf;
uint32_t thread_id;
const char *module;
};
static void
ut_iobuf_finish_cb(void *ctx)
{
*(int *)ctx = 1;
}
static void
ut_iobuf_get_buf_cb(struct spdk_iobuf_entry *entry, void *buf)
{
struct ut_iobuf_entry *ut_entry = SPDK_CONTAINEROF(entry, struct ut_iobuf_entry, iobuf);
ut_entry->buf = buf;
}
static int
ut_iobuf_foreach_cb(struct spdk_iobuf_channel *ch, struct spdk_iobuf_entry *entry, void *cb_arg)
{
struct ut_iobuf_entry *ut_entry = SPDK_CONTAINEROF(entry, struct ut_iobuf_entry, iobuf);
ut_entry->buf = cb_arg;
return 0;
}
#define SMALL_BUFSIZE 128
#define LARGE_BUFSIZE 512
static void
iobuf(void)
{
struct spdk_iobuf_opts opts = {
.small_pool_count = 2,
.large_pool_count = 2,
.small_bufsize = SMALL_BUFSIZE,
.large_bufsize = LARGE_BUFSIZE,
};
struct ut_iobuf_entry *entry;
struct spdk_iobuf_channel mod0_ch[2], mod1_ch[2];
struct ut_iobuf_entry mod0_entries[] = {
{ .thread_id = 0, .module = "ut_module0", },
{ .thread_id = 0, .module = "ut_module0", },
{ .thread_id = 0, .module = "ut_module0", },
{ .thread_id = 0, .module = "ut_module0", },
{ .thread_id = 1, .module = "ut_module0", },
{ .thread_id = 1, .module = "ut_module0", },
{ .thread_id = 1, .module = "ut_module0", },
{ .thread_id = 1, .module = "ut_module0", },
};
struct ut_iobuf_entry mod1_entries[] = {
{ .thread_id = 0, .module = "ut_module1", },
{ .thread_id = 0, .module = "ut_module1", },
{ .thread_id = 0, .module = "ut_module1", },
{ .thread_id = 0, .module = "ut_module1", },
{ .thread_id = 1, .module = "ut_module1", },
{ .thread_id = 1, .module = "ut_module1", },
{ .thread_id = 1, .module = "ut_module1", },
{ .thread_id = 1, .module = "ut_module1", },
};
int rc, finish = 0;
uint32_t i;
allocate_cores(2);
allocate_threads(2);
set_thread(0);
/* We cannot use spdk_iobuf_set_opts(), as it won't allow us to use such small pools */
g_iobuf.opts = opts;
rc = spdk_iobuf_initialize();
CU_ASSERT_EQUAL(rc, 0);
rc = spdk_iobuf_register_module("ut_module0");
CU_ASSERT_EQUAL(rc, 0);
rc = spdk_iobuf_register_module("ut_module1");
CU_ASSERT_EQUAL(rc, 0);
set_thread(0);
rc = spdk_iobuf_channel_init(&mod0_ch[0], "ut_module0", 0, 0);
CU_ASSERT_EQUAL(rc, 0);
set_thread(1);
rc = spdk_iobuf_channel_init(&mod0_ch[1], "ut_module0", 0, 0);
CU_ASSERT_EQUAL(rc, 0);
for (i = 0; i < SPDK_COUNTOF(mod0_entries); ++i) {
mod0_entries[i].ioch = &mod0_ch[mod0_entries[i].thread_id];
}
set_thread(0);
rc = spdk_iobuf_channel_init(&mod1_ch[0], "ut_module1", 0, 0);
CU_ASSERT_EQUAL(rc, 0);
set_thread(1);
rc = spdk_iobuf_channel_init(&mod1_ch[1], "ut_module1", 0, 0);
CU_ASSERT_EQUAL(rc, 0);
for (i = 0; i < SPDK_COUNTOF(mod1_entries); ++i) {
mod1_entries[i].ioch = &mod1_ch[mod1_entries[i].thread_id];
}
/* First check that it's possible to retrieve the whole pools from a single module */
set_thread(0);
entry = &mod0_entries[0];
entry->buf = spdk_iobuf_get(entry->ioch, LARGE_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NOT_NULL(entry->buf);
entry = &mod0_entries[1];
entry->buf = spdk_iobuf_get(entry->ioch, LARGE_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NOT_NULL(entry->buf);
/* The next two should be put onto the large buf wait queue */
entry = &mod0_entries[2];
entry->buf = spdk_iobuf_get(entry->ioch, LARGE_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NULL(entry->buf);
entry = &mod0_entries[3];
entry->buf = spdk_iobuf_get(entry->ioch, LARGE_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NULL(entry->buf);
/* Pick the two next buffers from the small pool */
set_thread(1);
entry = &mod0_entries[4];
entry->buf = spdk_iobuf_get(entry->ioch, SMALL_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NOT_NULL(entry->buf);
entry = &mod0_entries[5];
entry->buf = spdk_iobuf_get(entry->ioch, SMALL_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NOT_NULL(entry->buf);
/* The next two should be put onto the small buf wait queue */
entry = &mod0_entries[6];
entry->buf = spdk_iobuf_get(entry->ioch, SMALL_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NULL(entry->buf);
entry = &mod0_entries[7];
entry->buf = spdk_iobuf_get(entry->ioch, SMALL_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NULL(entry->buf);
/* Now return one of the large buffers to the pool and verify that the first request's
* (entry 2) callback was executed and it was removed from the wait queue.
*/
set_thread(0);
entry = &mod0_entries[0];
spdk_iobuf_put(entry->ioch, entry->buf, LARGE_BUFSIZE);
entry = &mod0_entries[2];
CU_ASSERT_PTR_NOT_NULL(entry->buf);
entry = &mod0_entries[3];
CU_ASSERT_PTR_NULL(entry->buf);
/* Return the second buffer and check that the other request is satisfied */
entry = &mod0_entries[1];
spdk_iobuf_put(entry->ioch, entry->buf, LARGE_BUFSIZE);
entry = &mod0_entries[3];
CU_ASSERT_PTR_NOT_NULL(entry->buf);
/* Return the remaining two buffers */
entry = &mod0_entries[2];
spdk_iobuf_put(entry->ioch, entry->buf, LARGE_BUFSIZE);
entry = &mod0_entries[3];
spdk_iobuf_put(entry->ioch, entry->buf, LARGE_BUFSIZE);
/* Check that it didn't change the requests waiting for the small buffers */
entry = &mod0_entries[6];
CU_ASSERT_PTR_NULL(entry->buf);
entry = &mod0_entries[7];
CU_ASSERT_PTR_NULL(entry->buf);
/* Do the same test as above, this time using the small pool */
set_thread(1);
entry = &mod0_entries[4];
spdk_iobuf_put(entry->ioch, entry->buf, SMALL_BUFSIZE);
entry = &mod0_entries[6];
CU_ASSERT_PTR_NOT_NULL(entry->buf);
entry = &mod0_entries[7];
CU_ASSERT_PTR_NULL(entry->buf);
/* Return the second buffer and check that the other request is satisfied */
entry = &mod0_entries[5];
spdk_iobuf_put(entry->ioch, entry->buf, SMALL_BUFSIZE);
entry = &mod0_entries[7];
CU_ASSERT_PTR_NOT_NULL(entry->buf);
/* Return the remaining two buffers */
entry = &mod0_entries[6];
spdk_iobuf_put(entry->ioch, entry->buf, SMALL_BUFSIZE);
entry = &mod0_entries[7];
spdk_iobuf_put(entry->ioch, entry->buf, SMALL_BUFSIZE);
/* Now check requesting buffers from different modules - first request all of them from one
* module, starting from the large pool
*/
set_thread(0);
entry = &mod0_entries[0];
entry->buf = spdk_iobuf_get(entry->ioch, LARGE_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NOT_NULL(entry->buf);
entry = &mod0_entries[1];
entry->buf = spdk_iobuf_get(entry->ioch, LARGE_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NOT_NULL(entry->buf);
/* Request all of them from the small one */
set_thread(1);
entry = &mod0_entries[4];
entry->buf = spdk_iobuf_get(entry->ioch, SMALL_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NOT_NULL(entry->buf);
entry = &mod0_entries[5];
entry->buf = spdk_iobuf_get(entry->ioch, SMALL_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NOT_NULL(entry->buf);
/* Request one buffer per module from each pool */
set_thread(0);
entry = &mod1_entries[0];
entry->buf = spdk_iobuf_get(entry->ioch, LARGE_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NULL(entry->buf);
entry = &mod0_entries[3];
entry->buf = spdk_iobuf_get(entry->ioch, LARGE_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NULL(entry->buf);
/* Change the order from the small pool and request a buffer from mod0 first */
set_thread(1);
entry = &mod0_entries[6];
entry->buf = spdk_iobuf_get(entry->ioch, SMALL_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NULL(entry->buf);
entry = &mod1_entries[4];
entry->buf = spdk_iobuf_get(entry->ioch, SMALL_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NULL(entry->buf);
/* Now return one buffer to the large pool */
set_thread(0);
entry = &mod0_entries[0];
spdk_iobuf_put(entry->ioch, entry->buf, LARGE_BUFSIZE);
/* Make sure the request from mod1 got the buffer, as it was the first to request it */
entry = &mod1_entries[0];
CU_ASSERT_PTR_NOT_NULL(entry->buf);
entry = &mod0_entries[3];
CU_ASSERT_PTR_NULL(entry->buf);
/* Return second buffer to the large pool and check the outstanding mod0 request */
entry = &mod0_entries[1];
spdk_iobuf_put(entry->ioch, entry->buf, LARGE_BUFSIZE);
entry = &mod0_entries[3];
CU_ASSERT_PTR_NOT_NULL(entry->buf);
/* Return the remaining two buffers */
entry = &mod1_entries[0];
spdk_iobuf_put(entry->ioch, entry->buf, LARGE_BUFSIZE);
entry = &mod0_entries[3];
spdk_iobuf_put(entry->ioch, entry->buf, LARGE_BUFSIZE);
/* Check the same for the small pool, but this time the order of the request is reversed
* (mod0 before mod1)
*/
set_thread(1);
entry = &mod0_entries[4];
spdk_iobuf_put(entry->ioch, entry->buf, SMALL_BUFSIZE);
entry = &mod0_entries[6];
CU_ASSERT_PTR_NOT_NULL(entry->buf);
/* mod1 request was second in this case, so it still needs to wait */
entry = &mod1_entries[4];
CU_ASSERT_PTR_NULL(entry->buf);
/* Return the second requested buffer */
entry = &mod0_entries[5];
spdk_iobuf_put(entry->ioch, entry->buf, SMALL_BUFSIZE);
entry = &mod1_entries[4];
CU_ASSERT_PTR_NOT_NULL(entry->buf);
/* Return the remaining two buffers */
entry = &mod0_entries[6];
spdk_iobuf_put(entry->ioch, entry->buf, SMALL_BUFSIZE);
entry = &mod1_entries[4];
spdk_iobuf_put(entry->ioch, entry->buf, SMALL_BUFSIZE);
/* Request buffers to make the pools empty */
set_thread(0);
entry = &mod0_entries[0];
entry->buf = spdk_iobuf_get(entry->ioch, LARGE_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NOT_NULL(entry->buf);
entry = &mod1_entries[0];
entry->buf = spdk_iobuf_get(entry->ioch, LARGE_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NOT_NULL(entry->buf);
entry = &mod0_entries[1];
entry->buf = spdk_iobuf_get(entry->ioch, SMALL_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NOT_NULL(entry->buf);
entry = &mod1_entries[1];
entry->buf = spdk_iobuf_get(entry->ioch, SMALL_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NOT_NULL(entry->buf);
/* Queue more requests from both modules */
entry = &mod0_entries[2];
entry->buf = spdk_iobuf_get(entry->ioch, LARGE_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NULL(entry->buf);
entry = &mod1_entries[2];
entry->buf = spdk_iobuf_get(entry->ioch, LARGE_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NULL(entry->buf);
entry = &mod1_entries[3];
entry->buf = spdk_iobuf_get(entry->ioch, SMALL_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NULL(entry->buf);
entry = &mod0_entries[3];
entry->buf = spdk_iobuf_get(entry->ioch, SMALL_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NULL(entry->buf);
/* Check that abort correctly remove an entry from the queue */
entry = &mod0_entries[2];
spdk_iobuf_entry_abort(entry->ioch, &entry->iobuf, LARGE_BUFSIZE);
entry = &mod1_entries[3];
spdk_iobuf_entry_abort(entry->ioch, &entry->iobuf, SMALL_BUFSIZE);
entry = &mod0_entries[0];
spdk_iobuf_put(entry->ioch, entry->buf, LARGE_BUFSIZE);
CU_ASSERT_PTR_NOT_NULL(mod1_entries[2].buf);
entry = &mod0_entries[1];
spdk_iobuf_put(entry->ioch, entry->buf, SMALL_BUFSIZE);
CU_ASSERT_PTR_NOT_NULL(mod0_entries[3].buf);
/* Clean up */
entry = &mod1_entries[0];
spdk_iobuf_put(entry->ioch, entry->buf, LARGE_BUFSIZE);
entry = &mod1_entries[2];
spdk_iobuf_put(entry->ioch, entry->buf, LARGE_BUFSIZE);
entry = &mod1_entries[1];
spdk_iobuf_put(entry->ioch, entry->buf, SMALL_BUFSIZE);
entry = &mod0_entries[3];
spdk_iobuf_put(entry->ioch, entry->buf, SMALL_BUFSIZE);
/* Request buffers to make the pools empty */
set_thread(0);
entry = &mod0_entries[0];
entry->buf = spdk_iobuf_get(entry->ioch, LARGE_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NOT_NULL(entry->buf);
entry = &mod1_entries[0];
entry->buf = spdk_iobuf_get(entry->ioch, LARGE_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NOT_NULL(entry->buf);
entry = &mod0_entries[1];
entry->buf = spdk_iobuf_get(entry->ioch, SMALL_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NOT_NULL(entry->buf);
entry = &mod1_entries[1];
entry->buf = spdk_iobuf_get(entry->ioch, SMALL_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NOT_NULL(entry->buf);
/* Request a buffer from each queue and each module on thread 0 */
set_thread(0);
entry = &mod0_entries[2];
entry->buf = spdk_iobuf_get(entry->ioch, LARGE_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NULL(entry->buf);
entry = &mod1_entries[2];
entry->buf = spdk_iobuf_get(entry->ioch, LARGE_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NULL(entry->buf);
entry = &mod0_entries[3];
entry->buf = spdk_iobuf_get(entry->ioch, SMALL_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NULL(entry->buf);
entry = &mod1_entries[3];
entry->buf = spdk_iobuf_get(entry->ioch, SMALL_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NULL(entry->buf);
/* Do the same on thread 1 */
set_thread(1);
entry = &mod0_entries[6];
entry->buf = spdk_iobuf_get(entry->ioch, LARGE_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NULL(entry->buf);
entry = &mod1_entries[6];
entry->buf = spdk_iobuf_get(entry->ioch, LARGE_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NULL(entry->buf);
entry = &mod0_entries[7];
entry->buf = spdk_iobuf_get(entry->ioch, SMALL_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NULL(entry->buf);
entry = &mod1_entries[7];
entry->buf = spdk_iobuf_get(entry->ioch, SMALL_BUFSIZE, &entry->iobuf, ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NULL(entry->buf);
/* Now do the foreach and check that correct entries are iterated over by assigning their
* ->buf pointers to different values.
*/
set_thread(0);
rc = spdk_iobuf_for_each_entry(&mod0_ch[0], &mod0_ch[0].large,
ut_iobuf_foreach_cb, (void *)0xdeadbeef);
CU_ASSERT_EQUAL(rc, 0);
rc = spdk_iobuf_for_each_entry(&mod0_ch[0], &mod0_ch[0].small,
ut_iobuf_foreach_cb, (void *)0xbeefdead);
CU_ASSERT_EQUAL(rc, 0);
rc = spdk_iobuf_for_each_entry(&mod1_ch[0], &mod1_ch[0].large,
ut_iobuf_foreach_cb, (void *)0xfeedbeef);
CU_ASSERT_EQUAL(rc, 0);
rc = spdk_iobuf_for_each_entry(&mod1_ch[0], &mod1_ch[0].small,
ut_iobuf_foreach_cb, (void *)0xbeeffeed);
CU_ASSERT_EQUAL(rc, 0);
set_thread(1);
rc = spdk_iobuf_for_each_entry(&mod0_ch[1], &mod0_ch[1].large,
ut_iobuf_foreach_cb, (void *)0xcafebabe);
CU_ASSERT_EQUAL(rc, 0);
rc = spdk_iobuf_for_each_entry(&mod0_ch[1], &mod0_ch[1].small,
ut_iobuf_foreach_cb, (void *)0xbabecafe);
CU_ASSERT_EQUAL(rc, 0);
rc = spdk_iobuf_for_each_entry(&mod1_ch[1], &mod1_ch[1].large,
ut_iobuf_foreach_cb, (void *)0xbeefcafe);
CU_ASSERT_EQUAL(rc, 0);
rc = spdk_iobuf_for_each_entry(&mod1_ch[1], &mod1_ch[1].small,
ut_iobuf_foreach_cb, (void *)0xcafebeef);
CU_ASSERT_EQUAL(rc, 0);
/* thread 0 */
CU_ASSERT_PTR_EQUAL(mod0_entries[2].buf, (void *)0xdeadbeef);
CU_ASSERT_PTR_EQUAL(mod0_entries[3].buf, (void *)0xbeefdead);
CU_ASSERT_PTR_EQUAL(mod1_entries[2].buf, (void *)0xfeedbeef);
CU_ASSERT_PTR_EQUAL(mod1_entries[3].buf, (void *)0xbeeffeed);
/* thread 1 */
CU_ASSERT_PTR_EQUAL(mod0_entries[6].buf, (void *)0xcafebabe);
CU_ASSERT_PTR_EQUAL(mod0_entries[7].buf, (void *)0xbabecafe);
CU_ASSERT_PTR_EQUAL(mod1_entries[6].buf, (void *)0xbeefcafe);
CU_ASSERT_PTR_EQUAL(mod1_entries[7].buf, (void *)0xcafebeef);
/* Clean everything up */
set_thread(0);
entry = &mod0_entries[2];
spdk_iobuf_entry_abort(entry->ioch, &entry->iobuf, LARGE_BUFSIZE);
entry = &mod0_entries[3];
spdk_iobuf_entry_abort(entry->ioch, &entry->iobuf, SMALL_BUFSIZE);
entry = &mod1_entries[2];
spdk_iobuf_entry_abort(entry->ioch, &entry->iobuf, LARGE_BUFSIZE);
entry = &mod1_entries[3];
spdk_iobuf_entry_abort(entry->ioch, &entry->iobuf, SMALL_BUFSIZE);
entry = &mod0_entries[0];
spdk_iobuf_put(entry->ioch, entry->buf, LARGE_BUFSIZE);
entry = &mod1_entries[0];
spdk_iobuf_put(entry->ioch, entry->buf, LARGE_BUFSIZE);
entry = &mod0_entries[1];
spdk_iobuf_put(entry->ioch, entry->buf, SMALL_BUFSIZE);
entry = &mod1_entries[1];
spdk_iobuf_put(entry->ioch, entry->buf, SMALL_BUFSIZE);
set_thread(1);
entry = &mod0_entries[6];
spdk_iobuf_entry_abort(entry->ioch, &entry->iobuf, LARGE_BUFSIZE);
entry = &mod0_entries[7];
spdk_iobuf_entry_abort(entry->ioch, &entry->iobuf, SMALL_BUFSIZE);
entry = &mod1_entries[6];
spdk_iobuf_entry_abort(entry->ioch, &entry->iobuf, LARGE_BUFSIZE);
entry = &mod1_entries[7];
spdk_iobuf_entry_abort(entry->ioch, &entry->iobuf, SMALL_BUFSIZE);
set_thread(0);
spdk_iobuf_channel_fini(&mod0_ch[0]);
poll_threads();
spdk_iobuf_channel_fini(&mod1_ch[0]);
poll_threads();
set_thread(1);
spdk_iobuf_channel_fini(&mod0_ch[1]);
poll_threads();
spdk_iobuf_channel_fini(&mod1_ch[1]);
poll_threads();
spdk_iobuf_finish(ut_iobuf_finish_cb, &finish);
poll_threads();
CU_ASSERT_EQUAL(finish, 1);
free_threads();
free_cores();
}
static void
iobuf_cache(void)
{
struct spdk_iobuf_opts opts = {
.small_pool_count = 4,
.large_pool_count = 4,
.small_bufsize = SMALL_BUFSIZE,
.large_bufsize = LARGE_BUFSIZE,
};
struct spdk_iobuf_channel iobuf_ch[2];
struct ut_iobuf_entry *entry;
struct ut_iobuf_entry mod0_entries[] = {
{ .thread_id = 0, .module = "ut_module0", },
{ .thread_id = 0, .module = "ut_module0", },
{ .thread_id = 0, .module = "ut_module0", },
{ .thread_id = 0, .module = "ut_module0", },
};
struct ut_iobuf_entry mod1_entries[] = {
{ .thread_id = 0, .module = "ut_module1", },
{ .thread_id = 0, .module = "ut_module1", },
};
int rc, finish = 0;
uint32_t i, j, bufsize;
allocate_cores(1);
allocate_threads(1);
set_thread(0);
/* We cannot use spdk_iobuf_set_opts(), as it won't allow us to use such small pools */
g_iobuf.opts = opts;
rc = spdk_iobuf_initialize();
CU_ASSERT_EQUAL(rc, 0);
rc = spdk_iobuf_register_module("ut_module0");
CU_ASSERT_EQUAL(rc, 0);
rc = spdk_iobuf_register_module("ut_module1");
CU_ASSERT_EQUAL(rc, 0);
/* First check that channel initialization fails when it's not possible to fill in the cache
* from the pool.
*/
rc = spdk_iobuf_channel_init(&iobuf_ch[0], "ut_module0", 5, 1);
CU_ASSERT_EQUAL(rc, -ENOMEM);
rc = spdk_iobuf_channel_init(&iobuf_ch[0], "ut_module0", 1, 5);
CU_ASSERT_EQUAL(rc, -ENOMEM);
rc = spdk_iobuf_channel_init(&iobuf_ch[0], "ut_module0", 4, 4);
CU_ASSERT_EQUAL(rc, 0);
rc = spdk_iobuf_channel_init(&iobuf_ch[1], "ut_module1", 4, 4);
CU_ASSERT_EQUAL(rc, -ENOMEM);
spdk_iobuf_channel_fini(&iobuf_ch[0]);
poll_threads();
/* Initialize one channel with cache, acquire buffers, and check that a second one can be
* created once the buffers acquired from the first one are returned to the pool
*/
rc = spdk_iobuf_channel_init(&iobuf_ch[0], "ut_module0", 2, 2);
CU_ASSERT_EQUAL(rc, 0);
for (i = 0; i < 3; ++i) {
mod0_entries[i].buf = spdk_iobuf_get(&iobuf_ch[0], LARGE_BUFSIZE, &mod0_entries[i].iobuf,
ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NOT_NULL(mod0_entries[i].buf);
}
/* It should be able to create a channel with a single entry in the cache */
rc = spdk_iobuf_channel_init(&iobuf_ch[1], "ut_module1", 2, 1);
CU_ASSERT_EQUAL(rc, 0);
spdk_iobuf_channel_fini(&iobuf_ch[1]);
poll_threads();
/* But not with two entries */
rc = spdk_iobuf_channel_init(&iobuf_ch[1], "ut_module1", 2, 2);
CU_ASSERT_EQUAL(rc, -ENOMEM);
for (i = 0; i < 2; ++i) {
spdk_iobuf_put(&iobuf_ch[0], mod0_entries[i].buf, LARGE_BUFSIZE);
rc = spdk_iobuf_channel_init(&iobuf_ch[1], "ut_module1", 2, 2);
CU_ASSERT_EQUAL(rc, -ENOMEM);
}
spdk_iobuf_put(&iobuf_ch[0], mod0_entries[2].buf, LARGE_BUFSIZE);
/* The last buffer should be released back to the pool, so we should be able to create a new
* channel
*/
rc = spdk_iobuf_channel_init(&iobuf_ch[1], "ut_module1", 2, 2);
CU_ASSERT_EQUAL(rc, 0);
spdk_iobuf_channel_fini(&iobuf_ch[0]);
spdk_iobuf_channel_fini(&iobuf_ch[1]);
poll_threads();
/* Check that the pool is only used when the cache is empty and that the cache guarantees a
* certain set of buffers
*/
rc = spdk_iobuf_channel_init(&iobuf_ch[0], "ut_module0", 2, 2);
CU_ASSERT_EQUAL(rc, 0);
rc = spdk_iobuf_channel_init(&iobuf_ch[1], "ut_module1", 1, 1);
CU_ASSERT_EQUAL(rc, 0);
uint32_t buffer_sizes[] = { SMALL_BUFSIZE, LARGE_BUFSIZE };
for (i = 0; i < SPDK_COUNTOF(buffer_sizes); ++i) {
bufsize = buffer_sizes[i];
for (j = 0; j < 3; ++j) {
entry = &mod0_entries[j];
entry->buf = spdk_iobuf_get(&iobuf_ch[0], bufsize, &entry->iobuf,
ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NOT_NULL(entry->buf);
}
mod1_entries[0].buf = spdk_iobuf_get(&iobuf_ch[1], bufsize, &mod1_entries[0].iobuf,
ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NOT_NULL(mod1_entries[0].buf);
/* The whole pool is exhausted now */
mod1_entries[1].buf = spdk_iobuf_get(&iobuf_ch[1], bufsize, &mod1_entries[1].iobuf,
ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NULL(mod1_entries[1].buf);
mod0_entries[3].buf = spdk_iobuf_get(&iobuf_ch[0], bufsize, &mod0_entries[3].iobuf,
ut_iobuf_get_buf_cb);
CU_ASSERT_PTR_NULL(mod0_entries[3].buf);
/* If there are outstanding requests waiting for a buffer, they should have priority
* over filling in the cache, even if they're from different modules.
*/
spdk_iobuf_put(&iobuf_ch[0], mod0_entries[2].buf, bufsize);
/* Also make sure the queue is FIFO and doesn't care about which module requested
* and which module released the buffer.
*/
CU_ASSERT_PTR_NOT_NULL(mod1_entries[1].buf);
CU_ASSERT_PTR_NULL(mod0_entries[3].buf);
/* Return the buffers back */
spdk_iobuf_entry_abort(&iobuf_ch[0], &mod0_entries[3].iobuf, bufsize);
for (j = 0; j < 2; ++j) {
spdk_iobuf_put(&iobuf_ch[0], mod0_entries[j].buf, bufsize);
spdk_iobuf_put(&iobuf_ch[1], mod1_entries[j].buf, bufsize);
}
}
spdk_iobuf_channel_fini(&iobuf_ch[0]);
spdk_iobuf_channel_fini(&iobuf_ch[1]);
poll_threads();
spdk_iobuf_finish(ut_iobuf_finish_cb, &finish);
poll_threads();
CU_ASSERT_EQUAL(finish, 1);
free_threads();
free_cores();
}
int
main(int argc, char **argv)
{
CU_pSuite suite = NULL;
unsigned int num_failures;
CU_set_error_action(CUEA_ABORT);
CU_initialize_registry();
suite = CU_add_suite("io_channel", NULL, NULL);
CU_ADD_TEST(suite, iobuf);
CU_ADD_TEST(suite, iobuf_cache);
CU_basic_set_mode(CU_BRM_VERBOSE);
CU_basic_run_tests();
num_failures = CU_get_number_of_failures();
CU_cleanup_registry();
return num_failures;
}