From 9c6e742679bfe0c749a11fa275af58a2ed7880b9 Mon Sep 17 00:00:00 2001 From: Xiaodong Liu Date: Tue, 17 Sep 2019 15:31:59 +0800 Subject: [PATCH] blobfs: add blobfs_mount RPC as FUSE This RPC method will mount blobfs on bdev to one host path through FUSE. Then on the host path, user can directly do some file operations which will be mapped to blobfs. Note: * The FUSE mount of blobfs can be umounted directly by SHELL umount or fusermount command. Change-Id: I7c322d978b39bbc7255fced345a749ad5bfa7077 Signed-off-by: Xiaodong Liu Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/468777 Tested-by: SPDK CI Jenkins Reviewed-by: Changpeng Liu Reviewed-by: Jim Harris Reviewed-by: Paul Luse --- CHANGELOG.md | 4 ++ doc/jsonrpc.md | 37 +++++++++++++ module/blobfs/bdev/blobfs_bdev_rpc.c | 81 ++++++++++++++++++++++++++++ scripts/rpc.py | 10 ++++ scripts/rpc/blobfs.py | 14 +++++ test/blobfs/blobfs.sh | 16 +++++- 6 files changed, 161 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f001af635..d8f7ef594 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -151,6 +151,10 @@ 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. +Added `blobfs_mount` RPC method to mount blobfs on given bdev to a host path by FUSE. +Then on the host path, user can directly do some file operations which will be mapped +to blobfs. + ## v19.07: ### ftl diff --git a/doc/jsonrpc.md b/doc/jsonrpc.md index 10965827e..20c4a246e 100644 --- a/doc/jsonrpc.md +++ b/doc/jsonrpc.md @@ -5771,6 +5771,43 @@ Example response: } ~~~ +## blobfs_mount {#rpc_blobfs_mount} + +Mount a blobfs on bdev to one host path through FUSE + +### Parameters + +Name | Optional | Type | Description +----------------------- | -------- | ----------- | ----------- +bdev_name | Required | string | Block device name where the blobfs is +mountpoint | Required | string | Mountpoint path in host to mount blobfs + +### Example + +Example request: + +~~~ +{ + "jsonrpc": "2.0", + "id": 1, + "method": ""blobfs_mount"", + "params": { + "bdev_name": "Malloc0", + "mountpoint": "/mnt/" + } +} +~~~ + +Example response: + +~~~ +{ + "jsonrpc": "2.0", + "id": 1, + "result": "true" +} +~~~ + # Miscellaneous RPC commands ## bdev_nvme_send_cmd {#rpc_bdev_nvme_send_cmd} diff --git a/module/blobfs/bdev/blobfs_bdev_rpc.c b/module/blobfs/bdev/blobfs_bdev_rpc.c index 58526b77f..fe1b1f330 100644 --- a/module/blobfs/bdev/blobfs_bdev_rpc.c +++ b/module/blobfs/bdev/blobfs_bdev_rpc.c @@ -217,3 +217,84 @@ spdk_rpc_blobfs_create(struct spdk_jsonrpc_request *request, } SPDK_RPC_REGISTER("blobfs_create", spdk_rpc_blobfs_create, SPDK_RPC_RUNTIME) + +#ifdef SPDK_CONFIG_FUSE + +struct rpc_blobfs_mount { + char *bdev_name; + char *mountpoint; + + struct spdk_jsonrpc_request *request; +}; + +static void +free_rpc_blobfs_mount(struct rpc_blobfs_mount *req) +{ + free(req->bdev_name); + free(req->mountpoint); + free(req); +} + +static const struct spdk_json_object_decoder rpc_blobfs_mount_decoders[] = { + {"bdev_name", offsetof(struct rpc_blobfs_mount, bdev_name), spdk_json_decode_string}, + {"mountpoint", offsetof(struct rpc_blobfs_mount, mountpoint), spdk_json_decode_string}, +}; + +static void +_rpc_blobfs_mount_done(void *cb_arg, int fserrno) +{ + struct rpc_blobfs_mount *req = cb_arg; + struct spdk_json_write_ctx *w; + + if (fserrno == -EILSEQ) { + /* There is no blobfs existing on bdev */ + spdk_jsonrpc_send_error_response(req->request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "No blobfs detected on given bdev"); + + return; + } else 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_mount(req); +} + +static void +spdk_rpc_blobfs_mount(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct rpc_blobfs_mount *req; + + req = calloc(1, sizeof(*req)); + if (req == NULL) { + SPDK_ERRLOG("could not allocate rpc_blobfs_mount 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_mount_decoders, + SPDK_COUNTOF(rpc_blobfs_mount_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_mount(req); + + return; + } + + req->request = request; + spdk_blobfs_bdev_mount(req->bdev_name, req->mountpoint, _rpc_blobfs_mount_done, req); +} + +SPDK_RPC_REGISTER("blobfs_mount", spdk_rpc_blobfs_mount, SPDK_RPC_RUNTIME) + +#endif diff --git a/scripts/rpc.py b/scripts/rpc.py index 39df58fe6..812d89e8e 100755 --- a/scripts/rpc.py +++ b/scripts/rpc.py @@ -2087,6 +2087,16 @@ Format: 'user:u1 secret:s1 muser:mu1 msecret:ms1,user:u2 secret:s2 muser:mu2 mse 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 blobfs_mount(args): + print(rpc.blobfs.blobfs_mount(args.client, + bdev_name=args.bdev_name, + mountpoint=args.mountpoint)) + + p = subparsers.add_parser('blobfs_mount', help='Mount a blobfs on bdev to host path by FUSE') + p.add_argument('bdev_name', help='Blockdev name where the blobfs is. Example: Malloc0.') + p.add_argument('mountpoint', help='Mountpoint path in host to mount blobfs. Example: /mnt/.') + p.set_defaults(func=blobfs_mount) + 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 d0e2edae0..cf140420e 100644 --- a/scripts/rpc/blobfs.py +++ b/scripts/rpc/blobfs.py @@ -26,3 +26,17 @@ def blobfs_create(client, bdev_name, cluster_sz=None): if cluster_sz: params['cluster_sz'] = cluster_sz return client.call('blobfs_create', params) + + +def blobfs_mount(client, bdev_name, mountpoint): + """Mount blobfs on bdev by FUSE. + + Args: + bdev_name: block device name where the blobfs is + mountpoint: Mountpoint path in host to mount blobfs + """ + params = { + 'bdev_name': bdev_name, + 'mountpoint': mountpoint + } + return client.call('blobfs_mount', params) diff --git a/test/blobfs/blobfs.sh b/test/blobfs/blobfs.sh index 975d77b62..8b0286fa5 100755 --- a/test/blobfs/blobfs.sh +++ b/test/blobfs/blobfs.sh @@ -93,8 +93,22 @@ function blobfs_fuse_test() { # check mount status mount || grep $mount_dir - # basic file operations in mount dir + # create a rand file in mount dir dd if=/dev/urandom of=${mount_dir}/rand_file bs=4k count=32 + + umount ${mount_dir} + killprocess $blobfs_pid + + # Verify there is no file in mount dir now + if [ -f ${mount_dir}/rand_file ]; then + false + fi + + # use blobfs mount RPC + blobfs_start_app + $rpc_py blobfs_mount ${bdevname} $mount_dir + + # read and delete the rand file md5sum ${mount_dir}/rand_file rm ${mount_dir}/rand_file