From b73c52f10b0fe228ce3443d8ba8280039dd35198 Mon Sep 17 00:00:00 2001
From: Rafal Stefanowski <rafal.stefanowski@intel.com>
Date: Sun, 6 Jun 2021 12:02:55 +0200
Subject: [PATCH] Set cleaning policy

Add RPC methods for changing cleaning policy and parameters:
- bdev_ocf_set_cleaning_alru
- bdev_ocf_set_cleaning_acp
- bdev_ocf_set_cleaning_nop

Signed-off-by: Rafal Stefanowski <rafal.stefanowski@intel.com>
---
 module/bdev/ocf/vbdev_ocf.c     | 122 ++++++++++++++++++++++
 module/bdev/ocf/vbdev_ocf.h     |  24 +++++
 module/bdev/ocf/vbdev_ocf_rpc.c | 174 ++++++++++++++++++++++++++++++++
 scripts/rpc.py                  |  43 ++++++++
 scripts/rpc/bdev.py             |  54 ++++++++++
 5 files changed, 417 insertions(+)

diff --git a/module/bdev/ocf/vbdev_ocf.c b/module/bdev/ocf/vbdev_ocf.c
index fa777969b15..4733011419d 100644
--- a/module/bdev/ocf/vbdev_ocf.c
+++ b/module/bdev/ocf/vbdev_ocf.c
@@ -1642,6 +1642,128 @@ vbdev_ocf_set_cache_mode(struct vbdev_ocf *vbdev,
 	cb(rc, vbdev, cb_arg);
 }
 
+/* Set ALRU cleaning policy with parameters on OCF cache */
+void
+vbdev_ocf_set_cleaning_alru(
+		struct vbdev_ocf *vbdev,
+		int32_t wake_up,
+		int32_t staleness_time,
+		int32_t flush_max_buffers,
+		int32_t activity_threshold,
+		void (*cb)(int, void *),
+		void *cb_arg)
+{
+	ocf_cache_t cache;
+	int rc;
+
+	cache = vbdev->ocf_cache;
+
+	rc = ocf_mngt_cache_trylock(cache);
+	if (rc) {
+		cb(rc, cb_arg);
+		return;
+	}
+
+	rc = ocf_mngt_cache_cleaning_set_policy(cache, ocf_cleaning_alru);
+	if (rc)
+		goto end;
+
+	if (wake_up >= 0) {
+		rc = ocf_mngt_cache_cleaning_set_param(cache, ocf_cleaning_alru,
+				ocf_alru_wake_up_time, wake_up);
+		if (rc)
+			goto end;
+	}
+	if (staleness_time >= 0) {
+		rc = ocf_mngt_cache_cleaning_set_param(cache, ocf_cleaning_alru,
+				ocf_alru_stale_buffer_time, staleness_time);
+		if (rc)
+			goto end;
+	}
+	if (flush_max_buffers >= 0) {
+		rc = ocf_mngt_cache_cleaning_set_param(cache, ocf_cleaning_alru,
+				ocf_alru_flush_max_buffers, flush_max_buffers);
+		if (rc)
+			goto end;
+	}
+	if (activity_threshold >= 0) {
+		rc = ocf_mngt_cache_cleaning_set_param(cache, ocf_cleaning_alru,
+				ocf_alru_activity_threshold, activity_threshold);
+		if (rc)
+			goto end;
+	}
+
+end:
+	ocf_mngt_cache_unlock(cache);
+	cb(rc, cb_arg);
+}
+
+/* Set ACP cleaning policy with parameters on OCF cache */
+void
+vbdev_ocf_set_cleaning_acp(
+		struct vbdev_ocf *vbdev,
+		int32_t wake_up,
+		int32_t flush_max_buffers,
+		void (*cb)(int, void *),
+		void *cb_arg)
+{
+	ocf_cache_t cache;
+	int rc;
+
+	cache = vbdev->ocf_cache;
+
+	rc = ocf_mngt_cache_trylock(cache);
+	if (rc) {
+		cb(rc, cb_arg);
+		return;
+	}
+
+	rc = ocf_mngt_cache_cleaning_set_policy(cache, ocf_cleaning_acp);
+	if (rc)
+		goto end;
+
+	if (wake_up >= 0) {
+		rc = ocf_mngt_cache_cleaning_set_param(cache, ocf_cleaning_acp,
+				ocf_acp_wake_up_time, wake_up);
+		if (rc)
+			goto end;
+	}
+	if (flush_max_buffers >= 0) {
+		rc = ocf_mngt_cache_cleaning_set_param(cache, ocf_cleaning_acp,
+				ocf_acp_flush_max_buffers, flush_max_buffers);
+		if (rc)
+			goto end;
+	}
+
+end:
+	ocf_mngt_cache_unlock(cache);
+	cb(rc, cb_arg);
+}
+
+/* Set NOP cleaning policy on OCF cache */
+void
+vbdev_ocf_set_cleaning_nop(
+		struct vbdev_ocf *vbdev,
+		void (*cb)(int, void *),
+		void *cb_arg)
+{
+	ocf_cache_t cache;
+	int rc;
+
+	cache = vbdev->ocf_cache;
+
+	rc = ocf_mngt_cache_trylock(cache);
+	if (rc) {
+		cb(rc, cb_arg);
+		return;
+	}
+
+	rc = ocf_mngt_cache_cleaning_set_policy(cache, ocf_cleaning_nop);
+
+	ocf_mngt_cache_unlock(cache);
+	cb(rc, cb_arg);
+}
+
 /* This called if new device is created in SPDK application
  * If that device named as one of base bdevs of OCF vbdev,
  * claim and open them */
