bdev/ocf: Add runtime cache bdev flush support

Introduce two RPC calls for starting flush and getting flush
status of OCF cache bdev:
- bdev_ocf_flush_start
- bdev_ocf_flush_status

Signed-off-by: Robert Baldyga <robert.baldyga@intel.com>
Signed-off-by: Rafal Stefanowski <rafal.stefanowski@intel.com>
Change-Id: I1d659da6fc51396e0d070af35372ee130c40ae8b
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/8961
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Community-CI: Mellanox Build Bot
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Tomasz Zawadzki <tomasz.zawadzki@intel.com>
This commit is contained in:
Rafal Stefanowski 2021-07-24 00:23:26 +02:00 committed by Tomasz Zawadzki
parent 6ee3921083
commit 8000cedbe3
7 changed files with 354 additions and 1 deletions

View File

@ -2859,6 +2859,101 @@ Example response:
}
~~~
### bdev_ocf_flush_start {#rpc_bdev_ocf_flush_start}
Start flushing OCF cache device.
Automatic flushes of dirty data are managed by OCF cleaning policy settings.
In addition to that, all dirty data is flushed to core device when there is
an attempt to stop caching.
On the other hand, this RPC call gives a possibility to flush dirty data manually
when there is a need for it, e.g. to speed up the shutdown process when data
hasn't been flushed for a long time.
This RPC returns immediately, and flush is then being performed in the
background. To see the status of flushing operation use bdev_ocf_flush_status.
#### Parameters
Name | Optional | Type | Description
----------------------- | -------- | ----------- | -----------
name | Required | string | Bdev name
#### Example
Example request:
~~~json
{
"params": {
"name": "ocf0"
},
"jsonrpc": "2.0",
"method": "bdev_ocf_flush_start",
"id": 1
}
~~~
Example response:
~~~json
{
"jsonrpc": "2.0",
"id": 1,
"result": true
}
~~~
### bdev_ocf_flush_status {#rpc_bdev_ocf_flush_status}
Get flush status of OCF cache device.
Automatic flushes of dirty data are managed by OCF cleaning policy settings.
In addition to that, all dirty data is flushed to core device when there is
an attempt to stop caching.
On the other hand, there is a possibility to flush dirty data manually
when there is a need for it, e.g. to speed up the shutdown process when data
hasn't been flushed for a long time.
This RPC reports if such manual flush is still in progress and if the operation
was successful. To start manual flush use bdev_ocf_flush_start.
#### Parameters
Name | Optional | Type | Description
----------------------- | -------- | ----------- | -----------
name | Required | string | Bdev name
#### Response
Status of OCF cache device flush.
#### Example
Example request:
~~~json
{
"params": {
"name": "ocf0"
},
"jsonrpc": "2.0",
"method": "bdev_ocf_flush_status",
"id": 1
}
~~~
Example response:
~~~json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"in_progress": false,
"status": 0
}
}
~~~
### bdev_malloc_create {#rpc_bdev_malloc_create}
Construct @ref bdev_config_malloc

View File

