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