diff --git a/module/bdev/ocf/vbdev_ocf.h b/module/bdev/ocf/vbdev_ocf.h
index fa3814834e2..a98f9139e74 100644
--- a/module/bdev/ocf/vbdev_ocf.h
+++ b/module/bdev/ocf/vbdev_ocf.h
@@ -215,6 +215,30 @@ void vbdev_ocf_set_cache_mode(
 	void (*cb)(int, struct vbdev_ocf *, void *),
 	void *cb_arg);
 
+/* Set ALRU cleaning policy with parameters on OCF cache */
+void vbdev_ocf_set_cleaning_alru(
+	struct vbdev_ocf *vbdev,
+	int32_t wake_up,
+	int32_t staleness_time,
+	int32_t flush_max_buffers,
+	int32_t activity_threshold,
+	void (*cb)(int, void *),
+	void *cb_arg);
+
+/* Set ACP cleaning policy with parameters on OCF cache */
+void vbdev_ocf_set_cleaning_acp(
+	struct vbdev_ocf *vbdev,
+	int32_t wake_up,
+	int32_t flush_max_buffers,
+	void (*cb)(int, void *),
+	void *cb_arg);
+
+/* Set NOP cleaning policy on OCF cache */
+void vbdev_ocf_set_cleaning_nop(
+	struct vbdev_ocf *vbdev,
+	void (*cb)(int, void *),
+	void *cb_arg);
+
 typedef void (*vbdev_ocf_foreach_fn)(struct vbdev_ocf *, void *);
 
 /* Execute fn for each OCF device that is online or waits for base devices */
diff --git a/module/bdev/ocf/vbdev_ocf_rpc.c b/module/bdev/ocf/vbdev_ocf_rpc.c
index ff1173bb3dc..411683289f2 100644
--- a/module/bdev/ocf/vbdev_ocf_rpc.c
+++ b/module/bdev/ocf/vbdev_ocf_rpc.c
@@ -442,3 +442,177 @@ rpc_bdev_ocf_set_cache_mode(struct spdk_jsonrpc_request *request,
 	free_rpc_bdev_ocf_set_cache_mode(&req);
 }
 SPDK_RPC_REGISTER("bdev_ocf_set_cache_mode", rpc_bdev_ocf_set_cache_mode, SPDK_RPC_RUNTIME)