@ -141,9 +141,16 @@ struct vbdev_ocf {
/* Management context */
struct vbdev_ocf_mngt_ctx mngt_ctx;
/* Cache conext */
/* Cache context */
struct vbdev_ocf_cache_ctx *cache_ctx;
/* Status of flushing operation */
struct {
bool in_progress;
int status;
} flush;
/* Exposed SPDK bdev. Registered in bdev layer */
struct spdk_bdev exp_bdev;

View File

@ -438,3 +438,129 @@ end:
free_rpc_bdev_ocf_set_seqcutoff(&req);
}
SPDK_RPC_REGISTER("bdev_ocf_set_seqcutoff", rpc_bdev_ocf_set_seqcutoff, SPDK_RPC_RUNTIME)
struct get_ocf_flush_start_ctx {
struct spdk_jsonrpc_request *request;
struct vbdev_ocf *vbdev;
};
static void
rpc_bdev_ocf_flush_start_cmpl(ocf_cache_t cache, void *priv, int error)
{
struct get_ocf_flush_start_ctx *ctx = priv;
ctx->vbdev->flush.in_progress = false;
ctx->vbdev->flush.status = error;
ocf_mngt_cache_read_unlock(cache);
free(ctx);
}
static void
rpc_bdev_ocf_flush_start_lock_cmpl(ocf_cache_t cache, void *priv, int error)
{
struct get_ocf_flush_start_ctx *ctx = priv;
if (error) {
spdk_jsonrpc_send_error_response_fmt(ctx->request,
SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
"Could not lock cache: %d", error);
free(ctx);
return;
}
ctx->vbdev->flush.in_progress = true;
ocf_mngt_cache_flush(cache, rpc_bdev_ocf_flush_start_cmpl, ctx);
spdk_jsonrpc_send_bool_response(ctx->request, true);
}
static void
rpc_bdev_ocf_flush_start(struct spdk_jsonrpc_request *request,
const struct spdk_json_val *params)
{
struct rpc_bdev_ocf_name req = {NULL};
struct get_ocf_flush_start_ctx *ctx;
int status;
ctx = calloc(1, sizeof(*ctx));
if (!ctx) {
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
"Not enough memory to process request");
goto end;
}
status = spdk_json_decode_object(params, rpc_bdev_ocf_name_decoders,
SPDK_COUNTOF(rpc_bdev_ocf_name_decoders),
&req);
if (status) {
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
"Invalid parameters");
free(ctx);
goto end;
}
ctx->vbdev = vbdev_ocf_get_by_name(req.name);
if (ctx->vbdev == NULL) {
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
spdk_strerror(ENODEV));
free(ctx);
goto end;
}
if (!ctx->vbdev->ocf_cache) {
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
"Couldn't flush cache: device not attached");
free(ctx);
goto end;
}
ctx->request = request;
ocf_mngt_cache_read_lock(ctx->vbdev->ocf_cache, rpc_bdev_ocf_flush_start_lock_cmpl, ctx);
end:
free_rpc_bdev_ocf_name(&req);
}
SPDK_RPC_REGISTER("bdev_ocf_flush_start", rpc_bdev_ocf_flush_start, SPDK_RPC_RUNTIME)
static void
rpc_bdev_ocf_flush_status(struct spdk_jsonrpc_request *request,
const struct spdk_json_val *params)
{
struct rpc_bdev_ocf_name req = {NULL};
struct spdk_json_write_ctx *w;
struct vbdev_ocf *vbdev;
int status;
status = spdk_json_decode_object(params, rpc_bdev_ocf_name_decoders,
SPDK_COUNTOF(rpc_bdev_ocf_name_decoders),
&req);
if (status) {
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
"Invalid parameters");
goto end;
}
vbdev = vbdev_ocf_get_by_name(req.name);
if (vbdev == NULL) {
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
spdk_strerror(ENODEV));
goto end;
}
w = spdk_jsonrpc_begin_result(request);
spdk_json_write_object_begin(w);
spdk_json_write_named_bool(w, "in_progress", vbdev->flush.in_progress);
if (!vbdev->flush.in_progress) {
spdk_json_write_named_int32(w, "status", vbdev->flush.status);
}
spdk_json_write_object_end(w);
spdk_jsonrpc_end_result(request, w);
end:
free_rpc_bdev_ocf_name(&req);
}
SPDK_RPC_REGISTER("bdev_ocf_flush_status", rpc_bdev_ocf_flush_status, SPDK_RPC_RUNTIME)

View File

