Spdk/test/unit/lib/bdev/raid/concat.c/concat_ut.c
Konrad Sztyber 55f9479333 bdev: remove spdk_bdev_ext_io_opts from spdk_bdev_io
The spdk_bdev_ext_io_opts structure is used to pass extra options when
submitting a bdev IO request, without having to modify/add functions to
handle new options.  Additionally, the structure has a size field to
allow adding new fields without breaking the ABI (and thus having to
bump up the major version of a library).

It is also a part of spdk_bdev_io and there are several reasons for
removing it from that structure:

  1. The size field only makes sense in structures that are passed
     through pointers.  And spdk_bdev_ext_io_opts is indeed passed as a
     pointer to spdk_bdev_{readv,writev}_blocks_ext(), however it is
     also embedded in spdk_bdev_io (internal.ext_opts_copy), which is
     also part of the API.  It means that each time a new field is added
     to spdk_bdev_ext_io_opts, the size of spdk_bdev_io will also
     change, so we will need to bump the major version of libspdk_bdev
     anyway, thus making spdk_bdev_ext_io_opts.size useless.
  2. The size field also makes internal.ext_opts cumbersome to use, as
     each time one of its fields is accessed, we need to check the size.
     Currently the code doesn't do that, because all of the existing
     spdk_bdev_ext_io_opts fields were present when this structure was
     initially introduced, but we'd need to do check the size before
     accessing any new fields.
  3. spdk_bdev_ext_io_opts has a metadata field, while spdk_bdev_io
     already has u.bdev.md_buf, which means that we store the same thing
     in several different places in spdk_bdev_io (u.bdev.md_buf,
     u.bdev.ext_opts->metadata, internal.ext_opts->metadata).

Therefore, this patch removes all references to spdk_bdev_ext_io_opts
from spdk_bdev_io and replaces them with fields (memory_domain,
memory_domain_ctx) that were missing in spdk_bdev_io.  Unfortunately,
this change breaks the API and requires changes in bdev modules that
supported spdk_bdev_io.u.bdev.ext_opts.

Signed-off-by: Konrad Sztyber <konrad.sztyber@intel.com>
Change-Id: I49b7524eb84d1d4d7f12b7ab025fec36da1ee01f
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/16773
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Aleksey Marchuk <alexeymar@nvidia.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
2023-02-16 10:09:35 +00:00

504 lines
14 KiB
C

