raid5f: full stripe writes
Implement support for full stripe writes without parity calculation. The size and alignment of write IOs must be a multiple of full stripe size. To reflect this, the raid bdev's write_unit_size is set accordingly. We rely on the bdev layer to split larger IOs based on that. Signed-off-by: Artur Paszkiewicz <artur.paszkiewicz@intel.com> Change-Id: I6940280ad870f3bd678fd19346b06ba4bdadd52e Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/7702 Tested-by: SPDK CI Jenkins <sys_sgci@intel.com> Community-CI: Mellanox Build Bot Reviewed-by: Tomasz Zawadzki <tomasz.zawadzki@intel.com> Reviewed-by: Ben Walker <benjamin.walker@intel.com>
This commit is contained in:
parent
d6e9827e9f
commit
7131bec415
@ -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;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user