@ -234,6 +234,35 @@ def bdev_ocf_set_seqcutoff(client, name, policy, threshold, promotion_count):
return client.call('bdev_ocf_set_seqcutoff', params)
def bdev_ocf_flush_start(client, name):
"""Start flushing OCF cache device
Args:
name: name of OCF bdev
"""
params = {
'name': name,
}
return client.call('bdev_ocf_flush_start', params)
def bdev_ocf_flush_status(client, name):
"""Get flush status of OCF cache device
Args:
name: name of OCF bdev
Returns:
Flush status
"""
params = {
'name': name,
}
return client.call('bdev_ocf_flush_status', params)
def bdev_malloc_create(client, num_blocks, block_size, name=None, uuid=None, optimal_io_boundary=None,
md_size=None, md_interleave=None, dif_type=None, dif_is_head_of_md=None):
"""Construct a malloc block device.

View File

@ -364,6 +364,20 @@ if __name__ == "__main__":
help='Sequential cutoff policy')
p.set_defaults(func=bdev_ocf_set_seqcutoff)
def bdev_ocf_flush_start(args):
rpc.bdev.bdev_ocf_flush_start(args.client, name=args.name)
p = subparsers.add_parser('bdev_ocf_flush_start',
help='Start flushing OCF cache device')
p.add_argument('name', help='Name of OCF bdev')
p.set_defaults(func=bdev_ocf_flush_start)
def bdev_ocf_flush_status(args):
print_json(rpc.bdev.bdev_ocf_flush_status(args.client, name=args.name))
p = subparsers.add_parser('bdev_ocf_flush_status',
help='Get flush status of OCF cache device')
p.add_argument('name', help='Name of OCF bdev')
p.set_defaults(func=bdev_ocf_flush_status)
def bdev_malloc_create(args):
num_blocks = (args.total_size * 1024 * 1024) // args.block_size
print_json(rpc.bdev.bdev_malloc_create(args.client,

81
test/ocf/integrity/flush.sh Executable file
View File

@ -0,0 +1,81 @@
#!/usr/bin/env bash
curdir=$(dirname $(readlink -f "${BASH_SOURCE[0]}"))
rootdir=$(readlink -f $curdir/../../..)
source $rootdir/test/common/autotest_common.sh
bdevperf=$rootdir/test/bdev/bdevperf/bdevperf
rpc_py="$rootdir/scripts/rpc.py -s /var/tmp/spdk.sock"
check_flush_in_progress() {
$rpc_py bdev_ocf_flush_status MalCache0 \
| jq -e '.in_progress' > /dev/null
}
bdevperf_config() {
local config
config="$(
cat <<- JSON
{
"method": "bdev_malloc_create",
"params": {
"name": "Malloc0",
"num_blocks": 102400,
"block_size": 512
}
},
{
"method": "bdev_malloc_create",
"params": {
"name": "Malloc1",
"num_blocks": 1024000,
"block_size": 512
}
},
{
"method": "bdev_ocf_create",
"params": {
"name": "MalCache0",
"mode": "wb",
"cache_line_size": 4,
"cache_bdev_name": "Malloc0",
"core_bdev_name": "Malloc1"
}
}
JSON
)"
jq . <<- JSON
{
"subsystems": [
{
"subsystem": "bdev",
"config": [
$(
IFS=","
printf '%s\n' "$config"
),
{
"method": "bdev_wait_for_examine"
}
]
}
]
}
JSON
}
$bdevperf --json <(bdevperf_config) -q 128 -o 4096 -w write -t 120 -r /var/tmp/spdk.sock &
bdevperf_pid=$!
trap 'killprocess $bdevperf_pid' SIGINT SIGTERM EXIT
waitforlisten $bdevperf_pid
sleep 5
$rpc_py bdev_ocf_flush_start MalCache0
sleep 1
while check_flush_in_progress; do
sleep 1
done
$rpc_py bdev_ocf_flush_status MalCache0 | jq -e '.status == 0'

View File

@ -8,6 +8,7 @@ source $rootdir/test/common/autotest_common.sh
run_test "ocf_fio_modes" "$testdir/integrity/fio-modes.sh"
run_test "ocf_bdevperf_iotypes" "$testdir/integrity/bdevperf-iotypes.sh"
run_test "ocf_stats" "$testdir/integrity/stats.sh"
run_test "ocf_flush" "$testdir/integrity/flush.sh"
run_test "ocf_create_destruct" "$testdir/management/create-destruct.sh"
run_test "ocf_multicore" "$testdir/management/multicore.sh"
run_test "ocf_persistent_metadata" "$testdir/management/persistent-metadata.sh"