diff --git a/module/bdev/raid/bdev_raid.h b/module/bdev/raid/bdev_raid.h index 7f3f40399..d459b4ab8 100644 --- a/module/bdev/raid/bdev_raid.h +++ b/module/bdev/raid/bdev_raid.h @@ -80,6 +80,9 @@ struct raid_bdev_io { uint64_t base_bdev_io_remaining; uint8_t base_bdev_io_submitted; uint8_t base_bdev_io_status; + + /* Private data for the raid module */ + void *module_private; }; /* diff --git a/module/bdev/raid/raid5f.c b/module/bdev/raid/raid5f.c index 3131b08d4..8d89d8eb0 100644 --- a/module/bdev/raid/raid5f.c +++ b/module/bdev/raid/raid5f.c @@ -12,6 +12,41 @@ #include "spdk/likely.h" #include "spdk/log.h" +/* Maximum concurrent full stripe writes per io channel */ +#define RAID5F_MAX_STRIPES 32 + +struct chunk { + /* Corresponds to base_bdev index */ + uint8_t index; + + /* Array of iovecs */ + struct iovec *iovs; + + /* Number of used iovecs */ + int iovcnt; + + /* Total number of available iovecs in the array */ + int iovcnt_max; +}; + +struct stripe_request { + struct raid5f_io_channel *r5ch; + + /* The associated raid_bdev_io */ + struct raid_bdev_io *raid_io; + + /* The stripe's index in the raid array. */ + uint64_t stripe_index; + + /* The stripe's parity chunk */ + struct chunk *parity_chunk; + + TAILQ_ENTRY(stripe_request) link; + + /* Array of chunks corresponding to base_bdevs */ + struct chunk chunks[0]; +}; + struct raid5f_info { /* The parent raid bdev */ struct raid_bdev *raid_bdev; @@ -23,6 +58,39 @@ struct raid5f_info { uint64_t total_stripes; }; +struct raid5f_io_channel { + /* All available stripe requests on this channel */ + TAILQ_HEAD(, stripe_request) free_stripe_requests; +}; + +#define __CHUNK_IN_RANGE(req, c) \ + c < req->chunks + raid5f_ch_to_r5f_info(req->r5ch)->raid_bdev->num_base_bdevs + +#define FOR_EACH_CHUNK_FROM(req, c, from) \ + for (c = from; __CHUNK_IN_RANGE(req, c); c++) + +#define FOR_EACH_CHUNK(req, c) \ + FOR_EACH_CHUNK_FROM(req, c, req->chunks) + +#define __NEXT_DATA_CHUNK(req, c) \ + c == req->parity_chunk ? c+1 : c + +#define FOR_EACH_DATA_CHUNK(req, c) \ + for (c = __NEXT_DATA_CHUNK(req, req->chunks); __CHUNK_IN_RANGE(req, c); \ + c = __NEXT_DATA_CHUNK(req, c+1)) + +static inline struct raid5f_info * +raid5f_ch_to_r5f_info(struct raid5f_io_channel *r5ch) +{ + return spdk_io_channel_get_io_device(spdk_io_channel_from_ctx(r5ch)); +} + +static inline struct stripe_request * +raid5f_chunk_stripe_req(struct chunk *chunk) +{ + return SPDK_CONTAINEROF((chunk - chunk->index), struct stripe_request, chunks); +} + static inline uint8_t raid5f_stripe_data_chunks_num(const struct raid_bdev *raid_bdev) { @@ -35,6 +103,210 @@ raid5f_stripe_parity_chunk_index(const struct raid_bdev *raid_bdev, uint64_t str return raid5f_stripe_data_chunks_num(raid_bdev) - stripe_index % raid_bdev->num_base_bdevs; } +static inline void +raid5f_stripe_request_release(struct stripe_request *stripe_req) +{ + TAILQ_INSERT_HEAD(&stripe_req->r5ch->free_stripe_requests, stripe_req, link); +} + +static void +raid5f_chunk_write_complete(struct chunk *chunk, enum spdk_bdev_io_status status) +{ + struct stripe_request *stripe_req = raid5f_chunk_stripe_req(chunk); + + if (raid_bdev_io_complete_part(stripe_req->raid_io, 1, status)) { + raid5f_stripe_request_release(stripe_req); + } +} + +static void +raid5f_chunk_write_complete_bdev_io(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg) +{ + struct chunk *chunk = cb_arg; + + spdk_bdev_free_io(bdev_io); + + raid5f_chunk_write_complete(chunk, success ? SPDK_BDEV_IO_STATUS_SUCCESS : + SPDK_BDEV_IO_STATUS_FAILED); +} + +static void raid5f_stripe_request_submit_chunks(struct stripe_request *stripe_req); + +static void +raid5f_chunk_write_retry(void *_raid_io) +{ + struct raid_bdev_io *raid_io = _raid_io; + struct stripe_request *stripe_req = raid_io->module_private; + + raid5f_stripe_request_submit_chunks(stripe_req); +} + +static int +raid5f_chunk_write(struct chunk *chunk) +{ + struct stripe_request *stripe_req = raid5f_chunk_stripe_req(chunk); + struct raid_bdev_io *raid_io = stripe_req->raid_io; + struct raid_bdev *raid_bdev = raid_io->raid_bdev; + struct raid_base_bdev_info *base_info = &raid_bdev->base_bdev_info[chunk->index]; + struct spdk_io_channel *base_ch = raid_io->raid_ch->base_channel[chunk->index]; + uint64_t base_offset_blocks = (stripe_req->stripe_index << raid_bdev->strip_size_shift); + int ret; + + ret = spdk_bdev_writev_blocks(base_info->desc, base_ch, chunk->iovs, chunk->iovcnt, + base_offset_blocks, raid_bdev->strip_size, + raid5f_chunk_write_complete_bdev_io, chunk); + if (spdk_unlikely(ret)) { + if (ret == -ENOMEM) { + raid_bdev_queue_io_wait(raid_io, base_info->bdev, base_ch, + raid5f_chunk_write_retry); + } else { + /* + * Implicitly complete any I/Os not yet submitted as FAILED. If completing + * these means there are no more to complete for the stripe request, we can + * release the stripe request as well. + */ + uint64_t base_bdev_io_not_submitted = raid5f_stripe_data_chunks_num(raid_bdev) - + raid_io->base_bdev_io_submitted; + + if (raid_bdev_io_complete_part(stripe_req->raid_io, base_bdev_io_not_submitted, + SPDK_BDEV_IO_STATUS_FAILED)) { + raid5f_stripe_request_release(stripe_req); + } + } + } + + return ret; +} + +static int +raid5f_stripe_request_map_iovecs(struct stripe_request *stripe_req, + const struct iovec *raid_io_iovs, + int raid_io_iovcnt) +{ + struct raid_bdev *raid_bdev = stripe_req->raid_io->raid_bdev; + struct chunk *chunk; + int raid_io_iov_idx = 0; + size_t raid_io_offset = 0; + size_t raid_io_iov_offset = 0; + int i; + + FOR_EACH_DATA_CHUNK(stripe_req, chunk) { + int chunk_iovcnt = 0; + uint64_t len = raid_bdev->strip_size << raid_bdev->blocklen_shift; + size_t off = raid_io_iov_offset; + + for (i = raid_io_iov_idx; i < raid_io_iovcnt; i++) { + chunk_iovcnt++; + off += raid_io_iovs[i].iov_len; + if (off >= raid_io_offset + len) { + break; + } + } + + assert(raid_io_iov_idx + chunk_iovcnt <= raid_io_iovcnt); + + if (chunk_iovcnt > chunk->iovcnt_max) { + struct iovec *iovs = chunk->iovs; + + iovs = realloc(iovs, chunk_iovcnt * sizeof(*iovs)); + if (!iovs) { + return -ENOMEM; + } + chunk->iovs = iovs; + chunk->iovcnt_max = chunk_iovcnt; + } + chunk->iovcnt = chunk_iovcnt; + + for (i = 0; i < chunk_iovcnt; i++) { + struct iovec *chunk_iov = &chunk->iovs[i]; + const struct iovec *raid_io_iov = &raid_io_iovs[raid_io_iov_idx]; + size_t chunk_iov_offset = raid_io_offset - raid_io_iov_offset; + + chunk_iov->iov_base = raid_io_iov->iov_base + chunk_iov_offset; + chunk_iov->iov_len = spdk_min(len, raid_io_iov->iov_len - chunk_iov_offset); + raid_io_offset += chunk_iov->iov_len; + len -= chunk_iov->iov_len; + + if (raid_io_offset >= raid_io_iov_offset + raid_io_iov->iov_len) { + raid_io_iov_idx++; + raid_io_iov_offset += raid_io_iov->iov_len; + } + } + + if (spdk_unlikely(len > 0)) { + return -EINVAL; + } + } + + return 0; +} + +static void +raid5f_stripe_request_submit_chunks(struct stripe_request *stripe_req) +{ + struct raid_bdev_io *raid_io = stripe_req->raid_io; + struct chunk *start = &stripe_req->chunks[raid_io->base_bdev_io_submitted]; + struct chunk *chunk; + + if (start >= stripe_req->parity_chunk) { + start++; + } + + FOR_EACH_CHUNK_FROM(stripe_req, chunk, start) { + if (chunk == stripe_req->parity_chunk) { + continue; + } + + if (spdk_unlikely(raid5f_chunk_write(chunk) != 0)) { + break; + } + raid_io->base_bdev_io_submitted++; + } +} + +static void +raid5f_submit_stripe_request(struct stripe_request *stripe_req) +{ + /* TODO: parity */ + + raid5f_stripe_request_submit_chunks(stripe_req); +} + +static int +raid5f_submit_write_request(struct raid_bdev_io *raid_io, uint64_t stripe_index) +{ + struct raid_bdev *raid_bdev = raid_io->raid_bdev; + struct spdk_bdev_io *bdev_io = spdk_bdev_io_from_ctx(raid_io); + struct raid5f_io_channel *r5ch = spdk_io_channel_get_ctx(raid_io->raid_ch->module_channel); + struct stripe_request *stripe_req; + int ret; + + stripe_req = TAILQ_FIRST(&r5ch->free_stripe_requests); + if (!stripe_req) { + return -ENOMEM; + } + + stripe_req->stripe_index = stripe_index; + stripe_req->parity_chunk = stripe_req->chunks + raid5f_stripe_parity_chunk_index(raid_bdev, + stripe_req->stripe_index); + stripe_req->raid_io = raid_io; + + ret = raid5f_stripe_request_map_iovecs(stripe_req, bdev_io->u.bdev.iovs, + bdev_io->u.bdev.iovcnt); + if (spdk_unlikely(ret)) { + return ret; + } + + TAILQ_REMOVE(&r5ch->free_stripe_requests, stripe_req, link); + + raid_io->module_private = stripe_req; + raid_io->base_bdev_io_remaining = raid5f_stripe_data_chunks_num(raid_bdev); + + raid5f_submit_stripe_request(stripe_req); + + return 0; +} + static void raid5f_chunk_read_complete(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg) { @@ -88,7 +360,8 @@ static void raid5f_submit_rw_request(struct raid_bdev_io *raid_io) { struct spdk_bdev_io *bdev_io = spdk_bdev_io_from_ctx(raid_io); - struct raid5f_info *r5f_info = raid_io->raid_bdev->module_private; + struct raid_bdev *raid_bdev = raid_io->raid_bdev; + struct raid5f_info *r5f_info = raid_bdev->module_private; uint64_t offset_blocks = bdev_io->u.bdev.offset_blocks; uint64_t stripe_index = offset_blocks / r5f_info->stripe_blocks; uint64_t stripe_offset = offset_blocks % r5f_info->stripe_blocks; @@ -96,19 +369,103 @@ raid5f_submit_rw_request(struct raid_bdev_io *raid_io) switch (bdev_io->type) { case SPDK_BDEV_IO_TYPE_READ: - assert(bdev_io->u.bdev.num_blocks <= r5f_info->raid_bdev->strip_size); + assert(bdev_io->u.bdev.num_blocks <= raid_bdev->strip_size); ret = raid5f_submit_read_request(raid_io, stripe_index, stripe_offset); break; + case SPDK_BDEV_IO_TYPE_WRITE: + assert(stripe_offset == 0); + assert(bdev_io->u.bdev.num_blocks == r5f_info->stripe_blocks); + ret = raid5f_submit_write_request(raid_io, stripe_index); + break; default: ret = -EINVAL; break; } if (spdk_unlikely(ret)) { - raid_bdev_io_complete(raid_io, SPDK_BDEV_IO_STATUS_FAILED); + raid_bdev_io_complete(raid_io, ret == -ENOMEM ? SPDK_BDEV_IO_STATUS_NOMEM : + SPDK_BDEV_IO_STATUS_FAILED); } } +static void +raid5f_stripe_request_free(struct stripe_request *stripe_req) +{ + struct chunk *chunk; + + FOR_EACH_CHUNK(stripe_req, chunk) { + free(chunk->iovs); + } + + free(stripe_req); +} + +static struct stripe_request * +raid5f_stripe_request_alloc(struct raid5f_io_channel *r5ch) +{ + struct raid5f_info *r5f_info = raid5f_ch_to_r5f_info(r5ch); + struct raid_bdev *raid_bdev = r5f_info->raid_bdev; + struct stripe_request *stripe_req; + struct chunk *chunk; + + stripe_req = calloc(1, sizeof(*stripe_req) + + sizeof(struct chunk) * raid_bdev->num_base_bdevs); + if (!stripe_req) { + return NULL; + } + + stripe_req->r5ch = r5ch; + + FOR_EACH_CHUNK(stripe_req, chunk) { + chunk->index = chunk - stripe_req->chunks; + chunk->iovcnt_max = 4; + chunk->iovs = calloc(chunk->iovcnt_max, sizeof(chunk->iovs[0])); + if (!chunk->iovs) { + raid5f_stripe_request_free(stripe_req); + return NULL; + } + } + + return stripe_req; +} + +static void +raid5f_ioch_destroy(void *io_device, void *ctx_buf) +{ + struct raid5f_io_channel *r5ch = ctx_buf; + struct stripe_request *stripe_req; + + while ((stripe_req = TAILQ_FIRST(&r5ch->free_stripe_requests))) { + TAILQ_REMOVE(&r5ch->free_stripe_requests, stripe_req, link); + raid5f_stripe_request_free(stripe_req); + } +} + +static int +raid5f_ioch_create(void *io_device, void *ctx_buf) +{ + struct raid5f_io_channel *r5ch = ctx_buf; + struct raid5f_info *r5f_info = io_device; + int i; + + TAILQ_INIT(&r5ch->free_stripe_requests); + + for (i = 0; i < RAID5F_MAX_STRIPES; i++) { + struct stripe_request *stripe_req; + + stripe_req = raid5f_stripe_request_alloc(r5ch); + if (!stripe_req) { + SPDK_ERRLOG("Failed to initialize io channel\n"); + raid5f_ioch_destroy(r5f_info, r5ch); + return -ENOMEM; + } + + TAILQ_INSERT_HEAD(&r5ch->free_stripe_requests, stripe_req, link); + } + + return 0; +} + static int raid5f_start(struct raid_bdev *raid_bdev) { @@ -133,18 +490,39 @@ raid5f_start(struct raid_bdev *raid_bdev) raid_bdev->bdev.blockcnt = r5f_info->stripe_blocks * r5f_info->total_stripes; raid_bdev->bdev.optimal_io_boundary = raid_bdev->strip_size; raid_bdev->bdev.split_on_optimal_io_boundary = true; + raid_bdev->bdev.write_unit_size = r5f_info->stripe_blocks; + raid_bdev->bdev.split_on_write_unit = true; raid_bdev->module_private = r5f_info; + spdk_io_device_register(r5f_info, raid5f_ioch_create, raid5f_ioch_destroy, + sizeof(struct raid5f_io_channel), NULL); + return 0; } +static void +raid5f_io_device_unregister_done(void *io_device) +{ + struct raid5f_info *r5f_info = io_device; + + free(r5f_info); +} + static void raid5f_stop(struct raid_bdev *raid_bdev) { struct raid5f_info *r5f_info = raid_bdev->module_private; - free(r5f_info); + spdk_io_device_unregister(r5f_info, raid5f_io_device_unregister_done); +} + +static struct spdk_io_channel * +raid5f_get_io_channel(struct raid_bdev *raid_bdev) +{ + struct raid5f_info *r5f_info = raid_bdev->module_private; + + return spdk_get_io_channel(r5f_info); } static struct raid_bdev_module g_raid5f_module = { @@ -154,6 +532,7 @@ static struct raid_bdev_module g_raid5f_module = { .start = raid5f_start, .stop = raid5f_stop, .submit_rw_request = raid5f_submit_rw_request, + .get_io_channel = raid5f_get_io_channel, }; RAID_MODULE_REGISTER(&g_raid5f_module) diff --git a/test/unit/lib/bdev/raid/raid5f.c/raid5f_ut.c b/test/unit/lib/bdev/raid/raid5f.c/raid5f_ut.c index d9f41c51c..94bbad905 100644 --- a/test/unit/lib/bdev/raid/raid5f.c/raid5f_ut.c +++ b/test/unit/lib/bdev/raid/raid5f.c/raid5f_ut.c @@ -7,7 +7,8 @@ #include "spdk_cunit.h" #include "spdk/env.h" -#include "common/lib/test_env.c" +#include "common/lib/ut_multithread.c" + #include "bdev/raid/raid5f.c" DEFINE_STUB_V(raid_bdev_module_list_add, (struct raid_bdev_module *raid_module)); @@ -24,6 +25,25 @@ raid_bdev_io_complete(struct raid_bdev_io *raid_io, enum spdk_bdev_io_status sta } } +bool +raid_bdev_io_complete_part(struct raid_bdev_io *raid_io, uint64_t completed, + enum spdk_bdev_io_status status) +{ + assert(raid_io->base_bdev_io_remaining >= completed); + raid_io->base_bdev_io_remaining -= completed; + + if (status != SPDK_BDEV_IO_STATUS_SUCCESS) { + raid_io->base_bdev_io_status = status; + } + + if (raid_io->base_bdev_io_remaining == 0) { + raid_bdev_io_complete(raid_io, raid_io->base_bdev_io_status); + return true; + } else { + return false; + } +} + struct raid5f_params { uint8_t num_base_bdevs; uint64_t base_bdev_blockcnt; @@ -117,7 +137,9 @@ create_raid_bdev(struct raid5f_params *params) } raid_bdev->strip_size = params->strip_size; + raid_bdev->strip_size_kb = params->strip_size * params->base_bdev_blocklen / 1024; raid_bdev->strip_size_shift = spdk_u32log2(raid_bdev->strip_size); + raid_bdev->blocklen_shift = spdk_u32log2(params->base_bdev_blocklen); raid_bdev->bdev.blocklen = params->base_bdev_blocklen; return raid_bdev; @@ -172,6 +194,7 @@ test_raid5f_start(void) (params->num_base_bdevs - 1)); CU_ASSERT_EQUAL(r5f_info->raid_bdev->bdev.optimal_io_boundary, params->strip_size); CU_ASSERT_TRUE(r5f_info->raid_bdev->bdev.split_on_optimal_io_boundary); + CU_ASSERT_EQUAL(r5f_info->raid_bdev->bdev.write_unit_size, r5f_info->stripe_blocks); delete_raid5f(r5f_info); } @@ -297,6 +320,47 @@ process_io_completions(struct raid_io_info *io_info) } } +int +spdk_bdev_writev_blocks(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 chunk *chunk = cb_arg; + struct stripe_request *stripe_req; + struct test_raid_bdev_io *test_raid_bdev_io; + struct raid_io_info *io_info; + struct raid_bdev *raid_bdev; + uint64_t stripe_idx_off; + uint8_t data_chunk_idx; + void *dest_buf; + + SPDK_CU_ASSERT_FATAL(cb == raid5f_chunk_write_complete_bdev_io); + SPDK_CU_ASSERT_FATAL(iovcnt == 1); + + stripe_req = raid5f_chunk_stripe_req(chunk); + test_raid_bdev_io = (struct test_raid_bdev_io *)spdk_bdev_io_from_ctx(stripe_req->raid_io); + io_info = test_raid_bdev_io->io_info; + raid_bdev = io_info->r5f_info->raid_bdev; + + SPDK_CU_ASSERT_FATAL(chunk != stripe_req->parity_chunk); + + stripe_idx_off = offset_blocks / raid_bdev->strip_size - + io_info->offset_blocks / io_info->r5f_info->stripe_blocks; + + data_chunk_idx = chunk < stripe_req->parity_chunk ? chunk->index : chunk->index - 1; + dest_buf = test_raid_bdev_io->buf + + (stripe_idx_off * io_info->r5f_info->stripe_blocks + + data_chunk_idx * raid_bdev->strip_size) * + raid_bdev->bdev.blocklen; + + memcpy(dest_buf, iov->iov_base, iov->iov_len); + + submit_io(test_raid_bdev_io->io_info, desc, cb, cb_arg); + + return 0; +} + int spdk_bdev_readv_blocks(struct spdk_bdev_desc *desc, struct spdk_io_channel *ch, struct iovec *iov, int iovcnt, @@ -318,6 +382,20 @@ spdk_bdev_readv_blocks(struct spdk_bdev_desc *desc, struct spdk_io_channel *ch, return 0; } +static void +test_raid5f_write_request(struct raid_io_info *io_info) +{ + struct raid_bdev_io *raid_io; + + SPDK_CU_ASSERT_FATAL(io_info->num_blocks / io_info->r5f_info->stripe_blocks == 1); + + raid_io = get_raid_io(io_info, 0, io_info->num_blocks); + + raid5f_submit_rw_request(raid_io); + + process_io_completions(io_info); +} + static void test_raid5f_read_request(struct raid_io_info *io_info) { @@ -399,6 +477,9 @@ test_raid5f_submit_rw_request(struct raid5f_info *r5f_info, struct raid_bdev_io_ case SPDK_BDEV_IO_TYPE_READ: test_raid5f_read_request(&io_info); break; + case SPDK_BDEV_IO_TYPE_WRITE: + test_raid5f_write_request(&io_info); + break; default: CU_FAIL_FATAL("unsupported io_type"); } @@ -425,8 +506,14 @@ run_for_each_raid5f_config(void (*test_fn)(struct raid_bdev *raid_bdev, raid_ch.base_channel = calloc(params->num_base_bdevs, sizeof(struct spdk_io_channel *)); SPDK_CU_ASSERT_FATAL(raid_ch.base_channel != NULL); + raid_ch.module_channel = raid5f_get_io_channel(r5f_info->raid_bdev); + SPDK_CU_ASSERT_FATAL(raid_ch.module_channel); + test_fn(r5f_info->raid_bdev, &raid_ch); + spdk_put_io_channel(raid_ch.module_channel); + poll_threads(); + free(raid_ch.base_channel); delete_raid5f(r5f_info); @@ -480,6 +567,86 @@ test_raid5f_submit_read_request(void) run_for_each_raid5f_config(__test_raid5f_submit_read_request); } +static void +__test_raid5f_stripe_request_map_iovecs(struct raid_bdev *raid_bdev, + struct raid_bdev_io_channel *raid_ch) +{ + struct raid5f_io_channel *r5ch = spdk_io_channel_get_ctx(raid_ch->module_channel); + size_t strip_bytes = raid_bdev->strip_size * raid_bdev->bdev.blocklen; + struct raid_bdev_io raid_io = { .raid_bdev = raid_bdev }; + struct stripe_request *stripe_req; + struct chunk *chunk; + struct iovec iovs[] = { + { .iov_base = (void *)0x0ff0000, .iov_len = strip_bytes }, + { .iov_base = (void *)0x1ff0000, .iov_len = strip_bytes / 2 }, + { .iov_base = (void *)0x2ff0000, .iov_len = strip_bytes * 2 }, + { .iov_base = (void *)0x3ff0000, .iov_len = strip_bytes * raid_bdev->num_base_bdevs }, + }; + size_t iovcnt = sizeof(iovs) / sizeof(iovs[0]); + int ret; + + stripe_req = raid5f_stripe_request_alloc(r5ch); + SPDK_CU_ASSERT_FATAL(stripe_req != NULL); + + stripe_req->parity_chunk = &stripe_req->chunks[raid5f_stripe_data_chunks_num(raid_bdev)]; + stripe_req->raid_io = &raid_io; + + ret = raid5f_stripe_request_map_iovecs(stripe_req, iovs, iovcnt); + CU_ASSERT(ret == 0); + + chunk = &stripe_req->chunks[0]; + CU_ASSERT_EQUAL(chunk->iovcnt, 1); + CU_ASSERT_EQUAL(chunk->iovs[0].iov_base, iovs[0].iov_base); + CU_ASSERT_EQUAL(chunk->iovs[0].iov_len, iovs[0].iov_len); + + chunk = &stripe_req->chunks[1]; + CU_ASSERT_EQUAL(chunk->iovcnt, 2); + CU_ASSERT_EQUAL(chunk->iovs[0].iov_base, iovs[1].iov_base); + CU_ASSERT_EQUAL(chunk->iovs[0].iov_len, iovs[1].iov_len); + CU_ASSERT_EQUAL(chunk->iovs[1].iov_base, iovs[2].iov_base); + CU_ASSERT_EQUAL(chunk->iovs[1].iov_len, iovs[2].iov_len / 4); + + if (raid_bdev->num_base_bdevs > 3) { + chunk = &stripe_req->chunks[2]; + CU_ASSERT_EQUAL(chunk->iovcnt, 1); + CU_ASSERT_EQUAL(chunk->iovs[0].iov_base, iovs[2].iov_base + strip_bytes / 2); + CU_ASSERT_EQUAL(chunk->iovs[0].iov_len, iovs[2].iov_len / 2); + } + if (raid_bdev->num_base_bdevs > 4) { + chunk = &stripe_req->chunks[3]; + CU_ASSERT_EQUAL(chunk->iovcnt, 2); + CU_ASSERT_EQUAL(chunk->iovs[0].iov_base, iovs[2].iov_base + (strip_bytes / 2) * 3); + CU_ASSERT_EQUAL(chunk->iovs[0].iov_len, iovs[2].iov_len / 4); + CU_ASSERT_EQUAL(chunk->iovs[1].iov_base, iovs[3].iov_base); + CU_ASSERT_EQUAL(chunk->iovs[1].iov_len, strip_bytes / 2); + } + + raid5f_stripe_request_free(stripe_req); +} +static void +test_raid5f_stripe_request_map_iovecs(void) +{ + run_for_each_raid5f_config(__test_raid5f_stripe_request_map_iovecs); +} + +static void +__test_raid5f_submit_full_stripe_write_request(struct raid_bdev *raid_bdev, + struct raid_bdev_io_channel *raid_ch) +{ + struct raid5f_info *r5f_info = raid_bdev->module_private; + uint64_t stripe_index; + + RAID5F_TEST_FOR_EACH_STRIPE(raid_bdev, stripe_index) { + test_raid5f_submit_rw_request(r5f_info, raid_ch, SPDK_BDEV_IO_TYPE_WRITE, + stripe_index, 0, r5f_info->stripe_blocks); + } +} +static void +test_raid5f_submit_full_stripe_write_request(void) +{ + run_for_each_raid5f_config(__test_raid5f_submit_full_stripe_write_request); +} + int main(int argc, char **argv) { @@ -492,10 +659,18 @@ main(int argc, char **argv) suite = CU_add_suite("raid5f", test_setup, test_cleanup); CU_ADD_TEST(suite, test_raid5f_start); CU_ADD_TEST(suite, test_raid5f_submit_read_request); + CU_ADD_TEST(suite, test_raid5f_stripe_request_map_iovecs); + CU_ADD_TEST(suite, test_raid5f_submit_full_stripe_write_request); + + allocate_threads(1); + set_thread(0); CU_basic_set_mode(CU_BRM_VERBOSE); CU_basic_run_tests(); num_failures = CU_get_number_of_failures(); CU_cleanup_registry(); + + free_threads(); + return num_failures; }