/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (C) 2022 Intel Corporation.
* All rights reserved.
*/
#include "spdk/stdinc.h"
#include "spdk_cunit.h"
#include "spdk/env.h"
#include "thread/thread_internal.h"
#include "spdk_internal/mock.h"
#include "bdev/raid/bdev_raid.h"
#include "bdev/raid/concat.c"
#include "../common.c"
DEFINE_STUB(spdk_bdev_readv_blocks_with_md, int, (struct spdk_bdev_desc *desc,
struct spdk_io_channel *ch,
struct iovec *iov, int iovcnt, void *md,
uint64_t offset_blocks, uint64_t num_blocks,
spdk_bdev_io_completion_cb cb, void *cb_arg), 0);
DEFINE_STUB(spdk_bdev_writev_blocks_with_md, int, (struct spdk_bdev_desc *desc,
struct spdk_io_channel *ch,
struct iovec *iov, int iovcnt, void *md,
uint64_t offset_blocks, uint64_t num_blocks,
spdk_bdev_io_completion_cb cb, void *cb_arg), 0);
#define BLOCK_LEN (4096)
enum CONCAT_IO_TYPE {
CONCAT_NONE = 0,
CONCAT_WRITEV,
CONCAT_READV,
CONCAT_FLUSH,
CONCAT_UNMAP,
};
#define MAX_RECORDS (10)
/*
* Store the information of io requests sent to the underlying bdevs.
* For a single null payload request to the concat bdev,
* we may send multiple requests to the underling bdevs,
* so we store the io request information to arrays.
*/
struct req_records {
uint64_t offset_blocks[MAX_RECORDS];
uint64_t num_blocks[MAX_RECORDS];
enum CONCAT_IO_TYPE io_type[MAX_RECORDS];
int count;
void *md;
} g_req_records;
/*
* g_succeed is true means the spdk_bdev_readv/writev/unmap/flush_blocks
* functions will return 0.
* g_succeed is false means the spdk_bdev_readv/writev/unmap/flush_blocks
* functions will return -ENOMEM.
* We always set it to false before an IO request, then the raid_bdev_queue_io_wait
* function will re-submit the request, and the raid_bdev_queue_io_wait function will
* set g_succeed to true, then the IO will succeed next time.
*/
bool g_succeed;
DEFINE_STUB_V(raid_bdev_module_list_add, (struct raid_bdev_module *raid_module));
DEFINE_STUB_V(raid_bdev_io_complete, (struct raid_bdev_io *raid_io,
enum spdk_bdev_io_status status));
DEFINE_STUB_V(spdk_bdev_free_io, (struct spdk_bdev_io *bdev_io));
DEFINE_STUB(raid_bdev_io_complete_part, bool,
(struct raid_bdev_io *raid_io, uint64_t completed,
enum spdk_bdev_io_status status),
true);
int
spdk_bdev_readv_blocks_ext(struct spdk_bdev_desc *desc, struct spdk_io_channel *ch,
struct iovec *iov, int iovcnt, uint64_t offset_blocks, uint64_t num_blocks,
spdk_bdev_io_completion_cb cb, void *cb_arg, struct spdk_bdev_ext_io_opts *opts)
{
if (g_succeed) {
int i = g_req_records.count;
g_req_records.offset_blocks[i] = offset_blocks;
g_req_records.num_blocks[i] = num_blocks;
g_req_records.io_type[i] = CONCAT_READV;
g_req_records.count++;
cb(NULL, true, cb_arg);
g_req_records.md = opts->metadata;
return 0;
} else {
return -ENOMEM;
}
}
int
spdk_bdev_writev_blocks_ext(struct spdk_bdev_desc *desc, struct spdk_io_channel *ch,
struct iovec *iov, int iovcnt, uint64_t offset_blocks, uint64_t num_blocks,
spdk_bdev_io_completion_cb cb, void *cb_arg, struct spdk_bdev_ext_io_opts *opts)
{
if (g_succeed) {
int i = g_req_records.count;
g_req_records.offset_blocks[i] = offset_blocks;
g_req_records.num_blocks[i] = num_blocks;
g_req_records.io_type[i] = CONCAT_WRITEV;
g_req_records.count++;
cb(NULL, true, cb_arg);
g_req_records.md = opts->metadata;
return 0;
} else {
return -ENOMEM;
}
}
int
spdk_bdev_unmap_blocks(struct spdk_bdev_desc *desc, struct spdk_io_channel *ch,
uint64_t offset_blocks, uint64_t num_blocks,
spdk_bdev_io_completion_cb cb, void *cb_arg)
{
if (g_succeed) {
int i = g_req_records.count;
g_req_records.offset_blocks[i] = offset_blocks;
g_req_records.num_blocks[i] = num_blocks;
g_req_records.io_type[i] = CONCAT_UNMAP;
g_req_records.count++;
cb(NULL, true, cb_arg);
return 0;
} else {
return -ENOMEM;
}
}
int
spdk_bdev_flush_blocks(struct spdk_bdev_desc *desc, struct spdk_io_channel *ch,
uint64_t offset_blocks, uint64_t num_blocks,
spdk_bdev_io_completion_cb cb, void *cb_arg)
{
if (g_succeed) {
int i = g_req_records.count;
g_req_records.offset_blocks[i] = offset_blocks;
g_req_records.num_blocks[i] = num_blocks;
g_req_records.io_type[i] = CONCAT_FLUSH;
g_req_records.count++;
cb(NULL, true, cb_arg);
return 0;
} else {
return -ENOMEM;
}
}
void
raid_bdev_queue_io_wait(struct raid_bdev_io *raid_io, struct spdk_bdev *bdev,
struct spdk_io_channel *ch, spdk_bdev_io_wait_cb cb_fn)
{
g_succeed = true;
cb_fn(raid_io);
}
static void
init_globals(void)
{
int i;
for (i = 0; i < MAX_RECORDS; i++) {
g_req_records.offset_blocks[i] = 0;
g_req_records.num_blocks[i] = 0;
g_req_records.io_type[i] = CONCAT_NONE;
}
g_req_records.count = 0;
g_succeed = false;
}
static int
test_setup(void)
{
uint8_t num_base_bdevs_values[] = { 3, 4, 5 };
uint64_t base_bdev_blockcnt_values[] = { 1, 1024, 1024 * 1024 };
uint32_t base_bdev_blocklen_values[] = { 512, 4096 };
uint32_t strip_size_kb_values[] = { 1, 4, 128 };
uint8_t *num_base_bdevs;
uint64_t *base_bdev_blockcnt;
uint32_t *base_bdev_blocklen;
uint32_t *strip_size_kb;
struct raid_params params;
uint64_t params_count;
int rc;
params_count = SPDK_COUNTOF(num_base_bdevs_values) *
SPDK_COUNTOF(base_bdev_blockcnt_values) *
SPDK_COUNTOF(base_bdev_blocklen_values) *
SPDK_COUNTOF(strip_size_kb_values);
rc = raid_test_params_alloc(params_count);
if (rc) {
return rc;
}
ARRAY_FOR_EACH(num_base_bdevs_values, num_base_bdevs) {
ARRAY_FOR_EACH(base_bdev_blockcnt_values, base_bdev_blockcnt) {
ARRAY_FOR_EACH(base_bdev_blocklen_values, base_bdev_blocklen) {
ARRAY_FOR_EACH(strip_size_kb_values, strip_size_kb) {
params.num_base_bdevs = *num_base_bdevs;
params.base_bdev_blockcnt = *base_bdev_blockcnt;
params.base_bdev_blocklen = *base_bdev_blocklen;
params.strip_size = *strip_size_kb * 1024 / *base_bdev_blocklen;
params.md_len = 0;
if (params.strip_size == 0 ||
params.strip_size > *base_bdev_blockcnt) {
continue;
}
raid_test_params_add(&params);
}
}
}
}
return 0;
}
static int
test_cleanup(void)
{
raid_test_params_free();
return 0;
}
static struct raid_bdev *
create_concat(struct raid_params *params)
{
struct raid_bdev *raid_bdev = raid_test_create_raid_bdev(params, &g_concat_module);
CU_ASSERT(concat_start(raid_bdev) == 0);
return raid_bdev;
}
static void
delete_concat(struct raid_bdev *raid_bdev)
{
concat_stop(raid_bdev);
raid_test_delete_raid_bdev(raid_bdev);
}
static void
test_concat_start(void)
{
struct raid_bdev *raid_bdev;
struct raid_params *params;
struct concat_block_range *block_range;
uint64_t total_blockcnt;
int i;
RAID_PARAMS_FOR_EACH(params) {
raid_bdev = create_concat(params);
block_range = raid_bdev->module_private;
total_blockcnt = 0;
for (i = 0; i < params->num_base_bdevs; i++) {
CU_ASSERT(block_range[i].start == total_blockcnt);
CU_ASSERT(block_range[i].length == params->base_bdev_blockcnt);
total_blockcnt += params->base_bdev_blockcnt;
}
delete_concat(raid_bdev);
}
}
static void
bdev_io_cleanup(struct spdk_bdev_io *bdev_io)
{
if (bdev_io->u.bdev.iovs) {
if (bdev_io->u.bdev.iovs->iov_base) {
free(bdev_io->u.bdev.iovs->iov_base);
}
free(bdev_io->u.bdev.iovs);
}
free(bdev_io);
}
static void
bdev_io_initialize(struct spdk_bdev_io *bdev_io, struct spdk_io_channel *ch, struct spdk_bdev *bdev,
uint64_t lba, uint64_t blocks, int16_t iotype)
{
struct spdk_bdev_channel *channel = spdk_io_channel_get_ctx(ch);
bdev_io->bdev = bdev;
bdev_io->u.bdev.offset_blocks = lba;
bdev_io->u.bdev.num_blocks = blocks;
bdev_io->type = iotype;
if (bdev_io->type == SPDK_BDEV_IO_TYPE_UNMAP || bdev_io->type == SPDK_BDEV_IO_TYPE_FLUSH) {
return;
}
bdev_io->u.bdev.iovcnt = 1;
bdev_io->u.bdev.iovs = calloc(1, sizeof(struct iovec));
SPDK_CU_ASSERT_FATAL(bdev_io->u.bdev.iovs != NULL);
bdev_io->u.bdev.iovs->iov_base = calloc(1, bdev_io->u.bdev.num_blocks * 4096);
SPDK_CU_ASSERT_FATAL(bdev_io->u.bdev.iovs->iov_base != NULL);
bdev_io->u.bdev.iovs->iov_len = bdev_io->u.bdev.num_blocks * BLOCK_LEN;
bdev_io->internal.ch = channel;
bdev_io->u.bdev.md_buf = (void *)0xAEDFEBAC;
}
static void
submit_and_verify_rw(enum CONCAT_IO_TYPE io_type, struct raid_params *params)
{
struct raid_bdev *raid_bdev;
struct spdk_bdev_io *bdev_io;
struct spdk_io_channel *ch;
struct raid_bdev_io *raid_io;
struct raid_bdev_io_channel *raid_ch;
uint64_t lba, blocks;
int i;
lba = 0;
blocks = 1;
for (i = 0; i < params->num_base_bdevs; i++) {
init_globals();
raid_bdev = create_concat(params);
bdev_io = calloc(1, sizeof(struct spdk_bdev_io) + sizeof(struct raid_bdev_io));
SPDK_CU_ASSERT_FATAL(bdev_io != NULL);
raid_io = (struct raid_bdev_io *)bdev_io->driver_ctx;
raid_ch = calloc(1, sizeof(struct raid_bdev_io_channel));
SPDK_CU_ASSERT_FATAL(raid_ch != NULL);
raid_ch->base_channel = calloc(params->num_base_bdevs,
sizeof(struct spdk_io_channel));
SPDK_CU_ASSERT_FATAL(raid_ch->base_channel != NULL);
raid_io->raid_ch = raid_ch;
raid_io->raid_bdev = raid_bdev;
ch = calloc(1, sizeof(struct spdk_io_channel));
SPDK_CU_ASSERT_FATAL(ch != NULL);
switch (io_type) {
case CONCAT_WRITEV:
bdev_io_initialize(bdev_io, ch, &raid_bdev->bdev, lba, blocks, SPDK_BDEV_IO_TYPE_WRITE);
concat_submit_rw_request(raid_io);
break;
case CONCAT_READV:
bdev_io_initialize(bdev_io, ch, &raid_bdev->bdev, lba, blocks, SPDK_BDEV_IO_TYPE_READ);
concat_submit_rw_request(raid_io);
break;
case CONCAT_UNMAP:
bdev_io_initialize(bdev_io, ch, &raid_bdev->bdev, lba, blocks, SPDK_BDEV_IO_TYPE_UNMAP);
concat_submit_null_payload_request(raid_io);
break;
case CONCAT_FLUSH:
bdev_io_initialize(bdev_io, ch, &raid_bdev->bdev, lba, blocks, SPDK_BDEV_IO_TYPE_FLUSH);
concat_submit_null_payload_request(raid_io);
break;
default:
CU_ASSERT(false);
}
/*
* We submit request to the first lba of each underlying device,
* so the offset of the underling device should always be 0.
*/
CU_ASSERT(g_req_records.offset_blocks[0] == 0);
CU_ASSERT(g_req_records.num_blocks[0] == blocks);
CU_ASSERT(g_req_records.io_type[0] == io_type);
CU_ASSERT(g_req_records.count == 1);
CU_ASSERT(g_req_records.md == (void *)0xAEDFEBAC);
bdev_io_cleanup(bdev_io);
free(ch);
free(raid_ch->base_channel);
free(raid_ch);
delete_concat(raid_bdev);
lba += params->base_bdev_blockcnt;
}
}
static void
test_concat_rw(void)
{
struct raid_params *params;
enum CONCAT_IO_TYPE io_type_list[] = {CONCAT_WRITEV, CONCAT_READV};
enum CONCAT_IO_TYPE io_type;
int i;
RAID_PARAMS_FOR_EACH(params) {
for (i = 0; i < 2; i ++) {
io_type = io_type_list[i];
submit_and_verify_rw(io_type, params);
}
}
}
static void
submit_and_verify_null_payload(enum CONCAT_IO_TYPE io_type, struct raid_params *params)
{
struct raid_bdev *raid_bdev;
struct spdk_bdev_io *bdev_io;
struct spdk_io_channel *ch;
struct raid_bdev_io *raid_io;
struct raid_bdev_io_channel *raid_ch;
uint64_t lba, blocks;
/*
* In this unittest, all base bdevs have the same blockcnt.
* If the base_bdev_blockcnt > 1, the request will start from
* the second bdev, and across two bdevs.
* If the base_bdev_blockcnt == 1, the request will start from
* the third bdev. In this case, if there are only 3 bdevs,
* we can not set blocks to base_bdev_blockcnt + 1 because the request
* will be beyond the end of the last bdev, so we set the blocks to 1
*/
lba = params->base_bdev_blockcnt + 1;
if (params->base_bdev_blockcnt == 1 && params->num_base_bdevs == 3) {
blocks = 1;
} else {
blocks = params->base_bdev_blockcnt + 1;
}
init_globals();
raid_bdev = create_concat(params);
bdev_io = calloc(1, sizeof(struct spdk_bdev_io) + sizeof(struct raid_bdev_io));
SPDK_CU_ASSERT_FATAL(bdev_io != NULL);
raid_io = (struct raid_bdev_io *)bdev_io->driver_ctx;
raid_ch = calloc(1, sizeof(struct raid_bdev_io_channel));
SPDK_CU_ASSERT_FATAL(raid_ch != NULL);
raid_ch->base_channel = calloc(params->num_base_bdevs,
sizeof(struct spdk_io_channel));
SPDK_CU_ASSERT_FATAL(raid_ch->base_channel != NULL);
raid_io->raid_ch = raid_ch;
raid_io->raid_bdev = raid_bdev;
ch = calloc(1, sizeof(struct spdk_io_channel));
SPDK_CU_ASSERT_FATAL(ch != NULL);
switch (io_type) {
case CONCAT_UNMAP:
bdev_io_initialize(bdev_io, ch, &raid_bdev->bdev, lba, blocks, SPDK_BDEV_IO_TYPE_UNMAP);
concat_submit_null_payload_request(raid_io);
break;
case CONCAT_FLUSH:
bdev_io_initialize(bdev_io, ch, &raid_bdev->bdev, lba, blocks, SPDK_BDEV_IO_TYPE_FLUSH);
concat_submit_null_payload_request(raid_io);
break;
default:
CU_ASSERT(false);
}
if (params->base_bdev_blockcnt == 1) {
if (params->num_base_bdevs == 3) {
CU_ASSERT(g_req_records.count == 1);
CU_ASSERT(g_req_records.offset_blocks[0] == 0);
CU_ASSERT(g_req_records.num_blocks[0] == 1);
} else {
CU_ASSERT(g_req_records.count == 2);
CU_ASSERT(g_req_records.offset_blocks[0] == 0);
CU_ASSERT(g_req_records.num_blocks[0] == 1);
CU_ASSERT(g_req_records.io_type[0] == io_type);
CU_ASSERT(g_req_records.offset_blocks[1] == 0);
CU_ASSERT(g_req_records.num_blocks[1] == 1);
CU_ASSERT(g_req_records.io_type[1] == io_type);
}
} else {
CU_ASSERT(g_req_records.count == 2);
CU_ASSERT(g_req_records.offset_blocks[0] == 1);
CU_ASSERT(g_req_records.num_blocks[0] == params->base_bdev_blockcnt - 1);
CU_ASSERT(g_req_records.io_type[0] == io_type);
CU_ASSERT(g_req_records.offset_blocks[1] == 0);
CU_ASSERT(g_req_records.num_blocks[1] == 2);
CU_ASSERT(g_req_records.io_type[1] == io_type);
}
bdev_io_cleanup(bdev_io);
free(ch);
free(raid_ch->base_channel);
free(raid_ch);
delete_concat(raid_bdev);
}
static void
test_concat_null_payload(void)
{
struct raid_params *params;
enum CONCAT_IO_TYPE io_type_list[] = {CONCAT_FLUSH, CONCAT_UNMAP};
enum CONCAT_IO_TYPE io_type;
int i;
RAID_PARAMS_FOR_EACH(params) {
for (i = 0; i < 2; i ++) {
io_type = io_type_list[i];
submit_and_verify_null_payload(io_type, params);
}
}
}
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("concat", test_setup, test_cleanup);
CU_ADD_TEST(suite, test_concat_start);
CU_ADD_TEST(suite, test_concat_rw);
CU_ADD_TEST(suite, test_concat_null_payload);
CU_basic_set_mode(CU_BRM_VERBOSE);
CU_basic_run_tests();
num_failures = CU_get_number_of_failures();
CU_cleanup_registry();
return num_failures;
}