From 6433c1037dd98b6aab2ab4d9c68fb16273d4e369 Mon Sep 17 00:00:00 2001 From: Xiaodong Liu Date: Wed, 28 Aug 2019 16:06:50 +0800 Subject: [PATCH] blobfs: add blobfs_create RPC as mkfs Following test/blobfs/mkfs case, add it as one RPC method to let build a new blobfs on given block device. Change-Id: I0ffbb1add95dfbc8655e0238ed6f3cd519dd945b Signed-off-by: Xiaodong Liu Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/466485 Tested-by: SPDK CI Jenkins Reviewed-by: Jim Harris Reviewed-by: Changpeng Liu --- CHANGELOG.md | 4 + doc/jsonrpc.md | 37 +++++++ include/spdk/blobfs_bdev.h | 11 ++ module/blobfs/bdev/blobfs_bdev.c | 68 ++++++++++++ module/blobfs/bdev/blobfs_bdev_rpc.c | 102 ++++++++++++++++++ scripts/rpc.py | 11 ++ scripts/rpc/blobfs.py | 15 +++ test/blobfs/blobfs.sh | 20 ++++ .../lib/blobfs/blobfs_bdev.c/blobfs_bdev_ut.c | 87 ++++++++++++++- 9 files changed, 354 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 96cb7a4a8..9e336e4db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -79,6 +79,8 @@ A new blobfs module `bdev` has been added to simplify the operations of blobfs o Function spdk_blobfs_bdev_detect is added to detect whether blobfs exists on the given block device. +Function spdk_blobfs_bdev_create is added to create a blobfs on the given block device. + ### nvme Added `no_shn_notification` to NVMe controller initialization options, users can enable @@ -137,6 +139,8 @@ RPC method. Added `blobfs_detect` RPC method to detect whether a blobfs exists on given bdev. +Added `blobfs_create` RPC method to build blobfs on given bdev. + ## v19.07: ### ftl diff --git a/doc/jsonrpc.md b/doc/jsonrpc.md index 97c8271d3..5c7be0183 100644 --- a/doc/jsonrpc.md +++ b/doc/jsonrpc.md @@ -5734,6 +5734,43 @@ Example response: } ~~~ +## blobfs_create {#rpc_blobfs_create} + +Build blobfs on bdev. + +### Parameters + +Name | Optional | Type | Description +----------------------- | -------- | ----------- | ----------- +bdev_name | Required | string | Block device name to create blobfs +cluster_sz | Optional | number | Size of cluster in bytes. Must be multiple of 4KiB page size, default and minimal value is 1M. + +### Example + +Example request: + +~~~ +{ + "jsonrpc": "2.0", + "id": 1, + "method": "blobfs_create", + "params": { + "bdev_name": "Malloc0", + "cluster_sz": 1M + } +} +~~~ + +Example response: + +~~~ +{ + "jsonrpc": "2.0", + "id": 1, + "result": "true" +} +~~~ + # Miscellaneous RPC commands ## bdev_nvme_send_cmd {#rpc_bdev_nvme_send_cmd} diff --git a/include/spdk/blobfs_bdev.h b/include/spdk/blobfs_bdev.h index fe3f0ce99..4632c1601 100644 --- a/include/spdk/blobfs_bdev.h +++ b/include/spdk/blobfs_bdev.h @@ -63,6 +63,17 @@ typedef void (*spdk_blobfs_bdev_op_complete)(void *cb_arg, int fserrno); void spdk_blobfs_bdev_detect(const char *bdev_name, spdk_blobfs_bdev_op_complete cb_fn, void *cb_arg); +/** + * Create a blobfs on the given device. + * + * \param bdev_name Name of block device. + * \param cluster_sz Size of cluster in bytes. Must be multiple of 4KiB page size. + * \param cb_fn Called when the creation is complete. + * \param cb_arg Argument passed to function cb_fn. + */ +void spdk_blobfs_bdev_create(const char *bdev_name, uint32_t cluster_sz, + spdk_blobfs_bdev_op_complete cb_fn, void *cb_arg); + #ifdef __cplusplus } #endif diff --git a/module/blobfs/bdev/blobfs_bdev.c b/module/blobfs/bdev/blobfs_bdev.c index 70859862b..3eb248766 100644 --- a/module/blobfs/bdev/blobfs_bdev.c +++ b/module/blobfs/bdev/blobfs_bdev.c @@ -34,6 +34,7 @@ #include "spdk/stdinc.h" #include "spdk/blobfs.h" #include "spdk/bdev.h" +#include "spdk/bdev_module.h" #include "spdk/event.h" #include "spdk/blob_bdev.h" #include "spdk/blobfs_bdev.h" @@ -44,6 +45,11 @@ #include "spdk_internal/log.h" +/* Dummy bdev module used to to claim bdevs. */ +static struct spdk_bdev_module blobfs_bdev_module = { + .name = "blobfs", +}; + static void blobfs_bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev, void *event_ctx) @@ -141,3 +147,65 @@ invalid: cb_fn(cb_arg, rc); } + +void +spdk_blobfs_bdev_create(const char *bdev_name, uint32_t cluster_sz, + spdk_blobfs_bdev_op_complete cb_fn, void *cb_arg) +{ + struct blobfs_bdev_operation_ctx *ctx; + struct spdk_blobfs_opts blobfs_opt; + struct spdk_bs_dev *bs_dev; + struct spdk_bdev_desc *desc; + int rc; + + ctx = calloc(1, sizeof(*ctx)); + if (ctx == NULL) { + SPDK_ERRLOG("Failed to allocate ctx.\n"); + cb_fn(cb_arg, -ENOMEM); + + return; + } + + ctx->bdev_name = bdev_name; + ctx->cb_fn = cb_fn; + ctx->cb_arg = cb_arg; + + /* Creation requires WRITE operation */ + rc = spdk_bdev_open_ext(bdev_name, true, blobfs_bdev_event_cb, NULL, &desc); + if (rc != 0) { + SPDK_INFOLOG(SPDK_LOG_BLOBFS, "Failed to open bdev(%s): %s\n", ctx->bdev_name, spdk_strerror(rc)); + + goto invalid; + } + + bs_dev = spdk_bdev_create_bs_dev_from_desc(desc); + if (bs_dev == NULL) { + SPDK_INFOLOG(SPDK_LOG_BLOBFS, "Failed to create a blobstore block device from bdev desc\n"); + rc = -ENOMEM; + spdk_bdev_close(desc); + + goto invalid; + } + + rc = spdk_bs_bdev_claim(bs_dev, &blobfs_bdev_module); + if (rc) { + SPDK_INFOLOG(SPDK_LOG_BLOBFS, "Blobfs base bdev already claimed by another bdev\n"); + bs_dev->destroy(bs_dev); + + goto invalid; + } + + spdk_fs_opts_init(&blobfs_opt); + if (cluster_sz) { + blobfs_opt.cluster_sz = cluster_sz; + } + + spdk_fs_init(bs_dev, &blobfs_opt, NULL, blobfs_bdev_load_cb_to_unload, ctx); + + return; + +invalid: + free(ctx); + + cb_fn(cb_arg, rc); +} diff --git a/module/blobfs/bdev/blobfs_bdev_rpc.c b/module/blobfs/bdev/blobfs_bdev_rpc.c index 14286cd57..58526b77f 100644 --- a/module/blobfs/bdev/blobfs_bdev_rpc.c +++ b/module/blobfs/bdev/blobfs_bdev_rpc.c @@ -44,6 +44,12 @@ #include "spdk_internal/log.h" +#ifndef PAGE_SIZE +#define PAGE_SIZE 4096 +#endif + +#define MIN_CLUSTER_SZ (1024 * 1024) + struct rpc_blobfs_detect { char *bdev_name; @@ -115,3 +121,99 @@ spdk_rpc_blobfs_detect(struct spdk_jsonrpc_request *request, } SPDK_RPC_REGISTER("blobfs_detect", spdk_rpc_blobfs_detect, SPDK_RPC_RUNTIME) + +struct rpc_blobfs_create { + char *bdev_name; + uint64_t cluster_sz; + + struct spdk_jsonrpc_request *request; +}; + +static void +free_rpc_blobfs_create(struct rpc_blobfs_create *req) +{ + free(req->bdev_name); + free(req); +} + +static int +rpc_decode_cluster_sz(const struct spdk_json_val *val, void *out) +{ + uint64_t *cluster_sz = out; + char *sz_str = NULL; + bool has_prefix; + int rc; + + rc = spdk_json_decode_string(val, &sz_str); + if (rc) { + SPDK_NOTICELOG("Invalid parameter value: cluster_sz\n"); + return -EINVAL; + } + + rc = spdk_parse_capacity(sz_str, cluster_sz, &has_prefix); + free(sz_str); + + if (rc || *cluster_sz % PAGE_SIZE != 0 || *cluster_sz < MIN_CLUSTER_SZ) { + SPDK_NOTICELOG("Invalid parameter value: cluster_sz\n"); + return -EINVAL; + } + + SPDK_DEBUGLOG(SPDK_LOG_BLOBFS, "cluster_sz of blobfs: %ld\n", *cluster_sz); + return 0; +} + +static const struct spdk_json_object_decoder rpc_blobfs_create_decoders[] = { + {"bdev_name", offsetof(struct rpc_blobfs_create, bdev_name), spdk_json_decode_string}, + {"cluster_sz", offsetof(struct rpc_blobfs_create, cluster_sz), rpc_decode_cluster_sz, true}, +}; + +static void +_rpc_blobfs_create_done(void *cb_arg, int fserrno) +{ + struct rpc_blobfs_create *req = cb_arg; + struct spdk_json_write_ctx *w; + + if (fserrno != 0) { + spdk_jsonrpc_send_error_response(req->request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, + spdk_strerror(-fserrno)); + + return; + } + + w = spdk_jsonrpc_begin_result(req->request); + spdk_json_write_bool(w, true); + spdk_jsonrpc_end_result(req->request, w); + + free_rpc_blobfs_create(req); +} + +static void +spdk_rpc_blobfs_create(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct rpc_blobfs_create *req; + + req = calloc(1, sizeof(*req)); + if (req == NULL) { + SPDK_ERRLOG("could not allocate rpc_blobfs_create request.\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "Out of memory"); + return; + } + + if (spdk_json_decode_object(params, rpc_blobfs_create_decoders, + SPDK_COUNTOF(rpc_blobfs_create_decoders), + req)) { + SPDK_ERRLOG("spdk_json_decode_object failed\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "spdk_json_decode_object failed"); + + free_rpc_blobfs_create(req); + + return; + } + + req->request = request; + spdk_blobfs_bdev_create(req->bdev_name, req->cluster_sz, _rpc_blobfs_create_done, req); +} + +SPDK_RPC_REGISTER("blobfs_create", spdk_rpc_blobfs_create, SPDK_RPC_RUNTIME) diff --git a/scripts/rpc.py b/scripts/rpc.py index d84b9c734..93d486844 100755 --- a/scripts/rpc.py +++ b/scripts/rpc.py @@ -2073,6 +2073,17 @@ Format: 'user:u1 secret:s1 muser:mu1 msecret:ms1,user:u2 secret:s2 muser:mu2 mse p.add_argument('bdev_name', help='Blockdev name to detect blobfs. Example: Malloc0.') p.set_defaults(func=blobfs_detect) + def blobfs_create(args): + print(rpc.blobfs.blobfs_create(args.client, + bdev_name=args.bdev_name, + cluster_sz=args.cluster_sz)) + + p = subparsers.add_parser('blobfs_create', help='Build a blobfs on bdev') + p.add_argument('bdev_name', help='Blockdev name to build blobfs. Example: Malloc0.') + p.add_argument('-c', '--cluster_sz', + help="""Size of cluster in bytes (Optional). Must be multiple of 4KB page size. Default and minimal value is 1M.""") + p.set_defaults(func=blobfs_create) + def check_called_name(name): if name in deprecated_aliases: print("{} is deprecated, use {} instead.".format(name, deprecated_aliases[name]), file=sys.stderr) diff --git a/scripts/rpc/blobfs.py b/scripts/rpc/blobfs.py index 4833827ef..d0e2edae0 100644 --- a/scripts/rpc/blobfs.py +++ b/scripts/rpc/blobfs.py @@ -11,3 +11,18 @@ def blobfs_detect(client, bdev_name): 'bdev_name': bdev_name } return client.call('blobfs_detect', params) + + +def blobfs_create(client, bdev_name, cluster_sz=None): + """Build blobfs on bdev. + + Args: + bdev_name: block device name to build blobfs + cluster_sz: Size of cluster in bytes (Optional). Must be multiple of 4KB page size. Default and minimal value is 1M. + """ + params = { + 'bdev_name': bdev_name + } + if cluster_sz: + params['cluster_sz'] = cluster_sz + return client.call('blobfs_create', params) diff --git a/test/blobfs/blobfs.sh b/test/blobfs/blobfs.sh index 31bef381b..0c6942f20 100755 --- a/test/blobfs/blobfs.sh +++ b/test/blobfs/blobfs.sh @@ -58,6 +58,21 @@ function blobfs_detect_test() { killprocess $blobfs_pid } +function blobfs_create_test() { + blobfs_start_app + + # Create blobfs on test bdev + $rpc_py blobfs_create ${bdevname} + + # Detect out there is a blobfs on test bdev + result=$($rpc_py blobfs_detect ${bdevname}) + if [ "${result}" != "True" ]; then + false + fi + + killprocess $blobfs_pid +} + timing_enter blobfs trap 'on_error_exit;' ERR @@ -69,6 +84,11 @@ echo "AIO ${tmp_file} ${bdevname} 4096" >> ${conf_file} blobfs_detect_test +# Clear blobfs on temp file +dd if=/dev/zero of=${tmp_file} bs=4k count=1M + +blobfs_create_test + rm -f $tmp_file report_test_completion "blobfs" diff --git a/test/unit/lib/blobfs/blobfs_bdev.c/blobfs_bdev_ut.c b/test/unit/lib/blobfs/blobfs_bdev.c/blobfs_bdev_ut.c index f7c1e76c4..31a6c0e4b 100644 --- a/test/unit/lib/blobfs/blobfs_bdev.c/blobfs_bdev_ut.c +++ b/test/unit/lib/blobfs/blobfs_bdev.c/blobfs_bdev_ut.c @@ -43,6 +43,7 @@ bool g_bdev_open_ext_fail = false; bool g_bdev_create_bs_dev_from_desc_fail = false; bool g_fs_load_fail = false; bool g_fs_unload_fail = false; +bool g_bs_bdev_claim_fail = false; const char *g_bdev_name = "ut_bdev"; @@ -57,6 +58,11 @@ spdk_bdev_open_ext(const char *bdev_name, bool write, spdk_bdev_event_cb_t event return 0; } +static void +bs_dev_destroy(struct spdk_bs_dev *dev) +{ +} + struct spdk_bs_dev * spdk_bdev_create_bs_dev_from_desc(struct spdk_bdev_desc *desc) { @@ -66,6 +72,7 @@ spdk_bdev_create_bs_dev_from_desc(struct spdk_bdev_desc *desc) return NULL; } + bs_dev.destroy = bs_dev_destroy; return &bs_dev; } @@ -96,6 +103,31 @@ spdk_fs_unload(struct spdk_filesystem *fs, spdk_fs_op_complete cb_fn, void *cb_a return; } +void +spdk_fs_init(struct spdk_bs_dev *dev, struct spdk_blobfs_opts *opt, + fs_send_request_fn send_request_fn, + spdk_fs_op_with_handle_complete cb_fn, void *cb_arg) +{ + int rc = 0; + + if (g_fs_load_fail) { + rc = -1; + } + + cb_fn(cb_arg, NULL, rc); + return; +} + +int +spdk_bs_bdev_claim(struct spdk_bs_dev *bs_dev, struct spdk_bdev_module *module) +{ + if (g_bs_bdev_claim_fail == true) { + return -1; + } + + return 0; +} + void spdk_bdev_close(struct spdk_bdev_desc *desc) { @@ -121,6 +153,11 @@ spdk_bdev_get_name(const struct spdk_bdev *bdev) return g_bdev_name; } +void +spdk_fs_opts_init(struct spdk_blobfs_opts *opts) +{ +} + static void blobfs_bdev_op_complete(void *cb_arg, int fserrno) { @@ -163,6 +200,51 @@ spdk_blobfs_bdev_detect_test(void) CU_ASSERT(g_fserrno == 0); } +static void +spdk_blobfs_bdev_create_test(void) +{ + uint32_t cluster_sz = 1024 * 1024; + + /* spdk_bdev_open_ext() fails */ + g_bdev_open_ext_fail = true; + spdk_blobfs_bdev_create(g_bdev_name, cluster_sz, blobfs_bdev_op_complete, NULL); + CU_ASSERT(g_fserrno != 0); + + g_bdev_open_ext_fail = false; + + /* spdk_bdev_create_bs_dev_from_desc() fails */ + g_bdev_create_bs_dev_from_desc_fail = true; + spdk_blobfs_bdev_create(g_bdev_name, cluster_sz, blobfs_bdev_op_complete, NULL); + CU_ASSERT(g_fserrno != 0); + + g_bdev_create_bs_dev_from_desc_fail = false; + + /* spdk_bs_bdev_claim() fails */ + g_bs_bdev_claim_fail = true; + spdk_blobfs_bdev_create(g_bdev_name, cluster_sz, blobfs_bdev_op_complete, NULL); + CU_ASSERT(g_fserrno != 0); + + g_bs_bdev_claim_fail = false; + + /* spdk_fs_init() fails */ + g_fs_load_fail = true; + spdk_blobfs_bdev_create(g_bdev_name, cluster_sz, blobfs_bdev_op_complete, NULL); + CU_ASSERT(g_fserrno != 0); + + g_fs_load_fail = false; + + /* spdk_fs_unload() fails */ + g_fs_unload_fail = true; + spdk_blobfs_bdev_create(g_bdev_name, cluster_sz, blobfs_bdev_op_complete, NULL); + CU_ASSERT(g_fserrno != 0); + + g_fs_unload_fail = false; + + /* no fail */ + spdk_blobfs_bdev_create(g_bdev_name, cluster_sz, blobfs_bdev_op_complete, NULL); + CU_ASSERT(g_fserrno == 0); +} + int main(int argc, char **argv) { CU_pSuite suite = NULL; @@ -178,7 +260,10 @@ int main(int argc, char **argv) return CU_get_error(); } - if (CU_add_test(suite, "spdk_blobfs_bdev_detect_test", spdk_blobfs_bdev_detect_test) == NULL) { + if ( + CU_add_test(suite, "spdk_blobfs_bdev_detect_test", spdk_blobfs_bdev_detect_test) == NULL || + CU_add_test(suite, "spdk_blobfs_bdev_create_test", spdk_blobfs_bdev_create_test) == NULL + ) { CU_cleanup_registry(); return CU_get_error(); }