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 <xiaodong.liu@intel.com>
Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/468777
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Changpeng Liu <changpeng.liu@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Paul Luse <paul.e.luse@intel.com>
This commit is contained in:
Xiaodong Liu 2019-09-17 15:31:59 +08:00 committed by Tomasz Zawadzki
parent 4c10e0bbca
commit 9c6e742679
6 changed files with 161 additions and 1 deletions

View File

@ -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

View File

@ -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}

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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