+
+static void
+cleaning_cb(int status, void *cb_arg)
+{
+	struct spdk_jsonrpc_request *request = cb_arg;
+
+	if (status) {
+		spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
+				"OCF could not set cleaning policy: %d", status);
+	} else {
+		spdk_jsonrpc_send_bool_response(request, true);
+	}
+}
+
+/* Structure to hold the parameters for this RPC method. */
+struct rpc_bdev_ocf_set_cleaning_alru {
+	char *name;		/* main vbdev name */
+	int32_t wake_up;
+	int32_t staleness_time;
+	int32_t flush_max_buffers;
+	int32_t activity_threshold;
+};
+
+static void
+free_rpc_bdev_ocf_set_cleaning_alru(struct rpc_bdev_ocf_set_cleaning_alru *r)
+{
+	free(r->name);
+}
+
+/* Structure to decode the input parameters for this RPC method. */
+static const struct spdk_json_object_decoder rpc_bdev_ocf_set_cleaning_alru_decoders[] = {
+	{"name", offsetof(struct rpc_bdev_ocf_set_cleaning_alru, name),
+			spdk_json_decode_string},
+	{"wake_up", offsetof(struct rpc_bdev_ocf_set_cleaning_alru, wake_up),
+			spdk_json_decode_int32},
+	{"staleness_time", offsetof(struct rpc_bdev_ocf_set_cleaning_alru, staleness_time),
+			spdk_json_decode_int32},
+	{"flush_max_buffers", offsetof(struct rpc_bdev_ocf_set_cleaning_alru, flush_max_buffers),
+			spdk_json_decode_int32},
+	{"activity_threshold", offsetof(struct rpc_bdev_ocf_set_cleaning_alru, activity_threshold),
+			spdk_json_decode_int32},
+};
+
+static void
+rpc_bdev_ocf_set_cleaning_alru(struct spdk_jsonrpc_request *request,
+		const struct spdk_json_val *params)
+{
+	struct rpc_bdev_ocf_set_cleaning_alru req = {NULL};
+	struct vbdev_ocf *vbdev;
+	int ret;
+
+	ret = spdk_json_decode_object(params, rpc_bdev_ocf_set_cleaning_alru_decoders,
+			SPDK_COUNTOF(rpc_bdev_ocf_set_cleaning_alru_decoders), &req);
+	if (ret) {
+		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;
+	}
+
+	vbdev_ocf_set_cleaning_alru(vbdev, req.wake_up, req.staleness_time,
+			req.flush_max_buffers, req.activity_threshold, cleaning_cb, request);
+
+end:
+	free_rpc_bdev_ocf_set_cleaning_alru(&req);
+}
+SPDK_RPC_REGISTER("bdev_ocf_set_cleaning_alru", rpc_bdev_ocf_set_cleaning_alru, SPDK_RPC_RUNTIME)
+
+/* Structure to hold the parameters for this RPC method. */
+struct rpc_bdev_ocf_set_cleaning_acp {
+	char *name;		/* main vbdev name */
+	int32_t wake_up;
+	int32_t flush_max_buffers;
+};
+
+static void
+free_rpc_bdev_ocf_set_cleaning_acp(struct rpc_bdev_ocf_set_cleaning_acp *r)
+{
+	free(r->name);
+}
+
+/* Structure to decode the input parameters for this RPC method. */
+static const struct spdk_json_object_decoder rpc_bdev_ocf_set_cleaning_acp_decoders[] = {
+	{"name", offsetof(struct rpc_bdev_ocf_set_cleaning_acp, name),
+			spdk_json_decode_string},
+	{"wake_up", offsetof(struct rpc_bdev_ocf_set_cleaning_acp, wake_up),
+			spdk_json_decode_int32},
+	{"flush_max_buffers", offsetof(struct rpc_bdev_ocf_set_cleaning_acp, flush_max_buffers),
+			spdk_json_decode_int32},
+};
+
+static void
+rpc_bdev_ocf_set_cleaning_acp(struct spdk_jsonrpc_request *request,
+		const struct spdk_json_val *params)
+{
+	struct rpc_bdev_ocf_set_cleaning_acp req = {NULL};
+	struct vbdev_ocf *vbdev;
+	int ret;
+
+	ret = spdk_json_decode_object(params, rpc_bdev_ocf_set_cleaning_acp_decoders,
+			SPDK_COUNTOF(rpc_bdev_ocf_set_cleaning_acp_decoders), &req);
+	if (ret) {
+		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;
+	}
+
+	vbdev_ocf_set_cleaning_acp(vbdev, req.wake_up, req.flush_max_buffers,
+			cleaning_cb, request);
+
+end:
+	free_rpc_bdev_ocf_set_cleaning_acp(&req);
+}
+SPDK_RPC_REGISTER("bdev_ocf_set_cleaning_acp", rpc_bdev_ocf_set_cleaning_acp, SPDK_RPC_RUNTIME)
+
+/* Structure to hold the parameters for this RPC method. */
+struct rpc_bdev_ocf_set_cleaning_nop {
+	char *name;		/* main vbdev name */
+};
+
+static void
+free_rpc_bdev_ocf_set_cleaning_nop(struct rpc_bdev_ocf_set_cleaning_nop *r)
+{
+	free(r->name);
+}
+
+/* Structure to decode the input parameters for this RPC method. */
+static const struct spdk_json_object_decoder rpc_bdev_ocf_set_cleaning_nop_decoders[] = {
+	{"name", offsetof(struct rpc_bdev_ocf_set_cleaning_nop, name),
+			spdk_json_decode_string},
+};
+
+static void
+rpc_bdev_ocf_set_cleaning_nop(struct spdk_jsonrpc_request *request,
+		const struct spdk_json_val *params)
+{
+	struct rpc_bdev_ocf_set_cleaning_nop req = {NULL};
+	struct vbdev_ocf *vbdev;
+	int ret;
+
+	ret = spdk_json_decode_object(params, rpc_bdev_ocf_set_cleaning_nop_decoders,
+			SPDK_COUNTOF(rpc_bdev_ocf_set_cleaning_nop_decoders), &req);
+	if (ret) {
+		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;
+	}
+
+	vbdev_ocf_set_cleaning_nop(vbdev, cleaning_cb, request);
+
+end:
+	free_rpc_bdev_ocf_set_cleaning_nop(&req);
+}
+SPDK_RPC_REGISTER("bdev_ocf_set_cleaning_nop", rpc_bdev_ocf_set_cleaning_nop, SPDK_RPC_RUNTIME)
diff --git a/scripts/rpc.py b/scripts/rpc.py
index 87ba5e1d6fa..b7fe2229289 100755
--- a/scripts/rpc.py
+++ b/scripts/rpc.py
@@ -341,6 +341,49 @@ def bdev_ocf_set_cache_mode(args):
     p.add_argument('mode', help='OCF cache mode', choices=['wb', 'wt', 'pt', 'wa', 'wi', 'wo'])
     p.set_defaults(func=bdev_ocf_set_cache_mode)
 
