diff --git a/include/spdk/reduce.h b/include/spdk/reduce.h index a28d204ae..e136c3702 100644 --- a/include/spdk/reduce.h +++ b/include/spdk/reduce.h @@ -44,6 +44,8 @@ * Describes the parameters of an spdk_reduce_vol. */ struct spdk_reduce_vol_params { + struct spdk_uuid uuid; + /** * Size in bytes of the IO unit for the backing device. This * is the unit in which space is allocated from the backing @@ -84,4 +86,69 @@ int64_t spdk_reduce_get_pm_file_size(struct spdk_reduce_vol_params *params); */ int64_t spdk_reduce_get_backing_device_size(struct spdk_reduce_vol_params *params); +struct spdk_reduce_vol; + +/** + * Describes a persistent memory file used to hold metadata associated with a + * compressed volume. + */ +struct spdk_reduce_pm_file { + /** Size of the persistent memory file in bytes. */ + uint64_t size; +}; + +typedef void (*spdk_reduce_vol_op_complete)(void *ctx, int ziperrno); +typedef void (*spdk_reduce_vol_op_with_handle_complete)(void *ctx, + struct spdk_reduce_vol *vol, + int ziperrno); + +typedef void (*spdk_reduce_dev_cpl)(void *cb_arg, int ziperrno); + +struct spdk_reduce_vol_cb_args { + spdk_reduce_dev_cpl cb_fn; + void *cb_arg; +}; + +struct spdk_reduce_backing_dev { + void (*readv)(struct spdk_reduce_backing_dev *dev, struct iovec *iov, int iovcnt, + uint64_t lba, uint32_t lba_count, struct spdk_reduce_vol_cb_args *args); + + void (*writev)(struct spdk_reduce_backing_dev *dev, struct iovec *iov, int iovcnt, + uint64_t lba, uint32_t lba_count, struct spdk_reduce_vol_cb_args *args); + + void (*unmap)(struct spdk_reduce_backing_dev *dev, + uint64_t lba, uint32_t lba_count, struct spdk_reduce_vol_cb_args *args); + + void (*close)(struct spdk_reduce_backing_dev *dev); + + uint64_t blockcnt; + uint32_t blocklen; +}; + +/** + * Initialize a new libreduce compressed volume. + * + * \param params Parameters for the new volume. + * \param backing_dev Structure describing the backing device to use for the new volume. + * \param pm_file Structure describing the persistent memory file to use for the new volume. + * \param cb_fn Callback function to signal completion of the initialization process. + * \param cb_arg Argument to pass to the callback function. + */ +void spdk_reduce_vol_init(struct spdk_reduce_vol_params *params, + struct spdk_reduce_backing_dev *backing_dev, + struct spdk_reduce_pm_file *pm_file, + spdk_reduce_vol_op_with_handle_complete cb_fn, + void *cb_arg); + +/** + * Unload a previously initialized or loaded libreduce compressed volume. + * + * \param vol Volume to unload. + * \param cb_fn Callback function to signal completion of the unload process. + * \param cb_arg Argument to pass to the callback function. + */ +void spdk_reduce_vol_unload(struct spdk_reduce_vol *vol, + spdk_reduce_vol_op_complete cb_fn, + void *cb_arg); + #endif /* SPDK_REDUCE_H_ */ diff --git a/lib/reduce/reduce.c b/lib/reduce/reduce.c index e78ded5ce..e35f7f694 100644 --- a/lib/reduce/reduce.c +++ b/lib/reduce/reduce.c @@ -34,6 +34,7 @@ #include "spdk/stdinc.h" #include "spdk/reduce.h" +#include "spdk/string.h" #include "spdk_internal/log.h" /* Always round up the size of the PM region to the nearest cacheline. */ @@ -42,11 +43,14 @@ /* Structure written to offset 0 of both the pm file and the backing device. */ struct spdk_reduce_vol_superblock { struct spdk_reduce_vol_params params; - uint8_t reserved[4080]; + uint8_t reserved[4064]; }; SPDK_STATIC_ASSERT(sizeof(struct spdk_reduce_vol_superblock) == 4096, "size incorrect"); struct spdk_reduce_vol { + struct spdk_uuid uuid; + struct spdk_reduce_pm_file pm_file; + struct spdk_reduce_backing_dev *backing_dev; }; /* @@ -154,4 +158,74 @@ spdk_reduce_get_backing_device_size(struct spdk_reduce_vol_params *params) return total_backing_size; } +void +spdk_reduce_vol_init(struct spdk_reduce_vol_params *params, + struct spdk_reduce_backing_dev *backing_dev, + struct spdk_reduce_pm_file *pm_file, + spdk_reduce_vol_op_with_handle_complete cb_fn, void *cb_arg) +{ + struct spdk_reduce_vol *vol; + int64_t size, size_needed; + int rc; + + rc = _validate_vol_params(params); + if (rc != 0) { + SPDK_ERRLOG("invalid vol params\n"); + cb_fn(cb_arg, NULL, rc); + return; + } + + size_needed = spdk_reduce_get_backing_device_size(params); + size = backing_dev->blockcnt * backing_dev->blocklen; + if (size_needed > size) { + SPDK_ERRLOG("backing device size %" PRIi64 " but %" PRIi64 " needed\n", + size, size_needed); + cb_fn(cb_arg, NULL, -EINVAL); + return; + } + + size_needed = spdk_reduce_get_pm_file_size(params); + size = pm_file->size; + if (size_needed > size) { + SPDK_ERRLOG("pm file size %" PRIi64 " but %" PRIi64 " needed\n", + size, size_needed); + cb_fn(cb_arg, NULL, -EINVAL); + return; + } + + if (spdk_mem_all_zero(¶ms->uuid, sizeof(params->uuid))) { + SPDK_ERRLOG("no uuid specified\n"); + cb_fn(cb_arg, NULL, -EINVAL); + return; + } + + vol = calloc(1, sizeof(*vol)); + if (vol == NULL) { + cb_fn(cb_arg, NULL, -ENOMEM); + return; + } + + memcpy(&vol->pm_file, pm_file, sizeof(*pm_file)); + + memcpy(&vol->uuid, ¶ms->uuid, sizeof(params->uuid)); + vol->backing_dev = backing_dev; + + cb_fn(cb_arg, vol, 0); +} + +void +spdk_reduce_vol_unload(struct spdk_reduce_vol *vol, + spdk_reduce_vol_op_complete cb_fn, void *cb_arg) +{ + if (vol == NULL) { + /* This indicates a programming error. */ + assert(false); + cb_fn(cb_arg, -EINVAL); + return; + } + + free(vol); + cb_fn(cb_arg, 0); +} + SPDK_LOG_REGISTER_COMPONENT("reduce", SPDK_LOG_REDUCE) diff --git a/test/unit/lib/reduce/reduce.c/reduce_ut.c b/test/unit/lib/reduce/reduce.c/reduce_ut.c index 5fc4ca075..6f762a651 100644 --- a/test/unit/lib/reduce/reduce.c/reduce_ut.c +++ b/test/unit/lib/reduce/reduce.c/reduce_ut.c @@ -38,6 +38,9 @@ #include "reduce/reduce.c" #include "common/lib/test_env.c" +static struct spdk_reduce_vol *g_vol; +static int g_ziperrno; + static void get_pm_file_size(void) { @@ -120,6 +123,76 @@ get_backing_device_size(void) CU_ASSERT(backing_size == expected_backing_size); } +static void +init_cb(void *cb_arg, struct spdk_reduce_vol *vol, int ziperrno) +{ + g_vol = vol; + g_ziperrno = ziperrno; +} + +static void +unload_cb(void *cb_arg, int ziperrno) +{ + g_ziperrno = ziperrno; +} + +static void +init(void) +{ + struct spdk_reduce_vol_params params = {}; + struct spdk_reduce_backing_dev backing_dev = {}; + struct spdk_reduce_pm_file pm_file = {}; + + backing_dev.blocklen = 512; + + params.vol_size = 1024 * 1024; /* 1MB */ + params.chunk_size = 16 * 1024; + params.backing_io_unit_size = backing_dev.blocklen; + + /* backing_dev and pm_file have an invalid size. This should fail. */ + g_vol = NULL; + g_ziperrno = 0; + spdk_reduce_vol_init(¶ms, &backing_dev, &pm_file, init_cb, NULL); + CU_ASSERT(g_ziperrno == -EINVAL); + SPDK_CU_ASSERT_FATAL(g_vol == NULL); + + /* backing_dev now has valid size, but pm_file is still invalid. + * This should fail. + */ + backing_dev.blockcnt = spdk_reduce_get_backing_device_size(¶ms) / backing_dev.blocklen; + + g_vol = NULL; + g_ziperrno = 0; + spdk_reduce_vol_init(¶ms, &backing_dev, &pm_file, init_cb, NULL); + CU_ASSERT(g_ziperrno == -EINVAL); + SPDK_CU_ASSERT_FATAL(g_vol == NULL); + + /* pm_file now has valid size, but uuid is still all zeroes. + * This should fail. + */ + pm_file.size = spdk_reduce_get_pm_file_size(¶ms); + + g_vol = NULL; + g_ziperrno = 0; + spdk_reduce_vol_init(¶ms, &backing_dev, &pm_file, init_cb, NULL); + CU_ASSERT(g_ziperrno == -EINVAL); + SPDK_CU_ASSERT_FATAL(g_vol == NULL); + + /* Now specify a uuid. spdk_reduce_vol_init() should then pass. */ + spdk_uuid_generate(¶ms.uuid); + + g_vol = NULL; + g_ziperrno = -1; + spdk_reduce_vol_init(¶ms, &backing_dev, &pm_file, init_cb, NULL); + CU_ASSERT(g_ziperrno == 0); + SPDK_CU_ASSERT_FATAL(g_vol != NULL); + CU_ASSERT(spdk_uuid_compare(¶ms.uuid, &g_vol->uuid) == 0); + + g_ziperrno = -1; + spdk_reduce_vol_unload(g_vol, unload_cb, NULL); + CU_ASSERT(g_ziperrno == 0); +} + int main(int argc, char **argv) { @@ -138,7 +211,8 @@ main(int argc, char **argv) if ( CU_add_test(suite, "get_pm_file_size", get_pm_file_size) == NULL || - CU_add_test(suite, "get_backing_device_size", get_backing_device_size) == NULL + CU_add_test(suite, "get_backing_device_size", get_backing_device_size) == NULL || + CU_add_test(suite, "init", init) == NULL ) { CU_cleanup_registry(); return CU_get_error();