+    def bdev_ocf_set_cleaning_alru(args):
+        print_json(rpc.bdev.bdev_ocf_set_cleaning_alru(args.client,
+                                                       name=args.name,
+                                                       wake_up=args.wake_up,
+                                                       staleness_time=args.staleness_time,
+                                                       flush_max_buffers=args.flush_max_buffers,
+                                                       activity_threshold=args.activity_threshold))
+    p = subparsers.add_parser('bdev_ocf_set_cleaning_alru',
+                              help='Set ALRU cleaning policy with parameters on OCF cache device')
+    p.add_argument('name', help='Name of OCF bdev')
+    p.add_argument('-w', '--wake-up', type=int, default=-1,
+                   help='Period of time between awakenings of flushing thread <0-3600>[s] (default: 20 s)')
+    p.add_argument('-s', '--staleness-time', type=int, default=-1,
+                   help='Time that has to pass from the last write operation before a dirty cache block '
+                        'can be scheduled to be flushed <1-3600>[s] (default: 120 s)')
+    p.add_argument('-b', '--flush-max-buffers', type=int, default=-1,
+                   help='Number of dirty cache blocks to be flushed in one cleaning cycle <1-10000> (default: 100)')
+    p.add_argument('-t', '--activity-threshold', type=int, default=-1,
+                   help='Cache idle time before flushing thread can start <0-1000000>[ms] (default: 10000 ms)')
+    p.set_defaults(func=bdev_ocf_set_cleaning_alru)
+
+    def bdev_ocf_set_cleaning_acp(args):
+        print_json(rpc.bdev.bdev_ocf_set_cleaning_acp(args.client,
+                                                      name=args.name,
+                                                      wake_up=args.wake_up,
+                                                      flush_max_buffers=args.flush_max_buffers))
+    p = subparsers.add_parser('bdev_ocf_set_cleaning_acp',
+                              help='Set ACP cleaning policy with parameters on OCF cache device')
+    p.add_argument('name', help='Name of OCF bdev')
+    p.add_argument('-w', '--wake-up', type=int, default=-1,
+                   help='Time between ACP cleaning thread iterations <0-10000>[ms] (default: 10 ms)')
+    p.add_argument('-b', '--flush-max-buffers', type=int, default=-1,
+                   help='Number of cache lines flushed in single ACP cleaning thread iteration <1-10000> (default: 128)')
+    p.set_defaults(func=bdev_ocf_set_cleaning_acp)
+
+    def bdev_ocf_set_cleaning_nop(args):
+        print_json(rpc.bdev.bdev_ocf_set_cleaning_nop(args.client,
+                                                      name=args.name))
+    p = subparsers.add_parser('bdev_ocf_set_cleaning_nop',
+                              help='Set NOP cleaning policy on OCF cache device')
+    p.add_argument('name', help='Name of OCF bdev')
+    p.set_defaults(func=bdev_ocf_set_cleaning_nop)
+
     def bdev_malloc_create(args):
         num_blocks = (args.total_size * 1024 * 1024) // args.block_size
         print_json(rpc.bdev.bdev_malloc_create(args.client,
diff --git a/scripts/rpc/bdev.py b/scripts/rpc/bdev.py
index c65896981bc..af223cb5d89 100644
--- a/scripts/rpc/bdev.py
+++ b/scripts/rpc/bdev.py
@@ -229,6 +229,60 @@ def bdev_ocf_set_cache_mode(client, name, mode):
     return client.call('bdev_ocf_set_cache_mode', params)
 
 
+def bdev_ocf_set_cleaning_alru(client, name, wake_up, staleness_time,
+                               flush_max_buffers, activity_threshold):
+    """Set ALRU cleaning policy with parameters on OCF cache device
+
+    Args:
+        name: name of OCF bdev
+        wake_up: Period of time between awakenings of flushing thread <0-3600>[s]
+        staleness_time: Time that has to pass from the last write operation before a
+                        dirty cache block can be scheduled to be flushed <1-3600>[s]
+        flush_max_buffers: Number of dirty cache blocks to be flushed
+                           in one cleaning cycle <1-10000>
+        activity_threshold: Cache idle time before flushing
+                            thread can start <0-1000000>[ms]
+    """
+    params = {
+        'name': name,
+        'wake_up': wake_up,
+        'staleness_time': staleness_time,
+        'flush_max_buffers': flush_max_buffers,
+        'activity_threshold': activity_threshold,
+    }
+
+    return client.call('bdev_ocf_set_cleaning_alru', params)
+
+
+def bdev_ocf_set_cleaning_acp(client, name, wake_up, flush_max_buffers):
+    """Set ACP cleaning policy with parameters on OCF cache device
+
+    Args:
+        name: name of OCF bdev
+        wake_up: Time between ACP cleaning thread iterations <0-10000>[ms]
+        flush_max_buffers: Number of cache lines flushed in single
+                           ACP cleaning thread iteration <1-10000>
+    """
+    params = {
+        'name': name,
+        'wake_up': wake_up,
+        'flush_max_buffers': flush_max_buffers,
+    }
+
+    return client.call('bdev_ocf_set_cleaning_acp', params)
+
+
+def bdev_ocf_set_cleaning_nop(client, name):
+    """Set NOP cleaning policy on OCF cache device
+
+    Args:
+        name: name of OCF bdev
+    """
+    params = {'name': name}
+
+    return client.call('bdev_ocf_set_cleaning_nop', params)
+
+
 @deprecated_alias('construct_malloc_bdev')
 def bdev_malloc_create(client, num_blocks, block_size, name=None, uuid=None):
     """Construct a malloc block device.