diff --git a/doc/bdev.md b/doc/bdev.md
index bb5ad1625cf..fd4b08614cd 100644
--- a/doc/bdev.md
+++ b/doc/bdev.md
@@ -311,32 +311,57 @@ To delete an aio bdev use the bdev_aio_delete command.
`rpc.py bdev_aio_delete aio0`
-## OCF Virtual bdev {#bdev_config_cas}
+## OCF Virtual bdev {#bdev_config_ocf}
-OCF virtual bdev module is based on [Open CAS Framework](https://github.com/Open-CAS/ocf) - a
-high performance block storage caching meta-library.
+OCF virtual bdev module is based on [Open CAS Framework](https://github.com/Open-CAS/ocf) - a high performance block storage caching meta-library.
To enable the module, configure SPDK using `--with-ocf` flag.
OCF bdev can be used to enable caching for any underlying bdev.
-Below is an example command for creating OCF bdev:
+To use OCF caching, first we need to create a caching device:
-`rpc.py bdev_ocf_create Cache1 wt Malloc0 Nvme0n1`
+`rpc.py bdev_ocf_start_cache Ocf_cache0 Malloc0`
-This command will create new OCF bdev `Cache1` having bdev `Malloc0` as caching-device
-and `Nvme0n1` as core-device and initial cache mode `Write-Through`.
-`Malloc0` will be used as cache for `Nvme0n1`, so data written to `Cache1` will be present
-on `Nvme0n1` eventually.
-By default, OCF will be configured with cache line size equal 4KiB
-and non-volatile metadata will be disabled.
+This will start OCF cache **Ocf_cache0** using **Malloc0** as a caching device. **Ocf_cache0** will be used internally by OCF and will not be exposed as an SPDK bdev.
-To remove `Cache1`:
+Then we can add a storage (core) device to our cache:
-`rpc.py bdev_ocf_delete Cache1`
+`rpc.py bdev_ocf_add_core Ocf_core0 Nvme0n1 Ocf_cache0`
-During removal OCF-cache will be stopped and all cached data will be written to the core device.
+This will add **Ocf_core0** to previously created cache instance **Ocf_cache0** using **Nvme0n1** as core device.
+**Ocf_core0** on the other hand, will be registered in the SPDK bdev layer and will be available for usage as any other SPDK bdev.
+(You can add up to 4096 core devices to a single cache instance.)
+In such configuration **Malloc0** will be used as cache for **Nvme0n1**,
+so data written to **Ocf_core0** will be present on **Nvme0n1** eventually, depending on chosen cache mode.
+By default, OCF will be configured with Write-Through cache mode, 4KiB cache line size and loading of previous configuration if OCF metadata is present on the cache device.
+You can change those parameters when creating cache with `bdev_ocf_start_cache`. In addition, cache mode can also be changed in runtime using `bdev_ocf_set_cachemode`.
+There are also other caching parameters to tweak (including: promotion, cleaning or sequential stream cut-off),
+as well as issuing manual flushing of dirty data or dumping OCF statistics.
-Note that OCF has a per-device RAM requirement. More details can be found in the
-[OCF documentation](https://open-cas.github.io/guide_system_requirements.html).
+To remove particular core device:
+
+`rpc.py bdev_ocf_remove_core Ocf_core0`
+
+To stop the whole OCF cache (and remove all of its core devices):
+
+`rpc.py bdev_ocf_stop_cache Ocf_cache0`
+
+During core removal or cache stop all cached data will be written (flushed) to the core device first.
+
+A few things to mention:
+
+- If cache's base bdev contains OCF metadata from previous runs, cache configuration will be loaded from the metadata, unless the no-load flag is specified.
+ This configuration includes all the cores that were added to this cache before it was stopped.
+ Those cores however will be added only as a place holders with a "loading" status until manually added to cache with `bdev_ocf_add_core`.
+ Also, the cache itself will be in incomplete state until all of its cores are added. This behavior has two reasons.
+ First and foremost, it prevents data corruption in case an arbitrary bdev is automatically added as a core device just because of a matching name.
+ And second, it allows for the same JSON bdev config either it's loading or starting a new cache. Such incomplete cache state might also be desired,
+ e.g. to just inspect which cores were previously added to cache.
+
+- When the core device is added using `bdev_ocf_add_core` but its base bdev or cache bdev is not present yet (or cache is in detached state - no base bdev),
+ it is put on a temporary core wait list. Such core will be automatically moved from this wait list to the cache once its base bdev is created and its cache is up and running.
+ This means that all constructing operations can be issued in any order and the outcome should always be the same.
+
+- OCF has a per-device RAM requirement. More details can be found in the [OCF documentation](https://open-cas.github.io/guide_system_requirements.html).
## Malloc bdev {#bdev_config_malloc}
diff --git a/doc/jsonrpc.md.jinja2 b/doc/jsonrpc.md.jinja2
index deb49ee1fc2..f303c552184 100644
--- a/doc/jsonrpc.md.jinja2
+++ b/doc/jsonrpc.md.jinja2
@@ -2898,19 +2898,19 @@ Example response:
}
~~~
-### bdev_ocf_create {#rpc_bdev_ocf_create}
+### bdev_ocf_start_cache {#rpc_bdev_ocf_start_cache}
-Construct new OCF bdev.
-Command accepts cache mode that is going to be used.
-You can find more details about supported cache modes in the [OCF documentation](https://open-cas.github.io/cache_configuration.html#cache-mode)
+Start OCF cache instance.
+
+You can find more details about supported options in the [OCF documentation](https://open-cas.com/cache_configuration.html)
#### Parameters
-{{ bdev_ocf_create_params }}
+{{ bdev_ocf_start_cache_params }}
#### Response
-Name of newly created bdev.
+Name of created OCF cache vbdev.
#### Example
@@ -2919,14 +2919,14 @@ Example request:
~~~json
{
"params": {
- "name": "ocf0",
- "mode": "wt",
+ "cache_name": "Ocf_cache0",
+ "base_name": "Malloc0",
+ "cache_mode": "wb",
"cache_line_size": 64,
- "cache_bdev_name": "Nvme0n1",
- "core_bdev_name": "aio0"
+ "no_load": true
},
"jsonrpc": "2.0",
- "method": "bdev_ocf_create",
+ "method": "bdev_ocf_start_cache",
"id": 1
}
~~~
@@ -2937,17 +2937,17 @@ Example response:
{
"jsonrpc": "2.0",
"id": 1,
- "result": "ocf0"
+ "result": "Ocf_cache0"
}
~~~
-### bdev_ocf_delete {#rpc_bdev_ocf_delete}
+### bdev_ocf_stop_cache {#rpc_bdev_ocf_stop_cache}
-Delete the OCF bdev
+Stop OCF cache instance.
#### Parameters
-{{ bdev_ocf_delete_params }}
+{{ bdev_ocf_stop_cache_params }}
#### Example
@@ -2956,10 +2956,10 @@ Example request:
~~~json
{
"params": {
- "name": "ocf0"
+ "cache_name": "Ocf_cache0"
},
"jsonrpc": "2.0",
- "method": "bdev_ocf_delete",
+ "method": "bdev_ocf_stop_cache",
"id": 1
}
~~~
@@ -2974,17 +2974,46 @@ Example response:
}
~~~
-### bdev_ocf_get_stats {#rpc_bdev_ocf_get_stats}
+### bdev_ocf_detach_cache {#rpc_bdev_ocf_detach_cache}
-Get statistics of chosen OCF block device.
+Detach caching device from OCF cache.
#### Parameters
-{{ bdev_ocf_get_stats_params }}
+{{ bdev_ocf_detach_cache_params }}
-#### Response
+#### Example
-Statistics as json object.
+Example request:
+
+~~~json
+{
+ "params": {
+ "cache_name": "Ocf_cache0"
+ },
+ "jsonrpc": "2.0",
+ "method": "bdev_ocf_detach_cache",
+ "id": 1
+}
+~~~
+
+Example response:
+
+~~~json
+{
+ "jsonrpc": "2.0",
+ "id": 1,
+ "result": true
+}
+~~~
+
+### bdev_ocf_attach_cache {#rpc_bdev_ocf_attach_cache}
+
+Attach caching device to OCF cache.
+
+#### Parameters
+
+{{ bdev_ocf_attach_cache_params }}
#### Example
@@ -2993,10 +3022,12 @@ Example request:
~~~json
{
"params": {
- "name": "ocf0"
+ "cache_name": "Ocf_cache0",
+ "base_name": "Nvme0n1",
+ "force": true
},
"jsonrpc": "2.0",
- "method": "bdev_ocf_get_stats",
+ "method": "bdev_ocf_attach_cache",
"id": 1
}
~~~
@@ -3007,198 +3038,21 @@ Example response:
{
"jsonrpc": "2.0",
"id": 1,
- "result": [
- {
- "usage": {
- "clean": {
- "count": 76033,
- "units": "4KiB blocks",
- "percentage": "100.0"
- },
- "free": {
- "count": 767,
- "units": "4KiB blocks",
- "percentage": "0.9"
- },
- "occupancy": {
- "count": 76033,
- "units": "4KiB blocks",
- "percentage": "99.0"
- },
- "dirty": {
- "count": 0,
- "units": "4KiB blocks",
- "percentage": "0.0"
- }
- }
- },
- {
- "requests": {
- "rd_total": {
- "count": 2,
- "units": "Requests",
- "percentage": "0.0"
- },
- "wr_full_misses": {
- "count": 76280,
- "units": "Requests",
- "percentage": "35.6"
- },
- "rd_full_misses": {
- "count": 1,
- "units": "Requests",
- "percentage": "0.0"
- },
- "rd_partial_misses": {
- "count": 0,
- "units": "Requests",
- "percentage": "0.0"
- },
- "wr_total": {
- "count": 212416,
- "units": "Requests",
- "percentage": "99.2"
- },
- "wr_pt": {
- "count": 1535,
- "units": "Requests",
- "percentage": "0.7"
- },
- "wr_partial_misses": {
- "count": 0,
- "units": "Requests",
- "percentage": "0.0"
- },
- "serviced": {
- "count": 212418,
- "units": "Requests",
- "percentage": "99.2"
- },
- "rd_pt": {
- "count": 0,
- "units": "Requests",
- "percentage": "0.0"
- },
- "total": {
- "count": 213953,
- "units": "Requests",
- "percentage": "100.0"
- },
- "rd_hits": {
- "count": 1,
- "units": "Requests",
- "percentage": "0.0"
- },
- "wr_hits": {
- "count": 136136,
- "units": "Requests",
- "percentage": "63.6"
- }
- }
- },
- {
- "errors": {
- "total": {
- "count": 0,
- "units": "Requests",
- "percentage": "0.0"
- },
- "cache_obj_total": {
- "count": 0,
- "units": "Requests",
- "percentage": "0.0"
- },
- "core_obj_total": {
- "count": 0,
- "units": "Requests",
- "percentage": "0.0"
- },
- "cache_obj_rd": {
- "count": 0,
- "units": "Requests",
- "percentage": "0.0"
- },
- "core_obj_wr": {
- "count": 0,
- "units": "Requests",
- "percentage": "0.0"
- },
- "core_obj_rd": {
- "count": 0,
- "units": "Requests",
- "percentage": "0.0"
- },
- "cache_obj_wr": {
- "count": 0,
- "units": "Requests",
- "percentage": "0.0"
- }
- }
- },
- {
- "blocks": {
- "volume_rd": {
- "count": 9,
- "units": "4KiB blocks",
- "percentage": "0.0"
- },
- "volume_wr": {
- "count": 213951,
- "units": "4KiB blocks",
- "percentage": "99.9"
- },
- "cache_obj_total": {
- "count": 212425,
- "units": "4KiB blocks",
- "percentage": "100.0"
- },
- "core_obj_total": {
- "count": 213959,
- "units": "4KiB blocks",
- "percentage": "100.0"
- },
- "cache_obj_rd": {
- "count": 1,
- "units": "4KiB blocks",
- "percentage": "0.0"
- },
- "core_obj_wr": {
- "count": 213951,
- "units": "4KiB blocks",
- "percentage": "99.9"
- },
- "volume_total": {
- "count": 213960,
- "units": "4KiB blocks",
- "percentage": "100.0"
- },
- "core_obj_rd": {
- "count": 8,
- "units": "4KiB blocks",
- "percentage": "0.0"
- },
- "cache_obj_wr": {
- "count": 212424,
- "units": "4KiB blocks",
- "percentage": "99.9"
- }
- }
- }
- ]
+ "result": true
}
~~~
-### bdev_ocf_reset_stats {#rpc_bdev_ocf_reset_stats}
+### bdev_ocf_add_core {#rpc_bdev_ocf_add_core}
-Reset statistics of chosen OCF block device.
+Add core (backend device) to OCF cache.
#### Parameters
-{{ bdev_ocf_reset_stats_params }}
+{{ bdev_ocf_add_core_params }}
#### Response
-Completion status of reset statistics operation returned as a boolean.
+Name of created OCF core vbdev.
#### Example
@@ -3207,10 +3061,12 @@ Example request:
~~~json
{
"params": {
- "name": "ocf0"
+ "core_name": "Ocf_core0",
+ "base_name": "Malloc0",
+ "cache_name": "Ocf_cache0"
},
"jsonrpc": "2.0",
- "method": "bdev_ocf_reset_stats",
+ "method": "bdev_ocf_add_core",
"id": 1
}
~~~
@@ -3221,21 +3077,52 @@ Example response:
{
"jsonrpc": "2.0",
"id": 1,
- "result": true
+ "result": "Ocf_core0"
}
~~~
-### bdev_ocf_get_bdevs {#rpc_bdev_ocf_get_bdevs}
+### bdev_ocf_remove_core {#rpc_bdev_ocf_remove_core}
-Get list of OCF devices including unregistered ones.
+Remove core (backend device) from OCF cache.
#### Parameters
-{{ bdev_ocf_get_bdevs_params }}
+{{ bdev_ocf_remove_core_params }}
-#### Response
+#### Example
+
+Example request:
+
+~~~json
+{
+ "params": {
+ "core_name": "Ocf_core0"
+ },
+ "jsonrpc": "2.0",
+ "method": "bdev_ocf_remove_core",
+ "id": 1
+}
+~~~
+
+Example response:
+
+~~~json
+{
+ "jsonrpc": "2.0",
+ "id": 1,
+ "result": true
+}
+~~~
+
+### bdev_ocf_set_cachemode {#rpc_bdev_ocf_set_cachemode}
-Array of OCF devices with their current status, along with core and cache bdevs.
+Set cache mode of OCF cache.
+
+You can find more details about supported cache modes in the [OCF documentation](https://open-cas.com/cache_configuration.html#cache-mode)
+
+#### Parameters
+
+{{ bdev_ocf_set_cachemode_params }}
#### Example
@@ -3243,8 +3130,12 @@ Example request:
~~~json
{
+ "params": {
+ "cache_name": "Ocf_cache0",
+ "cache_mode": "wb"
+ },
"jsonrpc": "2.0",
- "method": "bdev_ocf_get_bdevs",
+ "method": "bdev_ocf_set_cachemode",
"id": 1
}
~~~
@@ -3255,34 +3146,57 @@ Example response:
{
"jsonrpc": "2.0",
"id": 1,
- "result": [
- {
- "name": "PartCache",
- "started": false,
- "cache": {
- "name": "Malloc0",
- "attached": true
- },
- "core": {
- "name": "Malloc1",
- "attached": false
- }
- }
- ]
+ "result": true
}
~~~
-### bdev_ocf_set_cache_mode {#rpc_bdev_ocf_set_cache_mode}
+### bdev_ocf_set_promotion {#rpc_bdev_ocf_set_promotion}
-Set new cache mode on OCF bdev.
+Set promotion parameters for OCF cache.
+
+You can find more details about supported promotion parameters in the [OCF documentation](https://open-cas.com/promotion.html)
#### Parameters
-{{ bdev_ocf_set_cache_mode_params }}
+{{ bdev_ocf_set_promotion_params }}
-#### Response
+#### Example
+
+Example request:
+
+~~~json
+{
+ "params": {
+ "cache_name": "Ocf_cache0",
+ "policy": "nhit",
+ "nhit_insertion_threshold": 100,
+ "nhit_trigger_threshold": 50
+ },
+ "jsonrpc": "2.0",
+ "method": "bdev_ocf_set_promotion",
+ "id": 1
+}
+~~~
-New cache mode name.
+Example response:
+
+~~~json
+{
+ "jsonrpc": "2.0",
+ "id": 1,
+ "result": true
+}
+~~~
+
+### bdev_ocf_set_cleaning {#rpc_bdev_ocf_set_cleaning}
+
+Set cleaning parameters for OCF cache.
+
+You can find more details about supported cleaning parameters in the [OCF documentation](https://open-cas.com/cleaner.html)
+
+#### Parameters
+
+{{ bdev_ocf_set_cleaning_params }}
#### Example
@@ -3291,11 +3205,13 @@ Example request:
~~~json
{
"params": {
- "name": "ocf0",
- "mode": "pt"
+ "cache_name": "Ocf_cache0",
+ "policy": "acp",
+ "acp_wake_up_time": 150,
+ "acp_flush_max_buffers": 512
},
"jsonrpc": "2.0",
- "method": "bdev_ocf_set_cache_mode",
+ "method": "bdev_ocf_set_cleaning",
"id": 1
}
~~~
@@ -3306,14 +3222,15 @@ Example response:
{
"jsonrpc": "2.0",
"id": 1,
- "result": "pt"
+ "result": true
}
~~~
### bdev_ocf_set_seqcutoff {#rpc_bdev_ocf_set_seqcutoff}
-Set sequential cutoff parameters on all cores for the given OCF cache device.
-A brief description of this functionality can be found in [OpenCAS documentation](https://open-cas.github.io/guide_tool_details.html#seq-cutoff).
+Set sequential cut-off parameters for OCF core or all cores in given cache.
+
+You can find more details about supported sequential cut-off parameters in the [OCF documentation](https://open-cas.com/seq_cutoff.html)
#### Parameters
@@ -3326,10 +3243,11 @@ Example request:
~~~json
{
"params": {
- "name": "ocf0",
- "policy": "full",
- "threshold": 4,
- "promotion_count": 2
+ "bdev_name": "Ocf_core0",
+ "policy": "always",
+ "threshold": 4096,
+ "promotion_count": 65535,
+ "promote_on_threshold": 1
},
"jsonrpc": "2.0",
"method": "bdev_ocf_set_seqcutoff",
@@ -3349,16 +3267,10 @@ Example response:
### bdev_ocf_flush_start {#rpc_bdev_ocf_flush_start}
-Start flushing OCF cache device.
+Flush all dirty data on the given OCF device (from cache to all of its cores or to particular core only).
-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.
+Note that this call only starts the flushing process which will be running in background and may take some time depending on the underlying device size and speed.
+You can check flushing status using the bdev_ocf_get_bdevs call.
#### Parameters
@@ -3371,7 +3283,7 @@ Example request:
~~~json
{
"params": {
- "name": "ocf0"
+ "bdev_name": "Ocf_core0"
},
"jsonrpc": "2.0",
"method": "bdev_ocf_flush_start",
@@ -3389,26 +3301,223 @@ Example response:
}
~~~
-### bdev_ocf_flush_status {#rpc_bdev_ocf_flush_status}
+### bdev_ocf_get_stats {#rpc_bdev_ocf_get_stats}
-Get flush status of OCF cache device.
+Get statistics of OCF vbdev.
-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.
+Statistics are shown for particular core or all cores in given cache combined depending on which bdev was passed as a parameter.
#### Parameters
-{{ bdev_ocf_flush_status_params }}
+{{ bdev_ocf_get_stats_params }}
#### Response
-Status of OCF cache device flush.
+Statistics in JSON format.
+
+#### Example
+
+Example request:
+
+~~~json
+{
+ "params": {
+ "name": "Ocf_cache0"
+ },
+ "jsonrpc": "2.0",
+ "method": "bdev_ocf_get_stats",
+ "id": 1
+}
+~~~
+
+Example response:
+
+~~~json
+{
+ "jsonrpc": "2.0",
+ "id": 1,
+ "result": {
+ "usage": {
+ "occupancy": {
+ "count": 55521,
+ "percentage": "2.14",
+ "units": "4KiB blocks"
+ },
+ "free": {
+ "count": 2529759,
+ "percentage": "97.85",
+ "units": "4KiB blocks"
+ },
+ "clean": {
+ "count": 55521,
+ "percentage": "2.14",
+ "units": "4KiB blocks"
+ },
+ "dirty": {
+ "count": 0,
+ "percentage": "0.0",
+ "units": "4KiB blocks"
+ }
+ },
+ "requests": {
+ "rd_hits": {
+ "count": 12,
+ "percentage": "0.2",
+ "units": "Requests"
+ },
+ "rd_partial_misses": {
+ "count": 0,
+ "percentage": "0.0",
+ "units": "Requests"
+ },
+ "rd_full_misses": {
+ "count": 27726,
+ "percentage": "49.83",
+ "units": "Requests"
+ },
+ "rd_total": {
+ "count": 27738,
+ "percentage": "49.85",
+ "units": "Requests"
+ },
+ "wr_hits": {
+ "count": 7,
+ "percentage": "0.1",
+ "units": "Requests"
+ },
+ "wr_partial_misses": {
+ "count": 0,
+ "percentage": "0.0",
+ "units": "Requests"
+ },
+ "wr_full_misses": {
+ "count": 27893,
+ "percentage": "50.13",
+ "units": "Requests"
+ },
+ "wr_total": {
+ "count": 27900,
+ "percentage": "50.14",
+ "units": "Requests"
+ },
+ "rd_pt": {
+ "count": 0,
+ "percentage": "0.0",
+ "units": "Requests"
+ },
+ "wr_pt": {
+ "count": 0,
+ "percentage": "0.0",
+ "units": "Requests"
+ },
+ "serviced": {
+ "count": 55638,
+ "percentage": "100.0",
+ "units": "Requests"
+ },
+ "total": {
+ "count": 55638,
+ "percentage": "100.0",
+ "units": "Requests"
+ }
+ },
+ "blocks": {
+ "core_volume_rd": {
+ "count": 3474,
+ "percentage": "49.89",
+ "units": "4KiB blocks"
+ },
+ "core_volume_wr": {
+ "count": 3488,
+ "percentage": "50.10",
+ "units": "4KiB blocks"
+ },
+ "core_volume_total": {
+ "count": 6962,
+ "percentage": "100.0",
+ "units": "4KiB blocks"
+ },
+ "cache_volume_rd": {
+ "count": 3,
+ "percentage": "0.4",
+ "units": "4KiB blocks"
+ },
+ "cache_volume_wr": {
+ "count": 6961,
+ "percentage": "99.95",
+ "units": "4KiB blocks"
+ },
+ "cache_volume_total": {
+ "count": 6964,
+ "percentage": "100.0",
+ "units": "4KiB blocks"
+ },
+ "volume_rd": {
+ "count": 3476,
+ "percentage": "49.91",
+ "units": "4KiB blocks"
+ },
+ "volume_wr": {
+ "count": 3488,
+ "percentage": "50.8",
+ "units": "4KiB blocks"
+ },
+ "volume_total": {
+ "count": 6964,
+ "percentage": "100.0",
+ "units": "4KiB blocks"
+ }
+ },
+ "errors": {
+ "core_volume_rd": {
+ "count": 0,
+ "percentage": "0.0",
+ "units": "Requests"
+ },
+ "core_volume_wr": {
+ "count": 0,
+ "percentage": "0.0",
+ "units": "Requests"
+ },
+ "core_volume_total": {
+ "count": 0,
+ "percentage": "0.0",
+ "units": "Requests"
+ },
+ "cache_volume_rd": {
+ "count": 0,
+ "percentage": "0.0",
+ "units": "Requests"
+ },
+ "cache_volume_wr": {
+ "count": 0,
+ "percentage": "0.0",
+ "units": "Requests"
+ },
+ "cache_volume_total": {
+ "count": 0,
+ "percentage": "0.0",
+ "units": "Requests"
+ },
+ "total": {
+ "count": 0,
+ "percentage": "0.0",
+ "units": "Requests"
+ }
+ }
+ }
+}
+~~~
+
+### bdev_ocf_reset_stats {#rpc_bdev_ocf_reset_stats}
+
+Reset statistics of OCF vbdev.
+
+Statistics are reset for particular core or all cores in given cache depending on which bdev was passed as a parameter.
+
+#### Parameters
+
+{{ bdev_ocf_reset_stats_params }}
#### Example
@@ -3417,10 +3526,44 @@ Example request:
~~~json
{
"params": {
- "name": "ocf0"
+ "name": "Ocf_cache0"
},
"jsonrpc": "2.0",
- "method": "bdev_ocf_flush_status",
+ "method": "bdev_ocf_reset_stats",
+ "id": 1
+}
+~~~
+
+Example response:
+
+~~~json
+{
+ "jsonrpc": "2.0",
+ "id": 1,
+ "result": true
+}
+~~~
+
+### bdev_ocf_get_bdevs {#rpc_bdev_ocf_get_bdevs}
+
+Get detailed info about OCF vbdevs.
+
+#### Parameters
+
+{{ bdev_ocf_get_bdevs_params }}
+
+#### Response
+
+List of OCF vbdev details in JSON format.
+
+#### Example
+
+Example request:
+
+~~~json
+{
+ "jsonrpc": "2.0",
+ "method": "bdev_ocf_get_bdevs",
"id": 1
}
~~~
@@ -3432,8 +3575,139 @@ Example response:
"jsonrpc": "2.0",
"id": 1,
"result": {
- "in_progress": false,
- "status": 0
+ "cores_waitlist": [
+ {
+ "name": "Ocf_core_waitlist0",
+ "cache_name": "Ocf_cache2",
+ "base_name": "Nvme2n1",
+ "base_attached": false,
+ "size": null,
+ "block_size": null
+ },
+ {
+ "name": "Ocf_core_waitlist1",
+ "cache_name": "Ocf_cache2",
+ "base_name": "Malloc3",
+ "base_attached": true,
+ "size": 209715200,
+ "block_size": 512
+ }
+ ],
+ "caches": [
+ {
+ "name": "Ocf_cache0",
+ "base_name": "Malloc0",
+ "base_attached": true,
+ "size": 209715200,
+ "block_size": 512,
+ "cache_line_size": 4096,
+ "cache_mode": "wt",
+ "promotion": {
+ "policy": "always"
+ },
+ "cleaning": {
+ "policy": "alru",
+ "wake_up_time": 20,
+ "flush_max_buffers": 100,
+ "staleness_time": 120,
+ "activity_threshold": 10000,
+ "dirty_ratio_threshold": 100,
+ "dirty_ratio_inertia": 134217728
+ },
+ "flush": {
+ "in_progress": false,
+ "error": 0
+ },
+ "cores_count": 1,
+ "cores": [
+ {
+ "name": "Ocf_core0-0",
+ "cache_name": "Ocf_cache0",
+ "base_name": "Nvme0n1",
+ "base_attached": true,
+ "size": 10737418240,
+ "block_size": 512,
+ "loading": false,
+ "seq_cutoff": {
+ "policy": "full",
+ "threshold": 1024,
+ "promotion_count": 8,
+ "promote_on_threshold": false
+ },
+ "flush": {
+ "in_progress": false,
+ "error": 0
+ }
+ }
+ ]
+ },
+ {
+ "name": "Ocf_cache1",
+ "base_name": "Malloc1",
+ "base_attached": true,
+ "size": 209715200,
+ "block_size": 512,
+ "cache_line_size": 65536,
+ "cache_mode": "wb",
+ "promotion": {
+ "policy": "always"
+ },
+ "cleaning": {
+ "policy": "alru",
+ "wake_up_time": 20,
+ "flush_max_buffers": 100,
+ "staleness_time": 120,
+ "activity_threshold": 10000,
+ "dirty_ratio_threshold": 100,
+ "dirty_ratio_inertia": 134217728
+ },
+ "flush": {
+ "in_progress": false,
+ "error": 0
+ },
+ "cores_count": 2,
+ "cores": [
+ {
+ "name": "Ocf_core1-0",
+ "cache_name": "Ocf_cache1",
+ "base_name": "Nvme1n1",
+ "base_attached": true,
+ "size": 10737418240,
+ "block_size": 512,
+ "loading": false,
+ "seq_cutoff": {
+ "policy": "full",
+ "threshold": 1024,
+ "promotion_count": 8,
+ "promote_on_threshold": false
+ },
+ "flush": {
+ "in_progress": false,
+ "error": 0
+ }
+ },
+ {
+ "name": "Ocf_core1-1",
+ "cache_name": "Ocf_cache1",
+ "base_name": "Malloc2",
+ "base_attached": true,
+ "size": 262144000,
+ "block_size": 512,
+ "loading": false,
+ "seq_cutoff": {
+ "policy": "full",
+ "threshold": 1024,
+ "promotion_count": 8,
+ "promote_on_threshold": false
+ },
+ "flush": {
+ "in_progress": false,
+ "error": 0
+ }
+ }
+ ]
+ }
+ ]
}
}
~~~
diff --git a/lib/env_ocf/ocf_env_headers.h b/lib/env_ocf/ocf_env_headers.h
index 403c0bdcb21..21ddc372a97 100644
--- a/lib/env_ocf/ocf_env_headers.h
+++ b/lib/env_ocf/ocf_env_headers.h
@@ -8,7 +8,7 @@
#include "spdk/stdinc.h"
-#define OCF_VERSION_MAIN 20
+#define OCF_VERSION_MAIN 25
#define OCF_VERSION_MAJOR 3
#define OCF_VERSION_MINOR 0
diff --git a/lib/env_ocf/ocf_env_refcnt.c b/lib/env_ocf/ocf_env_refcnt.c
new file mode 100644
index 00000000000..4c2c3c6dabb
--- /dev/null
+++ b/lib/env_ocf/ocf_env_refcnt.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright(c) 2019-2021 Intel Corporation
+ * Copyright(c) 2024 Huawei Technologies
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "ocf_env_refcnt.h"
+
+int
+env_refcnt_init(struct env_refcnt *rc, const char *name, size_t name_len)
+{
+ env_atomic_set(&rc->counter, 0);
+ env_atomic_set(&rc->freeze, 0);
+ env_atomic_set(&rc->callback, 0);
+ rc->cb = NULL;
+
+ return 0;
+}
+
+void
+env_refcnt_deinit(struct env_refcnt *rc)
+{
+
+}
+
+void
+env_refcnt_dec(struct env_refcnt *rc)
+{
+ int val = env_atomic_dec_return(&rc->counter);
+ ENV_BUG_ON(val < 0);
+
+ if (!val && env_atomic_cmpxchg(&rc->callback, 1, 0)) {
+ rc->cb(rc->priv);
+ }
+}
+
+bool
+env_refcnt_inc(struct env_refcnt *rc)
+{
+ int val;
+
+ if (!env_atomic_read(&rc->freeze)) {
+ val = env_atomic_inc_return(&rc->counter);
+ if (!env_atomic_read(&rc->freeze)) {
+ return !!val;
+ } else {
+ env_refcnt_dec(rc);
+ }
+ }
+
+ return 0;
+}
+
+
+void
+env_refcnt_freeze(struct env_refcnt *rc)
+{
+ env_atomic_inc(&rc->freeze);
+}
+
+void
+env_refcnt_register_zero_cb(struct env_refcnt *rc, env_refcnt_cb_t cb,
+ void *priv)
+{
+ ENV_BUG_ON(!env_atomic_read(&rc->freeze));
+ ENV_BUG_ON(env_atomic_read(&rc->callback));
+
+ env_atomic_inc(&rc->counter);
+ rc->cb = cb;
+ rc->priv = priv;
+ env_atomic_set(&rc->callback, 1);
+ env_refcnt_dec(rc);
+}
+
+void
+env_refcnt_unfreeze(struct env_refcnt *rc)
+{
+ int val = env_atomic_dec_return(&rc->freeze);
+ ENV_BUG_ON(val < 0);
+}
+
+bool
+env_refcnt_frozen(struct env_refcnt *rc)
+{
+ return !!env_atomic_read(&rc->freeze);
+}
+
+bool
+env_refcnt_zeroed(struct env_refcnt *rc)
+{
+ return (env_atomic_read(&rc->counter) == 0);
+}
diff --git a/lib/env_ocf/ocf_env_refcnt.h b/lib/env_ocf/ocf_env_refcnt.h
new file mode 100644
index 00000000000..e4ea113ff62
--- /dev/null
+++ b/lib/env_ocf/ocf_env_refcnt.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright(c) 2019-2021 Intel Corporation
+ * Copyright(c) 2024-2025 Huawei Technologies
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef __OCF_ENV_REFCNT_H__
+#define __OCF_ENV_REFCNT_H__
+
+#include "ocf_env.h"
+
+typedef void (*env_refcnt_cb_t)(void *priv);
+
+struct env_refcnt {
+ env_atomic counter __attribute__((aligned(64)));
+ env_atomic freeze;
+ env_atomic callback;
+ env_refcnt_cb_t cb;
+ void *priv;
+};
+
+/* Initialize reference counter */
+int env_refcnt_init(struct env_refcnt *rc, const char *name, size_t name_len);
+
+void env_refcnt_deinit(struct env_refcnt *rc);
+
+/* Try to increment counter. Returns true if successfull, false
+ * if counter is frozen */
+bool env_refcnt_inc(struct env_refcnt *rc);
+
+/* Decrement reference counter */
+void env_refcnt_dec(struct env_refcnt *rc);
+
+/* Disallow incrementing of underlying counter - attempts to increment counter
+ * will be failing until env_refcnt_unfreeze is calleed.
+ * It's ok to call freeze multiple times, in which case counter is frozen
+ * until all freeze calls are offset by a corresponding unfreeze. */
+void env_refcnt_freeze(struct env_refcnt *rc);
+
+/* Cancel the effect of single env_refcnt_freeze call */
+void env_refcnt_unfreeze(struct env_refcnt *rc);
+
+bool env_refcnt_frozen(struct env_refcnt *rc);
+
+bool env_refcnt_zeroed(struct env_refcnt *rc);
+
+/* Register callback to be called when reference counter drops to 0.
+ * Must be called after counter is frozen.
+ * Cannot be called until previously regsitered callback had fired. */
+void env_refcnt_register_zero_cb(struct env_refcnt *rc, env_refcnt_cb_t cb,
+ void *priv);
+
+#endif /* __OCF_ENV_REFCNT_H__ */
diff --git a/module/bdev/ocf/ctx.c b/module/bdev/ocf/ctx.c
index 72bb0cc51c4..35ac9465866 100644
--- a/module/bdev/ocf/ctx.c
+++ b/module/bdev/ocf/ctx.c
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (C) 2018 Intel Corporation.
+ * Copyright (C) 2025 Huawei Technologies
* All rights reserved.
*/
@@ -13,13 +14,14 @@
#include "ctx.h"
#include "data.h"
+#include "vbdev_ocf_cache.h"
ocf_ctx_t vbdev_ocf_ctx;
static ctx_data_t *
vbdev_ocf_ctx_data_alloc(uint32_t pages)
{
- struct bdev_ocf_data *data;
+ struct vbdev_ocf_data *data;
void *buf;
uint32_t sz;
@@ -46,7 +48,7 @@ vbdev_ocf_ctx_data_alloc(uint32_t pages)
static void
vbdev_ocf_ctx_data_free(ctx_data_t *ctx_data)
{
- struct bdev_ocf_data *data = ctx_data;
+ struct vbdev_ocf_data *data = ctx_data;
int i;
if (!data) {
@@ -105,7 +107,7 @@ iovec_flatten(struct iovec *iov, size_t iovcnt, void *buf, size_t size, size_t o
static uint32_t
vbdev_ocf_ctx_data_rd(void *dst, ctx_data_t *src, uint32_t size)
{
- struct bdev_ocf_data *s = src;
+ struct vbdev_ocf_data *s = src;
uint32_t size_local;
size_local = iovec_flatten(s->iovs, s->iovcnt, dst, size, s->seek);
@@ -146,7 +148,7 @@ buf_to_iovec(const void *buf, size_t size, struct iovec *iov, size_t iovcnt, siz
static uint32_t
vbdev_ocf_ctx_data_wr(ctx_data_t *dst, const void *src, uint32_t size)
{
- struct bdev_ocf_data *d = dst;
+ struct vbdev_ocf_data *d = dst;
uint32_t size_local;
size_local = buf_to_iovec(src, size, d->iovs, d->iovcnt, d->seek);
@@ -186,7 +188,7 @@ iovset(struct iovec *iov, size_t iovcnt, int byte, size_t size, size_t offset)
static uint32_t
vbdev_ocf_ctx_data_zero(ctx_data_t *dst, uint32_t size)
{
- struct bdev_ocf_data *d = dst;
+ struct vbdev_ocf_data *d = dst;
uint32_t size_local;
size_local = iovset(d->iovs, d->iovcnt, 0, size, d->seek);
@@ -198,7 +200,7 @@ vbdev_ocf_ctx_data_zero(ctx_data_t *dst, uint32_t size)
static uint32_t
vbdev_ocf_ctx_data_seek(ctx_data_t *dst, ctx_data_seek_t seek, uint32_t offset)
{
- struct bdev_ocf_data *d = dst;
+ struct vbdev_ocf_data *d = dst;
uint32_t off = 0;
switch (seek) {
@@ -219,8 +221,8 @@ static uint64_t
vbdev_ocf_ctx_data_cpy(ctx_data_t *dst, ctx_data_t *src, uint64_t to,
uint64_t from, uint64_t bytes)
{
- struct bdev_ocf_data *s = src;
- struct bdev_ocf_data *d = dst;
+ struct vbdev_ocf_data *s = src;
+ struct vbdev_ocf_data *d = dst;
uint32_t it_iov = 0;
uint32_t it_off = 0;
uint32_t n, sz;
@@ -255,7 +257,7 @@ vbdev_ocf_ctx_data_cpy(ctx_data_t *dst, ctx_data_t *src, uint64_t to,
static void
vbdev_ocf_ctx_data_secure_erase(ctx_data_t *ctx_data)
{
- struct bdev_ocf_data *data = ctx_data;
+ struct vbdev_ocf_data *data = ctx_data;
struct iovec *iovs = data->iovs;
int i;
@@ -285,18 +287,19 @@ vbdev_ocf_queue_put(ocf_queue_t queue)
ocf_queue_put(queue);
}
-void
-vbdev_ocf_cache_ctx_put(struct vbdev_ocf_cache_ctx *ctx)
+int
+vbdev_ocf_queue_poller(void *ctx)
{
- if (env_atomic_dec_return(&ctx->refcnt) == 0) {
- free(ctx);
+ ocf_queue_t queue = ctx;
+ int i, queue_runs;
+
+ queue_runs = spdk_min(ocf_queue_pending_io(queue), VBDEV_OCF_QUEUE_RUN_MAX);
+
+ for (i = 0; i < queue_runs; i++) {
+ ocf_queue_run_single(queue);
}
-}
-void
-vbdev_ocf_cache_ctx_get(struct vbdev_ocf_cache_ctx *ctx)
-{
- env_atomic_inc(&ctx->refcnt);
+ return queue_runs ? SPDK_POLLER_BUSY : SPDK_POLLER_IDLE;
}
struct cleaner_priv {
@@ -332,13 +335,14 @@ vbdev_ocf_ctx_cleaner_init(ocf_cleaner_t c)
{
struct cleaner_priv *priv = calloc(1, sizeof(*priv));
ocf_cache_t cache = ocf_cleaner_get_cache(c);
- struct vbdev_ocf_cache_ctx *cctx = ocf_cache_get_priv(cache);
+ struct vbdev_ocf_cache *vbdev_ocf_cache = ocf_cache_get_priv(cache);
if (priv == NULL) {
return -ENOMEM;
}
- priv->mngt_queue = cctx->mngt_queue;
+ priv->mngt_queue = vbdev_ocf_cache->cache_mngt_q;
+ ocf_queue_get(priv->mngt_queue);
ocf_cleaner_set_cmpl(c, cleaner_cmpl);
ocf_cleaner_set_priv(c, priv);
@@ -353,10 +357,13 @@ vbdev_ocf_ctx_cleaner_stop(ocf_cleaner_t c)
if (priv) {
spdk_poller_unregister(&priv->poller);
+ vbdev_ocf_queue_put(priv->mngt_queue);
free(priv);
}
}
+/* TODO?: this one should only set next_run to "now" and poller
+ * registration should be move to _init with saved thread */
static void
vbdev_ocf_ctx_cleaner_kick(ocf_cleaner_t cleaner)
{
@@ -408,7 +415,7 @@ vbdev_ocf_ctx_log_printf(ocf_logger_t logger, ocf_logger_lvl_t lvl,
}
static const struct ocf_ctx_config vbdev_ocf_ctx_cfg = {
- .name = "OCF SPDK",
+ .name = "SPDK_OCF",
.ops = {
.data = {
diff --git a/module/bdev/ocf/ctx.h b/module/bdev/ocf/ctx.h
index 3d3e5bbcc58..8bfa24e8521 100644
--- a/module/bdev/ocf/ctx.h
+++ b/module/bdev/ocf/ctx.h
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (C) 2018 Intel Corporation.
+ * Copyright (C) 2025 Huawei Technologies
* All rights reserved.
*/
@@ -7,8 +8,6 @@
#define VBDEV_OCF_CTX_H
#include
-#include "ocf_env.h"
-#include "spdk/thread.h"
extern ocf_ctx_t vbdev_ocf_ctx;
@@ -16,14 +15,7 @@ extern ocf_ctx_t vbdev_ocf_ctx;
#define SPDK_OBJECT 1
-/* Context of cache instance */
-struct vbdev_ocf_cache_ctx {
- ocf_queue_t mngt_queue;
- env_atomic refcnt;
-};
-
-void vbdev_ocf_cache_ctx_put(struct vbdev_ocf_cache_ctx *ctx);
-void vbdev_ocf_cache_ctx_get(struct vbdev_ocf_cache_ctx *ctx);
+#define VBDEV_OCF_QUEUE_RUN_MAX 32
int vbdev_ocf_ctx_init(void);
void vbdev_ocf_ctx_cleanup(void);
@@ -34,5 +26,6 @@ int vbdev_ocf_queue_create(ocf_cache_t cache, ocf_queue_t *queue, const struct o
int vbdev_ocf_queue_create_mngt(ocf_cache_t cache, ocf_queue_t *queue,
const struct ocf_queue_ops *ops);
void vbdev_ocf_queue_put(ocf_queue_t queue);
+int vbdev_ocf_queue_poller(void *ctx);
#endif
diff --git a/module/bdev/ocf/data.c b/module/bdev/ocf/data.c
index ff3f347ed60..46c3cc55086 100644
--- a/module/bdev/ocf/data.c
+++ b/module/bdev/ocf/data.c
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (C) 2018 Intel Corporation.
+ * Copyright (C) 2025 Huawei Technologies
* All rights reserved.
*/
@@ -7,10 +8,10 @@
#include "spdk/bdev.h"
#include "data.h"
-struct bdev_ocf_data *
+struct vbdev_ocf_data *
vbdev_ocf_data_alloc(uint32_t iovcnt)
{
- struct bdev_ocf_data *data;
+ struct vbdev_ocf_data *data;
data = env_malloc(sizeof(*data), ENV_MEM_NOIO);
if (!data) {
@@ -34,7 +35,7 @@ vbdev_ocf_data_alloc(uint32_t iovcnt)
}
void
-vbdev_ocf_data_free(struct bdev_ocf_data *data)
+vbdev_ocf_data_free(struct vbdev_ocf_data *data)
{
if (!data) {
return;
@@ -48,7 +49,7 @@ vbdev_ocf_data_free(struct bdev_ocf_data *data)
}
void
-vbdev_ocf_iovs_add(struct bdev_ocf_data *data, void *base, size_t len)
+vbdev_ocf_iovs_add(struct vbdev_ocf_data *data, void *base, size_t len)
{
assert(NULL != data);
assert(data->iovalloc != -1);
@@ -62,33 +63,3 @@ vbdev_ocf_iovs_add(struct bdev_ocf_data *data, void *base, size_t len)
data->iovs[data->iovcnt].iov_len = len;
data->iovcnt++;
}
-
-struct bdev_ocf_data *
-vbdev_ocf_data_from_spdk_io(struct spdk_bdev_io *bdev_io)
-{
- struct bdev_ocf_data *data;
-
- if (bdev_io == NULL) {
- return NULL;
- }
-
- switch (bdev_io->type) {
- case SPDK_BDEV_IO_TYPE_WRITE:
- case SPDK_BDEV_IO_TYPE_READ:
- assert(bdev_io->u.bdev.iovs);
- break;
- case SPDK_BDEV_IO_TYPE_FLUSH:
- case SPDK_BDEV_IO_TYPE_UNMAP:
- break;
- default:
- SPDK_ERRLOG("Unsupported IO type %d\n", bdev_io->type);
- return NULL;
- }
-
- data = (struct bdev_ocf_data *)bdev_io->driver_ctx;
- data->iovs = bdev_io->u.bdev.iovs;
- data->iovcnt = bdev_io->u.bdev.iovcnt;
- data->size = bdev_io->u.bdev.num_blocks * bdev_io->bdev->blocklen;
-
- return data;
-}
diff --git a/module/bdev/ocf/data.h b/module/bdev/ocf/data.h
index e1b571ddfca..6514f00dabd 100644
--- a/module/bdev/ocf/data.h
+++ b/module/bdev/ocf/data.h
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (C) 2018 Intel Corporation.
+ * Copyright (C) 2025 Huawei Technologies
* All rights reserved.
*/
@@ -9,7 +10,7 @@
#include "ocf_env.h"
#include "spdk/bdev_module.h"
-struct bdev_ocf_data {
+struct vbdev_ocf_data {
struct iovec *iovs;
int iovcnt;
int iovalloc;
@@ -17,14 +18,12 @@ struct bdev_ocf_data {
uint32_t seek;
};
-struct bdev_ocf_data *vbdev_ocf_data_from_spdk_io(struct spdk_bdev_io *bdev_io);
+struct vbdev_ocf_data *vbdev_ocf_data_alloc(uint32_t nvecs);
-struct bdev_ocf_data *vbdev_ocf_data_alloc(uint32_t nvecs);
+void vbdev_ocf_data_free(struct vbdev_ocf_data *data);
-void vbdev_ocf_data_free(struct bdev_ocf_data *data);
+struct vbdev_ocf_data *vbdev_ocf_data_from_iov(struct iovec *iovs);
-struct bdev_ocf_data *vbdev_ocf_data_from_iov(struct iovec *iovs);
-
-void vbdev_ocf_iovs_add(struct bdev_ocf_data *data, void *base, size_t len);
+void vbdev_ocf_iovs_add(struct vbdev_ocf_data *data, void *base, size_t len);
#endif
diff --git a/module/bdev/ocf/stats.c b/module/bdev/ocf/stats.c
index f27036c269e..aa21fa04a06 100644
--- a/module/bdev/ocf/stats.c
+++ b/module/bdev/ocf/stats.c
@@ -1,38 +1,33 @@
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (C) 2018 Intel Corporation.
+ * Copyright (C) 2025 Huawei Technologies
* All rights reserved.
*/
-#include "ctx.h"
#include "stats.h"
int
-vbdev_ocf_stats_get(ocf_cache_t cache, char *core_name, struct vbdev_ocf_stats *stats)
+vbdev_ocf_stats_cache_get(ocf_cache_t cache, struct vbdev_ocf_stats *stats)
{
- int status;
- ocf_core_t core;
-
- status = ocf_core_get_by_name(cache, core_name, strlen(core_name), &core);
- if (status) {
- return status;
- }
-
- return ocf_stats_collect_core(core, &stats->usage, &stats->reqs, &stats->blocks, &stats->errors);
+ return ocf_stats_collect_cache(cache, &stats->usage, &stats->req, &stats->blocks, &stats->errors);
}
int
-vbdev_ocf_stats_reset(ocf_cache_t cache, char *core_name)
+vbdev_ocf_stats_core_get(ocf_core_t core, struct vbdev_ocf_stats *stats)
{
- int status;
- ocf_core_t core;
+ return ocf_stats_collect_core(core, &stats->usage, &stats->req, &stats->blocks, &stats->errors);
+}
- status = ocf_core_get_by_name(cache, core_name, strlen(core_name), &core);
- if (status) {
- return status;
- }
+int
+vbdev_ocf_stats_cache_reset(ocf_cache_t cache)
+{
+ return ocf_core_stats_initialize_all(cache);
+}
+int
+vbdev_ocf_stats_core_reset(ocf_core_t core)
+{
ocf_core_stats_initialize(core);
-
return 0;
}
@@ -47,8 +42,6 @@ vbdev_ocf_stats_reset(ocf_cache_t cache, char *core_name)
void
vbdev_ocf_stats_write_json(struct spdk_json_write_ctx *w, struct vbdev_ocf_stats *stats)
{
- spdk_json_write_object_begin(w);
-
spdk_json_write_named_object_begin(w, "usage");
WJSON_STAT(w, stats, usage, occupancy, "4KiB blocks");
WJSON_STAT(w, stats, usage, free, "4KiB blocks");
@@ -57,18 +50,18 @@ vbdev_ocf_stats_write_json(struct spdk_json_write_ctx *w, struct vbdev_ocf_stats
spdk_json_write_object_end(w);
spdk_json_write_named_object_begin(w, "requests");
- WJSON_STAT(w, stats, reqs, rd_hits, "Requests");
- WJSON_STAT(w, stats, reqs, rd_partial_misses, "Requests");
- WJSON_STAT(w, stats, reqs, rd_full_misses, "Requests");
- WJSON_STAT(w, stats, reqs, rd_total, "Requests");
- WJSON_STAT(w, stats, reqs, wr_hits, "Requests");
- WJSON_STAT(w, stats, reqs, wr_partial_misses, "Requests");
- WJSON_STAT(w, stats, reqs, wr_full_misses, "Requests");
- WJSON_STAT(w, stats, reqs, wr_total, "Requests");
- WJSON_STAT(w, stats, reqs, rd_pt, "Requests");
- WJSON_STAT(w, stats, reqs, wr_pt, "Requests");
- WJSON_STAT(w, stats, reqs, serviced, "Requests");
- WJSON_STAT(w, stats, reqs, total, "Requests");
+ WJSON_STAT(w, stats, req, rd_hits, "Requests");
+ WJSON_STAT(w, stats, req, rd_partial_misses, "Requests");
+ WJSON_STAT(w, stats, req, rd_full_misses, "Requests");
+ WJSON_STAT(w, stats, req, rd_total, "Requests");
+ WJSON_STAT(w, stats, req, wr_hits, "Requests");
+ WJSON_STAT(w, stats, req, wr_partial_misses, "Requests");
+ WJSON_STAT(w, stats, req, wr_full_misses, "Requests");
+ WJSON_STAT(w, stats, req, wr_total, "Requests");
+ WJSON_STAT(w, stats, req, rd_pt, "Requests");
+ WJSON_STAT(w, stats, req, wr_pt, "Requests");
+ WJSON_STAT(w, stats, req, serviced, "Requests");
+ WJSON_STAT(w, stats, req, total, "Requests");
spdk_json_write_object_end(w);
spdk_json_write_named_object_begin(w, "blocks");
@@ -92,6 +85,4 @@ vbdev_ocf_stats_write_json(struct spdk_json_write_ctx *w, struct vbdev_ocf_stats
WJSON_STAT(w, stats, errors, cache_volume_total, "Requests");
WJSON_STAT(w, stats, errors, total, "Requests");
spdk_json_write_object_end(w);
-
- spdk_json_write_object_end(w);
}
diff --git a/module/bdev/ocf/stats.h b/module/bdev/ocf/stats.h
index b48a30c0194..0ceb641560f 100644
--- a/module/bdev/ocf/stats.h
+++ b/module/bdev/ocf/stats.h
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (C) 2018 Intel Corporation.
+ * Copyright (C) 2025 Huawei Technologies
* All rights reserved.
*/
@@ -11,13 +12,15 @@
struct vbdev_ocf_stats {
struct ocf_stats_usage usage;
- struct ocf_stats_requests reqs;
+ struct ocf_stats_requests req;
struct ocf_stats_blocks blocks;
struct ocf_stats_errors errors;
};
-int vbdev_ocf_stats_get(ocf_cache_t cache, char *core_name, struct vbdev_ocf_stats *stats);
-int vbdev_ocf_stats_reset(ocf_cache_t cache, char *core_name);
+int vbdev_ocf_stats_cache_get(ocf_cache_t cache, struct vbdev_ocf_stats *stats);
+int vbdev_ocf_stats_core_get(ocf_core_t core, struct vbdev_ocf_stats *stats);
+int vbdev_ocf_stats_cache_reset(ocf_cache_t cache);
+int vbdev_ocf_stats_core_reset(ocf_core_t core);
void vbdev_ocf_stats_write_json(struct spdk_json_write_ctx *w, struct vbdev_ocf_stats *stats);
diff --git a/module/bdev/ocf/utils.c b/module/bdev/ocf/utils.c
index 4c70c70fadd..c67a6cd70d3 100644
--- a/module/bdev/ocf/utils.c
+++ b/module/bdev/ocf/utils.c
@@ -1,13 +1,10 @@
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (C) 2018 Intel Corporation.
+ * Copyright (C) 2025 Huawei Technologies
* All rights reserved.
*/
-#include "spdk/stdinc.h"
-#include "spdk/log.h"
-
#include "utils.h"
-#include "vbdev_ocf.h"
static char *cache_modes[ocf_cache_mode_max] = {
[ocf_cache_mode_wt] = "wt",
@@ -18,6 +15,17 @@ static char *cache_modes[ocf_cache_mode_max] = {
[ocf_cache_mode_wo] = "wo",
};
+static char *promotion_policies[ocf_promotion_max] = {
+ [ocf_promotion_always] = "always",
+ [ocf_promotion_nhit] = "nhit",
+};
+
+static char *cleaning_policies[ocf_cleaning_max] = {
+ [ocf_cleaning_nop] = "nop",
+ [ocf_cleaning_alru] = "alru",
+ [ocf_cleaning_acp] = "acp",
+};
+
static char *seqcutoff_policies[ocf_seq_cutoff_policy_max] = {
[ocf_seq_cutoff_policy_always] = "always",
[ocf_seq_cutoff_policy_full] = "full",
@@ -25,13 +33,17 @@ static char *seqcutoff_policies[ocf_seq_cutoff_policy_max] = {
};
ocf_cache_mode_t
-ocf_get_cache_mode(const char *cache_mode)
+vbdev_ocf_cachemode_get_by_name(const char *cache_mode_name)
{
- int i;
+ ocf_cache_mode_t cache_mode;
- for (i = 0; i < ocf_cache_mode_max; i++) {
- if (strcmp(cache_mode, cache_modes[i]) == 0) {
- return i;
+ if (!cache_mode_name) {
+ return ocf_cache_mode_none;
+ }
+
+ for (cache_mode = 0; cache_mode < ocf_cache_mode_max; cache_mode++) {
+ if (!strcmp(cache_mode_name, cache_modes[cache_mode])) {
+ return cache_mode;
}
}
@@ -39,96 +51,92 @@ ocf_get_cache_mode(const char *cache_mode)
}
const char *
-ocf_get_cache_modename(ocf_cache_mode_t mode)
+vbdev_ocf_cachemode_get_name(ocf_cache_mode_t cache_mode)
{
- if (mode > ocf_cache_mode_none && mode < ocf_cache_mode_max) {
- return cache_modes[mode];
- } else {
- return NULL;
+ if (cache_mode > ocf_cache_mode_none && cache_mode < ocf_cache_mode_max) {
+ return cache_modes[cache_mode];
}
-}
-int
-ocf_get_cache_line_size(ocf_cache_t cache)
-{
- return ocf_cache_get_line_size(cache) / KiB;
+ return NULL;
}
-ocf_seq_cutoff_policy
-ocf_get_seqcutoff_policy(const char *policy_name)
+ocf_promotion_t
+vbdev_ocf_promotion_policy_get_by_name(const char *policy_name)
{
- int policy;
+ ocf_promotion_t policy;
- for (policy = 0; policy < ocf_seq_cutoff_policy_max; policy++)
- if (!strcmp(policy_name, seqcutoff_policies[policy])) {
+ if (!policy_name) {
+ return -EINVAL;
+ }
+
+ for (policy = 0; policy < ocf_promotion_max; policy++)
+ if (!strcmp(policy_name, promotion_policies[policy])) {
return policy;
}
- return ocf_seq_cutoff_policy_max;
+ return -EINVAL;
}
-int
-vbdev_ocf_mngt_start(struct vbdev_ocf *vbdev, vbdev_ocf_mngt_fn *path,
- vbdev_ocf_mngt_callback cb, void *cb_arg)
+const char *
+vbdev_ocf_promotion_policy_get_name(ocf_promotion_t policy)
{
- if (vbdev->mngt_ctx.current_step) {
- return -EBUSY;
+ if (policy >= ocf_promotion_always && policy < ocf_promotion_max) {
+ return promotion_policies[policy];
}
- memset(&vbdev->mngt_ctx, 0, sizeof(vbdev->mngt_ctx));
-
- vbdev->mngt_ctx.current_step = path;
- vbdev->mngt_ctx.cb = cb;
- vbdev->mngt_ctx.cb_arg = cb_arg;
-
- (*vbdev->mngt_ctx.current_step)(vbdev);
-
- return 0;
+ return NULL;
}
-void
-vbdev_ocf_mngt_stop(struct vbdev_ocf *vbdev, vbdev_ocf_mngt_fn *rollback_path, int status)
+ocf_cleaning_t
+vbdev_ocf_cleaning_policy_get_by_name(const char *policy_name)
{
- if (status) {
- vbdev->mngt_ctx.status = status;
- }
+ ocf_cleaning_t policy;
- if (vbdev->mngt_ctx.status && rollback_path) {
- vbdev->mngt_ctx.poller_fn = NULL;
- vbdev->mngt_ctx.current_step = rollback_path;
- (*vbdev->mngt_ctx.current_step)(vbdev);
- return;
+ if (!policy_name) {
+ return -EINVAL;
}
- if (vbdev->mngt_ctx.cb) {
- vbdev->mngt_ctx.cb(vbdev->mngt_ctx.status, vbdev, vbdev->mngt_ctx.cb_arg);
- }
+ for (policy = 0; policy < ocf_cleaning_max; policy++)
+ if (!strcmp(policy_name, cleaning_policies[policy])) {
+ return policy;
+ }
- memset(&vbdev->mngt_ctx, 0, sizeof(vbdev->mngt_ctx));
+ return -EINVAL;
}
-void
-vbdev_ocf_mngt_continue(struct vbdev_ocf *vbdev, int status)
+const char *
+vbdev_ocf_cleaning_policy_get_name(ocf_cleaning_t policy)
{
- if (vbdev->mngt_ctx.current_step == NULL) {
- return;
+ if (policy >= ocf_cleaning_nop && policy < ocf_cleaning_max) {
+ return cleaning_policies[policy];
}
- assert((*vbdev->mngt_ctx.current_step) != NULL);
+ return NULL;
+}
- vbdev->mngt_ctx.status = status;
+ocf_seq_cutoff_policy
+vbdev_ocf_seqcutoff_policy_get_by_name(const char *policy_name)
+{
+ ocf_seq_cutoff_policy policy;
- vbdev->mngt_ctx.current_step++;
- if (*vbdev->mngt_ctx.current_step) {
- (*vbdev->mngt_ctx.current_step)(vbdev);
- return;
+ if (!policy_name) {
+ return -EINVAL;
}
- vbdev_ocf_mngt_stop(vbdev, NULL, 0);
+ for (policy = 0; policy < ocf_seq_cutoff_policy_max; policy++)
+ if (!strcmp(policy_name, seqcutoff_policies[policy])) {
+ return policy;
+ }
+
+ return -EINVAL;
}
-int
-vbdev_ocf_mngt_get_status(struct vbdev_ocf *vbdev)
+const char *
+vbdev_ocf_seqcutoff_policy_get_name(ocf_seq_cutoff_policy policy)
{
- return vbdev->mngt_ctx.status;
+ if (policy >= ocf_seq_cutoff_policy_always && policy < ocf_seq_cutoff_policy_max) {
+ return seqcutoff_policies[policy];
+ }
+
+ return NULL;
}
diff --git a/module/bdev/ocf/utils.h b/module/bdev/ocf/utils.h
index 8f1688c6303..51c92fa9f84 100644
--- a/module/bdev/ocf/utils.h
+++ b/module/bdev/ocf/utils.h
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (C) 2018 Intel Corporation.
+ * Copyright (C) 2025 Huawei Technologies
* All rights reserved.
*/
@@ -7,39 +8,29 @@
#define VBDEV_OCF_UTILS_H
#include
-#include "vbdev_ocf.h"
-ocf_cache_mode_t ocf_get_cache_mode(const char *cache_mode);
-const char *ocf_get_cache_modename(ocf_cache_mode_t mode);
+/* Get OCF cache mode by its name. */
+ocf_cache_mode_t vbdev_ocf_cachemode_get_by_name(const char *cache_mode_name);
-/* Get cache line size in KiB units */
-int ocf_get_cache_line_size(ocf_cache_t cache);
+/* Get the name of OCF cache mode. */
+const char *vbdev_ocf_cachemode_get_name(ocf_cache_mode_t cache_mode);
-/* Get sequential cutoff policy by name */
-ocf_seq_cutoff_policy ocf_get_seqcutoff_policy(const char *policy_name);
+/* Get OCF promotion policy by its name. */
+ocf_promotion_t vbdev_ocf_promotion_policy_get_by_name(const char *policy_name);
-/* Initiate management operation
- * Receives NULL terminated array of functions (path)
- * and callback (cb)
- * and callback argument (cb_arg)
- * This function may fail with ENOMEM or EBUSY */
-int vbdev_ocf_mngt_start(struct vbdev_ocf *vbdev, vbdev_ocf_mngt_fn *path,
- vbdev_ocf_mngt_callback cb, void *cb_arg);
+/* Get the name of OCF promotion policy. */
+const char *vbdev_ocf_promotion_policy_get_name(ocf_promotion_t policy);
-/* Continue execution with polling operation (fn)
- * fn must invoke vbdev_ocf_mngt_continue() to stop polling
- * Poller has default timeout of 5 seconds */
-void vbdev_ocf_mngt_poll(struct vbdev_ocf *vbdev, vbdev_ocf_mngt_fn fn);
+/* Get OCF cleaning policy by its name. */
+ocf_cleaning_t vbdev_ocf_cleaning_policy_get_by_name(const char *policy_name);
-/* Continue execution with next function that is on path
- * If next function is NULL, finish management operation and invoke callback */
-void vbdev_ocf_mngt_continue(struct vbdev_ocf *vbdev, int status);
+/* Get the name of OCF cleaning policy. */
+const char *vbdev_ocf_cleaning_policy_get_name(ocf_cleaning_t policy);
-/* Stop the execution, if status is non zero set it,
- * if rollback function is not null invoke rollback
- * else invoke callback with last status returned */
-void vbdev_ocf_mngt_stop(struct vbdev_ocf *vbdev, vbdev_ocf_mngt_fn *rollback_path, int status);
+/* Get OCF sequential cut-off policy by its name. */
+ocf_seq_cutoff_policy vbdev_ocf_seqcutoff_policy_get_by_name(const char *policy_name);
+
+/* Get the name of OCF sequential cut-off policy. */
+const char *vbdev_ocf_seqcutoff_policy_get_name(ocf_seq_cutoff_policy policy);
-/* Get status */
-int vbdev_ocf_mngt_get_status(struct vbdev_ocf *vbdev);
#endif
diff --git a/module/bdev/ocf/vbdev_ocf.c b/module/bdev/ocf/vbdev_ocf.c
index 08e586f2061..3df7bb0397e 100644
--- a/module/bdev/ocf/vbdev_ocf.c
+++ b/module/bdev/ocf/vbdev_ocf.c
@@ -1,1794 +1,3207 @@
/* SPDX-License-Identifier: BSD-3-Clause
- * Copyright (C) 2018 Intel Corporation.
+ * Copyright (C) 2025 Huawei Technologies
* All rights reserved.
*/
#include
-#include
-#include
-
-#include "ctx.h"
-#include "data.h"
-#include "volume.h"
-#include "utils.h"
-#include "vbdev_ocf.h"
#include "spdk/bdev_module.h"
-#include "spdk/thread.h"
#include "spdk/string.h"
-#include "spdk/log.h"
-#include "spdk/cpuset.h"
-
-/* This namespace UUID was generated using uuid_generate() method. */
-#define BDEV_OCF_NAMESPACE_UUID "f92b7f49-f6c0-44c8-bd23-3205e8c3b6ad"
-
-static struct spdk_bdev_module ocf_if;
-static TAILQ_HEAD(, vbdev_ocf) g_ocf_vbdev_head
- = TAILQ_HEAD_INITIALIZER(g_ocf_vbdev_head);
-
-static TAILQ_HEAD(, examining_bdev) g_ocf_examining_bdevs_head
- = TAILQ_HEAD_INITIALIZER(g_ocf_examining_bdevs_head);
-
-static bool g_fini_started = false;
+#include "vbdev_ocf.h"
+#include "ctx.h"
+#include "data.h"
+#include "stats.h"
+#include "utils.h"
+#include "volume.h"
-/* Structure for keeping list of bdevs that are claimed but not used yet */
-struct examining_bdev {
- struct spdk_bdev *bdev;
- TAILQ_ENTRY(examining_bdev) tailq;
+bool g_vbdev_ocf_module_is_running = false;
+
+static int vbdev_ocf_module_init(void);
+static void vbdev_ocf_module_fini_start(void);
+static void vbdev_ocf_module_fini(void);
+static int vbdev_ocf_module_get_ctx_size(void);
+static void vbdev_ocf_module_examine_config(struct spdk_bdev *bdev);
+static void vbdev_ocf_module_examine_disk(struct spdk_bdev *bdev);
+static int vbdev_ocf_module_config_json(struct spdk_json_write_ctx *w);
+
+struct spdk_bdev_module ocf_if = {
+ .name = "OCF",
+ .module_init = vbdev_ocf_module_init,
+ .fini_start = vbdev_ocf_module_fini_start,
+ .async_fini_start = true,
+ .module_fini = vbdev_ocf_module_fini,
+ .get_ctx_size = vbdev_ocf_module_get_ctx_size,
+ .examine_config = vbdev_ocf_module_examine_config,
+ .examine_disk = vbdev_ocf_module_examine_disk,
+ .config_json = vbdev_ocf_module_config_json,
+};
+SPDK_BDEV_MODULE_REGISTER(ocf, &ocf_if)
+
+static int vbdev_ocf_fn_destruct(void *ctx);
+static void vbdev_ocf_fn_submit_request(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io);
+static bool vbdev_ocf_fn_io_type_supported(void *ctx, enum spdk_bdev_io_type);
+static struct spdk_io_channel *vbdev_ocf_fn_get_io_channel(void *ctx);
+static int vbdev_ocf_fn_dump_info_json(void *ctx, struct spdk_json_write_ctx *w);
+static void vbdev_ocf_fn_dump_device_stat_json(void *ctx, struct spdk_json_write_ctx *w);
+static void vbdev_ocf_fn_reset_device_stat(void *ctx);
+
+struct spdk_bdev_fn_table vbdev_ocf_fn_table = {
+ .destruct = vbdev_ocf_fn_destruct,
+ .submit_request = vbdev_ocf_fn_submit_request,
+ .io_type_supported = vbdev_ocf_fn_io_type_supported,
+ .get_io_channel = vbdev_ocf_fn_get_io_channel,
+ .dump_info_json = vbdev_ocf_fn_dump_info_json,
+ .dump_device_stat_json = vbdev_ocf_fn_dump_device_stat_json,
+ .reset_device_stat = vbdev_ocf_fn_reset_device_stat,
};
-/* Add bdev to list of claimed */
-static void
-examine_start(struct spdk_bdev *bdev)
+static int
+_bdev_exists_cache_visitor(ocf_cache_t cache, void *ctx)
{
- struct examining_bdev *entry = malloc(sizeof(*entry));
+ char *name = ctx;
+ ocf_core_t core;
+ int rc;
- assert(entry);
- entry->bdev = bdev;
- TAILQ_INSERT_TAIL(&g_ocf_examining_bdevs_head, entry, tailq);
+ rc = ocf_core_get_by_name(cache, name, OCF_CORE_NAME_SIZE, &core);
+ /* Check if found core has context (priv) attached as well. Only then
+ * it counts as a regular core and not just added during cache load. */
+ if (!rc && ocf_core_get_priv(core)) {
+ return -EEXIST;
+ } else if (rc && rc != -OCF_ERR_CORE_NOT_EXIST) {
+ SPDK_ERRLOG("OCF: failed to get core: %s\n", spdk_strerror(-rc));
+ }
+
+ return 0;
}
-/* Find bdev on list of claimed bdevs, then remove it,
- * if it was the last one on list then report examine done */
-static void
-examine_done(int status, struct vbdev_ocf *vbdev, void *cb_arg)
+static bool
+vbdev_ocf_bdev_exists(const char *name)
{
- struct spdk_bdev *bdev = cb_arg;
- struct examining_bdev *entry, *safe, *found = NULL;
-
- TAILQ_FOREACH_SAFE(entry, &g_ocf_examining_bdevs_head, tailq, safe) {
- if (entry->bdev == bdev) {
- if (found) {
- goto remove;
- } else {
- found = entry;
- }
- }
- }
+ ocf_cache_t cache;
+ int rc;
- assert(found);
- spdk_bdev_module_examine_done(&ocf_if);
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF '%s': looking for it in existing bdev names\n", name);
-remove:
- TAILQ_REMOVE(&g_ocf_examining_bdevs_head, found, tailq);
- free(found);
-}
+ if (vbdev_ocf_core_waitlist_get_by_name(name)) {
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF '%s': found in core wait list\n", name);
-/* Free allocated strings and structure itself
- * Used at shutdown only */
-static void
-free_vbdev(struct vbdev_ocf *vbdev)
-{
- if (!vbdev) {
- return;
+ return true;
}
- free(vbdev->name);
- free(vbdev->cache.name);
- free(vbdev->core.name);
- free(vbdev);
-}
+ rc = ocf_mngt_cache_get_by_name(vbdev_ocf_ctx, name, OCF_CACHE_NAME_SIZE, &cache);
+ if (!rc) {
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF '%s': found cache\n", name);
-/* Get existing cache base
- * that is attached to other vbdev */
-static struct vbdev_ocf_base *
-get_other_cache_base(struct vbdev_ocf_base *base)
-{
- struct vbdev_ocf *vbdev;
+ /* If cache was found, do not increase its refcount. */
+ ocf_mngt_cache_put(cache);
- TAILQ_FOREACH(vbdev, &g_ocf_vbdev_head, tailq) {
- if (&vbdev->cache == base || !vbdev->cache.attached) {
- continue;
- }
- if (!strcmp(vbdev->cache.name, base->name)) {
- return &vbdev->cache;
- }
+ return true;
+ } else if (rc && rc != -OCF_ERR_CACHE_NOT_EXIST) {
+ SPDK_ERRLOG("OCF: failed to get cache: %s\n", spdk_strerror(-rc));
}
- return NULL;
-}
+ rc = ocf_mngt_cache_visit(vbdev_ocf_ctx, _bdev_exists_cache_visitor, (char *)name);
+ if (rc == -EEXIST) {
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF '%s': found core\n", name);
-static bool
-is_ocf_cache_running(struct vbdev_ocf *vbdev)
-{
- if (vbdev->cache.attached && vbdev->ocf_cache) {
- return ocf_cache_is_running(vbdev->ocf_cache);
+ return true;
+ } else if (rc) {
+ SPDK_ERRLOG("OCF: failed to iterate over bdevs: %s\n", spdk_strerror(-rc));
}
- return false;
-}
-static bool
-is_ocf_cache_detached(struct vbdev_ocf *vbdev)
-{
- if (vbdev->cache.attached && vbdev->ocf_cache) {
- return ocf_cache_is_detached(vbdev->ocf_cache);
+ if (spdk_bdev_get_by_name(name)) {
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF '%s': found in SPDK bdev layer\n", name);
+
+ return true;
}
+
return false;
}
-/* Get existing OCF cache instance
- * that is started by other vbdev */
-static ocf_cache_t
-get_other_cache_instance(struct vbdev_ocf *vbdev)
+static int
+_bdev_resolve_cache_visitor(ocf_cache_t cache, void *ctx)
{
- struct vbdev_ocf *cmp;
+ struct vbdev_ocf_mngt_ctx *mngt_ctx = ctx;
+ int rc;
- TAILQ_FOREACH(cmp, &g_ocf_vbdev_head, tailq) {
- if (cmp->state.doing_finish || cmp == vbdev) {
- continue;
- }
- if (strcmp(cmp->cache.name, vbdev->cache.name)) {
- continue;
- }
- if (is_ocf_cache_running(cmp) || is_ocf_cache_detached(cmp)) {
- return cmp->ocf_cache;
- }
+ rc = ocf_core_get_by_name(cache, mngt_ctx->bdev_name, OCF_CORE_NAME_SIZE, &mngt_ctx->core);
+ if (rc && rc != -OCF_ERR_CORE_NOT_EXIST) {
+ return rc;
+ } else if (!rc) {
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF '%s': found core\n", mngt_ctx->bdev_name);
+
+ return -EEXIST;
}
- return NULL;
+ return 0;
}
-static void
-_remove_base_bdev(void *ctx)
+/* Takes name of bdev and saves pointer to either cache or
+ * core of that name inside given management context. */
+static int
+vbdev_ocf_bdev_resolve(struct vbdev_ocf_mngt_ctx *mngt_ctx)
{
- struct spdk_bdev_desc *desc = ctx;
+ int rc;
- spdk_bdev_close(desc);
-}
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF '%s': looking for cache or core of that name\n",
+ mngt_ctx->bdev_name);
-/* Close and unclaim base bdev */
-static void
-remove_base_bdev(struct vbdev_ocf_base *base)
-{
- if (base->attached) {
- if (base->management_channel) {
- spdk_put_io_channel(base->management_channel);
- }
+ rc = ocf_mngt_cache_get_by_name(vbdev_ocf_ctx, mngt_ctx->bdev_name, OCF_CACHE_NAME_SIZE,
+ &mngt_ctx->cache);
+ if (rc && rc != -OCF_ERR_CACHE_NOT_EXIST) {
+ return rc;
+ } else if (!rc) {
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF '%s': found cache\n", mngt_ctx->bdev_name);
- spdk_bdev_module_release_bdev(base->bdev);
- /* Close the underlying bdev on its same opened thread. */
- if (base->thread && base->thread != spdk_get_thread()) {
- spdk_thread_send_msg(base->thread, _remove_base_bdev, base->desc);
- } else {
- spdk_bdev_close(base->desc);
- }
- base->attached = false;
+ /* If cache was found, do not increase its refcount. */
+ ocf_mngt_cache_put(mngt_ctx->cache);
+ return 0;
}
-}
-/* Finish unregister operation */
-static void
-unregister_finish(struct vbdev_ocf *vbdev)
-{
- spdk_bdev_destruct_done(&vbdev->exp_bdev, vbdev->state.stop_status);
-
- if (vbdev->ocf_cache) {
- ocf_mngt_cache_put(vbdev->ocf_cache);
+ rc = ocf_mngt_cache_visit(vbdev_ocf_ctx, _bdev_resolve_cache_visitor, mngt_ctx);
+ assert(!(mngt_ctx->cache && mngt_ctx->core));
+ if (rc && rc != -EEXIST) {
+ return rc;
+ } else if (!rc || (!mngt_ctx->cache && !mngt_ctx->core)) {
+ return -ENXIO;
}
- if (vbdev->cache_ctx) {
- vbdev_ocf_cache_ctx_put(vbdev->cache_ctx);
- }
- vbdev_ocf_mngt_continue(vbdev, 0);
+ return 0;
}
static void
-close_core_bdev(struct vbdev_ocf *vbdev)
+vbdev_ocf_mem_calculate(ocf_cache_t cache)
{
- remove_base_bdev(&vbdev->core);
- vbdev_ocf_mngt_continue(vbdev, 0);
-}
+ struct vbdev_ocf_cache *cache_ctx = ocf_cache_get_priv(cache);
+ uint64_t mem_needed, volume_size;
-static void
-remove_core_cmpl(void *priv, int error)
-{
- struct vbdev_ocf *vbdev = priv;
+ volume_size = spdk_bdev_get_block_size(cache_ctx->base.bdev) *
+ spdk_bdev_get_num_blocks(cache_ctx->base.bdev);
+ mem_needed = ocf_mngt_get_ram_needed(cache, volume_size);
- ocf_mngt_cache_unlock(vbdev->ocf_cache);
- vbdev_ocf_mngt_continue(vbdev, error);
+ SPDK_NOTICELOG("Needed memory to start cache in this configuration "
+ "(device size: %"PRIu64", cache line size: %"PRIu64"): %"PRIu64"\n",
+ volume_size, cache_ctx->cache_cfg.cache_line_size, mem_needed);
}
-/* Try to lock cache, then remove core */
-static void
-remove_core_cache_lock_cmpl(ocf_cache_t cache, void *priv, int error)
+static int
+vbdev_ocf_module_init(void)
{
- struct vbdev_ocf *vbdev = (struct vbdev_ocf *)priv;
+ int rc = 0;
- if (error) {
- SPDK_ERRLOG("Error %d, can not lock cache instance %s\n",
- error, vbdev->name);
- vbdev_ocf_mngt_continue(vbdev, error);
- return;
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF: starting module\n");
+
+ if ((rc = vbdev_ocf_ctx_init())) {
+ SPDK_ERRLOG("OCF: failed to initialize context: %s\n", spdk_strerror(-rc));
+ return rc;
}
- ocf_mngt_cache_remove_core(vbdev->ocf_core, remove_core_cmpl, vbdev);
+ if ((rc = vbdev_ocf_volume_init())) {
+ vbdev_ocf_ctx_cleanup();
+ SPDK_ERRLOG("OCF: failed to register volume: %s\n", spdk_strerror(-rc));
+ return rc;
+ }
+
+ g_vbdev_ocf_module_is_running = true;
+
+ return rc;
}
-/* Detach core base */
static void
-detach_core(struct vbdev_ocf *vbdev)
+_cache_stop_cb(ocf_cache_t cache, void *cb_arg, int error)
{
- if (is_ocf_cache_running(vbdev)) {
- ocf_mngt_cache_lock(vbdev->ocf_cache, remove_core_cache_lock_cmpl, vbdev);
+ struct vbdev_ocf_mngt_ctx *mngt_ctx = cb_arg;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF cache '%s': finishing stop of OCF cache\n",
+ ocf_cache_get_name(cache));
+
+ if (error) {
+ SPDK_ERRLOG("OCF cache '%s': failed to properly stop OCF cache (OCF error: %d)\n",
+ ocf_cache_get_name(cache), error);
} else {
- vbdev_ocf_mngt_continue(vbdev, 0);
+ SPDK_NOTICELOG("OCF cache '%s': stopped\n", ocf_cache_get_name(cache));
}
-}
-static void
-close_cache_bdev(struct vbdev_ocf *vbdev)
-{
- remove_base_bdev(&vbdev->cache);
- vbdev_ocf_mngt_continue(vbdev, 0);
+ /* In module fini (no management context) do the cleanup despite the error. */
+ if (!error || !mngt_ctx) {
+ if (vbdev_ocf_cache_is_base_attached(cache)) {
+ vbdev_ocf_cache_base_detach(cache);
+ }
+ vbdev_ocf_cache_mngt_queue_put(cache);
+ vbdev_ocf_cache_destroy(cache);
+ }
+
+ ocf_mngt_cache_unlock(cache);
+
+ if (mngt_ctx) {
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF cache '%s': finishing stop\n",
+ ocf_cache_get_name(cache));
+
+ mngt_ctx->rpc_cb_fn(ocf_cache_get_name(cache), mngt_ctx->rpc_cb_arg, error);
+ free(mngt_ctx);
+ } else if (!ocf_mngt_cache_get_count(vbdev_ocf_ctx)) {
+ /* In module fini (no management context) call spdk_bdev_module_fini_start_done()
+ * if there are no caches left to stop. */
+ spdk_bdev_module_fini_start_done();
+ }
}
-/* Detach cache base */
static void
-detach_cache(struct vbdev_ocf *vbdev)
+_cache_stop_flush_cb(ocf_cache_t cache, void *cb_arg, int error)
{
- vbdev->state.stop_status = vbdev->mngt_ctx.status;
-
- /* If some other vbdev references this cache bdev,
- * we detach this only by changing the flag, without actual close */
- if (get_other_cache_base(&vbdev->cache)) {
- vbdev->cache.attached = false;
+ if (error) {
+ SPDK_ERRLOG("OCF cache '%s': failed to flush OCF cache (OCF error: %d)\n",
+ ocf_cache_get_name(cache), error);
}
- vbdev_ocf_mngt_continue(vbdev, 0);
+ ocf_mngt_cache_stop(cache, _cache_stop_cb, cb_arg);
}
static void
-stop_vbdev_cmpl(ocf_cache_t cache, void *priv, int error)
+_cache_stop_lock_cb(ocf_cache_t cache, void *cb_arg, int error)
{
- struct vbdev_ocf *vbdev = priv;
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF cache '%s': initiating stop of OCF cache\n",
+ ocf_cache_get_name(cache));
- vbdev_ocf_queue_put(vbdev->cache_ctx->mngt_queue);
- ocf_mngt_cache_unlock(cache);
+ if (error) {
+ SPDK_ERRLOG("OCF cache '%s': failed to acquire OCF cache lock (OCF error: %d)\n",
+ ocf_cache_get_name(cache), error);
+ }
- vbdev_ocf_mngt_continue(vbdev, error);
+ if (ocf_mngt_cache_is_dirty(cache)) {
+ ocf_mngt_cache_flush(cache, _cache_stop_flush_cb, cb_arg);
+ } else {
+ ocf_mngt_cache_stop(cache, _cache_stop_cb, cb_arg);
+ }
}
-/* Try to lock cache, then stop it */
static void
-stop_vbdev_cache_lock_cmpl(ocf_cache_t cache, void *priv, int error)
+_cache_stop_core_unregister_cb(void *cb_arg, int error)
{
- struct vbdev_ocf *vbdev = (struct vbdev_ocf *)priv;
+ ocf_core_t core = cb_arg;
+ ocf_cache_t cache = ocf_core_get_cache(core);
+ struct vbdev_ocf_core *core_ctx = ocf_core_get_priv(core);
+ struct vbdev_ocf_mngt_ctx *mngt_ctx = core_ctx->mngt_ctx;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s': finishing unregister of OCF vbdev\n",
+ ocf_core_get_name(core));
if (error) {
- SPDK_ERRLOG("Error %d, can not lock cache instance %s\n",
- error, vbdev->name);
- vbdev_ocf_mngt_continue(vbdev, error);
- return;
+ SPDK_ERRLOG("OCF core '%s': failed to unregister OCF vbdev during cache stop: %s\n",
+ ocf_core_get_name(core), spdk_strerror(-error));
}
- ocf_mngt_cache_stop(vbdev->ocf_cache, stop_vbdev_cmpl, vbdev);
+ vbdev_ocf_core_destroy(core_ctx);
+
+ if (ocf_cache_get_core_count(cache) == ocf_cache_get_core_inactive_count(cache)) {
+ /* All cores in this cache were already unregistered
+ * and detached, so proceed with stopping the cache. */
+ ocf_mngt_cache_lock(cache, _cache_stop_lock_cb, mngt_ctx);
+ }
}
-/* Stop OCF cache object
- * vbdev_ocf is not operational after this */
-static void
-stop_vbdev(struct vbdev_ocf *vbdev)
+static int
+_cache_stop_core_visitor(ocf_core_t core, void *cb_arg)
{
- if (!is_ocf_cache_running(vbdev)) {
- vbdev_ocf_mngt_continue(vbdev, 0);
- return;
+ struct vbdev_ocf_mngt_ctx *mngt_ctx = cb_arg;
+ struct vbdev_ocf_core *core_ctx = ocf_core_get_priv(core);
+ int rc = 0;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s': cache stop visit\n", ocf_core_get_name(core));
+
+ if (!core_ctx) {
+ /* Skip this core. If there is no context, it means that this core
+ * was added from metadata during cache load and it's just an empty shell. */
+ return 0;
}
- if (!g_fini_started && get_other_cache_instance(vbdev)) {
- SPDK_NOTICELOG("Not stopping cache instance '%s'"
- " because it is referenced by other OCF bdev\n",
- vbdev->cache.name);
- vbdev_ocf_mngt_continue(vbdev, 0);
- return;
+ /* If core is detached it's already unregistered, so just free its data and exit. */
+ if (!vbdev_ocf_core_is_base_attached(core_ctx)) {
+ vbdev_ocf_core_destroy(core_ctx);
+ return 0;
}
- ocf_mngt_cache_lock(vbdev->ocf_cache, stop_vbdev_cache_lock_cmpl, vbdev);
-}
+ core_ctx->mngt_ctx = mngt_ctx;
-static void
-flush_vbdev_cmpl(ocf_cache_t cache, void *priv, int error)
-{
- struct vbdev_ocf *vbdev = priv;
+ if ((rc = vbdev_ocf_core_unregister(core_ctx, _cache_stop_core_unregister_cb, core))) {
+ SPDK_ERRLOG("OCF core '%s': failed to start unregistering OCF vbdev: %s\n",
+ ocf_core_get_name(core), spdk_strerror(-rc));
+ return rc;
+ }
- ocf_mngt_cache_unlock(cache);
- vbdev_ocf_mngt_continue(vbdev, error);
+ return rc;
}
-static void
-flush_vbdev_cache_lock_cmpl(ocf_cache_t cache, void *priv, int error)
+static int
+_module_fini_cache_visitor(ocf_cache_t cache, void *cb_arg)
{
- struct vbdev_ocf *vbdev = (struct vbdev_ocf *)priv;
+ int rc;
- if (error) {
- SPDK_ERRLOG("Error %d, can not lock cache instance %s\n",
- error, vbdev->name);
- vbdev_ocf_mngt_continue(vbdev, error);
- return;
- }
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF cache '%s': module stop visit\n", ocf_cache_get_name(cache));
- ocf_mngt_cache_flush(vbdev->ocf_cache, flush_vbdev_cmpl, vbdev);
-}
+ if (!ocf_cache_get_core_count(cache) ||
+ ocf_cache_get_core_count(cache) == ocf_cache_get_core_inactive_count(cache)) {
+ /* If there are no cores or all of them are detached,
+ * then cache stop can be triggered already. */
+ ocf_mngt_cache_lock(cache, _cache_stop_lock_cb, NULL);
+ }
-static void
-flush_vbdev(struct vbdev_ocf *vbdev)
-{
- if (!is_ocf_cache_running(vbdev)) {
- vbdev_ocf_mngt_continue(vbdev, -EINVAL);
- return;
+ if ((rc = ocf_core_visit(cache, _cache_stop_core_visitor, NULL, false))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to iterate over core bdevs: %s\n",
+ ocf_cache_get_name(cache), spdk_strerror(-rc));
+ ocf_mngt_cache_lock(cache, _cache_stop_lock_cb, NULL);
}
- ocf_mngt_cache_lock(vbdev->ocf_cache, flush_vbdev_cache_lock_cmpl, vbdev);
+ /* In module fini return 0 despite any errors to keep shutting down all caches. */
+ return 0;
}
-/* Procedures called during dirty unregister */
-vbdev_ocf_mngt_fn unregister_path_dirty[] = {
- flush_vbdev,
- stop_vbdev,
- detach_cache,
- close_cache_bdev,
- detach_core,
- close_core_bdev,
- unregister_finish,
- NULL
-};
-
-/* Procedures called during clean unregister */
-vbdev_ocf_mngt_fn unregister_path_clean[] = {
- flush_vbdev,
- detach_core,
- close_core_bdev,
- stop_vbdev,
- detach_cache,
- close_cache_bdev,
- unregister_finish,
- NULL
-};
-
-/* Start asynchronous management operation using unregister_path */
static void
-unregister_cb(void *opaque)
+vbdev_ocf_module_fini_start(void)
{
- struct vbdev_ocf *vbdev = opaque;
- vbdev_ocf_mngt_fn *unregister_path;
+ struct vbdev_ocf_core *core_ctx;
int rc;
- unregister_path = vbdev->state.doing_clean_delete ?
- unregister_path_clean : unregister_path_dirty;
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF: initiating module stop\n");
- rc = vbdev_ocf_mngt_start(vbdev, unregister_path, NULL, NULL);
- if (rc) {
- SPDK_ERRLOG("Unable to unregister OCF bdev: %d\n", rc);
- spdk_bdev_destruct_done(&vbdev->exp_bdev, rc);
+ g_vbdev_ocf_module_is_running = false;
+
+ vbdev_ocf_foreach_core_in_waitlist(core_ctx) {
+ if (vbdev_ocf_core_is_base_attached(core_ctx)) {
+ vbdev_ocf_core_base_detach(core_ctx);
+ }
}
-}
-/* Clean remove case - remove core and then cache, this order
- * will remove instance permanently */
-static void
-_vbdev_ocf_destruct_clean(struct vbdev_ocf *vbdev)
-{
- if (vbdev->core.attached) {
- detach_core(vbdev);
- close_core_bdev(vbdev);
+ if (!ocf_mngt_cache_get_count(vbdev_ocf_ctx)) {
+ spdk_bdev_module_fini_start_done();
+ return;
}
- if (vbdev->cache.attached) {
- detach_cache(vbdev);
- close_cache_bdev(vbdev);
+ if ((rc = ocf_mngt_cache_visit(vbdev_ocf_ctx, _module_fini_cache_visitor, NULL))) {
+ SPDK_ERRLOG("OCF: failed to iterate over bdevs: %s\n", spdk_strerror(-rc));
+ spdk_bdev_module_fini_start_done();
+ return;
}
}
-/* Dirty shutdown/hot remove case - remove cache and then core, this order
- * will allow us to recover this instance in the future */
static void
-_vbdev_ocf_destruct_dirty(struct vbdev_ocf *vbdev)
+vbdev_ocf_module_fini(void)
{
- if (vbdev->cache.attached) {
- detach_cache(vbdev);
- close_cache_bdev(vbdev);
- }
+ struct vbdev_ocf_core *core_ctx;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF: finishing module stop\n");
- if (vbdev->core.attached) {
- detach_core(vbdev);
- close_core_bdev(vbdev);
+ while ((core_ctx = STAILQ_FIRST(&g_vbdev_ocf_core_waitlist))) {
+ vbdev_ocf_core_waitlist_remove(core_ctx);
+ vbdev_ocf_core_destroy(core_ctx);
}
+
+ vbdev_ocf_volume_cleanup();
+ vbdev_ocf_ctx_cleanup();
+}
+
+static int
+vbdev_ocf_module_get_ctx_size(void)
+{
+ return sizeof(struct vbdev_ocf_data);
}
-/* Unregister io device with callback to unregister_cb
- * This function is called during spdk_bdev_unregister */
static int
-vbdev_ocf_destruct(void *opaque)
+_examine_config_core_visitor(ocf_core_t core, void *cb_arg)
{
- struct vbdev_ocf *vbdev = opaque;
+ struct vbdev_ocf_core *core_ctx = ocf_core_get_priv(core);
+ char *bdev_name = cb_arg;
+ int rc = 0;
- if (vbdev->state.doing_finish) {
- return -EALREADY;
+ if (!core_ctx) {
+ /* Skip this core. If there is no context, it means that this core
+ * was added from metadata during cache load and it's just an empty shell. */
+ return 0;
}
- if (vbdev->state.starting && !vbdev->state.started) {
- /* Prevent before detach cache/core during register path of
- this bdev */
- return -EBUSY;
+ if (strcmp(bdev_name, core_ctx->base.name)) {
+ return 0;
}
- vbdev->state.doing_finish = true;
+ SPDK_NOTICELOG("OCF core '%s': base bdev '%s' found\n",
+ ocf_core_get_name(core), bdev_name);
- if (vbdev->state.started) {
- spdk_io_device_unregister(vbdev, unregister_cb);
- /* Return 1 because unregister is delayed */
- return 1;
+ if (!strcmp(spdk_bdev_get_product_name(spdk_bdev_get_by_name(bdev_name)), "OCF_disk")) {
+ SPDK_ERRLOG("OCF core '%s': base bdev '%s' is already an OCF core\n",
+ ocf_core_get_name(core), bdev_name);
+ return -ENOTSUP;
}
- if (vbdev->state.doing_clean_delete) {
- _vbdev_ocf_destruct_clean(vbdev);
- } else {
- _vbdev_ocf_destruct_dirty(vbdev);
+ assert(!vbdev_ocf_core_is_base_attached(core_ctx));
+
+ if ((rc = vbdev_ocf_core_base_attach(core_ctx, bdev_name))) {
+ SPDK_ERRLOG("OCF core '%s': failed to attach base bdev '%s'\n",
+ vbdev_ocf_core_get_name(core_ctx), bdev_name);
+ return rc;
}
- return 0;
+ /* This whole situation with core being present in cache without base bdev attached
+ * is only possible when core was previously hot removed from SPDK.
+ * In such case it was detached from cache (not removed), so set 'try_add' in core
+ * config to 'true' to indicate that this core is still in cache metadata. */
+ core_ctx->core_cfg.try_add = true;
+
+ return -EEXIST;
}
-/* Stop OCF cache and unregister SPDK bdev */
-int
-vbdev_ocf_delete(struct vbdev_ocf *vbdev, void (*cb)(void *, int), void *cb_arg)
+static int
+_examine_config_cache_visitor(ocf_cache_t cache, void *cb_arg)
{
+ struct vbdev_ocf_cache *cache_ctx = ocf_cache_get_priv(cache);
+ char *bdev_name = cb_arg;
int rc = 0;
- if (vbdev->state.started) {
- spdk_bdev_unregister(&vbdev->exp_bdev, cb, cb_arg);
- } else {
- rc = vbdev_ocf_destruct(vbdev);
- if (rc == 0 && cb) {
- cb(cb_arg, 0);
- }
+ if (strcmp(bdev_name, cache_ctx->base.name)) {
+ return ocf_core_visit(cache, _examine_config_core_visitor, bdev_name, false);
}
- return rc;
-}
-
-/* Remove cores permanently and then stop OCF cache and unregister SPDK bdev */
-int
-vbdev_ocf_delete_clean(struct vbdev_ocf *vbdev, void (*cb)(void *, int),
- void *cb_arg)
-{
- vbdev->state.doing_clean_delete = true;
-
- return vbdev_ocf_delete(vbdev, cb, cb_arg);
-}
+ SPDK_NOTICELOG("OCF cache '%s': base bdev '%s' found\n",
+ ocf_cache_get_name(cache), bdev_name);
+ assert(!ocf_cache_is_device_attached(cache));
+ assert(!vbdev_ocf_cache_is_base_attached(cache));
-/* If vbdev is online, return its object */
-struct vbdev_ocf *
-vbdev_ocf_get_by_name(const char *name)
-{
- struct vbdev_ocf *vbdev;
+ if ((rc = vbdev_ocf_cache_base_attach(cache, bdev_name))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to attach base bdev '%s'\n",
+ ocf_cache_get_name(cache), bdev_name);
+ return rc;
+ }
- if (name == NULL) {
- assert(false);
- return NULL;
+ /* Update cache IO channel in all cores before attaching new cache device to OCF. */
+ if ((rc = vbdev_ocf_core_create_cache_channel(cache))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to create IO channel for new cache device\n",
+ ocf_cache_get_name(cache));
+ vbdev_ocf_cache_base_detach(cache);
+ return rc;
}
- TAILQ_FOREACH(vbdev, &g_ocf_vbdev_head, tailq) {
- if (vbdev->name == NULL || vbdev->state.doing_finish) {
- continue;
- }
- if (strcmp(vbdev->name, name) == 0) {
- return vbdev;
- }
+ if ((rc = vbdev_ocf_cache_config_volume_create(cache))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to create config volume\n",
+ ocf_cache_get_name(cache));
+ vbdev_ocf_core_destroy_cache_channel(cache);
+ vbdev_ocf_cache_base_detach(cache);
+ return rc;
}
- return NULL;
+
+ return -EEXIST;
}
-/* Return matching base if parent vbdev is online */
-struct vbdev_ocf_base *
-vbdev_ocf_get_base_by_name(const char *name)
+static void
+vbdev_ocf_module_examine_config(struct spdk_bdev *bdev)
{
- struct vbdev_ocf *vbdev;
+ struct vbdev_ocf_core *core_ctx;
+ char *bdev_name = (char *)spdk_bdev_get_name(bdev);
+ int rc;
- if (name == NULL) {
- assert(false);
- return NULL;
- }
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF '%s': looking for vbdevs waiting for it\n", bdev_name);
- TAILQ_FOREACH(vbdev, &g_ocf_vbdev_head, tailq) {
- if (vbdev->state.doing_finish) {
+ vbdev_ocf_foreach_core_in_waitlist(core_ctx) {
+ if (strcmp(bdev_name, core_ctx->base.name)) {
continue;
}
- if (vbdev->cache.name && strcmp(vbdev->cache.name, name) == 0) {
- return &vbdev->cache;
+ SPDK_NOTICELOG("OCF core '%s': base bdev '%s' found\n",
+ vbdev_ocf_core_get_name(core_ctx), bdev_name);
+
+ if (!strcmp(spdk_bdev_get_product_name(bdev), "OCF_disk")) {
+ SPDK_ERRLOG("OCF core '%s': base bdev '%s' is already an OCF core\n",
+ vbdev_ocf_core_get_name(core_ctx), bdev_name);
+ spdk_bdev_module_examine_done(&ocf_if);
+ return;
}
- if (vbdev->core.name && strcmp(vbdev->core.name, name) == 0) {
- return &vbdev->core;
+
+ assert(!vbdev_ocf_core_is_base_attached(core_ctx));
+
+ if ((rc = vbdev_ocf_core_base_attach(core_ctx, bdev_name))) {
+ SPDK_ERRLOG("OCF core '%s': failed to attach base bdev '%s': %s\n",
+ vbdev_ocf_core_get_name(core_ctx), bdev_name, spdk_strerror(-rc));
+ spdk_bdev_module_examine_done(&ocf_if);
+ return;
}
+
+ spdk_bdev_module_examine_done(&ocf_if);
+ return;
+ }
+
+ rc = ocf_mngt_cache_visit(vbdev_ocf_ctx, _examine_config_cache_visitor, bdev_name);
+ if (rc && rc != -EEXIST) {
+ SPDK_ERRLOG("OCF: failed to iterate over bdevs: %s\n", spdk_strerror(-rc));
}
- return NULL;
+
+ spdk_bdev_module_examine_done(&ocf_if);
}
-/* Execute fn for each OCF device that is online or waits for base devices */
-void
-vbdev_ocf_foreach(vbdev_ocf_foreach_fn fn, void *ctx)
+static void
+_core_add_examine_err_cb(void *cb_arg, int error)
{
- struct vbdev_ocf *vbdev;
+ struct vbdev_ocf_core *core_ctx = cb_arg;
+ ocf_cache_t cache;
+ int rc;
- assert(fn != NULL);
+ if (error) {
+ SPDK_ERRLOG("OCF core '%s': failed to remove OCF core device (OCF error: %d)\n",
+ vbdev_ocf_core_get_name(core_ctx), error);
+ }
- TAILQ_FOREACH(vbdev, &g_ocf_vbdev_head, tailq) {
- if (!vbdev->state.doing_finish) {
- fn(vbdev, ctx);
- }
+ if ((rc = ocf_mngt_cache_get_by_name(vbdev_ocf_ctx, core_ctx->cache_name,
+ OCF_CACHE_NAME_SIZE, &cache))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to find cache of that name (OCF error: %d)\n",
+ core_ctx->cache_name, rc);
+ assert(false);
}
+ ocf_mngt_cache_put(cache);
+
+ ocf_mngt_cache_unlock(cache);
+ ocf_mngt_cache_put(cache);
+ vbdev_ocf_core_base_detach(core_ctx);
+ spdk_bdev_module_examine_done(&ocf_if);
}
-/* Called from OCF when SPDK_IO is completed */
static void
-vbdev_ocf_io_submit_cb(ocf_io_t io, void *priv1, void *priv2, int error)
+_core_add_examine_add_cb(ocf_cache_t cache, ocf_core_t core, void *cb_arg, int error)
{
- struct spdk_bdev_io *bdev_io = priv1;
+ struct vbdev_ocf_core *core_ctx = cb_arg;
+ int rc = 0;
- if (error == 0) {
- spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_SUCCESS);
- } else if (error == -OCF_ERR_NO_MEM) {
- spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_NOMEM);
- } else {
- spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED);
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s': finishing add of OCF core\n",
+ vbdev_ocf_core_get_name(core_ctx));
+
+ if (error) {
+ SPDK_ERRLOG("OCF core '%s': failed to add core to OCF cache '%s' (OCF error: %d)\n",
+ vbdev_ocf_core_get_name(core_ctx), ocf_cache_get_name(cache), error);
+ ocf_mngt_cache_unlock(cache);
+ ocf_mngt_cache_put(cache);
+ vbdev_ocf_core_base_detach(core_ctx);
+ spdk_bdev_module_examine_done(&ocf_if);
+ return;
}
- ocf_io_put(io);
-}
+ ocf_core_set_priv(core, core_ctx);
-/* Configure io parameters and send it to OCF */
-static int
-io_submit_to_ocf(struct spdk_bdev_io *bdev_io, ocf_io_t io)
-{
- switch (bdev_io->type) {
- case SPDK_BDEV_IO_TYPE_WRITE:
- case SPDK_BDEV_IO_TYPE_READ:
- ocf_core_submit_io(io);
- return 0;
- case SPDK_BDEV_IO_TYPE_FLUSH:
- ocf_core_submit_flush(io);
- return 0;
- case SPDK_BDEV_IO_TYPE_UNMAP:
- ocf_core_submit_discard(io);
- return 0;
- case SPDK_BDEV_IO_TYPE_RESET:
- case SPDK_BDEV_IO_TYPE_WRITE_ZEROES:
- default:
- SPDK_ERRLOG("Unsupported IO type: %d\n", bdev_io->type);
- return -EINVAL;
+ if ((rc = vbdev_ocf_core_register(core))) {
+ SPDK_ERRLOG("OCF core '%s': failed to register vbdev: %s\n",
+ ocf_core_get_name(core), spdk_strerror(-rc));
+ ocf_mngt_cache_remove_core(core, _core_add_examine_err_cb, core_ctx);
+ return;
+ }
+
+ SPDK_NOTICELOG("OCF core '%s': added to cache '%s'\n",
+ ocf_core_get_name(core), ocf_cache_get_name(cache));
+
+ /* If core was taken from wait list, remove it from there. */
+ if (vbdev_ocf_core_waitlist_get_by_name(vbdev_ocf_core_get_name(core_ctx))) {
+ vbdev_ocf_core_waitlist_remove(core_ctx);
}
+
+ ocf_mngt_cache_unlock(cache);
+ ocf_mngt_cache_put(cache);
+ spdk_bdev_module_examine_done(&ocf_if);
}
-/* Submit SPDK-IO to OCF */
static void
-io_handle(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io)
+_core_add_examine_lock_cb(ocf_cache_t cache, void *cb_arg, int error)
{
- struct vbdev_ocf *vbdev = bdev_io->bdev->ctxt;
- ocf_io_t io = NULL;
- struct bdev_ocf_data *data = NULL;
- struct vbdev_ocf_qctx *qctx = spdk_io_channel_get_ctx(ch);
- uint64_t len = bdev_io->u.bdev.num_blocks * bdev_io->bdev->blocklen;
- uint64_t offset = bdev_io->u.bdev.offset_blocks * bdev_io->bdev->blocklen;
- int dir, flags = 0;
- int err;
+ struct vbdev_ocf_core *core_ctx = cb_arg;
- switch (bdev_io->type) {
- case SPDK_BDEV_IO_TYPE_READ:
- dir = OCF_READ;
- break;
- case SPDK_BDEV_IO_TYPE_WRITE:
- dir = OCF_WRITE;
- break;
- case SPDK_BDEV_IO_TYPE_FLUSH:
- dir = OCF_WRITE;
- break;
- case SPDK_BDEV_IO_TYPE_UNMAP:
- dir = OCF_WRITE;
- break;
- default:
- err = -EINVAL;
- goto fail;
- }
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s': initiating add of OCF core\n",
+ vbdev_ocf_core_get_name(core_ctx));
- if (bdev_io->type == SPDK_BDEV_IO_TYPE_FLUSH) {
- flags = OCF_WRITE_FLUSH;
+ if (error) {
+ SPDK_ERRLOG("OCF core '%s': failed to acquire OCF cache lock (OCF error: %d)\n",
+ vbdev_ocf_core_get_name(core_ctx), error);
+ ocf_mngt_cache_put(cache);
+ vbdev_ocf_core_base_detach(core_ctx);
+ spdk_bdev_module_examine_done(&ocf_if);
+ return;
}
- io = ocf_volume_new_io(ocf_core_get_front_volume(vbdev->ocf_core), qctx->queue, offset, len, dir, 0,
- flags);
- if (!io) {
- err = -ENOMEM;
- goto fail;
+ /* Check if core is loaded from metadata only if its try_add flag was not
+ * set to true already during examine_config stage. That would mean that
+ * this core was hot removed before and now it is being attached back. */
+ if (!core_ctx->core_cfg.try_add) {
+ core_ctx->core_cfg.try_add = vbdev_ocf_core_is_loaded(vbdev_ocf_core_get_name(core_ctx));
}
- data = vbdev_ocf_data_from_spdk_io(bdev_io);
- if (!data) {
- err = -ENOMEM;
- goto fail;
- }
+ ocf_mngt_cache_add_core(cache, &core_ctx->core_cfg, _core_add_examine_add_cb, core_ctx);
+}
- err = ocf_io_set_data(io, data, 0);
- if (err) {
- goto fail;
- }
+static void
+_cache_attach_cb(ocf_cache_t cache, void *cb_arg, int error)
+{
+ struct vbdev_ocf_mngt_ctx *mngt_ctx = cb_arg;
- ocf_io_set_cmpl(io, bdev_io, NULL, vbdev_ocf_io_submit_cb);
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF cache '%s': finishing device attach\n",
+ ocf_cache_get_name(cache));
- err = io_submit_to_ocf(bdev_io, io);
- if (err) {
- goto fail;
- }
+ /* At this point volume was either moved to ocf_cache_t struct or is no longer
+ * needed due to some errors, so we need to deallocate it either way. */
+ vbdev_ocf_cache_config_volume_destroy(cache);
+ ocf_mngt_cache_unlock(cache);
- return;
+ if (error) {
+ SPDK_ERRLOG("OCF cache '%s': failed to attach OCF cache device (OCF error: %d)\n",
+ ocf_cache_get_name(cache), error);
+
+ if (error == -OCF_ERR_NO_MEM) {
+ SPDK_ERRLOG("Not enough memory to handle cache device of this size. Try to increase hugepage memory size, increase cache line size or use smaller cache device.\n");
+ vbdev_ocf_mem_calculate(cache);
+ }
-fail:
- if (io) {
- ocf_io_put(io);
+ vbdev_ocf_core_destroy_cache_channel(cache);
+ vbdev_ocf_cache_base_detach(cache);
+ } else {
+ SPDK_NOTICELOG("OCF cache '%s': device attached\n", ocf_cache_get_name(cache));
+
+ vbdev_ocf_core_add_from_waitlist(cache);
}
- if (err == -ENOMEM) {
- spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_NOMEM);
+ if (mngt_ctx->rpc_cb_fn) {
+ mngt_ctx->rpc_cb_fn(ocf_cache_get_name(cache), mngt_ctx->rpc_cb_arg, error);
} else {
- spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED);
+ spdk_bdev_module_examine_done(&ocf_if);
}
+ free(mngt_ctx);
}
static void
-vbdev_ocf_get_buf_cb(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io,
- bool success)
+_cache_attach_examine_lock_cb(ocf_cache_t cache, void *cb_arg, int error)
{
- if (!success) {
- spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED);
- return;
+ struct vbdev_ocf_mngt_ctx *mngt_ctx;
+ int rc;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF cache '%s': attaching OCF cache device\n",
+ ocf_cache_get_name(cache));
+
+ if (error) {
+ SPDK_ERRLOG("OCF cache '%s': failed to acquire OCF cache lock (OCF error: %d)\n",
+ ocf_cache_get_name(cache), error);
+ goto err_lock;
+ }
+
+ mngt_ctx = calloc(1, sizeof(struct vbdev_ocf_mngt_ctx));
+ if (!mngt_ctx) {
+ SPDK_ERRLOG("OCF cache '%s': failed to allocate memory for examine attach context: %s\n",
+ ocf_cache_get_name(cache), spdk_strerror(-ENOMEM));
+ goto err_alloc;
}
+ mngt_ctx->cache = cache;
+ mngt_ctx->u.att_cb_fn = _cache_attach_cb;
- io_handle(ch, bdev_io);
+ if ((rc = vbdev_ocf_cache_volume_attach(cache, mngt_ctx))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to attach volume: %s\n",
+ ocf_cache_get_name(cache), spdk_strerror(-rc));
+ goto err_attach;
+ }
+
+ return;
+
+err_attach:
+ free(mngt_ctx);
+err_alloc:
+ ocf_mngt_cache_unlock(cache);
+err_lock:
+ vbdev_ocf_cache_config_volume_destroy(cache);
+ vbdev_ocf_core_destroy_cache_channel(cache);
+ vbdev_ocf_cache_base_detach(cache);
+ spdk_bdev_module_examine_done(&ocf_if);
}
-/* Called from bdev layer when an io to Cache vbdev is submitted */
-static void
-vbdev_ocf_submit_request(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io)
+static int
+_examine_disk_core_visitor(ocf_core_t core, void *cb_arg)
{
- switch (bdev_io->type) {
- case SPDK_BDEV_IO_TYPE_READ:
- /* User does not have to allocate io vectors for the request,
- * so in case they are not allocated, we allocate them here */
- spdk_bdev_io_get_buf(bdev_io, vbdev_ocf_get_buf_cb,
- bdev_io->u.bdev.num_blocks * bdev_io->bdev->blocklen);
- break;
- case SPDK_BDEV_IO_TYPE_WRITE:
- case SPDK_BDEV_IO_TYPE_FLUSH:
- case SPDK_BDEV_IO_TYPE_UNMAP:
- io_handle(ch, bdev_io);
- break;
- case SPDK_BDEV_IO_TYPE_RESET:
- case SPDK_BDEV_IO_TYPE_WRITE_ZEROES:
- default:
- SPDK_ERRLOG("Unknown I/O type %d\n", bdev_io->type);
- spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED);
- break;
+ ocf_cache_t cache = ocf_core_get_cache(core);
+ struct vbdev_ocf_core *core_ctx = ocf_core_get_priv(core);
+ char *bdev_name = cb_arg;
+
+ if (!core_ctx) {
+ /* Skip this core. If there is no context, it means that this core
+ * was added from metadata during cache load and it's just an empty shell. */
+ return 0;
}
+
+ if (strcmp(bdev_name, core_ctx->base.name)) {
+ return 0;
+ }
+
+ /* Get cache once to be in sync with adding core from wait list scenario. */
+ ocf_mngt_cache_get(cache);
+ ocf_mngt_cache_lock(cache, _core_add_examine_lock_cb, core_ctx);
+
+ return -EEXIST;
}
-/* Called from bdev layer */
-static bool
-vbdev_ocf_io_type_supported(void *opaque, enum spdk_bdev_io_type io_type)
+static int
+_examine_disk_cache_visitor(ocf_cache_t cache, void *cb_arg)
{
- struct vbdev_ocf *vbdev = opaque;
+ struct vbdev_ocf_cache *cache_ctx = ocf_cache_get_priv(cache);
+ char *bdev_name = cb_arg;
- switch (io_type) {
- case SPDK_BDEV_IO_TYPE_READ:
- case SPDK_BDEV_IO_TYPE_WRITE:
- case SPDK_BDEV_IO_TYPE_FLUSH:
- case SPDK_BDEV_IO_TYPE_UNMAP:
- return spdk_bdev_io_type_supported(vbdev->core.bdev, io_type);
- case SPDK_BDEV_IO_TYPE_RESET:
- case SPDK_BDEV_IO_TYPE_WRITE_ZEROES:
- default:
- return false;
+ if (strcmp(bdev_name, cache_ctx->base.name)) {
+ return ocf_core_visit(cache, _examine_disk_core_visitor, bdev_name, false);
}
+
+ assert(!ocf_cache_is_device_attached(cache));
+
+ ocf_mngt_cache_lock(cache, _cache_attach_examine_lock_cb, NULL);
+
+ return -EEXIST;
}
-/* Called from bdev layer */
-static struct spdk_io_channel *
-vbdev_ocf_get_io_channel(void *opaque)
+static void
+vbdev_ocf_module_examine_disk(struct spdk_bdev *bdev)
{
- struct vbdev_ocf *bdev = opaque;
+ ocf_cache_t cache;
+ struct vbdev_ocf_core *core_ctx;
+ char *bdev_name = (char *)spdk_bdev_get_name(bdev);
+ int rc;
+
+ vbdev_ocf_foreach_core_in_waitlist(core_ctx) {
+ if (strcmp(bdev_name, core_ctx->base.name)) {
+ continue;
+ }
+
+ SPDK_NOTICELOG("OCF core '%s': adding from wait list to cache '%s'\n",
+ vbdev_ocf_core_get_name(core_ctx), core_ctx->cache_name);
+
+ if (ocf_mngt_cache_get_by_name(vbdev_ocf_ctx, core_ctx->cache_name,
+ OCF_CACHE_NAME_SIZE, &cache)) {
+ SPDK_NOTICELOG("OCF core '%s': add deferred - waiting for OCF cache '%s'\n",
+ vbdev_ocf_core_get_name(core_ctx), core_ctx->cache_name);
+ spdk_bdev_module_examine_done(&ocf_if);
+ return;
+ }
- return spdk_get_io_channel(bdev);
+ if (!ocf_cache_is_device_attached(cache)) {
+ SPDK_NOTICELOG("OCF core '%s': add deferred - waiting for OCF cache device '%s'\n",
+ vbdev_ocf_core_get_name(core_ctx),
+ ((struct vbdev_ocf_cache *)ocf_cache_get_priv(cache))->base.name);
+ ocf_mngt_cache_put(cache);
+ spdk_bdev_module_examine_done(&ocf_if);
+ return;
+ }
+
+ ocf_mngt_cache_lock(cache, _core_add_examine_lock_cb, core_ctx);
+ return;
+ }
+
+ rc = ocf_mngt_cache_visit(vbdev_ocf_ctx, _examine_disk_cache_visitor, bdev_name);
+ if (rc && rc != -EEXIST) {
+ SPDK_ERRLOG("OCF: failed to iterate over bdevs: %s\n", spdk_strerror(-rc));
+ } else if (!rc) {
+ /* No visitor matched this new bdev, so no one called _examine_done(). */
+ spdk_bdev_module_examine_done(&ocf_if);
+ }
}
-static int
-vbdev_ocf_dump_info_json(void *opaque, struct spdk_json_write_ctx *w)
+static void
+dump_core_config(struct spdk_json_write_ctx *w, struct vbdev_ocf_core *core_ctx)
{
- struct vbdev_ocf *vbdev = opaque;
-
- spdk_json_write_named_string(w, "cache_device", vbdev->cache.name);
- spdk_json_write_named_string(w, "core_device", vbdev->core.name);
+ spdk_json_write_object_begin(w);
+ spdk_json_write_named_string(w, "method", "bdev_ocf_add_core");
- spdk_json_write_named_string(w, "mode",
- ocf_get_cache_modename(ocf_cache_get_mode(vbdev->ocf_cache)));
- spdk_json_write_named_uint32(w, "cache_line_size",
- ocf_get_cache_line_size(vbdev->ocf_cache));
- spdk_json_write_named_bool(w, "metadata_volatile",
- vbdev->cfg.cache.metadata_volatile);
+ spdk_json_write_named_object_begin(w, "params");
+ spdk_json_write_named_string(w, "core_name", vbdev_ocf_core_get_name(core_ctx));
+ spdk_json_write_named_string(w, "base_name", core_ctx->base.name);
+ spdk_json_write_named_string(w, "cache_name", core_ctx->cache_name);
+ spdk_json_write_object_end(w);
- return 0;
+ spdk_json_write_object_end(w);
}
static void
-vbdev_ocf_write_json_config(struct spdk_bdev *bdev, struct spdk_json_write_ctx *w)
+dump_cache_config(struct spdk_json_write_ctx *w, ocf_cache_t cache)
{
- struct vbdev_ocf *vbdev = bdev->ctxt;
+ struct vbdev_ocf_cache *cache_ctx = ocf_cache_get_priv(cache);
spdk_json_write_object_begin(w);
-
- spdk_json_write_named_string(w, "method", "bdev_ocf_create");
+ spdk_json_write_named_string(w, "method", "bdev_ocf_start_cache");
spdk_json_write_named_object_begin(w, "params");
- spdk_json_write_named_string(w, "name", vbdev->name);
- spdk_json_write_named_string(w, "mode",
- ocf_get_cache_modename(ocf_cache_get_mode(vbdev->ocf_cache)));
- spdk_json_write_named_uint32(w, "cache_line_size",
- ocf_get_cache_line_size(vbdev->ocf_cache));
- spdk_json_write_named_string(w, "cache_bdev_name", vbdev->cache.name);
- spdk_json_write_named_string(w, "core_bdev_name", vbdev->core.name);
+ spdk_json_write_named_string(w, "cache_name", ocf_cache_get_name(cache));
+ spdk_json_write_named_string(w, "base_name", cache_ctx->base.name);
+ spdk_json_write_named_string(w, "cache_mode",
+ vbdev_ocf_cachemode_get_name(ocf_cache_get_mode(cache)));
+ spdk_json_write_named_uint32(w, "cache_line_size", ocf_cache_get_line_size(cache));
spdk_json_write_object_end(w);
spdk_json_write_object_end(w);
}
-/* Cache vbdev function table
- * Used by bdev layer */
-static struct spdk_bdev_fn_table cache_dev_fn_table = {
- .destruct = vbdev_ocf_destruct,
- .io_type_supported = vbdev_ocf_io_type_supported,
- .submit_request = vbdev_ocf_submit_request,
- .get_io_channel = vbdev_ocf_get_io_channel,
- .write_config_json = vbdev_ocf_write_json_config,
- .dump_info_json = vbdev_ocf_dump_info_json,
-};
-
-/* Poller function for the OCF queue
- * We execute OCF requests here synchronously */
static int
-queue_poll(void *opaque)
+_module_config_json_core_visitor(ocf_core_t core, void *ctx)
{
- struct vbdev_ocf_qctx *qctx = opaque;
- uint32_t iono = ocf_queue_pending_io(qctx->queue);
- int i, max = spdk_min(32, iono);
+ struct spdk_json_write_ctx *w = ctx;
+ struct vbdev_ocf_core *core_ctx = ocf_core_get_priv(core);
- for (i = 0; i < max; i++) {
- ocf_queue_run_single(qctx->queue);
- }
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s': module config visit\n", ocf_core_get_name(core));
- if (iono > 0) {
- return SPDK_POLLER_BUSY;
- } else {
- return SPDK_POLLER_IDLE;
+ if (!core_ctx) {
+ /* Skip this core. If there is no context, it means that this core
+ * was added from metadata during cache load and not manually by RPC call. */
+ return 0;
}
-}
-/* Called during ocf_submit_io, ocf_purge*
- * and any other requests that need to submit io */
-static void
-vbdev_ocf_ctx_queue_kick(ocf_queue_t q)
-{
+ dump_core_config(w, core_ctx);
+
+ return 0;
}
-/* OCF queue deinitialization
- * Called at ocf_cache_stop */
-static void
-vbdev_ocf_ctx_queue_stop(ocf_queue_t q)
+static int
+_module_config_json_cache_visitor(ocf_cache_t cache, void *ctx)
{
- struct vbdev_ocf_qctx *qctx = ocf_queue_get_priv(q);
+ struct spdk_json_write_ctx *w = ctx;
- if (qctx) {
- spdk_put_io_channel(qctx->cache_ch);
- spdk_put_io_channel(qctx->core_ch);
- spdk_poller_unregister(&qctx->poller);
- if (qctx->allocated) {
- free(qctx);
- }
- }
-}
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF cache '%s': module config visit\n", ocf_cache_get_name(cache));
-/* Queue ops is an interface for running queue thread
- * stop() operation in called just before queue gets destroyed */
-const struct ocf_queue_ops queue_ops = {
- .kick_sync = vbdev_ocf_ctx_queue_kick,
- .kick = vbdev_ocf_ctx_queue_kick,
- .stop = vbdev_ocf_ctx_queue_stop,
-};
+ dump_cache_config(w, cache);
+
+ return ocf_core_visit(cache, _module_config_json_core_visitor, w, false);
+}
-/* Called on cache vbdev creation at every thread
- * We allocate OCF queues here and SPDK poller for it */
static int
-io_device_create_cb(void *io_device, void *ctx_buf)
+vbdev_ocf_module_config_json(struct spdk_json_write_ctx *w)
{
- struct vbdev_ocf *vbdev = io_device;
- struct vbdev_ocf_qctx *qctx = ctx_buf;
+ struct vbdev_ocf_core *core_ctx;
int rc;
- rc = vbdev_ocf_queue_create(vbdev->ocf_cache, &qctx->queue, &queue_ops);
- if (rc) {
- return rc;
- }
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF: generating current module configuration\n");
- ocf_queue_set_priv(qctx->queue, qctx);
+ vbdev_ocf_foreach_core_in_waitlist(core_ctx) {
+ dump_core_config(w, core_ctx);
+ }
- qctx->vbdev = vbdev;
- qctx->cache_ch = spdk_bdev_get_io_channel(vbdev->cache.desc);
- qctx->core_ch = spdk_bdev_get_io_channel(vbdev->core.desc);
- qctx->poller = SPDK_POLLER_REGISTER(queue_poll, qctx, 0);
+ if ((rc = ocf_mngt_cache_visit(vbdev_ocf_ctx, _module_config_json_cache_visitor, w))) {
+ SPDK_ERRLOG("OCF: failed to iterate over bdevs: %s\n", spdk_strerror(-rc));
+ return rc;
+ }
- return rc;
+ return 0;
}
-/* Called per thread
- * Put OCF queue and relaunch poller with new context to finish pending requests */
static void
-io_device_destroy_cb(void *io_device, void *ctx_buf)
-{
- /* Making a copy of context to use it after io channel will be destroyed */
- struct vbdev_ocf_qctx *copy = malloc(sizeof(*copy));
- struct vbdev_ocf_qctx *qctx = ctx_buf;
-
- if (copy) {
- ocf_queue_set_priv(qctx->queue, copy);
- memcpy(copy, qctx, sizeof(*copy));
- spdk_poller_unregister(&qctx->poller);
- copy->poller = SPDK_POLLER_REGISTER(queue_poll, copy, 0);
- copy->allocated = true;
- } else {
- SPDK_ERRLOG("Unable to stop OCF queue properly: %s\n",
- spdk_strerror(ENOMEM));
+_destruct_core_detach_cb(ocf_core_t core, void *cb_arg, int error)
+{
+ ocf_cache_t cache = cb_arg;
+ struct vbdev_ocf_core *core_ctx = ocf_core_get_priv(core);
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF vbdev '%s': finishing detach of OCF core\n", ocf_core_get_name(core));
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF vbdev '%s': finishing destruct\n", ocf_core_get_name(core));
+
+ if (error) {
+ SPDK_ERRLOG("OCF vbdev '%s': failed to remove OCF core device (OCF error: %d)\n",
+ ocf_core_get_name(core), error);
}
- vbdev_ocf_queue_put(qctx->queue);
+ ocf_mngt_cache_unlock(cache);
+ vbdev_ocf_core_base_detach(core_ctx);
+
+ /* This one finally calls the callback from spdk_bdev_unregister_by_name(). */
+ spdk_bdev_destruct_done(&core_ctx->ocf_vbdev, 0);
}
-/* OCF management queue deinitialization */
static void
-vbdev_ocf_ctx_mngt_queue_stop(ocf_queue_t q)
+_destruct_core_flush_cb(ocf_core_t core, void *cb_arg, int error)
{
- struct spdk_poller *poller = ocf_queue_get_priv(q);
-
- if (poller) {
- spdk_poller_unregister(&poller);
+ if (error) {
+ SPDK_ERRLOG("OCF vbdev '%s': failed to flush OCF core device (OCF error: %d)\n",
+ ocf_core_get_name(core), error);
}
+
+ /* Detach core instead of removing it, so it stays in the cache metadata. */
+ ocf_mngt_cache_detach_core(core, _destruct_core_detach_cb, cb_arg);
}
-static int
-mngt_queue_poll(void *opaque)
+static void
+_destruct_cache_lock_cb(ocf_cache_t cache, void *cb_arg, int error)
{
- ocf_queue_t q = opaque;
- uint32_t iono = ocf_queue_pending_io(q);
- int i, max = spdk_min(32, iono);
+ ocf_core_t core = cb_arg;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF vbdev '%s': initiating detach of OCF core\n",
+ ocf_core_get_name(core));
- for (i = 0; i < max; i++) {
- ocf_queue_run_single(q);
+ if (error) {
+ SPDK_ERRLOG("OCF vbdev '%s': failed to acquire OCF cache lock (OCF error: %d)\n",
+ ocf_core_get_name(core), error);
}
- if (iono > 0) {
- return SPDK_POLLER_BUSY;
+ if (ocf_mngt_core_is_dirty(core)) {
+ ocf_mngt_core_flush(core, _destruct_core_flush_cb, cache);
} else {
- return SPDK_POLLER_IDLE;
+ /* Detach core instead of removing it, so it stays in the cache metadata. */
+ ocf_mngt_cache_detach_core(core, _destruct_core_detach_cb, cache);
}
}
static void
-vbdev_ocf_ctx_mngt_queue_kick(ocf_queue_t q)
+_destruct_io_device_unregister_cb(void *io_device)
{
+ ocf_core_t core = io_device;
+
+ ocf_mngt_cache_lock(ocf_core_get_cache(core), _destruct_cache_lock_cb, core);
}
-/* Queue ops is an interface for running queue thread
- * stop() operation in called just before queue gets destroyed */
-const struct ocf_queue_ops mngt_queue_ops = {
- .kick_sync = NULL,
- .kick = vbdev_ocf_ctx_mngt_queue_kick,
- .stop = vbdev_ocf_ctx_mngt_queue_stop,
-};
+/* This is called internally by SPDK during spdk_bdev_unregister_by_name(). */
+static int
+vbdev_ocf_fn_destruct(void *ctx)
+{
+ ocf_core_t core = ctx;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF vbdev '%s': initiating destruct\n", ocf_core_get_name(core));
+
+ spdk_io_device_unregister(core, _destruct_io_device_unregister_cb);
+
+ /* Return one to indicate async destruct. */
+ return 1;
+}
static void
-vbdev_ocf_mngt_exit(struct vbdev_ocf *vbdev, vbdev_ocf_mngt_fn *rollback_path, int rc)
+_vbdev_ocf_submit_io_cb(ocf_io_t io, void *priv1, void *priv2, int error)
{
- vbdev->state.starting = false;
- vbdev_ocf_mngt_stop(vbdev, rollback_path, rc);
+ struct spdk_bdev_io *bdev_io = priv1;
+
+ ocf_io_put(io);
+
+ if (error == -OCF_ERR_NO_MEM) {
+ spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_NOMEM);
+ } else if (error) {
+ SPDK_ERRLOG("OCF vbdev '%s': failed to complete OCF IO: %s\n",
+ spdk_bdev_get_name(bdev_io->bdev), spdk_strerror(-error));
+ spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED);
+ } else {
+ spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_SUCCESS);
+ }
}
-/* Create exported spdk object */
+typedef void (*submit_io_to_ocf_fn)(ocf_io_t io);
+
static void
-finish_register(struct vbdev_ocf *vbdev)
-{
- struct spdk_uuid ns_uuid;
- int result;
-
- /* Copy properties of the base bdev */
- vbdev->exp_bdev.blocklen = vbdev->core.bdev->blocklen;
- vbdev->exp_bdev.write_cache = vbdev->core.bdev->write_cache;
- vbdev->exp_bdev.required_alignment = vbdev->core.bdev->required_alignment;
-
- vbdev->exp_bdev.name = vbdev->name;
- vbdev->exp_bdev.product_name = "SPDK OCF";
-
- vbdev->exp_bdev.blockcnt = vbdev->core.bdev->blockcnt;
- vbdev->exp_bdev.ctxt = vbdev;
- vbdev->exp_bdev.fn_table = &cache_dev_fn_table;
- vbdev->exp_bdev.module = &ocf_if;
-
- /* Generate UUID based on namespace UUID + base bdev UUID. */
- spdk_uuid_parse(&ns_uuid, BDEV_OCF_NAMESPACE_UUID);
- result = spdk_uuid_generate_sha1(&vbdev->exp_bdev.uuid, &ns_uuid,
- (const char *)&vbdev->core.bdev->uuid, sizeof(struct spdk_uuid));
- if (result) {
- SPDK_ERRLOG("Unable to generate new UUID for ocf bdev\n");
- vbdev_ocf_mngt_exit(vbdev, unregister_path_dirty, result);
+vbdev_ocf_submit_io(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io, uint64_t offset,
+ uint32_t len, uint32_t dir, uint64_t flags, submit_io_to_ocf_fn submit_io_fn)
+{
+ ocf_core_t core = bdev_io->bdev->ctxt;
+ struct vbdev_ocf_data *data = (struct vbdev_ocf_data *)bdev_io->driver_ctx;
+ struct vbdev_ocf_core_io_channel_ctx *ch_ctx = spdk_io_channel_get_ctx(ch);
+ ocf_io_t io = NULL;
+
+ /* OCF core should be added before vbdev register and removed after vbdev unregister. */
+ assert(core);
+
+ io = ocf_volume_new_io(ocf_core_get_front_volume(core), ch_ctx->queue,
+ offset, len, dir, 0, flags);
+ if (spdk_unlikely(!io)) {
+ spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_NOMEM);
return;
}
- /* Finally register vbdev in SPDK */
- spdk_io_device_register(vbdev, io_device_create_cb, io_device_destroy_cb,
- sizeof(struct vbdev_ocf_qctx), vbdev->name);
- result = spdk_bdev_register(&vbdev->exp_bdev);
- if (result) {
- SPDK_ERRLOG("Could not register exposed bdev %s\n",
- vbdev->name);
- vbdev_ocf_mngt_exit(vbdev, unregister_path_dirty, result);
+ data->iovs = bdev_io->u.bdev.iovs;
+ data->iovcnt = bdev_io->u.bdev.iovcnt;
+ data->size = bdev_io->u.bdev.num_blocks * bdev_io->bdev->blocklen;
+
+ ocf_io_set_data(io, data, 0);
+ ocf_io_set_cmpl(io, bdev_io, NULL, _vbdev_ocf_submit_io_cb);
+ submit_io_fn(io);
+}
+
+static void
+_io_read_get_buf_cb(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io, bool success)
+{
+ uint64_t offset = bdev_io->u.bdev.offset_blocks * bdev_io->bdev->blocklen;
+ uint32_t len = bdev_io->u.bdev.num_blocks * bdev_io->bdev->blocklen;
+
+ if (spdk_unlikely(!success)) {
+ SPDK_ERRLOG("OCF vbdev '%s': failed to allocate IO buffer - size of the "
+ "buffer to allocate might be greater than the permitted maximum\n",
+ spdk_bdev_get_name(bdev_io->bdev));
+ spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED);
return;
- } else {
- vbdev->state.started = true;
}
- vbdev_ocf_mngt_continue(vbdev, result);
+ vbdev_ocf_submit_io(ch, bdev_io, offset, len, OCF_READ, 0, ocf_core_submit_io);
}
static void
-add_core_cmpl(ocf_cache_t cache, ocf_core_t core, void *priv, int error)
+vbdev_ocf_fn_submit_request(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io)
{
- struct vbdev_ocf *vbdev = priv;
+ uint64_t offset = bdev_io->u.bdev.offset_blocks * bdev_io->bdev->blocklen;
+ uint32_t len = bdev_io->u.bdev.num_blocks * bdev_io->bdev->blocklen;
- ocf_mngt_cache_unlock(cache);
+ switch (bdev_io->type) {
+ case SPDK_BDEV_IO_TYPE_READ:
+ spdk_bdev_io_get_buf(bdev_io, _io_read_get_buf_cb, len);
+ break;
+ case SPDK_BDEV_IO_TYPE_WRITE:
+ vbdev_ocf_submit_io(ch, bdev_io, offset, len, OCF_WRITE, 0, ocf_core_submit_io);
+ break;
+ case SPDK_BDEV_IO_TYPE_UNMAP:
+ vbdev_ocf_submit_io(ch, bdev_io, offset, len, OCF_WRITE, 0, ocf_core_submit_discard);
+ break;
+ case SPDK_BDEV_IO_TYPE_FLUSH:
+ vbdev_ocf_submit_io(ch, bdev_io, 0, 0, OCF_WRITE, OCF_WRITE_FLUSH, ocf_core_submit_flush);
+ break;
+ default:
+ SPDK_ERRLOG("OCF vbdev '%s': unsupported IO type: %s\n", spdk_bdev_get_name(bdev_io->bdev),
+ spdk_bdev_get_io_type_name(bdev_io->type));
+ spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED);
+ }
+}
- if (error) {
- SPDK_ERRLOG("Error %d, failed to add core device to cache instance %s,"
- "starting rollback\n", error, vbdev->name);
- vbdev_ocf_mngt_exit(vbdev, unregister_path_dirty, error);
- return;
- } else {
- vbdev->ocf_core = core;
+static bool
+vbdev_ocf_fn_io_type_supported(void *ctx, enum spdk_bdev_io_type io_type)
+{
+ ocf_core_t core = ctx;
+ struct vbdev_ocf_core *core_ctx = ocf_core_get_priv(core);
+
+ switch (io_type) {
+ case SPDK_BDEV_IO_TYPE_READ:
+ case SPDK_BDEV_IO_TYPE_WRITE:
+ case SPDK_BDEV_IO_TYPE_UNMAP:
+ case SPDK_BDEV_IO_TYPE_FLUSH:
+ return spdk_bdev_io_type_supported(core_ctx->base.bdev, io_type);
+ default:
+ return false;
}
+}
+
+static struct spdk_io_channel *
+vbdev_ocf_fn_get_io_channel(void *ctx)
+{
+ ocf_core_t core = ctx;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF vbdev '%s': got request for IO channel\n",
+ spdk_bdev_get_name(&(((struct vbdev_ocf_core *)ocf_core_get_priv(core))->ocf_vbdev)));
+
+ return spdk_get_io_channel(core);
+}
+
+static int
+vbdev_ocf_fn_dump_info_json(void *ctx, struct spdk_json_write_ctx *w)
+{
+ ocf_core_t core = ctx;
+ ocf_cache_t cache = ocf_core_get_cache(core);
+ struct vbdev_ocf_core *core_ctx = ocf_core_get_priv(core);
+ struct vbdev_ocf_cache *cache_ctx = ocf_cache_get_priv(cache);
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF vbdev '%s': dumping driver specific info\n", ocf_core_get_name(core));
+
+ spdk_json_write_named_object_begin(w, "ocf");
+ spdk_json_write_named_string(w, "name", ocf_core_get_name(core));
+ spdk_json_write_named_string(w, "base_name", core_ctx ? core_ctx->base.name : "");
+
+ spdk_json_write_named_object_begin(w, "cache");
+ spdk_json_write_named_string(w, "name", ocf_cache_get_name(cache));
+ spdk_json_write_named_string(w, "base_name", cache_ctx->base.name);
+ spdk_json_write_named_string(w, "cache_mode",
+ vbdev_ocf_cachemode_get_name(ocf_cache_get_mode(cache)));
+ spdk_json_write_named_uint32(w, "cache_line_size", ocf_cache_get_line_size(cache));
+ spdk_json_write_object_end(w);
+
+ spdk_json_write_object_end(w);
- vbdev_ocf_mngt_continue(vbdev, error);
+ return 0;
}
-/* Try to lock cache, then add core */
static void
-add_core_cache_lock_cmpl(ocf_cache_t cache, void *priv, int error)
+vbdev_ocf_fn_dump_device_stat_json(void *ctx, struct spdk_json_write_ctx *w)
{
- struct vbdev_ocf *vbdev = (struct vbdev_ocf *)priv;
+ ocf_core_t core = ctx;
+ struct vbdev_ocf_stats stats;
+ int rc;
- if (error) {
- SPDK_ERRLOG("Error %d, can not lock cache instance %s,"
- "starting rollback\n", error, vbdev->name);
- vbdev_ocf_mngt_exit(vbdev, unregister_path_dirty, error);
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s': collecting statistics\n", ocf_core_get_name(core));
+
+ if ((rc = vbdev_ocf_stats_core_get(core, &stats))) {
+ SPDK_ERRLOG("OCF core '%s': failed to collect statistics (OCF error: %d)\n",
+ ocf_core_get_name(core), rc);
+ return;
}
- ocf_mngt_cache_add_core(vbdev->ocf_cache, &vbdev->cfg.core, add_core_cmpl, vbdev);
+
+ vbdev_ocf_stats_write_json(w, &stats);
}
-/* Add core for existing OCF cache instance */
+/* Do not define this function to not reset OCF stats when resetting exposed bdev's stats.
+ * Let the user reset OCF stats independently by calling bdev_ocf_reset_stats RPC. */
static void
-add_core(struct vbdev_ocf *vbdev)
+vbdev_ocf_fn_reset_device_stat(void *ctx)
{
- ocf_mngt_cache_lock(vbdev->ocf_cache, add_core_cache_lock_cmpl, vbdev);
}
static void
-start_cache_cmpl(ocf_cache_t cache, void *priv, int error)
+_cache_start_rpc_err_cb(ocf_cache_t cache, void *cb_arg, int error)
{
- struct vbdev_ocf *vbdev = priv;
- uint64_t volume_size;
- uint64_t mem_needed;
+ if (error) {
+ SPDK_ERRLOG("OCF cache '%s': failed to stop OCF cache properly (OCF error: %d)\n",
+ ocf_cache_get_name(cache), error);
+ }
+ vbdev_ocf_cache_destroy(cache);
ocf_mngt_cache_unlock(cache);
+}
+
+static void
+_cache_start_rpc_attach_cb(ocf_cache_t cache, void *cb_arg, int error)
+{
+ struct vbdev_ocf_mngt_ctx *mngt_ctx = cb_arg;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF cache '%s': finishing start\n", ocf_cache_get_name(cache));
+
+ /* At this point volume was either moved to ocf_cache_t struct or is no longer
+ * needed due to some errors, so we need to deallocate it either way. */
+ vbdev_ocf_cache_config_volume_destroy(cache);
if (error) {
- SPDK_ERRLOG("Error %d during start cache %s, starting rollback\n",
- error, vbdev->name);
+ SPDK_ERRLOG("OCF cache '%s': failed to attach OCF cache device (OCF error: %d)\n",
+ ocf_cache_get_name(cache), error);
if (error == -OCF_ERR_NO_MEM) {
- volume_size = vbdev->cache.bdev->blockcnt * vbdev->cache.bdev->blocklen;
- mem_needed = ocf_mngt_get_ram_needed(cache, volume_size);
-
- SPDK_NOTICELOG("Try to increase hugepage memory size or cache line size. "
- "For your configuration:\nDevice size: %"PRIu64" bytes\n"
- "Cache line size: %"PRIu64" bytes\nFree memory needed to start "
- "cache: %"PRIu64" bytes\n",
- volume_size, vbdev->cfg.cache.cache_line_size, mem_needed);
+ SPDK_ERRLOG("Not enough memory to handle cache device of this size. Try to increase hugepage memory size, increase cache line size or use smaller cache device.\n");
+ vbdev_ocf_mem_calculate(cache);
}
- vbdev_ocf_mngt_exit(vbdev, unregister_path_dirty, error);
- return;
+ vbdev_ocf_cache_base_detach(cache);
+ vbdev_ocf_cache_mngt_queue_put(cache);
+ ocf_mngt_cache_stop(cache, _cache_start_rpc_err_cb, NULL);
+ } else {
+ SPDK_NOTICELOG("OCF cache '%s': started\n", ocf_cache_get_name(cache));
+
+ ocf_mngt_cache_unlock(cache);
+ vbdev_ocf_core_add_from_waitlist(cache);
}
- vbdev_ocf_mngt_continue(vbdev, error);
+ mngt_ctx->rpc_cb_fn(ocf_cache_get_name(cache), mngt_ctx->rpc_cb_arg, error);
+ free(mngt_ctx);
}
-static int
-create_management_queue(struct vbdev_ocf *vbdev)
+/* RPC entry point. */
+void
+vbdev_ocf_cache_start(const char *cache_name, const char *base_name,
+ const char *cache_mode, const uint32_t cache_line_size, bool no_load,
+ vbdev_ocf_rpc_mngt_cb rpc_cb_fn, void *rpc_cb_arg)
{
- struct spdk_poller *mngt_poller;
- int rc;
+ ocf_cache_t cache;
+ struct vbdev_ocf_mngt_ctx *mngt_ctx;
+ int rc = 0;
- rc = vbdev_ocf_queue_create_mngt(vbdev->ocf_cache,
- &vbdev->cache_ctx->mngt_queue, &mngt_queue_ops);
- if (rc) {
- SPDK_ERRLOG("Unable to create mngt_queue: %d\n", rc);
- return rc;
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF cache '%s': initiating start\n", cache_name);
+
+ if (!g_vbdev_ocf_module_is_running) {
+ SPDK_ERRLOG("OCF: failed to handle the call - module stopping\n");
+ rc = -EPERM;
+ goto err_module;
}
- mngt_poller = SPDK_POLLER_REGISTER(mngt_queue_poll, vbdev->cache_ctx->mngt_queue, 100);
- if (mngt_poller == NULL) {
- SPDK_ERRLOG("Unable to initiate mngt request: %s", spdk_strerror(ENOMEM));
- return -ENOMEM;
+ if (vbdev_ocf_bdev_exists(cache_name)) {
+ SPDK_ERRLOG("OCF '%s': bdev already exists\n", cache_name);
+ rc = -EEXIST;
+ goto err_exist;
}
- ocf_queue_set_priv(vbdev->cache_ctx->mngt_queue, mngt_poller);
+ if ((rc = vbdev_ocf_cache_create(&cache, cache_name, cache_mode,
+ cache_line_size, no_load))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to create cache: %s\n",
+ cache_name, spdk_strerror(-rc));
+ goto err_create;
+ }
- return 0;
-}
+ if ((rc = vbdev_ocf_cache_mngt_queue_create(cache))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to create management queue: %s\n",
+ cache_name, spdk_strerror(-rc));
+ goto err_queue;
+ }
-/* Start OCF cache, attach caching device */
-static void
-start_cache(struct vbdev_ocf *vbdev)
+ /* Check if base device for this cache is already present. */
+ if ((rc = vbdev_ocf_cache_base_attach(cache, base_name))) {
+ if (rc == -ENODEV) {
+ /* If not, just leave started cache without the device and exit. */
+ /* It will be attached later at the examine stage when the device appears. */
+ SPDK_NOTICELOG("OCF cache '%s': start deferred - waiting for base bdev '%s'\n",
+ cache_name, base_name);
+ ocf_mngt_cache_unlock(cache);
+ rpc_cb_fn(cache_name, rpc_cb_arg, -ENODEV);
+ return;
+ }
+ SPDK_ERRLOG("OCF cache '%s': failed to attach base bdev '%s': %s\n",
+ cache_name, base_name, spdk_strerror(-rc));
+ goto err_base;
+ }
+
+ if ((rc = vbdev_ocf_cache_config_volume_create(cache))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to create config volume: %s\n",
+ cache_name, spdk_strerror(-rc));
+ goto err_volume;
+ }
+
+ mngt_ctx = calloc(1, sizeof(struct vbdev_ocf_mngt_ctx));
+ if (!mngt_ctx) {
+ SPDK_ERRLOG("OCF cache '%s': failed to allocate memory for cache start context\n",
+ cache_name);
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ mngt_ctx->rpc_cb_fn = rpc_cb_fn;
+ mngt_ctx->rpc_cb_arg = rpc_cb_arg;
+ mngt_ctx->cache = cache;
+ mngt_ctx->u.att_cb_fn = _cache_start_rpc_attach_cb;
+
+ if ((rc = vbdev_ocf_cache_volume_attach(cache, mngt_ctx))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to attach volume: %s\n",
+ cache_name, spdk_strerror(-rc));
+ goto err_attach;
+ }
+
+ return;
+
+err_attach:
+ free(mngt_ctx);
+err_alloc:
+ vbdev_ocf_cache_config_volume_destroy(cache);
+err_volume:
+ vbdev_ocf_cache_base_detach(cache);
+err_base:
+ vbdev_ocf_cache_mngt_queue_put(cache);
+err_queue:
+ ocf_mngt_cache_stop(cache, _cache_start_rpc_err_cb, NULL);
+err_create:
+err_exist:
+err_module:
+ rpc_cb_fn(cache_name, rpc_cb_arg, rc);
+}
+
+/* RPC entry point. */
+void
+vbdev_ocf_cache_stop(const char *cache_name, vbdev_ocf_rpc_mngt_cb rpc_cb_fn, void *rpc_cb_arg)
{
- ocf_cache_t existing;
- uint32_t cache_block_size = vbdev->cache.bdev->blocklen;
- uint32_t core_block_size = vbdev->core.bdev->blocklen;
+ ocf_cache_t cache;
+ struct vbdev_ocf_mngt_ctx *mngt_ctx;
int rc;
- if (is_ocf_cache_running(vbdev)) {
- vbdev_ocf_mngt_stop(vbdev, NULL, -EALREADY);
- return;
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF cache '%s': initiating stop\n", cache_name);
+
+ if (!g_vbdev_ocf_module_is_running) {
+ SPDK_ERRLOG("OCF: failed to handle the call - module stopping\n");
+ rc = -EPERM;
+ goto err_module;
}
+ if (ocf_mngt_cache_get_by_name(vbdev_ocf_ctx, cache_name, OCF_CACHE_NAME_SIZE, &cache)) {
+ SPDK_ERRLOG("OCF cache '%s': not exist\n", cache_name);
+ rc = -ENXIO;
+ goto err_cache;
+ }
+
+ mngt_ctx = calloc(1, sizeof(struct vbdev_ocf_mngt_ctx));
+ if (!mngt_ctx) {
+ SPDK_ERRLOG("OCF cache '%s': failed to allocate memory for cache stop context\n",
+ cache_name);
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ mngt_ctx->rpc_cb_fn = rpc_cb_fn;
+ mngt_ctx->rpc_cb_arg = rpc_cb_arg;
+ mngt_ctx->cache = cache;
+
+ if (!ocf_cache_get_core_count(cache) ||
+ ocf_cache_get_core_count(cache) == ocf_cache_get_core_inactive_count(cache)) {
+ /* If there are no cores or all of them are detached,
+ * then cache stop can be triggered already. */
+ ocf_mngt_cache_lock(cache, _cache_stop_lock_cb, mngt_ctx);
+ }
+
+ if ((rc = ocf_core_visit(cache, _cache_stop_core_visitor, mngt_ctx, false))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to iterate over core bdevs: %s\n",
+ ocf_cache_get_name(cache), spdk_strerror(-rc));
+ goto err_visit;
+ }
+
+ return;
+
+err_visit:
+ free(mngt_ctx);
+err_alloc:
+ ocf_mngt_cache_put(cache);
+err_cache:
+err_module:
+ rpc_cb_fn(cache_name, rpc_cb_arg, rc);
+}
+
+static void
+_cache_detach_rpc_detach_cb(ocf_cache_t cache, void *cb_arg, int error)
+{
+ struct vbdev_ocf_mngt_ctx *mngt_ctx = cb_arg;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF cache '%s': finishing device detach\n",
+ ocf_cache_get_name(cache));
+
+ if (error) {
+ SPDK_ERRLOG("OCF cache '%s': failed to detach OCF cache device (OCF error: %d)\n",
+ ocf_cache_get_name(cache), error);
+ } else {
+ vbdev_ocf_cache_base_detach(cache);
+
+ /* Update cache IO channel after device detach. */
+ if ((error = vbdev_ocf_core_destroy_cache_channel(cache))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to destroy channel for detached cache: %s\n",
+ ocf_cache_get_name(cache), spdk_strerror(-error));
+ }
+
+ SPDK_NOTICELOG("OCF cache '%s': device detached\n", ocf_cache_get_name(cache));
+ }
+
+ mngt_ctx->rpc_cb_fn(ocf_cache_get_name(cache), mngt_ctx->rpc_cb_arg, error);
+ ocf_mngt_cache_unlock(cache);
+ ocf_mngt_cache_put(cache);
+ free(mngt_ctx);
+}
+
+static void
+_cache_detach_rpc_flush_cb(ocf_cache_t cache, void *cb_arg, int error)
+{
+ struct vbdev_ocf_mngt_ctx *mngt_ctx = cb_arg;
+
+ if (error) {
+ SPDK_ERRLOG("OCF cache '%s': failed to flush OCF cache (OCF error: %d)\n",
+ ocf_cache_get_name(cache), error);
+ mngt_ctx->rpc_cb_fn(ocf_cache_get_name(cache), mngt_ctx->rpc_cb_arg, error);
+ ocf_mngt_cache_unlock(cache);
+ ocf_mngt_cache_put(cache);
+ free(mngt_ctx);
+ return;
+ }
+
+ ocf_mngt_cache_detach(cache, _cache_detach_rpc_detach_cb, mngt_ctx);
+}
+
+static void
+_cache_detach_rpc_lock_cb(ocf_cache_t cache, void *cb_arg, int error)
+{
+ struct vbdev_ocf_mngt_ctx *mngt_ctx = cb_arg;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF cache '%s': detaching OCF cache device\n",
+ ocf_cache_get_name(cache));
+
+ if (error) {
+ SPDK_ERRLOG("OCF cache '%s': failed to acquire OCF cache lock (OCF error: %d)\n",
+ ocf_cache_get_name(cache), error);
+ mngt_ctx->rpc_cb_fn(ocf_cache_get_name(cache), mngt_ctx->rpc_cb_arg, error);
+ ocf_mngt_cache_put(cache);
+ free(mngt_ctx);
+ return;
+ }
+
+ if (ocf_mngt_cache_is_dirty(cache)) {
+ ocf_mngt_cache_flush(cache, _cache_detach_rpc_flush_cb, mngt_ctx);
+ } else {
+ ocf_mngt_cache_detach(cache, _cache_detach_rpc_detach_cb, mngt_ctx);
+ }
+}
+
+/* RPC entry point. */
+void
+vbdev_ocf_cache_detach(const char *cache_name, vbdev_ocf_rpc_mngt_cb rpc_cb_fn, void *rpc_cb_arg)
+{
+ ocf_cache_t cache;
+ struct vbdev_ocf_mngt_ctx *mngt_ctx;
+ int rc;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF cache '%s': initiating device detach\n", cache_name);
+
+ if (!g_vbdev_ocf_module_is_running) {
+ SPDK_ERRLOG("OCF: failed to handle the call - module stopping\n");
+ rc = -EPERM;
+ goto err_module;
+ }
+
+ if (ocf_mngt_cache_get_by_name(vbdev_ocf_ctx, cache_name, OCF_CACHE_NAME_SIZE, &cache)) {
+ SPDK_ERRLOG("OCF cache '%s': not exist\n", cache_name);
+ rc = -ENXIO;
+ goto err_cache;
+ }
+
+ if (!ocf_cache_is_device_attached(cache)) {
+ SPDK_ERRLOG("OCF cache '%s': device already detached\n", cache_name);
+ rc = -EALREADY;
+ goto err_state;
+ }
+
+ mngt_ctx = calloc(1, sizeof(struct vbdev_ocf_mngt_ctx));
+ if (!mngt_ctx) {
+ SPDK_ERRLOG("OCF cache '%s': failed to allocate memory for cache detach context\n",
+ cache_name);
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ mngt_ctx->rpc_cb_fn = rpc_cb_fn;
+ mngt_ctx->rpc_cb_arg = rpc_cb_arg;
+
+ ocf_mngt_cache_lock(cache, _cache_detach_rpc_lock_cb, mngt_ctx);
+
+ return;
+
+err_alloc:
+err_state:
+ ocf_mngt_cache_put(cache);
+err_cache:
+err_module:
+ rpc_cb_fn(cache_name, rpc_cb_arg, rc);
+}
+
+static void
+_cache_attach_rpc_lock_cb(ocf_cache_t cache, void *cb_arg, int error)
+{
+ struct vbdev_ocf_mngt_ctx *mngt_ctx = cb_arg;
+ struct vbdev_ocf_cache *cache_ctx = ocf_cache_get_priv(cache);
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF cache '%s': attaching OCF cache device\n",
+ ocf_cache_get_name(cache));
+
+ if (error) {
+ SPDK_ERRLOG("OCF cache '%s': failed to acquire OCF cache lock (OCF error: %d)\n",
+ ocf_cache_get_name(cache), error);
+ mngt_ctx->rpc_cb_fn(ocf_cache_get_name(cache), mngt_ctx->rpc_cb_arg, error);
+ vbdev_ocf_cache_config_volume_destroy(cache);
+ vbdev_ocf_core_destroy_cache_channel(cache);
+ vbdev_ocf_cache_base_detach(cache);
+ free(mngt_ctx);
+ return;
+ }
+
+ ocf_mngt_cache_attach(cache, &cache_ctx->cache_att_cfg, _cache_attach_cb,
+ mngt_ctx);
+}
+
+/* RPC entry point. */
+void
+vbdev_ocf_cache_attach(const char *cache_name, const char *base_name, bool force,
+ vbdev_ocf_rpc_mngt_cb rpc_cb_fn, void *rpc_cb_arg)
+{
+ ocf_cache_t cache;
+ struct vbdev_ocf_cache *cache_ctx;
+ struct vbdev_ocf_mngt_ctx *mngt_ctx;
+ int rc;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF cache '%s': initiating device attach\n", cache_name);
+
+ if (!g_vbdev_ocf_module_is_running) {
+ SPDK_ERRLOG("OCF: failed to handle the call - module stopping\n");
+ rc = -EPERM;
+ goto err_module;
+ }
+
+ if (ocf_mngt_cache_get_by_name(vbdev_ocf_ctx, cache_name, OCF_CACHE_NAME_SIZE, &cache)) {
+ SPDK_ERRLOG("OCF cache '%s': not exist\n", cache_name);
+ rc = -ENXIO;
+ goto err_cache;
+ }
+
+ if (!ocf_cache_is_detached(cache)) {
+ SPDK_ERRLOG("OCF cache '%s': device already attached\n", cache_name);
+ rc = -EEXIST;
+ goto err_state;
+ }
+
+ cache_ctx = ocf_cache_get_priv(cache);
+ cache_ctx->no_load = force;
+
+ /* Check if base device to attach to this cache is already present. */
+ if ((rc = vbdev_ocf_cache_base_attach(cache, base_name))) {
+ if (rc == -ENODEV) {
+ /* If not, just leave it here and exit. It will be attached
+ * later at the examine stage when the device appears. */
+ SPDK_NOTICELOG("OCF cache '%s': attach deferred - waiting for base bdev '%s'\n",
+ cache_name, base_name);
+ rpc_cb_fn(cache_name, rpc_cb_arg, -ENODEV);
+ return;
+ }
+ SPDK_ERRLOG("OCF cache '%s': failed to attach base bdev '%s': %s\n",
+ cache_name, base_name, spdk_strerror(-rc));
+ goto err_base;
+ }
+
+ /* Update cache IO channel in all cores before attaching new cache device to OCF. */
+ if ((rc = vbdev_ocf_core_create_cache_channel(cache))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to create IO channel for new cache device: %s\n",
+ cache_name, spdk_strerror(-rc));
+ goto err_channel;
+ }
+
+ if ((rc = vbdev_ocf_cache_config_volume_create(cache))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to create config volume: %s\n",
+ cache_name, spdk_strerror(-rc));
+ goto err_volume;
+ }
+
+ mngt_ctx = calloc(1, sizeof(struct vbdev_ocf_mngt_ctx));
+ if (!mngt_ctx) {
+ SPDK_ERRLOG("OCF cache '%s': failed to allocate memory for cache attach context\n",
+ cache_name);
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ mngt_ctx->rpc_cb_fn = rpc_cb_fn;
+ mngt_ctx->rpc_cb_arg = rpc_cb_arg;
+
+ ocf_mngt_cache_put(cache);
+ ocf_mngt_cache_lock(cache, _cache_attach_rpc_lock_cb, mngt_ctx);
+
+ return;
+
+err_alloc:
+ vbdev_ocf_cache_config_volume_destroy(cache);
+err_volume:
+ vbdev_ocf_core_destroy_cache_channel(cache);
+err_channel:
+ vbdev_ocf_cache_base_detach(cache);
+err_base:
+err_state:
+ ocf_mngt_cache_put(cache);
+err_cache:
+err_module:
+ rpc_cb_fn(cache_name, rpc_cb_arg, rc);
+}
+
+static void
+_core_add_rpc_err_cb(void *cb_arg, int error)
+{
+ struct vbdev_ocf_mngt_ctx *mngt_ctx = cb_arg;
+ struct vbdev_ocf_core *core_ctx = mngt_ctx->u.core_ctx;
+ ocf_cache_t cache = mngt_ctx->cache;
+
+ if (error) {
+ SPDK_ERRLOG("OCF core '%s': failed to remove OCF core device (OCF error: %d)\n",
+ vbdev_ocf_core_get_name(core_ctx), error);
+ }
+
+ mngt_ctx->rpc_cb_fn(vbdev_ocf_core_get_name(core_ctx), mngt_ctx->rpc_cb_arg, error);
+ ocf_mngt_cache_unlock(cache);
+ ocf_mngt_cache_put(cache);
+ vbdev_ocf_core_base_detach(core_ctx);
+ vbdev_ocf_core_destroy(core_ctx);
+ free(mngt_ctx);
+}
+
+static void
+_core_add_rpc_add_cb(ocf_cache_t cache, ocf_core_t core, void *cb_arg, int error)
+{
+ struct vbdev_ocf_mngt_ctx *mngt_ctx = cb_arg;
+ struct vbdev_ocf_core *core_ctx = mngt_ctx->u.core_ctx;
+ int rc = 0;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s': finishing add of OCF core\n",
+ vbdev_ocf_core_get_name(core_ctx));
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s': finishing add\n", vbdev_ocf_core_get_name(core_ctx));
+
+ if (error) {
+ SPDK_ERRLOG("OCF core '%s': failed to add core to OCF cache '%s' (OCF error: %d)\n",
+ vbdev_ocf_core_get_name(core_ctx), ocf_cache_get_name(cache), error);
+ mngt_ctx->rpc_cb_fn(vbdev_ocf_core_get_name(core_ctx), mngt_ctx->rpc_cb_arg, error);
+ ocf_mngt_cache_unlock(cache);
+ ocf_mngt_cache_put(cache);
+ vbdev_ocf_core_base_detach(core_ctx);
+ vbdev_ocf_core_destroy(core_ctx);
+ free(mngt_ctx);
+ return;
+ }
+
+ ocf_core_set_priv(core, core_ctx);
+
+ if ((rc = vbdev_ocf_core_register(core))) {
+ SPDK_ERRLOG("OCF core '%s': failed to register vbdev: %s\n",
+ ocf_core_get_name(core), spdk_strerror(-rc));
+ ocf_mngt_cache_remove_core(core, _core_add_rpc_err_cb, mngt_ctx);
+ return;
+ }
+
+ SPDK_NOTICELOG("OCF core '%s': added to cache '%s'\n",
+ ocf_core_get_name(core), ocf_cache_get_name(cache));
+
+ mngt_ctx->rpc_cb_fn(ocf_core_get_name(core), mngt_ctx->rpc_cb_arg, rc);
+ ocf_mngt_cache_unlock(cache);
+ ocf_mngt_cache_put(cache);
+ free(mngt_ctx);
+}
+
+static void
+_core_add_rpc_lock_cb(ocf_cache_t cache, void *cb_arg, int error)
+{
+ struct vbdev_ocf_mngt_ctx *mngt_ctx = cb_arg;
+ struct vbdev_ocf_core *core_ctx = mngt_ctx->u.core_ctx;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s': initiating add of OCF core\n",
+ vbdev_ocf_core_get_name(core_ctx));
+
+ if (error) {
+ SPDK_ERRLOG("OCF core '%s': failed to acquire OCF cache lock (OCF error: %d)\n",
+ vbdev_ocf_core_get_name(core_ctx), error);
+ mngt_ctx->rpc_cb_fn(vbdev_ocf_core_get_name(core_ctx), mngt_ctx->rpc_cb_arg, error);
+ ocf_mngt_cache_put(cache);
+ vbdev_ocf_core_base_detach(core_ctx);
+ vbdev_ocf_core_destroy(core_ctx);
+ free(mngt_ctx);
+ return;
+ }
+
+ ocf_mngt_cache_add_core(cache, &core_ctx->core_cfg, _core_add_rpc_add_cb, mngt_ctx);
+}
+
+/* RPC entry point. */
+void
+vbdev_ocf_core_add(const char *core_name, const char *base_name, const char *cache_name,
+ vbdev_ocf_rpc_mngt_cb rpc_cb_fn, void *rpc_cb_arg)
+{
+ ocf_cache_t cache;
+ struct vbdev_ocf_cache *cache_ctx;
+ struct vbdev_ocf_core *core_ctx;
+ struct vbdev_ocf_mngt_ctx *mngt_ctx;
+ uint32_t cache_block_size, core_block_size;
+ int rc = 0;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s': initiating add\n", core_name);
+
+ if (!g_vbdev_ocf_module_is_running) {
+ SPDK_ERRLOG("OCF: failed to handle the call - module stopping\n");
+ rc = -EPERM;
+ goto err_module;
+ }
+
+ if (vbdev_ocf_bdev_exists(core_name)) {
+ SPDK_ERRLOG("OCF '%s': bdev already exists\n", core_name);
+ rc = -EEXIST;
+ goto err_exist;
+ }
+
+ if ((rc = vbdev_ocf_core_create(&core_ctx, core_name, cache_name))) {
+ SPDK_ERRLOG("OCF core '%s': failed to create core: %s\n",
+ core_name, spdk_strerror(-rc));
+ goto err_create;
+ }
+
+ /* First, check if base device for this core is already present. */
+ if ((rc = vbdev_ocf_core_base_attach(core_ctx, base_name))) {
+ if (rc == -ENODEV) {
+ /* If not, just put core context on the temporary core wait list and exit. */
+ /* It will be attached later at the examine stage when the device appears. */
+ SPDK_NOTICELOG("OCF core '%s': add deferred - waiting for base bdev '%s'\n",
+ core_name, base_name);
+ vbdev_ocf_core_waitlist_add(core_ctx);
+ rpc_cb_fn(core_name, rpc_cb_arg, -ENODEV);
+ return;
+ }
+ SPDK_ERRLOG("OCF core '%s': failed to attach base bdev '%s': %s\n",
+ core_name, base_name, spdk_strerror(-rc));
+ goto err_no_base;
+ }
+
+ if (!strcmp(spdk_bdev_get_product_name(spdk_bdev_get_by_name(base_name)), "OCF_disk")) {
+ SPDK_ERRLOG("OCF core '%s': base bdev '%s' is already an OCF core\n", core_name, base_name);
+ rc = -ENOTSUP;
+ goto err_ocf_base;
+ }
+
+ /* Second, check if OCF cache for this core is already started. */
+ if (ocf_mngt_cache_get_by_name(vbdev_ocf_ctx, cache_name, OCF_CACHE_NAME_SIZE, &cache)) {
+ /* If not, just put core context on the temporary core wait list and exit. */
+ /* Core will be automatically added later when this cache finally starts. */
+ SPDK_NOTICELOG("OCF core '%s': add deferred - waiting for OCF cache '%s'\n",
+ core_name, cache_name);
+ vbdev_ocf_core_waitlist_add(core_ctx);
+ rpc_cb_fn(core_name, rpc_cb_arg, -ENODEV);
+ return;
+ }
+
+ cache_ctx = ocf_cache_get_priv(cache);
+
+ /* And finally, check if OCF cache device is already attached.
+ * We need to have cache device attached to know if cache was loaded or attached
+ * and then set 'try_add' in core config accordingly. */
+ if (!ocf_cache_is_device_attached(cache) || !vbdev_ocf_cache_is_base_attached(cache)) {
+ /* If not, just put core context on the temporary core wait list and exit. */
+ /* Core will be automatically added later when device for this cache gets attached. */
+ SPDK_NOTICELOG("OCF core '%s': add deferred - waiting for OCF cache device '%s'\n",
+ core_name, cache_ctx->base.name);
+ ocf_mngt_cache_put(cache);
+ vbdev_ocf_core_waitlist_add(core_ctx);
+ rpc_cb_fn(core_name, rpc_cb_arg, -ENODEV);
+ return;
+ }
+
+ cache_block_size = spdk_bdev_get_block_size(cache_ctx->base.bdev);
+ core_block_size = spdk_bdev_get_block_size(core_ctx->base.bdev);
if (cache_block_size > core_block_size) {
- SPDK_ERRLOG("Cache bdev block size (%d) is bigger then core bdev block size (%d)\n",
- cache_block_size, core_block_size);
- vbdev_ocf_mngt_exit(vbdev, unregister_path_dirty, -EINVAL);
+ SPDK_ERRLOG("OCF core '%s': block size (%d) is less than cache '%s' block size (%d)\n",
+ core_name, core_block_size, cache_name, cache_block_size);
+ rc = -ENOTSUP;
+ goto err_bsize;
+ }
+
+ core_ctx->core_cfg.try_add = vbdev_ocf_core_is_loaded(core_name);
+
+ mngt_ctx = calloc(1, sizeof(struct vbdev_ocf_mngt_ctx));
+ if (!mngt_ctx) {
+ SPDK_ERRLOG("OCF core '%s': failed to allocate memory for core add context\n",
+ core_name);
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ mngt_ctx->rpc_cb_fn = rpc_cb_fn;
+ mngt_ctx->rpc_cb_arg = rpc_cb_arg;
+ mngt_ctx->cache = cache;
+ mngt_ctx->u.core_ctx = core_ctx;
+
+ ocf_mngt_cache_lock(cache, _core_add_rpc_lock_cb, mngt_ctx);
+
+ return;
+
+err_alloc:
+err_bsize:
+ ocf_mngt_cache_put(cache);
+err_ocf_base:
+ vbdev_ocf_core_base_detach(core_ctx);
+err_no_base:
+ vbdev_ocf_core_destroy(core_ctx);
+err_create:
+err_exist:
+err_module:
+ rpc_cb_fn(core_name, rpc_cb_arg, rc);
+}
+
+static void
+_core_remove_rpc_remove_cb(void *cb_arg, int error)
+{
+ struct vbdev_ocf_mngt_ctx *mngt_ctx = cb_arg;
+ struct vbdev_ocf_core *core_ctx = mngt_ctx->u.core_ctx;
+ ocf_cache_t cache = mngt_ctx->cache;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s': finishing remove of OCF core\n",
+ vbdev_ocf_core_get_name(core_ctx));
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s': finishing removal\n",
+ vbdev_ocf_core_get_name(core_ctx));
+
+ if (error) {
+ SPDK_ERRLOG("OCF core '%s': failed to remove OCF core device (OCF error: %d)\n",
+ vbdev_ocf_core_get_name(core_ctx), error);
+ } else {
+ SPDK_NOTICELOG("OCF core '%s': removed from cache '%s'\n",
+ vbdev_ocf_core_get_name(core_ctx), ocf_cache_get_name(cache));
+ }
+
+ mngt_ctx->rpc_cb_fn(vbdev_ocf_core_get_name(core_ctx), mngt_ctx->rpc_cb_arg, error);
+ ocf_mngt_cache_unlock(cache);
+ vbdev_ocf_core_destroy(core_ctx);
+ free(mngt_ctx);
+}
+
+static void
+_core_remove_rpc_lock_cb(ocf_cache_t cache, void *cb_arg, int error)
+{
+ struct vbdev_ocf_mngt_ctx *mngt_ctx = cb_arg;
+ ocf_core_t core = mngt_ctx->core;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s': initiating remove of OCF core\n",
+ ocf_core_get_name(core));
+
+ if (error) {
+ SPDK_ERRLOG("OCF vbdev '%s': failed to acquire OCF cache lock (OCF error: %d)\n",
+ ocf_core_get_name(core), error);
+ }
+
+ /* Do not check core's dirtiness as it was already detached during destruct phase. */
+ ocf_mngt_cache_remove_core(core, _core_remove_rpc_remove_cb, mngt_ctx);
+}
+
+static void
+_core_remove_rpc_unregister_cb(void *cb_arg, int error)
+{
+ struct vbdev_ocf_mngt_ctx *mngt_ctx = cb_arg;
+ ocf_core_t core = mngt_ctx->core;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s': finishing unregister of OCF vbdev\n",
+ ocf_core_get_name(core));
+
+ if (error) {
+ SPDK_ERRLOG("OCF core '%s': failed to unregister OCF vbdev during core removal: %s\n",
+ ocf_core_get_name(core), spdk_strerror(-error));
+ }
+
+ ocf_mngt_cache_lock(ocf_core_get_cache(core), _core_remove_rpc_lock_cb, mngt_ctx);
+}
+
+/* RPC entry point. */
+void
+vbdev_ocf_core_remove(const char *core_name, vbdev_ocf_rpc_mngt_cb rpc_cb_fn, void *rpc_cb_arg)
+{
+ ocf_cache_t cache;
+ ocf_core_t core;
+ struct vbdev_ocf_core *core_ctx;
+ struct vbdev_ocf_mngt_ctx *mngt_ctx;
+ int rc;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s': initiating removal\n", core_name);
+
+ if (!g_vbdev_ocf_module_is_running) {
+ SPDK_ERRLOG("OCF: failed to handle the call - module stopping\n");
+ rc = -EPERM;
+ goto err_module;
+ }
+
+ /* If core was not added yet due to lack of base or cache device,
+ * just free its structs (and detach its base if exists) and exit. */
+ if ((core_ctx = vbdev_ocf_core_waitlist_get_by_name(core_name))) {
+ vbdev_ocf_core_waitlist_remove(core_ctx);
+ if (vbdev_ocf_core_is_base_attached(core_ctx)) {
+ vbdev_ocf_core_base_detach(core_ctx);
+ }
+ vbdev_ocf_core_destroy(core_ctx);
+ rpc_cb_fn(core_name, rpc_cb_arg, 0);
return;
}
- existing = get_other_cache_instance(vbdev);
- if (existing) {
- SPDK_NOTICELOG("OCF bdev %s connects to existing cache device %s\n",
- vbdev->name, vbdev->cache.name);
- vbdev->ocf_cache = existing;
- ocf_mngt_cache_get(vbdev->ocf_cache);
- vbdev->cache_ctx = ocf_cache_get_priv(existing);
- vbdev_ocf_cache_ctx_get(vbdev->cache_ctx);
- vbdev_ocf_mngt_continue(vbdev, 0);
- return;
- }
+ mngt_ctx = calloc(1, sizeof(struct vbdev_ocf_mngt_ctx));
+ if (!mngt_ctx) {
+ SPDK_ERRLOG("OCF core '%s': failed to allocate memory for core remove context\n",
+ core_name);
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ strlcpy(mngt_ctx->bdev_name, core_name, OCF_CORE_NAME_SIZE);
+
+ if ((rc = vbdev_ocf_bdev_resolve(mngt_ctx))) {
+ SPDK_ERRLOG("OCF core '%s': failed to find core of that name: %s\n",
+ core_name, spdk_strerror(-rc));
+ goto err_resolve;
+ }
+ core = mngt_ctx->core;
+
+ /* Check if given core exists and have context assigned.
+ * If there is no context, it means that this core was added
+ * from metadata during cache load and it's just an empty shell. */
+ if (!core || !(core_ctx = ocf_core_get_priv(core))) {
+ SPDK_ERRLOG("OCF core '%s': not exist\n", core_name);
+ rc = -ENXIO;
+ goto err_exist;
+ }
+ cache = ocf_core_get_cache(core);
+
+ mngt_ctx->rpc_cb_fn = rpc_cb_fn;
+ mngt_ctx->rpc_cb_arg = rpc_cb_arg;
+ mngt_ctx->cache = cache;
+ mngt_ctx->u.core_ctx = core_ctx;
+
+ if (!vbdev_ocf_core_is_base_attached(core_ctx)) {
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s': removing detached (no unregister)\n", core_name);
+
+ ocf_mngt_cache_lock(cache, _core_remove_rpc_lock_cb, mngt_ctx);
+ return;
+ }
+
+ /* Unregister (and detach) core first before removing it
+ * to send hotremove signal to all opened descriptors. */
+ if ((rc = vbdev_ocf_core_unregister(core_ctx, _core_remove_rpc_unregister_cb, mngt_ctx))) {
+ SPDK_ERRLOG("OCF core '%s': failed to start unregistering OCF vbdev during core removal: %s\n",
+ core_name, spdk_strerror(-rc));
+ goto err_unregister;
+ }
+
+ return;
+
+err_unregister:
+err_exist:
+err_resolve:
+ free(mngt_ctx);
+err_alloc:
+err_module:
+ rpc_cb_fn(core_name, rpc_cb_arg, rc);
+}
+
+static void
+_cache_save_cb(ocf_cache_t cache, void *cb_arg, int error)
+{
+ struct vbdev_ocf_mngt_ctx *mngt_ctx = cb_arg;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF cache '%s': saving cache state\n", ocf_cache_get_name(cache));
+
+ ocf_mngt_cache_unlock(cache);
+
+ if (error) {
+ SPDK_WARNLOG("OCF cache '%s': failed to save cache state (OCF error: %d)\n",
+ ocf_cache_get_name(cache), error);
+ }
+
+ /* Ignore state save error caused by not attached cache volume. */
+ mngt_ctx->rpc_cb_fn(ocf_cache_get_name(cache), mngt_ctx->rpc_cb_arg,
+ error == -OCF_ERR_CACHE_DETACHED ? 0 : error);
+ ocf_mngt_cache_put(cache);
+ free(mngt_ctx);
+}
+
+static void
+_cache_mode_lock_cb(ocf_cache_t cache, void *cb_arg, int error)
+{
+ struct vbdev_ocf_mngt_ctx *mngt_ctx = cb_arg;
+ ocf_cache_mode_t cache_mode = mngt_ctx->u.cache_mode;
+ int rc;
+
+ if ((rc = error)) {
+ SPDK_ERRLOG("OCF cache '%s': failed to acquire OCF cache lock (OCF error: %d)\n",
+ ocf_cache_get_name(cache), error);
+ goto err;
+ }
+
+ if ((rc = ocf_mngt_cache_set_mode(cache, cache_mode))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to change cache mode to '%s' (OCF error: %d)\n",
+ ocf_cache_get_name(cache),
+ vbdev_ocf_cachemode_get_name(cache_mode), rc);
+ ocf_mngt_cache_unlock(cache);
+ goto err;
+ }
+
+ SPDK_NOTICELOG("OCF cache '%s': cache mode set to '%s'\n",
+ ocf_cache_get_name(cache), vbdev_ocf_cachemode_get_name(cache_mode));
+
+ ocf_mngt_cache_save(cache, _cache_save_cb, mngt_ctx);
+
+ return;
+
+err:
+ mngt_ctx->rpc_cb_fn(ocf_cache_get_name(cache), mngt_ctx->rpc_cb_arg, rc);
+ ocf_mngt_cache_put(cache);
+ free(mngt_ctx);
+}
+
+/* RPC entry point. */
+void
+vbdev_ocf_set_cachemode(const char *cache_name, const char *cache_mode,
+ vbdev_ocf_rpc_mngt_cb rpc_cb_fn, void *rpc_cb_arg)
+{
+ ocf_cache_t cache;
+ struct vbdev_ocf_mngt_ctx *mngt_ctx;
+ int rc = 0;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF cache '%s': setting new cache mode '%s'\n", cache_name, cache_mode);
+
+ if (!g_vbdev_ocf_module_is_running) {
+ SPDK_ERRLOG("OCF: failed to handle the call - module stopping\n");
+ rc = -EPERM;
+ goto err_module;
+ }
+
+ if (ocf_mngt_cache_get_by_name(vbdev_ocf_ctx, cache_name, OCF_CACHE_NAME_SIZE, &cache)) {
+ SPDK_ERRLOG("OCF cache '%s': not exist\n", cache_name);
+ rc = -ENXIO;
+ goto err_cache;
+ }
+
+ mngt_ctx = calloc(1, sizeof(struct vbdev_ocf_mngt_ctx));
+ if (!mngt_ctx) {
+ SPDK_ERRLOG("OCF cache '%s': failed to allocate memory for cache mode change context\n",
+ cache_name);
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ mngt_ctx->rpc_cb_fn = rpc_cb_fn;
+ mngt_ctx->rpc_cb_arg = rpc_cb_arg;
+ mngt_ctx->u.cache_mode = vbdev_ocf_cachemode_get_by_name(cache_mode);
+
+ ocf_mngt_cache_lock(cache, _cache_mode_lock_cb, mngt_ctx);
+
+ return;
+
+err_alloc:
+ ocf_mngt_cache_put(cache);
+err_cache:
+err_module:
+ rpc_cb_fn(cache_name, rpc_cb_arg, rc);
+}
+
+static void
+_promotion_lock_cb(ocf_cache_t cache, void *cb_arg, int error)
+{
+ struct vbdev_ocf_mngt_ctx *mngt_ctx = cb_arg;
+ int rc;
+
+ if ((rc = error)) {
+ SPDK_ERRLOG("OCF cache '%s': failed to acquire OCF cache lock (OCF error: %d)\n",
+ ocf_cache_get_name(cache), error);
+ goto err_lock;
+ }
+
+ if (mngt_ctx->u.promotion.policy >= ocf_promotion_always &&
+ mngt_ctx->u.promotion.policy < ocf_promotion_max) {
+ if ((rc = ocf_mngt_cache_promotion_set_policy(cache, mngt_ctx->u.promotion.policy))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to set promotion policy (OCF error: %d)\n",
+ ocf_cache_get_name(cache), rc);
+ goto err_param;
+ }
+ }
+
+ if (mngt_ctx->u.promotion.nhit_insertion_threshold >= 0) {
+ if ((rc = ocf_mngt_cache_promotion_set_param(cache, ocf_promotion_nhit,
+ ocf_nhit_insertion_threshold,
+ mngt_ctx->u.promotion.nhit_insertion_threshold))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to set promotion nhit_insertion_threshold param (OCF error: %d)\n",
+ ocf_cache_get_name(cache), rc);
+ goto err_param;
+ }
+ }
+
+ if (mngt_ctx->u.promotion.nhit_trigger_threshold >= 0) {
+ if ((rc = ocf_mngt_cache_promotion_set_param(cache, ocf_promotion_nhit, ocf_nhit_trigger_threshold,
+ mngt_ctx->u.promotion.nhit_trigger_threshold))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to set promotion nhit_trigger_threshold param (OCF error: %d)\n",
+ ocf_cache_get_name(cache), rc);
+ goto err_param;
+ }
+ }
+
+ SPDK_NOTICELOG("OCF cache '%s': promotion params set\n", ocf_cache_get_name(cache));
+
+ ocf_mngt_cache_save(cache, _cache_save_cb, mngt_ctx);
+
+ return;
+
+err_param:
+ ocf_mngt_cache_unlock(cache);
+err_lock:
+ mngt_ctx->rpc_cb_fn(ocf_cache_get_name(cache), mngt_ctx->rpc_cb_arg, rc);
+ ocf_mngt_cache_put(cache);
+ free(mngt_ctx);
+}
+
+/* RPC entry point. */
+void
+vbdev_ocf_set_promotion(const char *cache_name, const char *policy,
+ int32_t nhit_insertion_threshold, int32_t nhit_trigger_threshold,
+ vbdev_ocf_rpc_mngt_cb rpc_cb_fn, void *rpc_cb_arg)
+{
+ ocf_cache_t cache;
+ struct vbdev_ocf_mngt_ctx *mngt_ctx;
+ int rc = 0;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF cache '%s': setting promotion params\n", cache_name);
+
+ if (!g_vbdev_ocf_module_is_running) {
+ SPDK_ERRLOG("OCF: failed to handle the call - module stopping\n");
+ rc = -EPERM;
+ goto err_module;
+ }
+
+ if (ocf_mngt_cache_get_by_name(vbdev_ocf_ctx, cache_name, OCF_CACHE_NAME_SIZE, &cache)) {
+ SPDK_ERRLOG("OCF cache '%s': not exist\n", cache_name);
+ rc = -ENXIO;
+ goto err_cache;
+ }
+
+ mngt_ctx = calloc(1, sizeof(struct vbdev_ocf_mngt_ctx));
+ if (!mngt_ctx) {
+ SPDK_ERRLOG("OCF cache '%s': failed to allocate memory for promotion set context\n",
+ cache_name);
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ mngt_ctx->rpc_cb_fn = rpc_cb_fn;
+ mngt_ctx->rpc_cb_arg = rpc_cb_arg;
+ mngt_ctx->u.promotion.policy = vbdev_ocf_promotion_policy_get_by_name(policy);
+ mngt_ctx->u.promotion.nhit_insertion_threshold = nhit_insertion_threshold;
+ mngt_ctx->u.promotion.nhit_trigger_threshold = nhit_trigger_threshold;
+
+ ocf_mngt_cache_lock(cache, _promotion_lock_cb, mngt_ctx);
+
+ return;
+
+err_alloc:
+ ocf_mngt_cache_put(cache);
+err_cache:
+err_module:
+ rpc_cb_fn(cache_name, rpc_cb_arg, rc);
+}
+
+static void
+_cleaning_policy_cb(void *cb_arg, int error)
+{
+ struct vbdev_ocf_mngt_ctx *mngt_ctx = cb_arg;
+ ocf_cache_t cache = mngt_ctx->cache;
+
+ if (error) {
+ SPDK_ERRLOG("OCF cache '%s': failed to set cleaning policy (OCF error: %d)\n",
+ ocf_cache_get_name(cache), error);
+ mngt_ctx->rpc_cb_fn(ocf_cache_get_name(cache), mngt_ctx->rpc_cb_arg, error);
+ ocf_mngt_cache_unlock(cache);
+ ocf_mngt_cache_put(cache);
+ free(mngt_ctx);
+ return;
+ }
+
+ SPDK_NOTICELOG("OCF cache '%s': cleaning params set\n", ocf_cache_get_name(cache));
+
+ ocf_mngt_cache_save(cache, _cache_save_cb, mngt_ctx);
+}
+
+static void
+_cleaning_lock_cb(ocf_cache_t cache, void *cb_arg, int error)
+{
+ struct vbdev_ocf_mngt_ctx *mngt_ctx = cb_arg;
+ int rc;
+
+ if ((rc = error)) {
+ SPDK_ERRLOG("OCF cache '%s': failed to acquire OCF cache lock (OCF error: %d)\n",
+ ocf_cache_get_name(cache), error);
+ goto err_lock;
+ }
+
+ if (mngt_ctx->u.cleaning.acp_wake_up_time >= 0) {
+ if ((rc = ocf_mngt_cache_cleaning_set_param(cache, ocf_cleaning_acp, ocf_acp_wake_up_time,
+ mngt_ctx->u.cleaning.acp_wake_up_time))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to set cleaning acp_wake_up_time param (OCF error: %d)\n",
+ ocf_cache_get_name(cache), rc);
+ goto err_param;
+ }
+ }
+
+ if (mngt_ctx->u.cleaning.acp_flush_max_buffers >= 0) {
+ if ((rc = ocf_mngt_cache_cleaning_set_param(cache, ocf_cleaning_acp, ocf_acp_flush_max_buffers,
+ mngt_ctx->u.cleaning.acp_flush_max_buffers))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to set cleaning acp_flush_max_buffers param (OCF error: %d)\n",
+ ocf_cache_get_name(cache), rc);
+ goto err_param;
+ }
+ }
+
+ if (mngt_ctx->u.cleaning.alru_wake_up_time >= 0) {
+ if ((rc = ocf_mngt_cache_cleaning_set_param(cache, ocf_cleaning_alru, ocf_alru_wake_up_time,
+ mngt_ctx->u.cleaning.alru_wake_up_time))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to set cleaning alru_wake_up_time param (OCF error: %d)\n",
+ ocf_cache_get_name(cache), rc);
+ goto err_param;
+ }
+ }
+
+ if (mngt_ctx->u.cleaning.alru_flush_max_buffers >= 0) {
+ if ((rc = ocf_mngt_cache_cleaning_set_param(cache, ocf_cleaning_alru, ocf_alru_flush_max_buffers,
+ mngt_ctx->u.cleaning.alru_flush_max_buffers))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to set cleaning alru_flush_max_buffers param (OCF error: %d)\n",
+ ocf_cache_get_name(cache), rc);
+ goto err_param;
+ }
+ }
+
+ if (mngt_ctx->u.cleaning.alru_staleness_time >= 0) {
+ if ((rc = ocf_mngt_cache_cleaning_set_param(cache, ocf_cleaning_alru, ocf_alru_stale_buffer_time,
+ mngt_ctx->u.cleaning.alru_staleness_time))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to set cleaning alru_staleness_time param (OCF error: %d)\n",
+ ocf_cache_get_name(cache), rc);
+ goto err_param;
+ }
+ }
+
+ if (mngt_ctx->u.cleaning.alru_activity_threshold >= 0) {
+ if ((rc = ocf_mngt_cache_cleaning_set_param(cache, ocf_cleaning_alru, ocf_alru_activity_threshold,
+ mngt_ctx->u.cleaning.alru_activity_threshold))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to set cleaning alru_activity_threshold param (OCF error: %d)\n",
+ ocf_cache_get_name(cache), rc);
+ goto err_param;
+ }
+ }
+
+ if (mngt_ctx->u.cleaning.alru_dirty_ratio_threshold >= 0) {
+ if ((rc = ocf_mngt_cache_cleaning_set_param(cache, ocf_cleaning_alru,
+ ocf_alru_dirty_ratio_threshold, mngt_ctx->u.cleaning.alru_dirty_ratio_threshold))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to set cleaning alru_dirty_ratio_threshold param (OCF error: %d)\n",
+ ocf_cache_get_name(cache), rc);
+ goto err_param;
+ }
+ }
+
+ if (mngt_ctx->u.cleaning.alru_dirty_ratio_inertia >= 0) {
+ /* Maximum value of dirty ratio inertia is the size of unsigned int,
+ * so check its value to prevent exceeding before even sending it to OCF. */
+ if (mngt_ctx->u.cleaning.alru_dirty_ratio_inertia * MiB > OCF_ALRU_MAX_DIRTY_RATIO_INERTIA) {
+ SPDK_ERRLOG("OCF cache '%s': alru_dirty_ratio_inertia param out of range\n",
+ ocf_cache_get_name(cache));
+ rc = -EINVAL;
+ goto err_param;
+ }
+ if ((rc = ocf_mngt_cache_cleaning_set_param(cache, ocf_cleaning_alru, ocf_alru_dirty_ratio_inertia,
+ mngt_ctx->u.cleaning.alru_dirty_ratio_inertia * MiB))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to set cleaning alru_dirty_ratio_inertia param (OCF error: %d)\n",
+ ocf_cache_get_name(cache), rc);
+ goto err_param;
+ }
+ }
+
+ if (mngt_ctx->u.cleaning.policy >= ocf_cleaning_nop &&
+ mngt_ctx->u.cleaning.policy < ocf_cleaning_max) {
+ ocf_mngt_cache_cleaning_set_policy(cache, mngt_ctx->u.cleaning.policy,
+ _cleaning_policy_cb, mngt_ctx);
+ } else {
+ SPDK_NOTICELOG("OCF cache '%s': cleaning params set\n", ocf_cache_get_name(cache));
+
+ ocf_mngt_cache_save(cache, _cache_save_cb, mngt_ctx);
+ }
+
+ return;
+
+err_param:
+ ocf_mngt_cache_unlock(cache);
+err_lock:
+ mngt_ctx->rpc_cb_fn(ocf_cache_get_name(cache), mngt_ctx->rpc_cb_arg, rc);
+ ocf_mngt_cache_put(cache);
+ free(mngt_ctx);
+}
+
+/* RPC entry point. */
+void
+vbdev_ocf_set_cleaning(const char *cache_name, const char *policy, int32_t acp_wake_up_time,
+ int32_t acp_flush_max_buffers, int32_t alru_wake_up_time,
+ int32_t alru_flush_max_buffers, int32_t alru_staleness_time,
+ int32_t alru_activity_threshold, int32_t alru_dirty_ratio_threshold,
+ int32_t alru_dirty_ratio_inertia, vbdev_ocf_rpc_mngt_cb rpc_cb_fn, void *rpc_cb_arg)
+{
+ ocf_cache_t cache;
+ struct vbdev_ocf_mngt_ctx *mngt_ctx;
+ int rc = 0;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF cache '%s': setting cleaning params\n", cache_name);
+
+ if (!g_vbdev_ocf_module_is_running) {
+ SPDK_ERRLOG("OCF: failed to handle the call - module stopping\n");
+ rc = -EPERM;
+ goto err_module;
+ }
+
+ if (ocf_mngt_cache_get_by_name(vbdev_ocf_ctx, cache_name, OCF_CACHE_NAME_SIZE, &cache)) {
+ SPDK_ERRLOG("OCF cache '%s': not exist\n", cache_name);
+ rc = -ENXIO;
+ goto err_cache;
+ }
+
+ mngt_ctx = calloc(1, sizeof(struct vbdev_ocf_mngt_ctx));
+ if (!mngt_ctx) {
+ SPDK_ERRLOG("OCF cache '%s': failed to allocate memory for cleaning set context\n",
+ cache_name);
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ mngt_ctx->rpc_cb_fn = rpc_cb_fn;
+ mngt_ctx->rpc_cb_arg = rpc_cb_arg;
+ mngt_ctx->cache = cache;
+ mngt_ctx->u.cleaning.policy = vbdev_ocf_cleaning_policy_get_by_name(policy);
+ mngt_ctx->u.cleaning.acp_wake_up_time = acp_wake_up_time;
+ mngt_ctx->u.cleaning.acp_flush_max_buffers = acp_flush_max_buffers;
+ mngt_ctx->u.cleaning.alru_wake_up_time = alru_wake_up_time;
+ mngt_ctx->u.cleaning.alru_flush_max_buffers = alru_flush_max_buffers;
+ mngt_ctx->u.cleaning.alru_staleness_time = alru_staleness_time;
+ mngt_ctx->u.cleaning.alru_activity_threshold = alru_activity_threshold;
+ mngt_ctx->u.cleaning.alru_dirty_ratio_threshold = alru_dirty_ratio_threshold;
+ mngt_ctx->u.cleaning.alru_dirty_ratio_inertia = alru_dirty_ratio_inertia;
+
+ ocf_mngt_cache_lock(cache, _cleaning_lock_cb, mngt_ctx);
+
+ return;
+
+err_alloc:
+ ocf_mngt_cache_put(cache);
+err_cache:
+err_module:
+ rpc_cb_fn(cache_name, rpc_cb_arg, rc);
+}
+
+static void
+_seqcutoff_lock_cb(ocf_cache_t cache, void *cb_arg, int error)
+{
+ struct vbdev_ocf_mngt_ctx *mngt_ctx = cb_arg;
+ ocf_core_t core = mngt_ctx->core;
+ int rc;
+
+ if ((rc = error)) {
+ SPDK_ERRLOG("OCF cache '%s': failed to acquire OCF cache lock (OCF error: %d)\n",
+ ocf_cache_get_name(cache), error);
+ goto err_lock;
+ }
+
+ if (core) {
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF '%s': setting sequential cut-off on core device\n",
+ ocf_core_get_name(core));
+
+ /* Once again check if selected core still exists in cache as it may have
+ * been hot removed between resolving its name and taking this lock. */
+ if ((rc = ocf_core_get_by_name(cache, mngt_ctx->bdev_name,
+ OCF_CORE_NAME_SIZE, &core))) {
+ SPDK_ERRLOG("OCF core '%s': already removed from cache (OCF error: %d)\n",
+ mngt_ctx->bdev_name, rc);
+ goto err_param;
+ }
+
+ if (mngt_ctx->u.seqcutoff.policy >= ocf_seq_cutoff_policy_always &&
+ mngt_ctx->u.seqcutoff.policy < ocf_seq_cutoff_policy_max) {
+ if ((rc = ocf_mngt_core_set_seq_cutoff_policy(core, mngt_ctx->u.seqcutoff.policy))) {
+ SPDK_ERRLOG("OCF core '%s': failed to set sequential cut-off policy (OCF error: %d)\n",
+ ocf_core_get_name(core), rc);
+ goto err_param;
+ }
+ }
+
+ if (mngt_ctx->u.seqcutoff.threshold >= 0) {
+ if ((rc = ocf_mngt_core_set_seq_cutoff_threshold(core, mngt_ctx->u.seqcutoff.threshold * KiB))) {
+ SPDK_ERRLOG("OCF core '%s': failed to set sequential cut-off threshold (OCF error: %d)\n",
+ ocf_core_get_name(core), rc);
+ goto err_param;
+ }
+ }
+
+ if (mngt_ctx->u.seqcutoff.promotion_count >= 0) {
+ if ((rc = ocf_mngt_core_set_seq_cutoff_promotion_count(core,
+ mngt_ctx->u.seqcutoff.promotion_count))) {
+ SPDK_ERRLOG("OCF core '%s': failed to set sequential cut-off promotion_count (OCF error: %d)\n",
+ ocf_core_get_name(core), rc);
+ goto err_param;
+ }
+ }
+
+ if (mngt_ctx->u.seqcutoff.promote_on_threshold >= 0) {
+ if ((rc = ocf_mngt_core_set_seq_cutoff_promote_on_threshold(core,
+ mngt_ctx->u.seqcutoff.promote_on_threshold))) {
+ SPDK_ERRLOG("OCF core '%s': failed to set sequential cut-off promote_on_threshold (OCF error: %d)\n",
+ ocf_core_get_name(core), rc);
+ goto err_param;
+ }
+ }
+
+ SPDK_NOTICELOG("OCF core '%s': sequential cut-off params set\n", ocf_core_get_name(core));
+ } else {
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF '%s': setting sequential cut-off on all cores in cache device\n",
+ ocf_cache_get_name(cache));
+
+ if (mngt_ctx->u.seqcutoff.policy >= ocf_seq_cutoff_policy_always &&
+ mngt_ctx->u.seqcutoff.policy < ocf_seq_cutoff_policy_max) {
+ if ((rc = ocf_mngt_core_set_seq_cutoff_policy_all(cache, mngt_ctx->u.seqcutoff.policy))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to set sequential cut-off policy (OCF error: %d)\n",
+ ocf_cache_get_name(cache), rc);
+ goto err_param;
+ }
+ }
+
+ if (mngt_ctx->u.seqcutoff.threshold >= 0) {
+ if ((rc = ocf_mngt_core_set_seq_cutoff_threshold_all(cache,
+ mngt_ctx->u.seqcutoff.threshold * KiB))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to set sequential cut-off threshold (OCF error: %d)\n",
+ ocf_cache_get_name(cache), rc);
+ goto err_param;
+ }
+ }
+
+ if (mngt_ctx->u.seqcutoff.promotion_count >= 0) {
+ if ((rc = ocf_mngt_core_set_seq_cutoff_promotion_count_all(cache,
+ mngt_ctx->u.seqcutoff.promotion_count))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to set sequential cut-off promotion_count (OCF error: %d)\n",
+ ocf_cache_get_name(cache), rc);
+ goto err_param;
+ }
+ }
+
+ if (mngt_ctx->u.seqcutoff.promote_on_threshold >= 0) {
+ if ((rc = ocf_mngt_core_set_seq_cutoff_promote_on_threshold_all(cache,
+ mngt_ctx->u.seqcutoff.promote_on_threshold))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to set sequential cut-off promote_on_threshold (OCF error: %d)\n",
+ ocf_cache_get_name(cache), rc);
+ goto err_param;
+ }
+ }
+
+ SPDK_NOTICELOG("OCF cache '%s': sequential cut-off params set\n", ocf_cache_get_name(cache));
+ }
+
+ /* For compatibility with global _cache_save_cb(). */
+ ocf_mngt_cache_get(cache);
+
+ ocf_mngt_cache_save(cache, _cache_save_cb, mngt_ctx);
+
+ return;
+
+err_param:
+ ocf_mngt_cache_unlock(cache);
+err_lock:
+ mngt_ctx->rpc_cb_fn(mngt_ctx->bdev_name, mngt_ctx->rpc_cb_arg, rc);
+ free(mngt_ctx);
+}
+
+/* RPC entry point. */
+void
+vbdev_ocf_set_seqcutoff(const char *bdev_name, const char *policy, int32_t threshold,
+ int32_t promotion_count, int32_t promote_on_threshold,
+ vbdev_ocf_rpc_mngt_cb rpc_cb_fn, void *rpc_cb_arg)
+{
+ struct vbdev_ocf_mngt_ctx *mngt_ctx;
+ int rc;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF '%s': setting sequential cut-off params\n", bdev_name);
- vbdev->cache_ctx = calloc(1, sizeof(struct vbdev_ocf_cache_ctx));
- if (vbdev->cache_ctx == NULL) {
- vbdev_ocf_mngt_exit(vbdev, unregister_path_dirty, -ENOMEM);
+ if (!g_vbdev_ocf_module_is_running) {
+ SPDK_ERRLOG("OCF: failed to handle the call - module stopping\n");
+ rpc_cb_fn(bdev_name, rpc_cb_arg, -EPERM);
return;
}
- vbdev_ocf_cache_ctx_get(vbdev->cache_ctx);
-
- rc = ocf_mngt_cache_start(vbdev_ocf_ctx, &vbdev->ocf_cache, &vbdev->cfg.cache, NULL);
- if (rc) {
- SPDK_ERRLOG("Could not start cache %s: %d\n", vbdev->name, rc);
- vbdev_ocf_mngt_exit(vbdev, unregister_path_dirty, rc);
+ mngt_ctx = calloc(1, sizeof(struct vbdev_ocf_mngt_ctx));
+ if (!mngt_ctx) {
+ SPDK_ERRLOG("OCF '%s': failed to allocate memory for sequential cut-off set context\n",
+ bdev_name);
+ rpc_cb_fn(bdev_name, rpc_cb_arg, -ENOMEM);
return;
}
- ocf_mngt_cache_get(vbdev->ocf_cache);
-
- ocf_cache_set_priv(vbdev->ocf_cache, vbdev->cache_ctx);
-
- rc = create_management_queue(vbdev);
- if (rc) {
- SPDK_ERRLOG("Unable to create mngt_queue: %d\n", rc);
- vbdev_ocf_mngt_exit(vbdev, unregister_path_dirty, rc);
+ mngt_ctx->rpc_cb_fn = rpc_cb_fn;
+ mngt_ctx->rpc_cb_arg = rpc_cb_arg;
+ strlcpy(mngt_ctx->bdev_name, bdev_name, VBDEV_OCF_BDEV_NAME_SIZE);
+ /* Cache or core will be set using vbdev_ocf_bdev_resolve(). */
+ mngt_ctx->cache = NULL;
+ mngt_ctx->core = NULL;
+ mngt_ctx->u.seqcutoff.policy = vbdev_ocf_seqcutoff_policy_get_by_name(policy);
+ mngt_ctx->u.seqcutoff.threshold = threshold;
+ mngt_ctx->u.seqcutoff.promotion_count = promotion_count;
+ mngt_ctx->u.seqcutoff.promote_on_threshold = promote_on_threshold;
+
+ if ((rc = vbdev_ocf_bdev_resolve(mngt_ctx))) {
+ SPDK_ERRLOG("OCF '%s': failed to find cache or core of that name: %s\n",
+ bdev_name, spdk_strerror(-rc));
+ rpc_cb_fn(bdev_name, rpc_cb_arg, rc);
+ free(mngt_ctx);
return;
}
- if (vbdev->cfg.loadq) {
- ocf_mngt_cache_load(vbdev->ocf_cache, &vbdev->cfg.attach, start_cache_cmpl, vbdev);
- } else {
- ocf_mngt_cache_attach(vbdev->ocf_cache, &vbdev->cfg.attach, start_cache_cmpl, vbdev);
- }
+ ocf_mngt_cache_lock(mngt_ctx->cache ? : ocf_core_get_cache(mngt_ctx->core),
+ _seqcutoff_lock_cb, mngt_ctx);
}
-/* Procedures called during register operation */
-vbdev_ocf_mngt_fn register_path[] = {
- start_cache,
- add_core,
- finish_register,
- NULL
-};
-
-/* Start cache instance and register OCF bdev */
static void
-register_vbdev(struct vbdev_ocf *vbdev, vbdev_ocf_mngt_callback cb, void *cb_arg)
+_flush_cache_cb(ocf_cache_t cache, void *cb_arg, int error)
{
- int rc;
+ struct vbdev_ocf_cache *cache_ctx = ocf_cache_get_priv(cache);
- if (!(vbdev->core.attached && vbdev->cache.attached) || vbdev->state.started) {
- cb(-EPERM, vbdev, cb_arg);
- return;
- }
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF cache '%s': finishing flush operation\n",
+ ocf_cache_get_name(cache));
- vbdev->state.starting = true;
- rc = vbdev_ocf_mngt_start(vbdev, register_path, cb, cb_arg);
- if (rc) {
- cb(rc, vbdev, cb_arg);
- }
+ SPDK_NOTICELOG("OCF cache '%s': flushed\n", ocf_cache_get_name(cache));
+
+ ocf_mngt_cache_read_unlock(cache);
+
+ cache_ctx->flush.error = error;
+ cache_ctx->flush.in_progress = false;
}
-/* Init OCF configuration options
- * for core and cache devices */
-static int
-init_vbdev_config(struct vbdev_ocf *vbdev)
+static void
+_flush_core_cb(ocf_core_t core, void *cb_arg, int error)
{
- struct vbdev_ocf_config *cfg = &vbdev->cfg;
- struct ocf_volume_uuid uuid;
- ocf_volume_type_t type;
- int ret;
+ struct vbdev_ocf_core *core_ctx = ocf_core_get_priv(core);
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s': finishing flush operation\n",
+ ocf_core_get_name(core));
- /* Initialize OCF defaults first */
- ocf_mngt_cache_attach_config_set_default(&cfg->attach);
- ocf_mngt_cache_config_set_default(&cfg->cache);
- ocf_mngt_core_config_set_default(&cfg->core);
+ SPDK_NOTICELOG("OCF core '%s': flushed\n", ocf_core_get_name(core));
- ret = snprintf(cfg->cache.name, sizeof(cfg->cache.name), "%s", vbdev->name);
- if (ret < 0 || (size_t) ret >= sizeof(cfg->cache.name)) {
- return -EINVAL;
- }
- ret = snprintf(cfg->core.name, sizeof(cfg->core.name), "%s", vbdev->core.name);
- if (ret < 0 || (size_t) ret >= sizeof(cfg->core.name)) {
- return -EINVAL;
- }
+ ocf_mngt_cache_read_unlock(ocf_core_get_cache(core));
+
+ core_ctx->flush.error = error;
+ core_ctx->flush.in_progress = false;
+}
+
+static void
+_flush_lock_cb(ocf_cache_t cache, void *cb_arg, int error)
+{
+ struct vbdev_ocf_mngt_ctx *mngt_ctx = cb_arg;
+ ocf_core_t core = mngt_ctx->core;
+ struct vbdev_ocf_cache *cache_ctx;
+ struct vbdev_ocf_core *core_ctx;
+ int rc;
- cfg->attach.open_cores = false;
- cfg->attach.device.perform_test = false;
- cfg->attach.discard_on_start = false;
+ if ((rc = error)) {
+ SPDK_ERRLOG("OCF cache '%s': failed to acquire OCF cache lock (OCF error: %d)\n",
+ ocf_cache_get_name(cache), error);
+ goto end;
+ }
- vbdev->cfg.cache.locked = true;
+ if (core) {
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s': flushing...\n", ocf_core_get_name(core));
- cfg->core.volume_type = SPDK_OBJECT;
+ /* Once again check if selected core still exists in cache as it may have
+ * been hot removed between resolving its name and taking this lock. */
+ if ((rc = ocf_core_get_by_name(cache, mngt_ctx->bdev_name,
+ OCF_CORE_NAME_SIZE, &core))) {
+ SPDK_ERRLOG("OCF core '%s': already removed from cache (OCF error: %d)\n",
+ mngt_ctx->bdev_name, rc);
+ ocf_mngt_cache_read_unlock(cache);
+ goto end;
+ }
- if (vbdev->cfg.loadq) {
- /* When doing cache_load(), we need to set try_add to true,
- * otherwise OCF will interpret this core as new
- * instead of the inactive one */
- vbdev->cfg.core.try_add = true;
+ core_ctx = ocf_core_get_priv(core);
+ core_ctx->flush.in_progress = true;
+ ocf_mngt_core_flush(core, _flush_core_cb, NULL);
} else {
- /* When cache is initialized as new, set force flag to true,
- * to ignore warnings about existing metadata */
- cfg->attach.force = true;
- }
-
- /* Serialize bdev names in OCF UUID to interpret on future loads
- * Core UUID is a triple of (core name, vbdev name, cache name)
- * Cache UUID is cache bdev name */
- type = ocf_ctx_get_volume_type(vbdev_ocf_ctx, SPDK_OBJECT);
- if (!type) {
- SPDK_ERRLOG("Fail to get volume type\n");
- return -EINVAL;
- }
- uuid.size = strlen(vbdev->cache.name) + 1;
- uuid.data = vbdev->cache.name;
- ret = ocf_volume_create(&cfg->attach.device.volume, type, &uuid);
- if (ret) {
- SPDK_ERRLOG("Fail to create volume\n");
- return -EINVAL;
- }
-
- snprintf(vbdev->uuid, VBDEV_OCF_MD_MAX_LEN, "%s %s %s",
- vbdev->core.name, vbdev->name, vbdev->cache.name);
- cfg->core.uuid.size = strlen(vbdev->uuid) + 1;
- cfg->core.uuid.data = vbdev->uuid;
- vbdev->uuid[strlen(vbdev->core.name)] = 0;
- vbdev->uuid[strlen(vbdev->core.name) + 1 + strlen(vbdev->name)] = 0;
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF cache '%s': flushing...\n", ocf_cache_get_name(cache));
- return 0;
+ cache_ctx = ocf_cache_get_priv(cache);
+ cache_ctx->flush.in_progress = true;
+ ocf_mngt_cache_flush(cache, _flush_cache_cb, NULL);
+ }
+
+end:
+ /* Flushing process may take some time to finish, so call RPC callback now and
+ * leave flush running in background. Current status of flushing is dumped in
+ * the bdev_ocf_get_bdevs RPC call output. */
+ mngt_ctx->rpc_cb_fn(mngt_ctx->bdev_name, mngt_ctx->rpc_cb_arg, rc);
+ free(mngt_ctx);
}
-/* Allocate vbdev structure object and add it to the global list */
-static int
-init_vbdev(const char *vbdev_name,
- const char *cache_mode_name,
- const uint64_t cache_line_size,
- const char *cache_name,
- const char *core_name,
- bool loadq)
-{
- struct vbdev_ocf *vbdev;
- int rc = 0;
+/* RPC entry point. */
+void
+vbdev_ocf_flush_start(const char *bdev_name, vbdev_ocf_rpc_mngt_cb rpc_cb_fn, void *rpc_cb_arg)
+{
+ struct vbdev_ocf_mngt_ctx *mngt_ctx;
+ int rc;
- if (spdk_bdev_get_by_name(vbdev_name) || vbdev_ocf_get_by_name(vbdev_name)) {
- SPDK_ERRLOG("Device with name '%s' already exists\n", vbdev_name);
- return -EPERM;
- }
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF '%s': initiating flush operation\n", bdev_name);
- vbdev = calloc(1, sizeof(*vbdev));
- if (!vbdev) {
- goto error_mem;
+ if (!g_vbdev_ocf_module_is_running) {
+ SPDK_ERRLOG("OCF: failed to handle the call - module stopping\n");
+ rpc_cb_fn(bdev_name, rpc_cb_arg, -EPERM);
+ return;
}
- vbdev->name = strdup(vbdev_name);
- if (!vbdev->name) {
- goto error_mem;
+ mngt_ctx = calloc(1, sizeof(struct vbdev_ocf_mngt_ctx));
+ if (!mngt_ctx) {
+ SPDK_ERRLOG("OCF '%s': failed to allocate memory for flush context\n", bdev_name);
+ rpc_cb_fn(bdev_name, rpc_cb_arg, -ENOMEM);
+ return;
}
-
- vbdev->cache.name = strdup(cache_name);
- if (!vbdev->cache.name) {
- goto error_mem;
+ mngt_ctx->rpc_cb_fn = rpc_cb_fn;
+ mngt_ctx->rpc_cb_arg = rpc_cb_arg;
+ strlcpy(mngt_ctx->bdev_name, bdev_name, VBDEV_OCF_BDEV_NAME_SIZE);
+ /* Cache or core will be set using vbdev_ocf_bdev_resolve(). */
+ mngt_ctx->cache = NULL;
+ mngt_ctx->core = NULL;
+
+ if ((rc = vbdev_ocf_bdev_resolve(mngt_ctx))) {
+ SPDK_ERRLOG("OCF '%s': failed to find cache or core of that name: %s\n",
+ bdev_name, spdk_strerror(-rc));
+ rpc_cb_fn(bdev_name, rpc_cb_arg, rc);
+ free(mngt_ctx);
+ return;
}
- vbdev->core.name = strdup(core_name);
- if (!vbdev->core.name) {
- goto error_mem;
- }
+ ocf_mngt_cache_read_lock(mngt_ctx->cache ? : ocf_core_get_cache(mngt_ctx->core),
+ _flush_lock_cb, mngt_ctx);
+}
- vbdev->cache.parent = vbdev;
- vbdev->core.parent = vbdev;
- vbdev->cache.is_cache = true;
- vbdev->core.is_cache = false;
- vbdev->cfg.loadq = loadq;
+static void
+_get_stats_lock_cb(ocf_cache_t cache, void *cb_arg, int error)
+{
+ struct vbdev_ocf_mngt_ctx *mngt_ctx = cb_arg;
+ struct spdk_json_write_ctx *w = mngt_ctx->u.rpc_dump.rpc_cb_arg;
+ ocf_core_t core = mngt_ctx->core;
+ struct vbdev_ocf_stats stats;
+ int rc;
- rc = init_vbdev_config(vbdev);
- if (rc) {
- SPDK_ERRLOG("Fail to init vbdev config\n");
- goto error_free;
+ if ((rc = error)) {
+ SPDK_ERRLOG("OCF cache '%s': failed to acquire OCF cache lock (OCF error: %d)\n",
+ ocf_cache_get_name(cache), error);
+ goto end;
}
+ if (core) {
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s': collecting statistics\n", ocf_core_get_name(core));
- if (cache_mode_name) {
- vbdev->cfg.cache.cache_mode
- = ocf_get_cache_mode(cache_mode_name);
- } else if (!loadq) { /* In load path it is OK to pass NULL as cache mode */
- SPDK_ERRLOG("No cache mode specified\n");
- rc = -EINVAL;
- goto error_free;
- }
- if (vbdev->cfg.cache.cache_mode < 0) {
- SPDK_ERRLOG("Incorrect cache mode '%s'\n", cache_mode_name);
- rc = -EINVAL;
- goto error_free;
+ /* Once again check if selected core still exists in cache as it may have
+ * been hot removed between resolving its name and taking this lock. */
+ if ((rc = ocf_core_get_by_name(cache, mngt_ctx->bdev_name,
+ OCF_CORE_NAME_SIZE, &core))) {
+ SPDK_ERRLOG("OCF core '%s': already removed from cache (OCF error: %d)\n",
+ mngt_ctx->bdev_name, rc);
+ ocf_mngt_cache_read_unlock(cache);
+ goto end;
+ }
+
+ if ((rc = vbdev_ocf_stats_core_get(core, &stats))) {
+ SPDK_ERRLOG("OCF core '%s': failed to collect statistics (OCF error: %d)\n",
+ ocf_core_get_name(core), rc);
+ }
+ } else {
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF cache '%s': collecting statistics\n", ocf_cache_get_name(cache));
+
+ if ((rc = vbdev_ocf_stats_cache_get(cache, &stats))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to collect statistics (OCF error: %d)\n",
+ ocf_cache_get_name(cache), rc);
+ }
}
- ocf_cache_line_size_t set_cache_line_size = cache_line_size ?
- (ocf_cache_line_size_t)cache_line_size * KiB :
- ocf_cache_line_size_default;
- if (set_cache_line_size == 0) {
- SPDK_ERRLOG("Cache line size should be non-zero.\n");
- rc = -EINVAL;
- goto error_free;
+ if (!rc) {
+ vbdev_ocf_stats_write_json(w, &stats);
}
- vbdev->cfg.attach.cache_line_size = set_cache_line_size;
- vbdev->cfg.cache.cache_line_size = set_cache_line_size;
- TAILQ_INSERT_TAIL(&g_ocf_vbdev_head, vbdev, tailq);
- return rc;
+ ocf_mngt_cache_read_unlock(cache);
-error_mem:
- rc = -ENOMEM;
-error_free:
- free_vbdev(vbdev);
- return rc;
+end:
+ mngt_ctx->u.rpc_dump.rpc_cb_fn(mngt_ctx->u.rpc_dump.rpc_cb_arg, mngt_ctx->rpc_cb_arg);
+ free(mngt_ctx);
}
-/* Read configuration file at the start of SPDK application
- * This adds vbdevs to global list if some mentioned in config */
-static int
-vbdev_ocf_init(void)
+/* RPC entry point. */
+void
+vbdev_ocf_get_stats(const char *bdev_name, vbdev_ocf_rpc_dump_cb rpc_cb_fn,
+ void *rpc_cb_arg1, void *rpc_cb_arg2)
{
- int status;
+ struct vbdev_ocf_mngt_ctx *mngt_ctx;
+ int rc;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF '%s': getting statistics\n", bdev_name);
- status = vbdev_ocf_ctx_init();
- if (status) {
- SPDK_ERRLOG("OCF ctx initialization failed with=%d\n", status);
- return status;
+ if (!g_vbdev_ocf_module_is_running) {
+ SPDK_ERRLOG("OCF: failed to handle the call - module stopping\n");
+ goto err_module;
}
- status = vbdev_ocf_volume_init();
- if (status) {
- vbdev_ocf_ctx_cleanup();
- SPDK_ERRLOG("OCF volume initialization failed with=%d\n", status);
- return status;
+ mngt_ctx = calloc(1, sizeof(struct vbdev_ocf_mngt_ctx));
+ if (!mngt_ctx) {
+ SPDK_ERRLOG("OCF '%s': failed to allocate memory for getting statistics context\n",
+ bdev_name);
+ goto err_alloc;
}
+ mngt_ctx->u.rpc_dump.rpc_cb_fn = rpc_cb_fn;
+ mngt_ctx->u.rpc_dump.rpc_cb_arg = rpc_cb_arg1;
+ mngt_ctx->rpc_cb_arg = rpc_cb_arg2;
+ strlcpy(mngt_ctx->bdev_name, bdev_name, VBDEV_OCF_BDEV_NAME_SIZE);
+ /* Cache or core will be set using vbdev_ocf_bdev_resolve(). */
+ mngt_ctx->cache = NULL;
+ mngt_ctx->core = NULL;
- return status;
-}
+ if ((rc = vbdev_ocf_bdev_resolve(mngt_ctx))) {
+ SPDK_ERRLOG("OCF '%s': failed to find cache or core of that name: %s\n",
+ bdev_name, spdk_strerror(-rc));
+ goto err_resolve;
+ }
-/* Called after application shutdown started
- * Release memory of allocated structures here */
-static void
-vbdev_ocf_module_fini(void)
-{
- struct vbdev_ocf *vbdev;
+ ocf_mngt_cache_read_lock(mngt_ctx->cache ? : ocf_core_get_cache(mngt_ctx->core),
+ _get_stats_lock_cb, mngt_ctx);
- while ((vbdev = TAILQ_FIRST(&g_ocf_vbdev_head))) {
- TAILQ_REMOVE(&g_ocf_vbdev_head, vbdev, tailq);
- free_vbdev(vbdev);
- }
+ return;
- vbdev_ocf_volume_cleanup();
- vbdev_ocf_ctx_cleanup();
+err_resolve:
+ free(mngt_ctx);
+err_alloc:
+err_module:
+ rpc_cb_fn(rpc_cb_arg1, rpc_cb_arg2);
}
-/* When base device gets unplugged this is called
- * We will unregister cache vbdev here
- * When cache device is removed, we delete every OCF bdev that used it */
static void
-hotremove_cb(struct vbdev_ocf_base *base)
+_reset_stats_lock_cb(ocf_cache_t cache, void *cb_arg, int error)
{
- struct vbdev_ocf *vbdev;
-
- if (!base->is_cache) {
- if (base->parent->state.doing_finish) {
- return;
- }
+ struct vbdev_ocf_mngt_ctx *mngt_ctx = cb_arg;
+ ocf_core_t core = mngt_ctx->core;
+ int rc;
- SPDK_NOTICELOG("Deinitializing '%s' because its core device '%s' was removed\n",
- base->parent->name, base->name);
- vbdev_ocf_delete(base->parent, NULL, NULL);
- return;
+ if ((rc = error)) {
+ SPDK_ERRLOG("OCF cache '%s': failed to acquire OCF cache lock (OCF error: %d)\n",
+ ocf_cache_get_name(cache), error);
+ goto end;
}
- TAILQ_FOREACH(vbdev, &g_ocf_vbdev_head, tailq) {
- if (vbdev->state.doing_finish) {
- continue;
+ if (core) {
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s': resetting statistics\n", ocf_core_get_name(core));
+
+ /* Once again check if selected core still exists in cache as it may have
+ * been hot removed between resolving its name and taking this lock. */
+ if ((rc = ocf_core_get_by_name(cache, mngt_ctx->bdev_name,
+ OCF_CORE_NAME_SIZE, &core))) {
+ SPDK_ERRLOG("OCF core '%s': already removed from cache (OCF error: %d)\n",
+ mngt_ctx->bdev_name, rc);
+ ocf_mngt_cache_unlock(cache);
+ goto end;
}
- if (strcmp(base->name, vbdev->cache.name) == 0) {
- SPDK_NOTICELOG("Deinitializing '%s' because"
- " its cache device '%s' was removed\n",
- vbdev->name, base->name);
- vbdev_ocf_delete(vbdev, NULL, NULL);
+
+ if ((rc = vbdev_ocf_stats_core_reset(core))) {
+ SPDK_ERRLOG("OCF core '%s': failed to reset statistics (OCF error: %d)\n",
+ ocf_core_get_name(core), rc);
}
- }
-}
+ } else {
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF cache '%s': resetting statistics\n", ocf_cache_get_name(cache));
-static void
-base_bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev,
- void *event_ctx)
-{
- switch (type) {
- case SPDK_BDEV_EVENT_REMOVE:
- if (event_ctx) {
- hotremove_cb(event_ctx);
+ if ((rc = vbdev_ocf_stats_cache_reset(cache))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to reset statistics (OCF error: %d)\n",
+ ocf_cache_get_name(cache), rc);
}
- break;
- default:
- SPDK_NOTICELOG("Unsupported bdev event: type %d\n", type);
- break;
}
+
+ ocf_mngt_cache_unlock(cache);
+
+end:
+ mngt_ctx->rpc_cb_fn(mngt_ctx->bdev_name, mngt_ctx->rpc_cb_arg, rc);
+ free(mngt_ctx);
}
-/* Open base SPDK bdev and claim it */
-static int
-attach_base(struct vbdev_ocf_base *base)
+/* RPC entry point. */
+void
+vbdev_ocf_reset_stats(const char *bdev_name, vbdev_ocf_rpc_mngt_cb rpc_cb_fn, void *rpc_cb_arg)
{
- int status;
+ struct vbdev_ocf_mngt_ctx *mngt_ctx;
+ int rc;
- if (base->attached) {
- return -EALREADY;
- }
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF '%s': resetting statistics\n", bdev_name);
- /* If base cache bdev was already opened by other vbdev,
- * we just copy its descriptor here */
- if (base->is_cache) {
- struct vbdev_ocf_base *existing = get_other_cache_base(base);
- if (existing) {
- base->desc = existing->desc;
- base->management_channel = existing->management_channel;
- base->attached = true;
- return 0;
- }
+ if (!g_vbdev_ocf_module_is_running) {
+ SPDK_ERRLOG("OCF: failed to handle the call - module stopping\n");
+ rc = -EPERM;
+ goto err_module;
}
- status = spdk_bdev_open_ext(base->name, true, base_bdev_event_cb, base, &base->desc);
- if (status) {
- SPDK_ERRLOG("Unable to open device '%s' for writing\n", base->name);
- return status;
+ mngt_ctx = calloc(1, sizeof(struct vbdev_ocf_mngt_ctx));
+ if (!mngt_ctx) {
+ SPDK_ERRLOG("OCF '%s': failed to allocate memory for resetting statistics context\n",
+ bdev_name);
+ rc = -ENOMEM;
+ goto err_alloc;
}
+ mngt_ctx->rpc_cb_fn = rpc_cb_fn;
+ mngt_ctx->rpc_cb_arg = rpc_cb_arg;
+ strlcpy(mngt_ctx->bdev_name, bdev_name, VBDEV_OCF_BDEV_NAME_SIZE);
+ /* Cache or core will be set using vbdev_ocf_bdev_resolve(). */
+ mngt_ctx->cache = NULL;
+ mngt_ctx->core = NULL;
- status = spdk_bdev_module_claim_bdev(base->bdev, base->desc,
- &ocf_if);
- if (status) {
- SPDK_ERRLOG("Unable to claim device '%s'\n", base->name);
- spdk_bdev_close(base->desc);
- return status;
+ if ((rc = vbdev_ocf_bdev_resolve(mngt_ctx))) {
+ SPDK_ERRLOG("OCF '%s': failed to find cache or core of that name: %s\n",
+ bdev_name, spdk_strerror(-rc));
+ goto err_resolve;
}
- base->management_channel = spdk_bdev_get_io_channel(base->desc);
- if (!base->management_channel) {
- SPDK_ERRLOG("Unable to get io channel '%s'\n", base->name);
- spdk_bdev_module_release_bdev(base->bdev);
- spdk_bdev_close(base->desc);
- return -ENOMEM;
- }
+ ocf_mngt_cache_lock(mngt_ctx->cache ? : ocf_core_get_cache(mngt_ctx->core),
+ _reset_stats_lock_cb, mngt_ctx);
- /* Save the thread where the base device is opened */
- base->thread = spdk_get_thread();
+ return;
- base->attached = true;
- return status;
+err_resolve:
+ free(mngt_ctx);
+err_alloc:
+err_module:
+ rpc_cb_fn(bdev_name, rpc_cb_arg, rc);
}
-/* Attach base bdevs */
static int
-attach_base_bdevs(struct vbdev_ocf *vbdev,
- struct spdk_bdev *cache_bdev,
- struct spdk_bdev *core_bdev)
+dump_promotion_info(struct spdk_json_write_ctx *w, ocf_cache_t cache)
{
- int rc = 0;
+ ocf_promotion_t promotion_policy;
+ uint32_t param_val;
+ int rc;
- if (cache_bdev) {
- vbdev->cache.bdev = cache_bdev;
- rc |= attach_base(&vbdev->cache);
+ if ((rc = ocf_mngt_cache_promotion_get_policy(cache, &promotion_policy))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to get promotion policy (OCF error: %d)\n",
+ ocf_cache_get_name(cache), rc);
+ spdk_json_write_named_string(w, "policy", "");
+ return rc;
}
+ spdk_json_write_named_string(w, "policy",
+ vbdev_ocf_promotion_policy_get_name(promotion_policy));
+
+ if (promotion_policy == ocf_promotion_nhit) {
+ if ((rc = ocf_mngt_cache_promotion_get_param(cache, ocf_promotion_nhit,
+ ocf_nhit_insertion_threshold, ¶m_val))) {
+ return rc;
+ }
+ spdk_json_write_named_uint32(w, "insertion_threshold", param_val);
+
+ if ((rc = ocf_mngt_cache_promotion_get_param(cache, ocf_promotion_nhit,
+ ocf_nhit_trigger_threshold, ¶m_val))) {
+ return rc;
+ }
+ spdk_json_write_named_uint32(w, "trigger_threshold", param_val);
- if (core_bdev) {
- vbdev->core.bdev = core_bdev;
- rc |= attach_base(&vbdev->core);
}
- return rc;
+ return 0;
}
-/* Init and then start vbdev if all base devices are present */
-void
-vbdev_ocf_construct(const char *vbdev_name,
- const char *cache_mode_name,
- const uint64_t cache_line_size,
- const char *cache_name,
- const char *core_name,
- bool loadq,
- void (*cb)(int, struct vbdev_ocf *, void *),
- void *cb_arg)
+static int
+dump_cleaning_info(struct spdk_json_write_ctx *w, ocf_cache_t cache)
{
+ ocf_cleaning_t cleaning_policy;
+ uint32_t param_val;
int rc;
- struct spdk_bdev *cache_bdev = spdk_bdev_get_by_name(cache_name);
- struct spdk_bdev *core_bdev = spdk_bdev_get_by_name(core_name);
- struct vbdev_ocf *vbdev;
- rc = init_vbdev(vbdev_name, cache_mode_name, cache_line_size, cache_name, core_name, loadq);
- if (rc) {
- cb(rc, NULL, cb_arg);
- return;
+ if ((rc = ocf_mngt_cache_cleaning_get_policy(cache, &cleaning_policy))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to get cleaning policy (OCF error: %d)\n",
+ ocf_cache_get_name(cache), rc);
+ spdk_json_write_named_string(w, "policy", "");
+ return rc;
}
+ spdk_json_write_named_string(w, "policy",
+ vbdev_ocf_cleaning_policy_get_name(cleaning_policy));
- vbdev = vbdev_ocf_get_by_name(vbdev_name);
- if (vbdev == NULL) {
- cb(-ENODEV, NULL, cb_arg);
- return;
- }
+ if (cleaning_policy == ocf_cleaning_acp) {
+ if ((rc = ocf_mngt_cache_cleaning_get_param(cache, ocf_cleaning_acp,
+ ocf_acp_wake_up_time, ¶m_val))) {
+ return rc;
+ }
+ spdk_json_write_named_uint32(w, "wake_up_time", param_val);
- if (cache_bdev == NULL) {
- SPDK_NOTICELOG("OCF bdev '%s' is waiting for cache device '%s' to connect\n",
- vbdev->name, cache_name);
- }
- if (core_bdev == NULL) {
- SPDK_NOTICELOG("OCF bdev '%s' is waiting for core device '%s' to connect\n",
- vbdev->name, core_name);
- }
+ if ((rc = ocf_mngt_cache_cleaning_get_param(cache, ocf_cleaning_acp,
+ ocf_acp_flush_max_buffers, ¶m_val))) {
+ return rc;
+ }
+ spdk_json_write_named_uint32(w, "flush_max_buffers", param_val);
- rc = attach_base_bdevs(vbdev, cache_bdev, core_bdev);
- if (rc) {
- cb(rc, vbdev, cb_arg);
- return;
- }
+ } else if (cleaning_policy == ocf_cleaning_alru) {
+ if ((rc = ocf_mngt_cache_cleaning_get_param(cache, ocf_cleaning_alru,
+ ocf_alru_wake_up_time, ¶m_val))) {
+ return rc;
+ }
+ spdk_json_write_named_uint32(w, "wake_up_time", param_val);
- if (core_bdev && cache_bdev) {
- register_vbdev(vbdev, cb, cb_arg);
- } else {
- cb(0, vbdev, cb_arg);
- }
-}
+ if ((rc = ocf_mngt_cache_cleaning_get_param(cache, ocf_cleaning_alru,
+ ocf_alru_flush_max_buffers, ¶m_val))) {
+ return rc;
+ }
+ spdk_json_write_named_uint32(w, "flush_max_buffers", param_val);
-/* Set new cache mode on OCF cache */
-void
-vbdev_ocf_set_cache_mode(struct vbdev_ocf *vbdev,
- const char *cache_mode_name,
- void (*cb)(int, struct vbdev_ocf *, void *),
- void *cb_arg)
-{
- ocf_cache_t cache;
- ocf_cache_mode_t cache_mode;
- int rc;
+ if ((rc = ocf_mngt_cache_cleaning_get_param(cache, ocf_cleaning_alru,
+ ocf_alru_stale_buffer_time, ¶m_val))) {
+ return rc;
+ }
+ spdk_json_write_named_uint32(w, "staleness_time", param_val);
- cache = vbdev->ocf_cache;
- cache_mode = ocf_get_cache_mode(cache_mode_name);
+ if ((rc = ocf_mngt_cache_cleaning_get_param(cache, ocf_cleaning_alru,
+ ocf_alru_activity_threshold, ¶m_val))) {
+ return rc;
+ }
+ spdk_json_write_named_uint32(w, "activity_threshold", param_val);
- rc = ocf_mngt_cache_trylock(cache);
- if (rc) {
- cb(rc, vbdev, cb_arg);
- return;
+ if ((rc = ocf_mngt_cache_cleaning_get_param(cache, ocf_cleaning_alru,
+ ocf_alru_dirty_ratio_threshold, ¶m_val))) {
+ return rc;
+ }
+ spdk_json_write_named_uint32(w, "dirty_ratio_threshold", param_val);
+
+ if ((rc = ocf_mngt_cache_cleaning_get_param(cache, ocf_cleaning_alru,
+ ocf_alru_dirty_ratio_inertia, ¶m_val))) {
+ return rc;
+ }
+ spdk_json_write_named_uint32(w, "dirty_ratio_inertia", param_val);
}
- rc = ocf_mngt_cache_set_mode(cache, cache_mode);
- ocf_mngt_cache_unlock(cache);
- cb(rc, vbdev, cb_arg);
+ return 0;
}
-/* Set sequential cutoff parameters on OCF cache */
-void
-vbdev_ocf_set_seqcutoff(struct vbdev_ocf *vbdev, const char *policy_name, uint32_t threshold,
- uint32_t promotion_count, void (*cb)(int, void *), void *cb_arg)
+static int
+dump_seqcutoff_info(struct spdk_json_write_ctx *w, ocf_core_t core)
{
- ocf_cache_t cache;
- ocf_seq_cutoff_policy policy;
+ ocf_seq_cutoff_policy seqcutoff_policy;
+ uint32_t param_val_int;
+ bool param_val_bool;
int rc;
- cache = vbdev->ocf_cache;
-
- policy = ocf_get_seqcutoff_policy(policy_name);
- if (policy == ocf_seq_cutoff_policy_max) {
- cb(OCF_ERR_INVAL, cb_arg);
- return;
+ if ((rc = ocf_mngt_core_get_seq_cutoff_policy(core, &seqcutoff_policy))) {
+ SPDK_ERRLOG("OCF core '%s': failed to get sequential cut-off policy (OCF error: %d)\n",
+ ocf_core_get_name(core), rc);
+ spdk_json_write_named_string(w, "policy", "");
+ return rc;
}
+ spdk_json_write_named_string(w, "policy",
+ vbdev_ocf_seqcutoff_policy_get_name(seqcutoff_policy));
- rc = ocf_mngt_cache_trylock(cache);
- if (rc) {
- cb(rc, cb_arg);
- return;
+ if ((rc = ocf_mngt_core_get_seq_cutoff_threshold(core, ¶m_val_int))) {
+ return rc;
}
+ spdk_json_write_named_uint32(w, "threshold", param_val_int);
- rc = ocf_mngt_core_set_seq_cutoff_policy_all(cache, policy);
- if (rc) {
- goto end;
+ if ((rc = ocf_mngt_core_get_seq_cutoff_promotion_count(core, ¶m_val_int))) {
+ return rc;
}
+ spdk_json_write_named_uint32(w, "promotion_count", param_val_int);
- if (threshold) {
- threshold = threshold * KiB;
-
- rc = ocf_mngt_core_set_seq_cutoff_threshold_all(cache, threshold);
- if (rc) {
- goto end;
- }
+ if ((rc = ocf_mngt_core_get_seq_cutoff_promote_on_threshold(core, ¶m_val_bool))) {
+ return rc;
}
+ spdk_json_write_named_bool(w, "promote_on_threshold", param_val_bool);
+
+ return 0;
+}
- if (promotion_count) {
- rc = ocf_mngt_core_set_seq_cutoff_promotion_count_all(cache, promotion_count);
+static int
+dump_core_waitlist_info(struct spdk_json_write_ctx *w, struct vbdev_ocf_core *core_ctx)
+{
+ spdk_json_write_named_string(w, "name", vbdev_ocf_core_get_name(core_ctx));
+ spdk_json_write_named_string(w, "cache_name", core_ctx->cache_name);
+ spdk_json_write_named_string(w, "base_name", core_ctx->base.name);
+ spdk_json_write_named_bool(w, "base_attached", vbdev_ocf_core_is_base_attached(core_ctx));
+ if (vbdev_ocf_core_is_base_attached(core_ctx)) {
+ spdk_json_write_named_uint64(w, "size", spdk_bdev_get_block_size(core_ctx->base.bdev) *
+ spdk_bdev_get_num_blocks(core_ctx->base.bdev));
+ spdk_json_write_named_uint32(w, "block_size", spdk_bdev_get_block_size(core_ctx->base.bdev));
+ } else {
+ spdk_json_write_named_null(w, "size");
+ spdk_json_write_named_null(w, "block_size");
}
-end:
- ocf_mngt_cache_unlock(cache);
- cb(rc, cb_arg);
+ return 0;
}
-/* 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 */
-static void
-vbdev_ocf_examine(struct spdk_bdev *bdev)
+static int
+dump_core_info(struct spdk_json_write_ctx *w, ocf_core_t core)
{
- const char *bdev_name = spdk_bdev_get_name(bdev);
- struct vbdev_ocf *vbdev;
-
- TAILQ_FOREACH(vbdev, &g_ocf_vbdev_head, tailq) {
- if (vbdev->state.doing_finish) {
- continue;
- }
+ struct vbdev_ocf_core *core_ctx_priv = ocf_core_get_priv(core);
+ struct vbdev_ocf_core _core_ctx = {};
+ struct vbdev_ocf_core *core_ctx = &_core_ctx;
+ int rc;
- if (!strcmp(bdev_name, vbdev->cache.name)) {
- attach_base_bdevs(vbdev, bdev, NULL);
- continue;
- }
- if (!strcmp(bdev_name, vbdev->core.name)) {
- attach_base_bdevs(vbdev, NULL, bdev);
- break;
- }
+ /* If core context exists, make a local copy of it in
+ * case it is freed by other thread in the meantime.
+ * If it doesn't exist, a temporary empty context
+ * (initialized with zeros) will be used instead. */
+ if (core_ctx_priv) {
+ _core_ctx = *core_ctx_priv;
}
- spdk_bdev_module_examine_done(&ocf_if);
-}
-struct metadata_probe_ctx {
- struct vbdev_ocf_base base;
- ocf_volume_t volume;
-
- struct ocf_volume_uuid *core_uuids;
- unsigned int uuid_count;
+ spdk_json_write_named_string(w, "name", ocf_core_get_name(core));
+ if (!spdk_uuid_is_null(spdk_bdev_get_uuid(&core_ctx->ocf_vbdev))) {
+ spdk_json_write_named_uuid(w, "uuid", spdk_bdev_get_uuid(&core_ctx->ocf_vbdev));
+ } else {
+ spdk_json_write_named_null(w, "uuid");
+ }
+ spdk_json_write_named_string(w, "cache_name", ocf_cache_get_name(ocf_core_get_cache(core)));
+ spdk_json_write_named_string(w, "base_name", core_ctx->base.name);
+ spdk_json_write_named_bool(w, "base_attached", vbdev_ocf_core_is_base_attached(core_ctx));
+ if (vbdev_ocf_core_is_base_attached(core_ctx)) {
+ spdk_json_write_named_uint64(w, "size", spdk_bdev_get_block_size(core_ctx->base.bdev) *
+ spdk_bdev_get_num_blocks(core_ctx->base.bdev));
+ spdk_json_write_named_uint32(w, "block_size", spdk_bdev_get_block_size(core_ctx->base.bdev));
+ } else {
+ spdk_json_write_named_null(w, "size");
+ spdk_json_write_named_null(w, "block_size");
+ }
+ spdk_json_write_named_bool(w, "loading", !core_ctx_priv);
- int result;
- int refcnt;
-};
+ spdk_json_write_named_object_begin(w, "seq_cutoff");
+ if ((rc = dump_seqcutoff_info(w, core))) {
+ SPDK_ERRLOG("OCF core '%s': failed to get sequential cut-off params info: %s\n",
+ ocf_core_get_name(core), spdk_strerror(-rc));
+ }
+ spdk_json_write_object_end(w);
-static void
-_examine_ctx_put(void *ctx)
-{
- struct spdk_bdev_desc *desc = ctx;
+ spdk_json_write_named_object_begin(w, "flush");
+ spdk_json_write_named_bool(w, "in_progress", core_ctx->flush.in_progress);
+ spdk_json_write_named_int32(w, "error", core_ctx->flush.error);
+ spdk_json_write_object_end(w);
- spdk_bdev_close(desc);
+ return rc;
}
-static void
-examine_ctx_put(struct metadata_probe_ctx *ctx)
+static int
+dump_cache_info(struct spdk_json_write_ctx *w, ocf_cache_t cache)
{
- unsigned int i;
+ struct vbdev_ocf_cache *cache_ctx = ocf_cache_get_priv(cache);
+ int rc;
- ctx->refcnt--;
- if (ctx->refcnt > 0) {
- return;
+ spdk_json_write_named_string(w, "name", ocf_cache_get_name(cache));
+ spdk_json_write_named_string(w, "base_name", cache_ctx->base.name);
+ spdk_json_write_named_bool(w, "base_attached", ocf_cache_is_device_attached(cache) &&
+ vbdev_ocf_cache_is_base_attached(cache));
+ if (vbdev_ocf_cache_is_base_attached(cache)) {
+ spdk_json_write_named_uint64(w, "size", spdk_bdev_get_block_size(cache_ctx->base.bdev) *
+ spdk_bdev_get_num_blocks(cache_ctx->base.bdev));
+ spdk_json_write_named_uint32(w, "block_size", spdk_bdev_get_block_size(cache_ctx->base.bdev));
+ } else {
+ spdk_json_write_named_null(w, "size");
+ spdk_json_write_named_null(w, "block_size");
}
+ spdk_json_write_named_uint32(w, "cache_line_size", ocf_cache_get_line_size(cache));
+ spdk_json_write_named_string(w, "cache_mode",
+ vbdev_ocf_cachemode_get_name(ocf_cache_get_mode(cache)));
- if (ctx->result) {
- SPDK_ERRLOG("OCF metadata probe for bdev '%s' failed with %d\n",
- spdk_bdev_get_name(ctx->base.bdev), ctx->result);
+ spdk_json_write_named_object_begin(w, "promotion");
+ if ((rc = dump_promotion_info(w, cache))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to get promotion params info: %s\n",
+ ocf_cache_get_name(cache), spdk_strerror(-rc));
}
+ spdk_json_write_object_end(w);
- if (ctx->base.desc) {
- /* Close the underlying bdev on its same opened thread. */
- if (ctx->base.thread && ctx->base.thread != spdk_get_thread()) {
- spdk_thread_send_msg(ctx->base.thread, _examine_ctx_put, ctx->base.desc);
- } else {
- spdk_bdev_close(ctx->base.desc);
- }
+ spdk_json_write_named_object_begin(w, "cleaning");
+ if ((rc = dump_cleaning_info(w, cache))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to get cleaning params info: %s\n",
+ ocf_cache_get_name(cache), spdk_strerror(-rc));
}
+ spdk_json_write_object_end(w);
- if (ctx->volume) {
- ocf_volume_destroy(ctx->volume);
- }
+ spdk_json_write_named_object_begin(w, "flush");
+ spdk_json_write_named_bool(w, "in_progress", cache_ctx->flush.in_progress);
+ spdk_json_write_named_int32(w, "error", cache_ctx->flush.error);
+ spdk_json_write_object_end(w);
- if (ctx->core_uuids) {
- for (i = 0; i < ctx->uuid_count; i++) {
- free(ctx->core_uuids[i].data);
- }
+ spdk_json_write_named_uint16(w, "cores_count", ocf_cache_get_core_count(cache));
+
+ return rc;
+}
+
+static int
+_get_bdevs_core_visitor(ocf_core_t core, void *cb_arg)
+{
+ struct spdk_json_write_ctx *w = cb_arg;
+ int rc;
+
+ spdk_json_write_object_begin(w);
+ if ((rc = dump_core_info(w, core))) {
+ SPDK_ERRLOG("OCF core '%s': failed to get core info: %s\n",
+ ocf_core_get_name(core), spdk_strerror(-rc));
}
- free(ctx->core_uuids);
+ spdk_json_write_object_end(w);
- examine_done(ctx->result, NULL, ctx->base.bdev);
- free(ctx);
+ return rc;
}
-static void
-metadata_probe_cb(void *priv, int rc,
- struct ocf_metadata_probe_status *status)
+static int
+_get_bdevs_cache_visitor(ocf_cache_t cache, void *cb_arg)
{
- struct metadata_probe_ctx *ctx = priv;
+ struct spdk_json_write_ctx *w = cb_arg;
+ int rc;
+
+ spdk_json_write_object_begin(w);
- if (rc) {
- /* -ENODATA means device does not have cache metadata on it */
- if (rc != -OCF_ERR_NO_METADATA) {
- ctx->result = rc;
+ if ((rc = ocf_mngt_cache_read_trylock(cache))) {
+ if (rc == -OCF_ERR_NO_LOCK) {
+ SPDK_WARNLOG("OCF cache '%s': cache is busy - no info will be printed for it\n",
+ ocf_cache_get_name(cache));
+ } else {
+ SPDK_ERRLOG("OCF cache '%s': failed to acquire OCF cache lock (OCF error: %d)\n",
+ ocf_cache_get_name(cache), rc);
}
+ goto end;
+ }
+
+ if ((rc = dump_cache_info(w, cache))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to get cache info: %s\n",
+ ocf_cache_get_name(cache), spdk_strerror(-rc));
}
- examine_ctx_put(ctx);
+ spdk_json_write_named_array_begin(w, "cores");
+ rc = ocf_core_visit(cache, _get_bdevs_core_visitor, w, false);
+ spdk_json_write_array_end(w);
+
+ ocf_mngt_cache_read_unlock(cache);
+
+end:
+ spdk_json_write_object_end(w);
+ return 0;
}
-/* This is called after vbdev_ocf_examine
- * It allows to delay application initialization
- * until all OCF bdevs get registered
- * If vbdev has all of its base devices it starts asynchronously here
- * We first check if bdev appears in configuration,
- * if not we do metadata_probe() to create its configuration from bdev metadata */
-static void
-vbdev_ocf_examine_disk(struct spdk_bdev *bdev)
+/* RPC entry point. */
+void
+vbdev_ocf_get_bdevs(const char *bdev_name, vbdev_ocf_rpc_dump_cb rpc_cb_fn, void *rpc_cb_arg1,
+ void *rpc_cb_arg2)
{
- const char *bdev_name = spdk_bdev_get_name(bdev);
- struct vbdev_ocf *vbdev;
- struct metadata_probe_ctx *ctx;
- bool created_from_config = false;
+ struct spdk_json_write_ctx *w = rpc_cb_arg1;
+ struct vbdev_ocf_core *core_ctx;
+ struct vbdev_ocf_mngt_ctx *mngt_ctx;
+ ocf_cache_t cache;
int rc;
- examine_start(bdev);
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF: getting info about vbdevs\n");
- TAILQ_FOREACH(vbdev, &g_ocf_vbdev_head, tailq) {
- if (vbdev->state.doing_finish || vbdev->state.started) {
- continue;
- }
+ if (!g_vbdev_ocf_module_is_running) {
+ SPDK_ERRLOG("OCF: failed to handle the call - module stopping\n");
+ goto end_rpc;
+ }
- if (!strcmp(bdev_name, vbdev->cache.name)) {
- examine_start(bdev);
- register_vbdev(vbdev, examine_done, bdev);
- created_from_config = true;
- continue;
+ if (!bdev_name) {
+ spdk_json_write_named_array_begin(w, "cores_waitlist");
+ vbdev_ocf_foreach_core_in_waitlist(core_ctx) {
+ spdk_json_write_object_begin(w);
+ if ((rc = dump_core_waitlist_info(w, core_ctx))) {
+ SPDK_ERRLOG("OCF core '%s': failed to get wait list core info: %s\n",
+ vbdev_ocf_core_get_name(core_ctx), spdk_strerror(-rc));
+ }
+ spdk_json_write_object_end(w);
}
- if (!strcmp(bdev_name, vbdev->core.name)) {
- examine_start(bdev);
- register_vbdev(vbdev, examine_done, bdev);
- examine_done(0, NULL, bdev);
- return;
+ spdk_json_write_array_end(w);
+
+ spdk_json_write_named_array_begin(w, "caches");
+ if ((rc = ocf_mngt_cache_visit(vbdev_ocf_ctx, _get_bdevs_cache_visitor, w))) {
+ SPDK_ERRLOG("OCF: failed to iterate over bdevs: %s\n", spdk_strerror(-rc));
}
- }
+ spdk_json_write_array_end(w);
- /* If devices is discovered during config we do not check for metadata */
- if (created_from_config) {
- examine_done(0, NULL, bdev);
- return;
+ goto end_rpc;
}
- /* Metadata probe path
- * We create temporary OCF volume and a temporary base structure
- * to use them for ocf_metadata_probe() and for bottom adapter IOs
- * Then we get UUIDs of core devices an create configurations based on them */
- ctx = calloc(1, sizeof(*ctx));
- if (!ctx) {
- examine_done(-ENOMEM, NULL, bdev);
- return;
+ if ((core_ctx = vbdev_ocf_core_waitlist_get_by_name(bdev_name))) {
+ if ((rc = dump_core_waitlist_info(w, core_ctx))) {
+ SPDK_ERRLOG("OCF core '%s': failed to get wait list core info: %s\n",
+ vbdev_ocf_core_get_name(core_ctx), spdk_strerror(-rc));
+ }
+
+ goto end_rpc;
}
- ctx->base.bdev = bdev;
- ctx->refcnt = 1;
+ mngt_ctx = calloc(1, sizeof(struct vbdev_ocf_mngt_ctx));
+ if (!mngt_ctx) {
+ SPDK_ERRLOG("OCF '%s': failed to allocate memory for getting bdevs info context\n",
+ bdev_name);
+ goto end_rpc;
+ }
+ strlcpy(mngt_ctx->bdev_name, bdev_name, VBDEV_OCF_BDEV_NAME_SIZE);
+ /* Cache or core will be set using vbdev_ocf_bdev_resolve(). */
+ mngt_ctx->cache = NULL;
+ mngt_ctx->core = NULL;
- rc = spdk_bdev_open_ext(bdev_name, true, base_bdev_event_cb, NULL, &ctx->base.desc);
- if (rc) {
- ctx->result = rc;
- examine_ctx_put(ctx);
- return;
+ if ((rc = vbdev_ocf_bdev_resolve(mngt_ctx))) {
+ SPDK_ERRLOG("OCF '%s': failed to find cache or core of that name: %s\n",
+ bdev_name, spdk_strerror(-rc));
+ goto end_free;
}
- rc = ocf_ctx_volume_create(vbdev_ocf_ctx, &ctx->volume, NULL, SPDK_OBJECT);
- if (rc) {
- ctx->result = rc;
- examine_ctx_put(ctx);
- return;
+ cache = mngt_ctx->cache ? : ocf_core_get_cache(mngt_ctx->core);
+ if (!cache) {
+ /* This situation can happen only when ocf_core_get_cache() above
+ * returned NULL while core is being in the process of adding to its cache.
+ * However, this core was found previously by vbdev_ocf_bdev_resolve(),
+ * so it should not be the issue in this scenario. Nevertheless, check for this
+ * condition just in case, to avoid triggering BUG_ON() on trylock() below. */
+
+ SPDK_WARNLOG("OCF core '%s': adding to cache - no info will be printed for it\n",
+ ocf_core_get_name(mngt_ctx->core));
+ goto end_free;
}
- rc = ocf_volume_open(ctx->volume, &ctx->base);
- if (rc) {
- ctx->result = rc;
- examine_ctx_put(ctx);
- return;
+ if ((rc = ocf_mngt_cache_read_trylock(cache))) {
+ if (rc == -OCF_ERR_NO_LOCK) {
+ SPDK_WARNLOG("OCF cache '%s': cache is busy - no info will be printed for it\n",
+ ocf_cache_get_name(cache));
+ } else {
+ SPDK_ERRLOG("OCF cache '%s': failed to acquire OCF cache lock (OCF error: %d)\n",
+ ocf_cache_get_name(cache), rc);
+ }
+ goto end_free;
}
- /* Save the thread where the base device is opened */
- ctx->base.thread = spdk_get_thread();
+ if (mngt_ctx->core) {
+ if ((rc = dump_core_info(w, mngt_ctx->core))) {
+ SPDK_ERRLOG("OCF core '%s': failed to get core info: %s\n",
+ ocf_core_get_name(mngt_ctx->core), spdk_strerror(-rc));
+ }
+ } else {
+ if ((rc = dump_cache_info(w, mngt_ctx->cache))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to get cache info: %s\n",
+ ocf_cache_get_name(mngt_ctx->cache), spdk_strerror(-rc));
+ }
- ocf_metadata_probe(vbdev_ocf_ctx, ctx->volume, metadata_probe_cb, ctx);
-}
+ spdk_json_write_named_array_begin(w, "cores");
+ rc = ocf_core_visit(mngt_ctx->cache, _get_bdevs_core_visitor, w, false);
+ spdk_json_write_array_end(w);
+ }
-static int
-vbdev_ocf_get_ctx_size(void)
-{
- return sizeof(struct bdev_ocf_data);
-}
+ ocf_mngt_cache_read_unlock(cache);
-static void
-fini_start(void)
-{
- g_fini_started = true;
+end_free:
+ free(mngt_ctx);
+end_rpc:
+ rpc_cb_fn(rpc_cb_arg1, rpc_cb_arg2);
}
-/* Module-global function table
- * Does not relate to vbdev instances */
-static struct spdk_bdev_module ocf_if = {
- .name = "ocf",
- .module_init = vbdev_ocf_init,
- .fini_start = fini_start,
- .module_fini = vbdev_ocf_module_fini,
- .get_ctx_size = vbdev_ocf_get_ctx_size,
- .examine_config = vbdev_ocf_examine,
- .examine_disk = vbdev_ocf_examine_disk,
-};
-SPDK_BDEV_MODULE_REGISTER(ocf, &ocf_if);
+SPDK_LOG_REGISTER_COMPONENT(vbdev_ocf)
diff --git a/module/bdev/ocf/vbdev_ocf.h b/module/bdev/ocf/vbdev_ocf.h
index b901b3b8753..ddf3c90e4ed 100644
--- a/module/bdev/ocf/vbdev_ocf.h
+++ b/module/bdev/ocf/vbdev_ocf.h
@@ -1,206 +1,91 @@
/* SPDX-License-Identifier: BSD-3-Clause
- * Copyright (C) 2018 Intel Corporation.
+ * Copyright (C) 2025 Huawei Technologies
* All rights reserved.
*/
#ifndef SPDK_VBDEV_OCF_H
#define SPDK_VBDEV_OCF_H
-#include
-
-#include "spdk/bdev.h"
-#include "spdk/bdev_module.h"
-
-#define VBDEV_OCF_MD_MAX_LEN 4096
-
-struct vbdev_ocf;
-
-/* Context for OCF queue poller
- * Used for mapping SPDK threads to OCF queues */
-struct vbdev_ocf_qctx {
- /* OCF queue. Contains OCF requests */
- struct ocf_queue *queue;
- /* Poller for OCF queue. Runs OCF requests */
- struct spdk_poller *poller;
- /* Reference to parent vbdev */
- struct vbdev_ocf *vbdev;
- /* Base devices channels */
- struct spdk_io_channel *cache_ch;
- struct spdk_io_channel *core_ch;
- /* If true, we have to free this context on queue stop */
- bool allocated;
- /* Link to per-bdev list of queue contexts */
- TAILQ_ENTRY(vbdev_ocf_qctx) tailq;
-};
-
-/* Important states */
-struct vbdev_ocf_state {
- /* From the moment when clean delete started */
- bool doing_clean_delete;
- /* From the moment when finish started */
- bool doing_finish;
- /* From the moment when reset IO received, until it is completed */
- bool doing_reset;
- /* From the moment when exp_bdev is registered */
- bool started;
- /* From the moment when register path started */
- bool starting;
- /* Status of last attempt for stopping this device */
- int stop_status;
-};
+#include "vbdev_ocf_cache.h"
+#include "vbdev_ocf_core.h"
-/*
- * OCF cache configuration options
- */
-struct vbdev_ocf_config {
- /* Initial cache configuration */
- struct ocf_mngt_cache_config cache;
-
- /* Cache device config */
- struct ocf_mngt_cache_attach_config attach;
-
- /* Core initial config */
- struct ocf_mngt_core_config core;
-
- /* Load flag, if set to true, then we will try load cache instance from disk,
- * otherwise we will create new cache on that disk */
- bool loadq;
-};
-
-/* Types for management operations */
-typedef void (*vbdev_ocf_mngt_fn)(struct vbdev_ocf *);
-typedef void (*vbdev_ocf_mngt_callback)(int, struct vbdev_ocf *, void *);
-
-/* Context for asynchronous management operations
- * Single management operation usually contains a list of sub procedures,
- * this structure handles sharing between those sub procedures */
-struct vbdev_ocf_mngt_ctx {
- /* Pointer to function that is currently being executed
- * It gets incremented on each step until it dereferences to NULL */
- vbdev_ocf_mngt_fn *current_step;
-
- /* Function that gets invoked by poller on each iteration */
- vbdev_ocf_mngt_fn poller_fn;
- /* Poller timeout time stamp - when the poller should stop with error */
- uint64_t timeout_ts;
-
- /* Status of management operation */
- int status;
+/* Callback for the RPC layer used in all management operations. */
+typedef void (*vbdev_ocf_rpc_mngt_cb)(const char *bdev_name, void *cb_arg, int error);
- /* External callback and its argument */
- vbdev_ocf_mngt_callback cb;
- void *cb_arg;
-};
-
-/* Base device info */
-struct vbdev_ocf_base {
- /* OCF internal name */
- char *name;
-
- /* True if this is a caching device */
- bool is_cache;
-
- /* Connected SPDK block device */
- struct spdk_bdev *bdev;
-
- /* SPDK device io handle */
- struct spdk_bdev_desc *desc;
-
- /* True if SPDK bdev has been claimed and opened for writing */
- bool attached;
-
- /* Channel for cleaner operations */
- struct spdk_io_channel *management_channel;
-
- /* Reference to main vbdev */
- struct vbdev_ocf *parent;
-
- /* thread where base device is opened */
- struct spdk_thread *thread;
-};
+/* Callback for the RPC layer used in info dumping calls. */
+typedef void (*vbdev_ocf_rpc_dump_cb)(void *cb_arg1, void *cb_arg2);
/*
- * The main information provider
- * It's also registered as io_device
+ * RPC entry points:
*/
-struct vbdev_ocf {
- /* Exposed unique name */
- char *name;
-
- /* Base bdevs */
- struct vbdev_ocf_base cache;
- struct vbdev_ocf_base core;
-
- /* Base bdevs OCF objects */
- ocf_cache_t ocf_cache;
- ocf_core_t ocf_core;
-
- /* Parameters */
- struct vbdev_ocf_config cfg;
- struct vbdev_ocf_state state;
-
- /* Management context */
- struct vbdev_ocf_mngt_ctx mngt_ctx;
-
- /* 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;
-
- /* OCF uuid for core device of this vbdev */
- char uuid[VBDEV_OCF_MD_MAX_LEN];
-
- /* Link to global list of this type structures */
- TAILQ_ENTRY(vbdev_ocf) tailq;
-};
-
-void vbdev_ocf_construct(
- const char *vbdev_name,
- const char *cache_mode_name,
- const uint64_t cache_line_size,
- const char *cache_name,
- const char *core_name,
- bool loadq,
- void (*cb)(int, struct vbdev_ocf *, void *),
- void *cb_arg);
-
-/* If vbdev is online, return its object */
-struct vbdev_ocf *vbdev_ocf_get_by_name(const char *name);
-
-/* Return matching base if parent vbdev is online */
-struct vbdev_ocf_base *vbdev_ocf_get_base_by_name(const char *name);
-
-/* Stop OCF cache and unregister SPDK bdev */
-int vbdev_ocf_delete(struct vbdev_ocf *vbdev, void (*cb)(void *, int), void *cb_arg);
-
-int vbdev_ocf_delete_clean(struct vbdev_ocf *vbdev, void (*cb)(void *, int), void *cb_arg);
-
-/* Set new cache mode on OCF cache */
-void vbdev_ocf_set_cache_mode(
- struct vbdev_ocf *vbdev,
- const char *cache_mode_name,
- void (*cb)(int, struct vbdev_ocf *, void *),
- void *cb_arg);
-
-/* Set sequential cutoff parameters on OCF cache */
-void vbdev_ocf_set_seqcutoff(
- struct vbdev_ocf *vbdev,
- const char *policy_name,
- uint32_t threshold,
- uint32_t promotion_count,
- 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 */
-void vbdev_ocf_foreach(vbdev_ocf_foreach_fn fn, void *ctx);
+
+void vbdev_ocf_cache_start(const char *cache_name,
+ const char *base_name,
+ const char *cache_mode,
+ const uint32_t cache_line_size,
+ bool no_load,
+ vbdev_ocf_rpc_mngt_cb rpc_cb_fn,
+ void *rpc_cb_arg);
+
+void vbdev_ocf_cache_stop(const char *cache_name,
+ vbdev_ocf_rpc_mngt_cb rpc_cb_fn,
+ void *rpc_cb_arg);
+
+void vbdev_ocf_cache_attach(const char *cache_name,
+ const char *base_name,
+ bool force,
+ vbdev_ocf_rpc_mngt_cb rpc_cb_fn,
+ void *rpc_cb_arg);
+
+void vbdev_ocf_cache_detach(const char *cache_name,
+ vbdev_ocf_rpc_mngt_cb rpc_cb_fn,
+ void *rpc_cb_arg);
+
+void vbdev_ocf_core_add(const char *core_name,
+ const char *base_name,
+ const char *cache_name,
+ vbdev_ocf_rpc_mngt_cb rpc_cb_fn,
+ void *rpc_cb_arg);
+
+void vbdev_ocf_core_remove(const char *core_name,
+ vbdev_ocf_rpc_mngt_cb rpc_cb_fn,
+ void *rpc_cb_arg);
+
+void vbdev_ocf_set_cachemode(const char *cache_name,
+ const char *cache_mode,
+ vbdev_ocf_rpc_mngt_cb rpc_cb_fn,
+ void *rpc_cb_arg);
+
+void vbdev_ocf_set_promotion(const char *cache_name, const char *policy,
+ int32_t nhit_insertion_threshold, int32_t nhit_trigger_threshold,
+ vbdev_ocf_rpc_mngt_cb rpc_cb_fn, void *rpc_cb_arg);
+
+void vbdev_ocf_set_cleaning(const char *cache_name, const char *policy, int32_t acp_wake_up_time,
+ int32_t acp_flush_max_buffers, int32_t alru_wake_up_time,
+ int32_t alru_flush_max_buffers, int32_t alru_staleness_time,
+ int32_t alru_activity_threshold, int32_t alru_dirty_ratio_threshold,
+ int32_t alru_dirty_ratio_inertia, vbdev_ocf_rpc_mngt_cb rpc_cb_fn, void *rpc_cb_arg);
+
+void vbdev_ocf_set_seqcutoff(const char *bdev_name, const char *policy, int32_t threshold,
+ int32_t promotion_count, int32_t promote_on_threshold,
+ vbdev_ocf_rpc_mngt_cb rpc_cb_fn, void *rpc_cb_arg);
+
+void vbdev_ocf_flush_start(const char *bdev_name,
+ vbdev_ocf_rpc_mngt_cb rpc_cb_fn,
+ void *rpc_cb_arg);
+
+void vbdev_ocf_get_stats(const char *bdev_name,
+ vbdev_ocf_rpc_dump_cb rpc_cb_fn,
+ void *rpc_cb_arg1,
+ void *rpc_cb_arg2);
+
+void vbdev_ocf_reset_stats(const char *bdev_name,
+ vbdev_ocf_rpc_mngt_cb rpc_cb_fn,
+ void *rpc_cb_arg);
+
+void vbdev_ocf_get_bdevs(const char *bdev_name,
+ vbdev_ocf_rpc_dump_cb rpc_cb_fn,
+ void *rpc_cb_arg1,
+ void *rpc_cb_arg2);
#endif
diff --git a/module/bdev/ocf/vbdev_ocf_cache.c b/module/bdev/ocf/vbdev_ocf_cache.c
new file mode 100644
index 00000000000..f6753d4b2be
--- /dev/null
+++ b/module/bdev/ocf/vbdev_ocf_cache.c
@@ -0,0 +1,374 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C) 2025 Huawei Technologies
+ * All rights reserved.
+ */
+
+#include "spdk/string.h"
+
+#include "vbdev_ocf_cache.h"
+#include "ctx.h"
+#include "utils.h"
+
+bool
+vbdev_ocf_cache_is_base_attached(ocf_cache_t cache)
+{
+ struct vbdev_ocf_cache *cache_ctx = ocf_cache_get_priv(cache);
+
+ return cache_ctx->base.attached;
+}
+
+int
+vbdev_ocf_cache_create(ocf_cache_t *out, const char *cache_name, const char *cache_mode,
+ const uint32_t cache_line_size, bool no_load)
+{
+ ocf_cache_t cache;
+ struct ocf_mngt_cache_config *cache_cfg;
+ struct vbdev_ocf_cache *cache_ctx;
+ int rc = 0;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF cache '%s': allocating structs and starting cache\n",
+ cache_name);
+
+ cache_ctx = calloc(1, sizeof(struct vbdev_ocf_cache));
+ if (!cache_ctx) {
+ SPDK_ERRLOG("OCF cache '%s': failed to allocate memory for cache context\n",
+ cache_name);
+ return -ENOMEM;
+ }
+
+ cache_ctx->no_load = no_load;
+
+ cache_cfg = &cache_ctx->cache_cfg;
+ ocf_mngt_cache_config_set_default(cache_cfg);
+
+ strlcpy(cache_cfg->name, cache_name, OCF_CACHE_NAME_SIZE);
+
+ if (cache_mode) {
+ cache_cfg->cache_mode = vbdev_ocf_cachemode_get_by_name(cache_mode);
+ }
+ if (cache_line_size) {
+ cache_cfg->cache_line_size = cache_line_size;
+ }
+ cache_cfg->locked = true;
+
+ if ((rc = ocf_mngt_cache_start(vbdev_ocf_ctx, &cache, cache_cfg, cache_ctx))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to start OCF cache\n", cache_name);
+ free(cache_ctx);
+ return rc;
+ }
+
+ *out = cache;
+
+ return rc;
+}
+
+void
+vbdev_ocf_cache_destroy(ocf_cache_t cache)
+{
+ struct vbdev_ocf_cache *cache_ctx = ocf_cache_get_priv(cache);
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF cache '%s': deallocating structs\n",
+ ocf_cache_get_name(cache));
+
+ free(cache_ctx);
+}
+
+int vbdev_ocf_core_destroy_cache_channel(ocf_cache_t cache);
+
+static void
+_cache_hotremove_detach_cb(ocf_cache_t cache, void *cb_arg, int error)
+{
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF cache '%s': finishing hot removal\n",
+ ocf_cache_get_name(cache));
+
+ ocf_mngt_cache_unlock(cache);
+
+ if (error) {
+ SPDK_ERRLOG("OCF cache '%s': failed to detach OCF cache device (OCF error: %d)\n",
+ ocf_cache_get_name(cache), error);
+ } else {
+ SPDK_NOTICELOG("OCF cache '%s': device detached\n", ocf_cache_get_name(cache));
+ }
+
+ vbdev_ocf_cache_base_detach(cache);
+
+ /* Update cache IO channel after device detach. */
+ if ((error = vbdev_ocf_core_destroy_cache_channel(cache))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to destroy channel for detached cache: %s\n",
+ ocf_cache_get_name(cache), spdk_strerror(-error));
+ }
+}
+
+static void
+_cache_hotremove_lock_cb(ocf_cache_t cache, void *cb_arg, int error)
+{
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF cache '%s': detaching OCF cache device\n",
+ ocf_cache_get_name(cache));
+
+ if (error) {
+ SPDK_ERRLOG("OCF cache '%s': failed to acquire OCF cache lock (OCF error: %d)\n",
+ ocf_cache_get_name(cache), error);
+ }
+
+ ocf_mngt_cache_detach(cache, _cache_hotremove_detach_cb, NULL);
+}
+
+static void
+vbdev_ocf_cache_hotremove(struct spdk_bdev *bdev, void *event_ctx)
+{
+ ocf_cache_t cache = event_ctx;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF cache '%s': initiating hot removal of base bdev '%s'\n",
+ ocf_cache_get_name(cache), spdk_bdev_get_name(bdev));
+
+ assert(bdev == ((struct vbdev_ocf_cache *)ocf_cache_get_priv(cache))->base.bdev);
+ assert(ocf_cache_is_device_attached(cache));
+ assert(vbdev_ocf_cache_is_base_attached(cache));
+
+ ocf_mngt_cache_lock(cache, _cache_hotremove_lock_cb, NULL);
+}
+
+static void
+_vbdev_ocf_cache_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev, void *event_ctx)
+{
+ ocf_cache_t cache = event_ctx;
+
+ switch (type) {
+ case SPDK_BDEV_EVENT_REMOVE:
+ vbdev_ocf_cache_hotremove(bdev, event_ctx);
+ break;
+ default:
+ SPDK_NOTICELOG("OCF cache '%s': unsupported bdev event type: %d\n",
+ ocf_cache_get_name(cache), type);
+ }
+}
+
+int
+vbdev_ocf_cache_base_attach(ocf_cache_t cache, const char *base_name)
+{
+ struct vbdev_ocf_cache *cache_ctx = ocf_cache_get_priv(cache);
+ struct vbdev_ocf_base *base = &cache_ctx->base;
+ int rc = 0;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF cache '%s': attaching base bdev '%s'\n",
+ ocf_cache_get_name(cache), base_name);
+
+ strlcpy(base->name, base_name, OCF_CACHE_NAME_SIZE);
+
+ if ((rc = spdk_bdev_open_ext(base_name, true, _vbdev_ocf_cache_event_cb, cache, &base->desc))) {
+ return rc;
+ }
+
+ if ((rc = spdk_bdev_module_claim_bdev_desc(base->desc, SPDK_BDEV_CLAIM_READ_MANY_WRITE_ONE,
+ NULL, &ocf_if))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to claim base bdev '%s'\n",
+ ocf_cache_get_name(cache), base_name);
+ spdk_bdev_close(base->desc);
+ return rc;
+ }
+
+ base->mngt_ch = spdk_bdev_get_io_channel(base->desc);
+ if (!base->mngt_ch) {
+ SPDK_ERRLOG("OCF cache '%s': failed to get IO channel for base bdev '%s'\n",
+ ocf_cache_get_name(cache), base_name);
+ spdk_bdev_close(base->desc);
+ return -ENOMEM;
+ }
+
+ base->bdev = spdk_bdev_desc_get_bdev(base->desc);
+ base->thread = spdk_get_thread();
+ base->is_cache = true;
+ base->attached = true;
+
+ return rc;
+}
+
+void
+vbdev_ocf_cache_base_detach(ocf_cache_t cache)
+{
+ struct vbdev_ocf_cache *cache_ctx = ocf_cache_get_priv(cache);
+ struct vbdev_ocf_base *base = &cache_ctx->base;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF cache '%s': detaching base bdev '%s'\n",
+ ocf_cache_get_name(cache), spdk_bdev_get_name(base->bdev));
+
+ vbdev_ocf_base_detach(base);
+}
+
+int
+vbdev_ocf_cache_config_volume_create(ocf_cache_t cache)
+{
+ struct vbdev_ocf_cache *cache_ctx = ocf_cache_get_priv(cache);
+ struct ocf_mngt_cache_attach_config *cache_att_cfg = &cache_ctx->cache_att_cfg;
+ struct ocf_volume_uuid volume_uuid;
+ int rc = 0;
+
+ ocf_mngt_cache_attach_config_set_default(cache_att_cfg);
+ cache_att_cfg->cache_line_size = cache_ctx->cache_cfg.cache_line_size;
+ cache_att_cfg->open_cores = false;
+ cache_att_cfg->discard_on_start = false;
+ cache_att_cfg->device.perform_test = false;
+ cache_att_cfg->device.volume_params = &cache_ctx->base;
+ cache_att_cfg->force = cache_ctx->no_load;
+
+ if ((rc = ocf_uuid_set_str(&volume_uuid, (char *)ocf_cache_get_name(cache)))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to set OCF volume uuid\n",
+ ocf_cache_get_name(cache));
+ return rc;
+ }
+
+ if ((rc = ocf_ctx_volume_create(vbdev_ocf_ctx, &cache_att_cfg->device.volume,
+ &volume_uuid, SPDK_OBJECT))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to create OCF volume\n",
+ ocf_cache_get_name(cache));
+ return rc;
+ }
+
+ return rc;
+}
+
+void
+vbdev_ocf_cache_config_volume_destroy(ocf_cache_t cache)
+{
+ struct vbdev_ocf_cache *cache_ctx = ocf_cache_get_priv(cache);
+
+ ocf_volume_destroy(cache_ctx->cache_att_cfg.device.volume);
+}
+
+static void
+_volume_attach_metadata_probe_cb(void *priv, int error, struct ocf_metadata_probe_status *status)
+{
+ struct vbdev_ocf_mngt_ctx *mngt_ctx = priv;
+ ocf_cache_t cache = mngt_ctx->cache;
+ struct vbdev_ocf_cache *cache_ctx = ocf_cache_get_priv(cache);
+
+ ocf_volume_close(cache_ctx->cache_att_cfg.device.volume);
+
+ if (error && error != -OCF_ERR_NO_METADATA) {
+ SPDK_ERRLOG("OCF cache '%s': failed to probe metadata\n", ocf_cache_get_name(cache));
+ mngt_ctx->u.att_cb_fn(cache, mngt_ctx, error);
+ }
+
+ if (error == -OCF_ERR_NO_METADATA) {
+ SPDK_NOTICELOG("OCF cache '%s': metadata not found - starting new cache instance\n",
+ ocf_cache_get_name(cache));
+ ocf_mngt_cache_attach(cache, &cache_ctx->cache_att_cfg, mngt_ctx->u.att_cb_fn, mngt_ctx);
+ } else {
+ SPDK_NOTICELOG("OCF cache '%s': metadata found - loading previous cache instance\n",
+ ocf_cache_get_name(cache));
+ SPDK_NOTICELOG("(start cache with 'no-load' flag to create new cache instead of loading config from metadata)\n");
+ ocf_mngt_cache_load(cache, &cache_ctx->cache_att_cfg, mngt_ctx->u.att_cb_fn, mngt_ctx);
+ }
+}
+
+int
+vbdev_ocf_cache_volume_attach(ocf_cache_t cache, struct vbdev_ocf_mngt_ctx *mngt_ctx)
+{
+ struct vbdev_ocf_cache *cache_ctx = ocf_cache_get_priv(cache);
+ int rc;
+
+ if (cache_ctx->no_load) {
+ SPDK_NOTICELOG("'no-load' flag specified - starting new cache without looking for metadata\n");
+ ocf_mngt_cache_attach(cache, &cache_ctx->cache_att_cfg, mngt_ctx->u.att_cb_fn, mngt_ctx);
+ return 0;
+ }
+
+ if ((rc = ocf_volume_open(cache_ctx->cache_att_cfg.device.volume, &cache_ctx->base))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to open volume\n", ocf_cache_get_name(cache));
+ return rc;
+ }
+
+ ocf_metadata_probe(vbdev_ocf_ctx, cache_ctx->cache_att_cfg.device.volume,
+ _volume_attach_metadata_probe_cb, mngt_ctx);
+
+ return 0;
+}
+
+static void
+_cache_mngt_queue_stop(void *ctx)
+{
+ struct vbdev_ocf_cache_mngt_queue_ctx *mngt_q_ctx = ctx;
+
+ spdk_poller_unregister(&mngt_q_ctx->poller);
+ free(mngt_q_ctx);
+}
+
+static void
+vbdev_ocf_cache_mngt_queue_stop(ocf_queue_t queue)
+{
+ struct vbdev_ocf_cache_mngt_queue_ctx *mngt_q_ctx = ocf_queue_get_priv(queue);
+ int rc;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF cache '%s': destroying OCF management queue\n",
+ ocf_cache_get_name(mngt_q_ctx->cache));
+
+ if (mngt_q_ctx->thread && mngt_q_ctx->thread != spdk_get_thread()) {
+ if ((rc = spdk_thread_send_msg(mngt_q_ctx->thread, _cache_mngt_queue_stop, mngt_q_ctx))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to send message to thread (name: %s, id: %ld): %s\n",
+ ocf_cache_get_name(mngt_q_ctx->cache),
+ spdk_thread_get_name(mngt_q_ctx->thread),
+ spdk_thread_get_id(mngt_q_ctx->thread),
+ spdk_strerror(-rc));
+ assert(false);
+ }
+ } else {
+ _cache_mngt_queue_stop(mngt_q_ctx);
+ }
+}
+
+static void
+vbdev_ocf_cache_mngt_queue_kick(ocf_queue_t queue)
+{
+}
+
+const struct ocf_queue_ops cache_mngt_queue_ops = {
+ .kick_sync = NULL,
+ .kick = vbdev_ocf_cache_mngt_queue_kick,
+ .stop = vbdev_ocf_cache_mngt_queue_stop,
+};
+
+int
+vbdev_ocf_cache_mngt_queue_create(ocf_cache_t cache)
+{
+ struct vbdev_ocf_cache *cache_ctx = ocf_cache_get_priv(cache);
+ struct vbdev_ocf_cache_mngt_queue_ctx *mngt_q_ctx;
+ int rc = 0;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF cache '%s': creating OCF management queue\n",
+ ocf_cache_get_name(cache));
+
+ mngt_q_ctx = calloc(1, sizeof(struct vbdev_ocf_cache_mngt_queue_ctx));
+ if (!mngt_q_ctx) {
+ SPDK_ERRLOG("OCF cache '%s': failed to allocate memory for management queue context\n",
+ ocf_cache_get_name(cache));
+ return -ENOMEM;
+ }
+ mngt_q_ctx->cache = cache;
+ mngt_q_ctx->thread = spdk_get_thread();
+
+ if ((rc = ocf_queue_create_mngt(cache, &cache_ctx->cache_mngt_q, &cache_mngt_queue_ops))) {
+ SPDK_ERRLOG("OCF cache '%s': failed to create OCF management queue\n",
+ ocf_cache_get_name(cache));
+ free(mngt_q_ctx);
+ return rc;
+ }
+ ocf_queue_set_priv(cache_ctx->cache_mngt_q, mngt_q_ctx);
+
+ mngt_q_ctx->poller = SPDK_POLLER_REGISTER(vbdev_ocf_queue_poller, cache_ctx->cache_mngt_q, 1000);
+ if (!mngt_q_ctx->poller) {
+ SPDK_ERRLOG("OCF cache '%s': failed to create management queue poller\n",
+ ocf_cache_get_name(cache));
+ ocf_queue_put(cache_ctx->cache_mngt_q);
+ return -ENOMEM;
+ }
+
+ return rc;
+}
+
+void
+vbdev_ocf_cache_mngt_queue_put(ocf_cache_t cache)
+{
+ struct vbdev_ocf_cache *cache_ctx = ocf_cache_get_priv(cache);
+
+ vbdev_ocf_queue_put(cache_ctx->cache_mngt_q);
+}
diff --git a/module/bdev/ocf/vbdev_ocf_cache.h b/module/bdev/ocf/vbdev_ocf_cache.h
new file mode 100644
index 00000000000..bc13e88a500
--- /dev/null
+++ b/module/bdev/ocf/vbdev_ocf_cache.h
@@ -0,0 +1,159 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C) 2025 Huawei Technologies
+ * All rights reserved.
+ */
+
+#ifndef SPDK_VBDEV_OCF_CACHE_H
+#define SPDK_VBDEV_OCF_CACHE_H
+
+#include
+#include "spdk/bdev_module.h"
+#include "volume.h"
+
+/* OCF module interface. */
+extern struct spdk_bdev_module ocf_if;
+
+/* Cache context (assigned as cache priv). */
+struct vbdev_ocf_cache {
+ /* Base bdev data. */
+ struct vbdev_ocf_base base;
+
+ /* Cache start config. */
+ struct ocf_mngt_cache_config cache_cfg;
+
+ /* Cache device attach config. */
+ struct ocf_mngt_cache_attach_config cache_att_cfg;
+
+ /* Cache management queue. */
+ ocf_queue_t cache_mngt_q;
+
+ /* Indicator for cache start to ignore cache metadata found on device. */
+ bool no_load;
+
+ /* Status of cache flush operation. */
+ struct {
+ bool in_progress;
+ int error;
+ } flush;
+};
+
+/* Callbacks for the RPC layer. */
+typedef void (*vbdev_ocf_rpc_mngt_cb)(const char *bdev_name, void *cb_arg, int error);
+typedef void (*vbdev_ocf_rpc_dump_cb)(void *cb_arg1, void *cb_arg2);
+
+/* Temporary context for management operations. */
+struct vbdev_ocf_mngt_ctx {
+ /* RPC callback. */
+ vbdev_ocf_rpc_mngt_cb rpc_cb_fn;
+
+ /* RPC context. */
+ void *rpc_cb_arg;
+
+ /* Name of bdev. */
+ char bdev_name[VBDEV_OCF_BDEV_NAME_SIZE];
+
+ /* OCF cache handle. */
+ ocf_cache_t cache;
+
+ /* OCF core handle. */
+ ocf_core_t core;
+
+ union {
+ /* Callback for cache attach operation that needs to be passed through metadata probe
+ * while determining if cache needs to be loaded or just started as new. */
+ ocf_mngt_cache_attach_end_t att_cb_fn;
+
+ /* Core context (priv). */
+ struct vbdev_ocf_core *core_ctx;
+
+ /* OCF cache mode. */
+ ocf_cache_mode_t cache_mode;
+
+ /* Callback and context for RPCs that dump info. */
+ struct {
+ vbdev_ocf_rpc_dump_cb rpc_cb_fn;
+ void *rpc_cb_arg;
+ } rpc_dump;
+
+ /* Promotion parameters. */
+ struct {
+ ocf_promotion_t policy;
+ int32_t nhit_insertion_threshold;
+ int32_t nhit_trigger_threshold;
+ } promotion;
+
+ /* Cleaning parameters. */
+ struct {
+ ocf_cleaning_t policy;
+ int32_t acp_wake_up_time;
+ int32_t acp_flush_max_buffers;
+ int32_t alru_wake_up_time;
+ int32_t alru_flush_max_buffers;
+ int32_t alru_staleness_time;
+ int32_t alru_activity_threshold;
+ int32_t alru_dirty_ratio_threshold;
+ int32_t alru_dirty_ratio_inertia;
+ } cleaning;
+
+ /* Sequential cut-off parameters. */
+ struct {
+ ocf_seq_cutoff_policy policy;
+ int32_t threshold;
+ int32_t promotion_count;
+ int32_t promote_on_threshold;
+ } seqcutoff;
+ } u;
+};
+
+/* Cache management queue context. */
+struct vbdev_ocf_cache_mngt_queue_ctx {
+ /* Registered poller. */
+ struct spdk_poller *poller;
+
+ /* Thread on which poller was registered. */
+ struct spdk_thread *thread;
+
+ /* Currently kept only for its name used in debug log. */
+ ocf_cache_t cache;
+};
+
+/*
+ * Helpers:
+ */
+
+/* Check if cache base is already attached. */
+bool vbdev_ocf_cache_is_base_attached(ocf_cache_t cache);
+
+/*
+ * Management:
+ */
+
+/* Create cache context, fill config and start OCF cache. */
+int vbdev_ocf_cache_create(ocf_cache_t *out, const char *cache_name, const char *cache_mode,
+ const uint32_t cache_line_size, bool no_load);
+
+/* Free cache context. */
+void vbdev_ocf_cache_destroy(ocf_cache_t cache);
+
+/* Open, claim and create IO channel for base bdev. */
+int vbdev_ocf_cache_base_attach(ocf_cache_t cache, const char *base_name);
+
+/* Close base bdev and IO channel. */
+void vbdev_ocf_cache_base_detach(ocf_cache_t cache);
+
+/* Create OCF cache volume and fill device config. */
+int vbdev_ocf_cache_config_volume_create(ocf_cache_t cache);
+
+/* Destroy OCF cache volume from config. */
+void vbdev_ocf_cache_config_volume_destroy(ocf_cache_t cache);
+
+/* Attach or load cache device. */
+int vbdev_ocf_cache_volume_attach(ocf_cache_t cache, struct vbdev_ocf_mngt_ctx *ctx);
+
+/* Create cache management queue poller. */
+int vbdev_ocf_cache_mngt_queue_create(ocf_cache_t cache);
+
+/* Decrement refcount on cache management queue. */
+void vbdev_ocf_cache_mngt_queue_put(ocf_cache_t cache);
+
+#endif
diff --git a/module/bdev/ocf/vbdev_ocf_core.c b/module/bdev/ocf/vbdev_ocf_core.c
new file mode 100644
index 00000000000..ea2e7283b01
--- /dev/null
+++ b/module/bdev/ocf/vbdev_ocf_core.c
@@ -0,0 +1,689 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C) 2025 Huawei Technologies
+ * All rights reserved.
+ */
+
+#include "spdk/string.h"
+
+#include "vbdev_ocf_core.h"
+#include "vbdev_ocf_cache.h"
+#include "ctx.h"
+
+struct vbdev_ocf_core_waitlist_head g_vbdev_ocf_core_waitlist =
+ STAILQ_HEAD_INITIALIZER(g_vbdev_ocf_core_waitlist);
+
+void
+vbdev_ocf_core_waitlist_add(struct vbdev_ocf_core *core_ctx)
+{
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s': adding to wait list\n",
+ vbdev_ocf_core_get_name(core_ctx));
+
+ STAILQ_INSERT_TAIL(&g_vbdev_ocf_core_waitlist, core_ctx, waitlist_entry);
+}
+
+void
+vbdev_ocf_core_waitlist_remove(struct vbdev_ocf_core *core_ctx)
+{
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s': removing from wait list\n",
+ vbdev_ocf_core_get_name(core_ctx));
+
+ STAILQ_REMOVE(&g_vbdev_ocf_core_waitlist, core_ctx, vbdev_ocf_core, waitlist_entry);
+}
+
+struct vbdev_ocf_core *
+vbdev_ocf_core_waitlist_get_by_name(const char *core_name)
+{
+ struct vbdev_ocf_core *core_ctx;
+
+ vbdev_ocf_foreach_core_in_waitlist(core_ctx) {
+ if (!strcmp(core_name, vbdev_ocf_core_get_name(core_ctx))) {
+ return core_ctx;
+ }
+ }
+
+ return NULL;
+}
+
+char *
+vbdev_ocf_core_get_name(struct vbdev_ocf_core *core_ctx)
+{
+ return core_ctx->core_cfg.name;
+}
+
+bool
+vbdev_ocf_core_is_base_attached(struct vbdev_ocf_core *core_ctx)
+{
+ return core_ctx->base.attached;
+}
+
+static int
+_core_is_loaded_cache_visitor(ocf_cache_t cache, void *cb_arg)
+{
+ const char *core_name = cb_arg;
+ ocf_core_t core;
+ int rc;
+
+ rc = ocf_core_get_by_name(cache, core_name, OCF_CORE_NAME_SIZE, &core);
+ if (!rc && !ocf_core_get_priv(core)) {
+ /* Core context is assigned only after manual core add (either right
+ * away if all devices are present, after corresponding cache start,
+ * or base bdev showing up).
+ * If there is no context, it means that this core was just added from
+ * metadata during cache load and that's what we're looking for here. */
+
+ return -EEXIST;
+ } else if (rc && rc != -OCF_ERR_CORE_NOT_EXIST) {
+ SPDK_ERRLOG("OCF: failed to get core: %s\n", spdk_strerror(-rc));
+ }
+
+ return 0;
+}
+
+bool
+vbdev_ocf_core_is_loaded(const char *core_name)
+{
+ int rc;
+
+ rc = ocf_mngt_cache_visit(vbdev_ocf_ctx, _core_is_loaded_cache_visitor, (char *)core_name);
+ if (rc == -EEXIST) {
+ return true;
+ } else if (rc) {
+ SPDK_ERRLOG("OCF: failed to iterate over bdevs: %s\n", spdk_strerror(-rc));
+ }
+
+ return false;
+}
+
+int
+vbdev_ocf_core_create(struct vbdev_ocf_core **out, const char *core_name, const char *cache_name)
+{
+ struct vbdev_ocf_core *core_ctx;
+ struct ocf_mngt_core_config *core_cfg;
+ int rc = 0;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s': allocating structs\n",
+ core_name);
+
+ core_ctx = calloc(1, sizeof(struct vbdev_ocf_core));
+ if (!core_ctx) {
+ SPDK_ERRLOG("OCF core '%s': failed to allocate memory for core context\n",
+ core_name);
+ return -ENOMEM;
+ }
+
+ core_cfg = &core_ctx->core_cfg;
+ ocf_mngt_core_config_set_default(core_cfg);
+
+ strlcpy(core_cfg->name, core_name, OCF_CORE_NAME_SIZE);
+ strlcpy(core_ctx->cache_name, cache_name, OCF_CACHE_NAME_SIZE);
+
+ if ((rc = ocf_uuid_set_str(&core_cfg->uuid, core_cfg->name))) {
+ SPDK_ERRLOG("OCF core '%s': failed to set OCF volume uuid\n", core_name);
+ free(core_ctx);
+ return rc;
+ }
+
+ *out = core_ctx;
+
+ return rc;
+}
+
+void
+vbdev_ocf_core_destroy(struct vbdev_ocf_core *core_ctx)
+{
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s': deallocating structs\n",
+ vbdev_ocf_core_get_name(core_ctx));
+
+ free(core_ctx);
+}
+
+static void
+vbdev_ocf_core_hotremove(struct spdk_bdev *bdev, void *event_ctx)
+{
+ struct vbdev_ocf_core *core_ctx = event_ctx;
+ int rc;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s': initiating hot removal of base bdev '%s'\n",
+ vbdev_ocf_core_get_name(core_ctx), spdk_bdev_get_name(bdev));
+
+ assert(bdev == core_ctx->base.bdev);
+ assert(vbdev_ocf_core_is_base_attached(core_ctx));
+
+ if (vbdev_ocf_core_waitlist_get_by_name(vbdev_ocf_core_get_name(core_ctx))) {
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s': hot removing from wait list\n",
+ vbdev_ocf_core_get_name(core_ctx));
+
+ vbdev_ocf_core_base_detach(core_ctx);
+ return;
+ }
+
+ if ((rc = vbdev_ocf_core_unregister(core_ctx, NULL, NULL))) {
+ SPDK_ERRLOG("OCF core '%s': failed to start unregistering OCF vbdev during core hot removal: %s\n",
+ vbdev_ocf_core_get_name(core_ctx), spdk_strerror(-rc));
+ /* Base bdev is already removed so detach it despite the error. */
+ vbdev_ocf_core_base_detach(core_ctx);
+ }
+}
+
+static void
+_vbdev_ocf_core_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev, void *event_ctx)
+{
+ struct vbdev_ocf_core *core_ctx = event_ctx;
+
+ switch (type) {
+ case SPDK_BDEV_EVENT_REMOVE:
+ vbdev_ocf_core_hotremove(bdev, event_ctx);
+ break;
+ default:
+ SPDK_NOTICELOG("OCF core '%s': unsupported bdev event type: %d\n",
+ vbdev_ocf_core_get_name(core_ctx), type);
+ }
+}
+
+int
+vbdev_ocf_core_base_attach(struct vbdev_ocf_core *core_ctx, const char *base_name)
+{
+ struct vbdev_ocf_base *base = &core_ctx->base;
+ struct ocf_mngt_core_config *core_cfg = &core_ctx->core_cfg;
+ int rc = 0;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s': attaching base bdev '%s'\n",
+ vbdev_ocf_core_get_name(core_ctx), base_name);
+
+ strlcpy(base->name, base_name, OCF_CORE_NAME_SIZE);
+
+ if ((rc = spdk_bdev_open_ext(base_name, true, _vbdev_ocf_core_event_cb, core_ctx, &base->desc))) {
+ return rc;
+ }
+
+ if ((rc = spdk_bdev_module_claim_bdev_desc(base->desc, SPDK_BDEV_CLAIM_READ_MANY_WRITE_ONE,
+ NULL, &ocf_if))) {
+ SPDK_ERRLOG("OCF core '%s': failed to claim base bdev '%s'\n",
+ vbdev_ocf_core_get_name(core_ctx), base_name);
+ spdk_bdev_close(base->desc);
+ return rc;
+ }
+
+ base->mngt_ch = spdk_bdev_get_io_channel(base->desc);
+ if (!base->mngt_ch) {
+ SPDK_ERRLOG("OCF core '%s': failed to get IO channel for base bdev '%s'\n",
+ vbdev_ocf_core_get_name(core_ctx), base_name);
+ spdk_bdev_close(base->desc);
+ return -ENOMEM;
+ }
+
+ base->bdev = spdk_bdev_desc_get_bdev(base->desc);
+ base->thread = spdk_get_thread();
+ base->is_cache = false;
+ base->attached = true;
+ core_cfg->volume_type = SPDK_OBJECT;
+ core_cfg->volume_params = base;
+
+ return rc;
+}
+
+void
+vbdev_ocf_core_base_detach(struct vbdev_ocf_core *core_ctx)
+{
+ struct vbdev_ocf_base *base = &core_ctx->base;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s': detaching base bdev '%s'\n",
+ vbdev_ocf_core_get_name(core_ctx), spdk_bdev_get_name(base->bdev));
+
+ vbdev_ocf_base_detach(base);
+}
+
+static void
+_core_io_queue_stop(void *ctx)
+{
+ struct vbdev_ocf_core_io_channel_ctx *ch_ctx = ctx;
+
+ spdk_poller_unregister(&ch_ctx->poller);
+
+ if (ch_ctx->cache_ch) {
+ spdk_put_io_channel(ch_ctx->cache_ch);
+ }
+
+ /* Core channel may not exist only on error path. */
+ if (spdk_likely(ch_ctx->core_ch)) {
+ spdk_put_io_channel(ch_ctx->core_ch);
+ }
+
+ free(ch_ctx);
+}
+
+static void
+vbdev_ocf_core_io_queue_stop(ocf_queue_t queue)
+{
+ struct vbdev_ocf_core_io_channel_ctx *ch_ctx = ocf_queue_get_priv(queue);
+ struct spdk_bdev *ocf_vbdev = &((struct vbdev_ocf_core *)ocf_core_get_priv(
+ ch_ctx->core))->ocf_vbdev;
+ int rc;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF vbdev '%s': deallocating external IO channel context\n",
+ spdk_bdev_get_name(ocf_vbdev));
+
+ if (ch_ctx->thread && ch_ctx->thread != spdk_get_thread()) {
+ if ((rc = spdk_thread_send_msg(ch_ctx->thread, _core_io_queue_stop, ch_ctx))) {
+ SPDK_ERRLOG("OCF vbdev '%s': failed to send message to thread (name: %s, id: %ld): %s\n",
+ spdk_bdev_get_name(ocf_vbdev),
+ spdk_thread_get_name(ch_ctx->thread),
+ spdk_thread_get_id(ch_ctx->thread),
+ spdk_strerror(-rc));
+ assert(false);
+ }
+ } else {
+ _core_io_queue_stop(ch_ctx);
+ }
+}
+
+static void
+vbdev_ocf_core_io_queue_kick(ocf_queue_t queue)
+{
+}
+
+const struct ocf_queue_ops core_io_queue_ops = {
+ .kick_sync = NULL,
+ .kick = vbdev_ocf_core_io_queue_kick,
+ .stop = vbdev_ocf_core_io_queue_stop,
+};
+
+static int
+_vbdev_ocf_ch_create_cb(void *io_device, void *ctx_buf)
+{
+ ocf_core_t core = io_device;
+ ocf_cache_t cache = ocf_core_get_cache(core);
+ struct vbdev_ocf_core *core_ctx = ocf_core_get_priv(core);
+ struct vbdev_ocf_cache *cache_ctx = ocf_cache_get_priv(cache);
+ struct vbdev_ocf_base *core_base = &core_ctx->base;
+ struct vbdev_ocf_base *cache_base = &cache_ctx->base;
+ struct vbdev_ocf_core_io_channel_ctx *ch_destroy_ctx = ctx_buf;
+ struct vbdev_ocf_core_io_channel_ctx *ch_ctx;
+ const char *vbdev_name = spdk_bdev_get_name(&core_ctx->ocf_vbdev);
+ int rc = 0;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF vbdev '%s': creating IO channel and allocating external context\n",
+ vbdev_name);
+
+ /* Do not use provided buffer for IO channel context, as it will be freed
+ * when this channel is destroyed. Instead allocate our own and keep it
+ * in queue priv. It will be needed later, after the channel was closed,
+ * as it may be referenced by backfill. */
+ ch_ctx = calloc(1, sizeof(struct vbdev_ocf_core_io_channel_ctx));
+ if (!ch_ctx) {
+ SPDK_ERRLOG("OCF vbdev '%s': failed to allocate memory for IO channel context\n",
+ vbdev_name);
+ return -ENOMEM;
+ }
+ ch_ctx->core = core;
+ ch_ctx->thread = spdk_get_thread();
+
+ if ((rc = ocf_queue_create(cache, &ch_ctx->queue, &core_io_queue_ops))) {
+ SPDK_ERRLOG("OCF vbdev '%s': failed to create OCF queue\n", vbdev_name);
+ free(ch_ctx);
+ return rc;
+ }
+ ocf_queue_set_priv(ch_ctx->queue, ch_ctx);
+ /* Save queue pointer in buffer provided by the IO channel callback.
+ * Only this will be needed in channel destroy callback to decrement
+ * the refcount. The rest is freed in queue stop callback. */
+ ch_destroy_ctx->queue = ch_ctx->queue;
+
+ if (!ocf_cache_is_detached(cache)) {
+ ch_ctx->cache_ch = spdk_bdev_get_io_channel(cache_base->desc);
+ if (!ch_ctx->cache_ch) {
+ SPDK_ERRLOG("OCF vbdev '%s': failed to create IO channel for base bdev '%s'\n",
+ vbdev_name, spdk_bdev_get_name(cache_base->bdev));
+ ocf_queue_put(ch_ctx->queue);
+ return -ENOMEM;
+ }
+ }
+
+ ch_ctx->core_ch = spdk_bdev_get_io_channel(core_base->desc);
+ if (!ch_ctx->core_ch) {
+ SPDK_ERRLOG("OCF vbdev '%s': failed to create IO channel for base bdev '%s'\n",
+ vbdev_name, spdk_bdev_get_name(core_base->bdev));
+ /* Do not spdk_put_io_channel() here as it
+ * will be done during stop of OCF queue. */
+ ocf_queue_put(ch_ctx->queue);
+ return -ENOMEM;
+ }
+
+ ch_ctx->poller = SPDK_POLLER_REGISTER(vbdev_ocf_queue_poller, ch_ctx->queue, 0);
+ if (!ch_ctx->poller) {
+ SPDK_ERRLOG("OCF vbdev '%s': failed to create IO queue poller\n", vbdev_name);
+ /* Do not spdk_put_io_channel() here as it
+ * will be done during stop of OCF queue. */
+ ocf_queue_put(ch_ctx->queue);
+ return -ENOMEM;
+ }
+
+ return rc;
+}
+
+static void
+_vbdev_ocf_ch_destroy_cb(void *io_device, void *ctx_buf)
+{
+ struct vbdev_ocf_core_io_channel_ctx *ch_destroy_ctx = ctx_buf;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF vbdev '%s': destroying IO channel\n",
+ spdk_bdev_get_name(&(((struct vbdev_ocf_core *)ocf_core_get_priv(
+ (ocf_core_t)io_device))->ocf_vbdev)));
+
+ ocf_queue_put(ch_destroy_ctx->queue);
+}
+
+static void
+_core_register(void *ctx)
+{
+ struct spdk_bdev *ocf_vbdev = ctx;
+ ocf_core_t core = ocf_vbdev->ctxt;
+ int rc;
+
+ if ((rc = spdk_bdev_register(ocf_vbdev))) {
+ SPDK_ERRLOG("OCF vbdev '%s': failed to register SPDK bdev through message to app thread: %s\n",
+ spdk_bdev_get_name(ocf_vbdev), spdk_strerror(-rc));
+ spdk_io_device_unregister(core, NULL);
+ }
+}
+
+int
+vbdev_ocf_core_register(ocf_core_t core)
+{
+ struct vbdev_ocf_core *core_ctx = ocf_core_get_priv(core);
+ struct vbdev_ocf_base *base = &core_ctx->base;
+ struct spdk_bdev *ocf_vbdev = &core_ctx->ocf_vbdev;
+ struct spdk_uuid ns_uuid;
+ int rc = 0;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s': registering OCF vbdev in SPDK bdev layer\n",
+ ocf_core_get_name(core));
+
+ ocf_vbdev->ctxt = core;
+ ocf_vbdev->name = (char *)ocf_core_get_name(core);
+ ocf_vbdev->product_name = "OCF_disk";
+ ocf_vbdev->write_cache = base->bdev->write_cache;
+ ocf_vbdev->blocklen = base->bdev->blocklen;
+ ocf_vbdev->blockcnt = base->bdev->blockcnt;
+ ocf_vbdev->required_alignment = base->bdev->required_alignment;
+ ocf_vbdev->optimal_io_boundary = base->bdev->optimal_io_boundary;
+ ocf_vbdev->fn_table = &vbdev_ocf_fn_table;
+ ocf_vbdev->module = &ocf_if;
+
+ /* Generate UUID based on namespace UUID + base bdev UUID. */
+ if ((rc = spdk_uuid_parse(&ns_uuid, BDEV_OCF_NAMESPACE_UUID))) {
+ SPDK_ERRLOG("OCF vbdev '%s': failed to parse namespace UUID\n",
+ spdk_bdev_get_name(ocf_vbdev));
+ return rc;
+ }
+ if ((rc = spdk_uuid_generate_sha1(&ocf_vbdev->uuid, &ns_uuid,
+ (const char *)&base->bdev->uuid, sizeof(struct spdk_uuid)))) {
+ SPDK_ERRLOG("OCF vbdev '%s': failed to generate new UUID\n",
+ spdk_bdev_get_name(ocf_vbdev));
+ return rc;
+ }
+
+ spdk_io_device_register(core, _vbdev_ocf_ch_create_cb, _vbdev_ocf_ch_destroy_cb,
+ sizeof(struct vbdev_ocf_core_io_channel_ctx), ocf_core_get_name(core));
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF vbdev '%s': io_device created at %p\n",
+ spdk_bdev_get_name(ocf_vbdev), core);
+
+ if (!spdk_thread_is_app_thread(NULL)) {
+ if ((rc = spdk_thread_send_msg(spdk_thread_get_app_thread(), _core_register, ocf_vbdev))) {
+ SPDK_ERRLOG("OCF vbdev '%s': failed to send message to thread (name: %s, id: %ld): %s\n",
+ spdk_bdev_get_name(ocf_vbdev),
+ spdk_thread_get_name(spdk_thread_get_app_thread()),
+ spdk_thread_get_id(spdk_thread_get_app_thread()),
+ spdk_strerror(-rc));
+ assert(false);
+ }
+ } else {
+ if ((rc = spdk_bdev_register(ocf_vbdev))) {
+ SPDK_ERRLOG("OCF vbdev '%s': failed to register SPDK bdev\n",
+ spdk_bdev_get_name(ocf_vbdev));
+ spdk_io_device_unregister(core, NULL);
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
+int
+vbdev_ocf_core_unregister(struct vbdev_ocf_core *core_ctx, spdk_bdev_unregister_cb cb_fn,
+ void *cb_arg)
+{
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s': initiating unregister of OCF vbdev\n",
+ vbdev_ocf_core_get_name(core_ctx));
+
+ return spdk_bdev_unregister_by_name(spdk_bdev_get_name(&core_ctx->ocf_vbdev),
+ &ocf_if, cb_fn, cb_arg);
+}
+
+static void
+_core_add_from_waitlist_err_cb(void *cb_arg, int error)
+{
+ ocf_cache_t cache = cb_arg;
+
+ if (error) {
+ SPDK_ERRLOG("OCF core: failed to remove OCF core device (OCF error: %d)\n", error);
+ }
+
+ ocf_mngt_cache_unlock(cache);
+}
+
+static void
+_core_add_from_waitlist_add_cb(ocf_cache_t cache, ocf_core_t core, void *cb_arg, int error)
+{
+ struct vbdev_ocf_core *core_ctx = cb_arg;
+ int rc = 0;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s': finishing add of OCF core\n",
+ vbdev_ocf_core_get_name(core_ctx));
+
+ if (error) {
+ SPDK_ERRLOG("OCF core '%s': failed to add core to OCF cache '%s' (OCF error: %d)\n",
+ vbdev_ocf_core_get_name(core_ctx), ocf_cache_get_name(cache), error);
+ ocf_mngt_cache_unlock(cache);
+ return;
+ }
+
+ ocf_core_set_priv(core, core_ctx);
+
+ if ((rc = vbdev_ocf_core_register(core))) {
+ SPDK_ERRLOG("OCF core '%s': failed to register vbdev: %s\n",
+ ocf_core_get_name(core), spdk_strerror(-rc));
+ ocf_mngt_cache_remove_core(core, _core_add_from_waitlist_err_cb, cache);
+ return;
+ }
+
+ SPDK_NOTICELOG("OCF core '%s': added to cache '%s'\n",
+ ocf_core_get_name(core), ocf_cache_get_name(cache));
+
+ ocf_mngt_cache_unlock(cache);
+ vbdev_ocf_core_waitlist_remove(core_ctx);
+}
+
+static void
+_core_add_from_waitlist_lock_cb(ocf_cache_t cache, void *cb_arg, int error)
+{
+ struct vbdev_ocf_core *core_ctx = cb_arg;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s': initiating add of OCF core\n",
+ vbdev_ocf_core_get_name(core_ctx));
+
+ if (error) {
+ SPDK_ERRLOG("OCF core '%s': failed to acquire OCF cache lock (OCF error: %d)\n",
+ vbdev_ocf_core_get_name(core_ctx), error);
+ return;
+ }
+
+ core_ctx->core_cfg.try_add = vbdev_ocf_core_is_loaded(vbdev_ocf_core_get_name(core_ctx));
+
+ ocf_mngt_cache_add_core(cache, &core_ctx->core_cfg, _core_add_from_waitlist_add_cb, core_ctx);
+}
+
+static void
+_core_add_from_waitlist_read_lock_cb(ocf_cache_t cache, void *cb_arg, int error)
+{
+ struct vbdev_ocf_cache *cache_ctx = ocf_cache_get_priv(cache);
+ struct vbdev_ocf_core *core_ctx;
+ uint32_t cache_block_size = spdk_bdev_get_block_size(cache_ctx->base.bdev);
+ uint32_t core_block_size;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF cache '%s': looking for its cores in wait list\n",
+ ocf_cache_get_name(cache));
+
+ if (error) {
+ SPDK_ERRLOG("OCF cache '%s': failed to acquire OCF cache lock (OCF error: %d)\n",
+ ocf_cache_get_name(cache), error);
+ return;
+ }
+
+ vbdev_ocf_foreach_core_in_waitlist(core_ctx) {
+ if (strcmp(ocf_cache_get_name(cache), core_ctx->cache_name) ||
+ !vbdev_ocf_core_is_base_attached(core_ctx)) {
+ continue;
+ }
+
+ SPDK_NOTICELOG("OCF core '%s': adding from wait list to cache '%s'\n",
+ vbdev_ocf_core_get_name(core_ctx), ocf_cache_get_name(cache));
+
+ core_block_size = spdk_bdev_get_block_size(core_ctx->base.bdev);
+ if (cache_block_size > core_block_size) {
+ SPDK_ERRLOG("OCF core '%s': failed to add to cache '%s': cache block size (%d) is greater than core block size (%d)\n",
+ vbdev_ocf_core_get_name(core_ctx), ocf_cache_get_name(cache),
+ cache_block_size, core_block_size);
+ continue;
+ }
+
+ ocf_mngt_cache_lock(cache, _core_add_from_waitlist_lock_cb, core_ctx);
+ }
+
+ ocf_mngt_cache_read_unlock(cache);
+}
+
+void
+vbdev_ocf_core_add_from_waitlist(ocf_cache_t cache)
+{
+ ocf_mngt_cache_read_lock(cache, _core_add_from_waitlist_read_lock_cb, NULL);
+}
+
+static void
+_create_cache_ch_cb(struct spdk_io_channel_iter *i, int error)
+{
+ ocf_core_t core = spdk_io_channel_iter_get_io_device(i);
+
+ if (error) {
+ SPDK_ERRLOG("OCF core '%s': failed to create cache IO channels: %s\n",
+ ocf_core_get_name(core), spdk_strerror(-error));
+ return;
+ }
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s': all cache IO channels created\n",
+ ocf_core_get_name(core));
+}
+
+static void
+_create_cache_ch_fn(struct spdk_io_channel_iter *i)
+{
+ ocf_cache_t cache = spdk_io_channel_iter_get_ctx(i);
+ struct vbdev_ocf_cache *cache_ctx = ocf_cache_get_priv(cache);
+ struct vbdev_ocf_core_io_channel_ctx *ch_ctx, *queue_ch_ctx;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s' on channel %p: creating cache IO channel\n",
+ ocf_core_get_name(spdk_io_channel_iter_get_io_device(i)),
+ spdk_io_channel_iter_get_channel(i));
+
+ /* The actual IO channel context is saved in queue priv.
+ * The one saved in channel context buffer is used only
+ * to properly destroy the channel.
+ * (See the comment in _vbdev_ocf_ch_create_cb() in vbdev_ocf_core.c
+ * for more details.) */
+ ch_ctx = spdk_io_channel_get_ctx(spdk_io_channel_iter_get_channel(i));
+ queue_ch_ctx = ocf_queue_get_priv(ch_ctx->queue);
+
+ /* Cache device IO channel should not exist at this point.
+ * It should be cleaned after cache device detach (either
+ * manual or hot removed) or not initialized after starting
+ * cache without device present. */
+ assert(!queue_ch_ctx->cache_ch);
+
+ queue_ch_ctx->cache_ch = spdk_bdev_get_io_channel(cache_ctx->base.desc);
+
+ spdk_for_each_channel_continue(i, 0);
+}
+
+static int
+_create_cache_ch_core_visitor(ocf_core_t core, void *ctx)
+{
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s': creating all cache IO channels\n",
+ ocf_core_get_name(core));
+
+ spdk_for_each_channel(core, _create_cache_ch_fn, ctx, _create_cache_ch_cb);
+
+ return 0;
+}
+
+int
+vbdev_ocf_core_create_cache_channel(ocf_cache_t cache)
+{
+ return ocf_core_visit(cache, _create_cache_ch_core_visitor, cache, true);
+}
+
+static void
+_destroy_cache_ch_cb(struct spdk_io_channel_iter *i, int error)
+{
+ ocf_core_t core = spdk_io_channel_iter_get_io_device(i);
+
+ if (error) {
+ SPDK_ERRLOG("OCF core '%s': failed to destroy cache IO channels: %s\n",
+ ocf_core_get_name(core), spdk_strerror(-error));
+ return;
+ }
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s': all cache IO channels destroyed\n",
+ ocf_core_get_name(core));
+}
+
+static void
+_destroy_cache_ch_fn(struct spdk_io_channel_iter *i)
+{
+ struct vbdev_ocf_core_io_channel_ctx *ch_ctx, *queue_ch_ctx;
+
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s' on channel %p: destroying cache IO channel\n",
+ ocf_core_get_name(spdk_io_channel_iter_get_io_device(i)),
+ spdk_io_channel_iter_get_channel(i));
+
+ /* The actual IO channel context is saved in queue priv.
+ * The one saved in channel context buffer is used only
+ * to properly destroy the channel.
+ * (See the comment in _vbdev_ocf_ch_create_cb() in vbdev_ocf_core.c
+ * for more details.) */
+ ch_ctx = spdk_io_channel_get_ctx(spdk_io_channel_iter_get_channel(i));
+ queue_ch_ctx = ocf_queue_get_priv(ch_ctx->queue);
+
+ assert(queue_ch_ctx->cache_ch);
+
+ spdk_put_io_channel(queue_ch_ctx->cache_ch);
+ queue_ch_ctx->cache_ch = NULL;
+
+ spdk_for_each_channel_continue(i, 0);
+}
+
+static int
+_destroy_cache_ch_core_visitor(ocf_core_t core, void *ctx)
+{
+ SPDK_DEBUGLOG(vbdev_ocf, "OCF core '%s': destroying all cache IO channels\n",
+ ocf_core_get_name(core));
+
+ spdk_for_each_channel(core, _destroy_cache_ch_fn, ctx, _destroy_cache_ch_cb);
+
+ return 0;
+}
+
+int
+vbdev_ocf_core_destroy_cache_channel(ocf_cache_t cache)
+{
+ return ocf_core_visit(cache, _destroy_cache_ch_core_visitor, NULL, true);
+}
diff --git a/module/bdev/ocf/vbdev_ocf_core.h b/module/bdev/ocf/vbdev_ocf_core.h
new file mode 100644
index 00000000000..b24db73e24f
--- /dev/null
+++ b/module/bdev/ocf/vbdev_ocf_core.h
@@ -0,0 +1,132 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C) 2025 Huawei Technologies
+ * All rights reserved.
+ */
+
+#ifndef SPDK_VBDEV_OCF_CORE_H
+#define SPDK_VBDEV_OCF_CORE_H
+
+#include
+#include "spdk/bdev_module.h"
+#include "volume.h"
+
+/* This namespace UUID was generated using uuid_generate() method. */
+#define BDEV_OCF_NAMESPACE_UUID "f92b7f49-f6c0-44c8-bd23-3205e8c3b6ad"
+
+/* OCF module interface. */
+extern struct spdk_bdev_module ocf_if;
+
+/* Function table of exposed OCF vbdev. */
+extern struct spdk_bdev_fn_table vbdev_ocf_fn_table;
+
+/* Global wait list of all incomplete cores (not added to
+ * the cache yet due to lack of cache or base device. */
+extern STAILQ_HEAD(vbdev_ocf_core_waitlist_head, vbdev_ocf_core) g_vbdev_ocf_core_waitlist;
+
+/* Core wait list iterator. */
+#define vbdev_ocf_foreach_core_in_waitlist(core_ctx) \
+ STAILQ_FOREACH(core_ctx, &g_vbdev_ocf_core_waitlist, waitlist_entry)
+
+/* Core context (assigned as core priv). */
+struct vbdev_ocf_core {
+ /* Exposed OCF vbdev; the one that is registered in bdev layer for usage. */
+ struct spdk_bdev ocf_vbdev;
+
+ /* Base bdev data. */
+ struct vbdev_ocf_base base;
+
+ /* Core add config. */
+ struct ocf_mngt_core_config core_cfg;
+
+ /* Name of cache that this core is assigned to. */
+ char cache_name[OCF_CACHE_NAME_SIZE];
+
+ /* Context for cache stop management operation. */
+ struct vbdev_ocf_mngt_ctx *mngt_ctx;
+
+ /* Status of core flush operation. */
+ struct {
+ bool in_progress;
+ int error;
+ } flush;
+
+ STAILQ_ENTRY(vbdev_ocf_core) waitlist_entry;
+};
+
+/* Core IO channel context. */
+struct vbdev_ocf_core_io_channel_ctx {
+ /* OCF queue. */
+ ocf_queue_t queue;
+
+ /* Cache base bdev IO channel. */
+ struct spdk_io_channel *cache_ch;
+
+ /* Core base bdev IO channel. */
+ struct spdk_io_channel *core_ch;
+
+ /* Registered poller. */
+ struct spdk_poller *poller;
+
+ /* Thread on which poller was registered and channels were opened. */
+ struct spdk_thread *thread;
+
+ /* Currently kept only for its name used in debug log. */
+ ocf_core_t core;
+};
+
+/*
+ * Helpers:
+ */
+
+/* Add core context to wait list. */
+void vbdev_ocf_core_waitlist_add(struct vbdev_ocf_core *core_ctx);
+
+/* Remove core context from wait list. */
+void vbdev_ocf_core_waitlist_remove(struct vbdev_ocf_core *core_ctx);
+
+/* Get core from wait list by its name. */
+struct vbdev_ocf_core *vbdev_ocf_core_waitlist_get_by_name(const char *core_name);
+
+/* Return core name string from given core context. */
+char *vbdev_ocf_core_get_name(struct vbdev_ocf_core *core_ctx);
+
+/* Check if core base is already attached. */
+bool vbdev_ocf_core_is_base_attached(struct vbdev_ocf_core *core_ctx);
+
+/* Check if core was added to cache from metadata during cache load. */
+bool vbdev_ocf_core_is_loaded(const char *core_name);
+
+/*
+ * Management:
+ */
+
+/* Create core context and fill config. */
+int vbdev_ocf_core_create(struct vbdev_ocf_core **out, const char *core_name,
+ const char *cache_name);
+
+/* Free core context. */
+void vbdev_ocf_core_destroy(struct vbdev_ocf_core *core_ctx);
+
+/* Open, claim and create IO channel for base bdev. */
+int vbdev_ocf_core_base_attach(struct vbdev_ocf_core *core_ctx, const char *base_name);
+
+/* Close base bdev and IO channel. */
+void vbdev_ocf_core_base_detach(struct vbdev_ocf_core *core_ctx);
+
+/* Register core in SPDK bdev layer for usage. */
+int vbdev_ocf_core_register(ocf_core_t core);
+
+/* Unregister core from SPDK bdev layer. */
+int vbdev_ocf_core_unregister(struct vbdev_ocf_core *core_ctx, spdk_bdev_unregister_cb cb_fn,
+ void *cb_arg);
+
+/* Add all cores from wait list assigned to given cache. */
+void vbdev_ocf_core_add_from_waitlist(ocf_cache_t cache);
+
+/* Create new cache IO channel on all opened channels context for each opened core. */
+int vbdev_ocf_core_create_cache_channel(ocf_cache_t cache);
+
+/* Destroy cache IO channel on all opened channels context for each opened core. */
+int vbdev_ocf_core_destroy_cache_channel(ocf_cache_t cache);
+
+#endif
diff --git a/module/bdev/ocf/vbdev_ocf_rpc.c b/module/bdev/ocf/vbdev_ocf_rpc.c
index 1396bb0ea47..1144ce6b523 100644
--- a/module/bdev/ocf/vbdev_ocf_rpc.c
+++ b/module/bdev/ocf/vbdev_ocf_rpc.c
@@ -1,631 +1,781 @@
/* SPDX-License-Identifier: BSD-3-Clause
- * Copyright (C) 2018 Intel Corporation.
+ * Copyright (C) 2025 Huawei Technologies
* All rights reserved.
*/
#include "vbdev_ocf.h"
-#include "stats.h"
-#include "utils.h"
-#include "spdk/log.h"
#include "spdk/rpc.h"
#include "spdk/string.h"
-/* Common structure to hold the name parameter for RPC methods using bdev name only. */
-struct rpc_bdev_ocf_name {
- char *name; /* main vbdev name */
+struct rpc_bdev_ocf_start_cache {
+ char *cache_name;
+ char *base_name;
+ char *cache_mode;
+ uint32_t cache_line_size;
+ bool no_load;
};
-/* Common free function for RPC methods using bdev name only. */
static void
-free_rpc_bdev_ocf_name(struct rpc_bdev_ocf_name *r)
+free_rpc_bdev_ocf_start_cache(struct rpc_bdev_ocf_start_cache *r)
{
- free(r->name);
+ free(r->cache_name);
+ free(r->base_name);
+ free(r->cache_mode);
}
-/* Common function to decode the name input parameter for RPC methods using bdev name only. */
-static const struct spdk_json_object_decoder rpc_bdev_ocf_name_decoders[] = {
- {"name", offsetof(struct rpc_bdev_ocf_name, name), spdk_json_decode_string},
+static const struct spdk_json_object_decoder rpc_bdev_ocf_start_cache_decoders[] = {
+ {"cache_name", offsetof(struct rpc_bdev_ocf_start_cache, cache_name), spdk_json_decode_string},
+ {"base_name", offsetof(struct rpc_bdev_ocf_start_cache, base_name), spdk_json_decode_string},
+ {"cache_mode", offsetof(struct rpc_bdev_ocf_start_cache, cache_mode), spdk_json_decode_string, true},
+ {"cache_line_size", offsetof(struct rpc_bdev_ocf_start_cache, cache_line_size), spdk_json_decode_uint32, true},
+ {"no_load", offsetof(struct rpc_bdev_ocf_start_cache, no_load), spdk_json_decode_bool, true},
};
+static void
+rpc_bdev_ocf_start_cache_cb(const char *bdev_name, void *cb_arg, int error)
+{
+ struct spdk_jsonrpc_request *request = cb_arg;
+ struct spdk_json_write_ctx *w;
+
+ if (error && error != -ENODEV) {
+ spdk_jsonrpc_send_error_response_fmt(request, error,
+ "Failed to start OCF cache: %s",
+ spdk_strerror(-error));
+ return;
+ }
+
+ w = spdk_jsonrpc_begin_result(request);
+ spdk_json_write_string(w, bdev_name);
+ spdk_jsonrpc_end_result(request, w);
+}
-/* Structure to hold the parameters for this RPC method. */
-struct rpc_bdev_ocf_create {
- char *name; /* main vbdev */
- char *mode; /* OCF mode (choose one) */
- uint64_t cache_line_size; /* OCF cache line size */
- char *cache_bdev_name; /* sub bdev */
- char *core_bdev_name; /* sub bdev */
+static void
+rpc_bdev_ocf_start_cache(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
+{
+ struct rpc_bdev_ocf_start_cache req = {};
+
+ if (spdk_json_decode_object(params, rpc_bdev_ocf_start_cache_decoders,
+ SPDK_COUNTOF(rpc_bdev_ocf_start_cache_decoders),
+ &req)) {
+ SPDK_DEBUGLOG(vbdev_ocf_rpc, "spdk_json_decode_object failed\n");
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "Invalid parameters");
+ goto cleanup;
+ }
+
+ vbdev_ocf_cache_start(req.cache_name, req.base_name, req.cache_mode, req.cache_line_size,
+ req.no_load, rpc_bdev_ocf_start_cache_cb, request);
+
+cleanup:
+ free_rpc_bdev_ocf_start_cache(&req);
+}
+SPDK_RPC_REGISTER("bdev_ocf_start_cache", rpc_bdev_ocf_start_cache, SPDK_RPC_RUNTIME)
+
+struct rpc_bdev_ocf_stop_cache {
+ char *cache_name;
};
static void
-free_rpc_bdev_ocf_create(struct rpc_bdev_ocf_create *r)
+free_rpc_bdev_ocf_stop_cache(struct rpc_bdev_ocf_stop_cache *r)
{
- free(r->name);
- free(r->core_bdev_name);
- free(r->cache_bdev_name);
- free(r->mode);
+ free(r->cache_name);
}
-/* Structure to decode the input parameters for this RPC method. */
-static const struct spdk_json_object_decoder rpc_bdev_ocf_create_decoders[] = {
- {"name", offsetof(struct rpc_bdev_ocf_create, name), spdk_json_decode_string},
- {"mode", offsetof(struct rpc_bdev_ocf_create, mode), spdk_json_decode_string},
- {"cache_line_size", offsetof(struct rpc_bdev_ocf_create, cache_line_size), spdk_json_decode_uint64, true},
- {"cache_bdev_name", offsetof(struct rpc_bdev_ocf_create, cache_bdev_name), spdk_json_decode_string},
- {"core_bdev_name", offsetof(struct rpc_bdev_ocf_create, core_bdev_name), spdk_json_decode_string},
+static const struct spdk_json_object_decoder rpc_bdev_ocf_stop_cache_decoders[] = {
+ {"cache_name", offsetof(struct rpc_bdev_ocf_stop_cache, cache_name), spdk_json_decode_string},
};
static void
-construct_cb(int status, struct vbdev_ocf *vbdev, void *cb_arg)
+rpc_bdev_ocf_stop_cache_cb(const char *bdev_name, void *cb_arg, int error)
{
struct spdk_jsonrpc_request *request = cb_arg;
- struct spdk_json_write_ctx *w;
- if (status) {
- spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
- "Could not create OCF vbdev: %d",
- status);
- } else {
- w = spdk_jsonrpc_begin_result(request);
- spdk_json_write_string(w, vbdev->name);
- spdk_jsonrpc_end_result(request, w);
+ if (error) {
+ spdk_jsonrpc_send_error_response_fmt(request, error,
+ "Failed to stop OCF cache '%s': %s",
+ bdev_name, spdk_strerror(-error));
+ return;
}
+
+ spdk_jsonrpc_send_bool_response(request, true);
}
static void
-rpc_bdev_ocf_create(struct spdk_jsonrpc_request *request,
- const struct spdk_json_val *params)
+rpc_bdev_ocf_stop_cache(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
{
- struct rpc_bdev_ocf_create req = {NULL};
- int ret;
+ struct rpc_bdev_ocf_stop_cache req = {};
- ret = spdk_json_decode_object(params, rpc_bdev_ocf_create_decoders,
- SPDK_COUNTOF(rpc_bdev_ocf_create_decoders),
- &req);
- if (ret) {
+ if (spdk_json_decode_object(params, rpc_bdev_ocf_stop_cache_decoders,
+ SPDK_COUNTOF(rpc_bdev_ocf_stop_cache_decoders),
+ &req)) {
+ SPDK_DEBUGLOG(vbdev_ocf_rpc, "spdk_json_decode_object failed\n");
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
"Invalid parameters");
- free_rpc_bdev_ocf_create(&req);
- return;
+ goto cleanup;
}
- vbdev_ocf_construct(req.name, req.mode, req.cache_line_size, req.cache_bdev_name,
- req.core_bdev_name, false, construct_cb, request);
- free_rpc_bdev_ocf_create(&req);
+ vbdev_ocf_cache_stop(req.cache_name, rpc_bdev_ocf_stop_cache_cb, request);
+
+cleanup:
+ free_rpc_bdev_ocf_stop_cache(&req);
}
-SPDK_RPC_REGISTER("bdev_ocf_create", rpc_bdev_ocf_create, SPDK_RPC_RUNTIME)
+SPDK_RPC_REGISTER("bdev_ocf_stop_cache", rpc_bdev_ocf_stop_cache, SPDK_RPC_RUNTIME)
+
+struct rpc_bdev_ocf_detach_cache {
+ char *cache_name;
+};
static void
-delete_cb(void *cb_arg, int status)
+free_rpc_bdev_ocf_detach_cache(struct rpc_bdev_ocf_detach_cache *r)
+{
+ free(r->cache_name);
+}
+
+static const struct spdk_json_object_decoder rpc_bdev_ocf_detach_cache_decoders[] = {
+ {"cache_name", offsetof(struct rpc_bdev_ocf_detach_cache, cache_name), spdk_json_decode_string},
+};
+
+static void
+rpc_bdev_ocf_detach_cache_cb(const char *bdev_name, void *cb_arg, int error)
{
struct spdk_jsonrpc_request *request = cb_arg;
- if (status) {
- spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
- "Could not delete OCF vbdev: %d",
- status);
- } else {
- spdk_jsonrpc_send_bool_response(request, true);
+ if (error) {
+ spdk_jsonrpc_send_error_response_fmt(request, error,
+ "Failed to detach device from OCF cache '%s': %s",
+ bdev_name, spdk_strerror(-error));
+ return;
}
+
+ spdk_jsonrpc_send_bool_response(request, true);
}
static void
-rpc_bdev_ocf_delete(struct spdk_jsonrpc_request *request,
- const struct spdk_json_val *params)
+rpc_bdev_ocf_detach_cache(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
{
- struct rpc_bdev_ocf_name req = {NULL};
- struct vbdev_ocf *vbdev;
- int status;
+ struct rpc_bdev_ocf_detach_cache req = {};
- status = spdk_json_decode_object(params, rpc_bdev_ocf_name_decoders,
- SPDK_COUNTOF(rpc_bdev_ocf_name_decoders),
- &req);
- if (status) {
+ if (spdk_json_decode_object(params, rpc_bdev_ocf_detach_cache_decoders,
+ SPDK_COUNTOF(rpc_bdev_ocf_detach_cache_decoders),
+ &req)) {
+ SPDK_DEBUGLOG(vbdev_ocf_rpc, "spdk_json_decode_object failed\n");
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
"Invalid parameters");
- goto end;
+ goto cleanup;
}
- 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_cache_detach(req.cache_name, rpc_bdev_ocf_detach_cache_cb, request);
- status = vbdev_ocf_delete_clean(vbdev, delete_cb, request);
- if (status) {
- spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
- "Could not delete OCF vbdev: %s",
- spdk_strerror(-status));
- goto end;
- }
+cleanup:
+ free_rpc_bdev_ocf_detach_cache(&req);
+}
+SPDK_RPC_REGISTER("bdev_ocf_detach_cache", rpc_bdev_ocf_detach_cache, SPDK_RPC_RUNTIME)
+
+struct rpc_bdev_ocf_attach_cache {
+ char *cache_name;
+ char *base_name;
+ bool force;
+};
-end:
- free_rpc_bdev_ocf_name(&req);
+static void
+free_rpc_bdev_ocf_attach_cache(struct rpc_bdev_ocf_attach_cache *r)
+{
+ free(r->cache_name);
+ free(r->base_name);
}
-SPDK_RPC_REGISTER("bdev_ocf_delete", rpc_bdev_ocf_delete, SPDK_RPC_RUNTIME)
-struct get_ocf_stats_ctx {
- struct spdk_jsonrpc_request *request;
- char *core_name;
+static const struct spdk_json_object_decoder rpc_bdev_ocf_attach_cache_decoders[] = {
+ {"cache_name", offsetof(struct rpc_bdev_ocf_attach_cache, cache_name), spdk_json_decode_string},
+ {"base_name", offsetof(struct rpc_bdev_ocf_attach_cache, base_name), spdk_json_decode_string},
+ {"force", offsetof(struct rpc_bdev_ocf_attach_cache, force), spdk_json_decode_bool, true},
};
static void
-rpc_bdev_ocf_get_stats_cmpl(ocf_cache_t cache, void *priv, int error)
+rpc_bdev_ocf_attach_cache_cb(const char *bdev_name, void *cb_arg, int error)
{
- struct get_ocf_stats_ctx *ctx = (struct get_ocf_stats_ctx *) priv;
- struct spdk_json_write_ctx *w;
- struct vbdev_ocf_stats stats;
+ struct spdk_jsonrpc_request *request = cb_arg;
- if (error) {
- goto end;
+ if (error && error != -ENODEV) {
+ spdk_jsonrpc_send_error_response_fmt(request, error,
+ "Failed to attach device to OCF cache '%s': %s",
+ bdev_name, spdk_strerror(-error));
+ return;
}
- error = vbdev_ocf_stats_get(cache, ctx->core_name, &stats);
+ spdk_jsonrpc_send_bool_response(request, true);
+}
- ocf_mngt_cache_read_unlock(cache);
+static void
+rpc_bdev_ocf_attach_cache(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
+{
+ struct rpc_bdev_ocf_attach_cache req = {};
- if (error) {
- goto end;
+ if (spdk_json_decode_object(params, rpc_bdev_ocf_attach_cache_decoders,
+ SPDK_COUNTOF(rpc_bdev_ocf_attach_cache_decoders),
+ &req)) {
+ SPDK_DEBUGLOG(vbdev_ocf_rpc, "spdk_json_decode_object failed\n");
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "Invalid parameters");
+ goto cleanup;
}
- w = spdk_jsonrpc_begin_result(ctx->request);
- vbdev_ocf_stats_write_json(w, &stats);
- spdk_jsonrpc_end_result(ctx->request, w);
+ vbdev_ocf_cache_attach(req.cache_name, req.base_name, req.force,
+ rpc_bdev_ocf_attach_cache_cb, request);
-end:
- if (error) {
- spdk_jsonrpc_send_error_response_fmt(ctx->request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
- "Could not get stats: %s",
- spdk_strerror(-error));
- }
- free(ctx);
+cleanup:
+ free_rpc_bdev_ocf_attach_cache(&req);
}
+SPDK_RPC_REGISTER("bdev_ocf_attach_cache", rpc_bdev_ocf_attach_cache, SPDK_RPC_RUNTIME)
+
+struct rpc_bdev_ocf_add_core {
+ char *core_name;
+ char *base_name;
+ char *cache_name;
+};
static void
-rpc_bdev_ocf_get_stats(struct spdk_jsonrpc_request *request,
- const struct spdk_json_val *params)
+free_rpc_bdev_ocf_add_core(struct rpc_bdev_ocf_add_core *r)
{
- struct rpc_bdev_ocf_name req = {NULL};
- struct vbdev_ocf *vbdev;
- struct get_ocf_stats_ctx *ctx;
+ free(r->core_name);
+ free(r->base_name);
+ free(r->cache_name);
+}
- 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;
+static const struct spdk_json_object_decoder rpc_bdev_ocf_add_core_decoders[] = {
+ {"core_name", offsetof(struct rpc_bdev_ocf_add_core, core_name), spdk_json_decode_string},
+ {"base_name", offsetof(struct rpc_bdev_ocf_add_core, base_name), spdk_json_decode_string},
+ {"cache_name", offsetof(struct rpc_bdev_ocf_add_core, cache_name), spdk_json_decode_string},
+};
+
+static void
+rpc_bdev_ocf_add_core_cb(const char *bdev_name, void *cb_arg, int error)
+{
+ struct spdk_jsonrpc_request *request = cb_arg;
+ struct spdk_json_write_ctx *w;
+
+ if (error && error != -ENODEV) {
+ spdk_jsonrpc_send_error_response_fmt(request, error,
+ "Failed to add core '%s' to OCF cache: %s",
+ bdev_name, spdk_strerror(-error));
+ return;
}
- if (spdk_json_decode_object(params, rpc_bdev_ocf_name_decoders,
- SPDK_COUNTOF(rpc_bdev_ocf_name_decoders),
+ w = spdk_jsonrpc_begin_result(request);
+ spdk_json_write_string(w, bdev_name);
+ spdk_jsonrpc_end_result(request, w);
+}
+
+static void
+rpc_bdev_ocf_add_core(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
+{
+ struct rpc_bdev_ocf_add_core req = {};
+
+ if (spdk_json_decode_object(params, rpc_bdev_ocf_add_core_decoders,
+ SPDK_COUNTOF(rpc_bdev_ocf_add_core_decoders),
&req)) {
+ SPDK_DEBUGLOG(vbdev_ocf_rpc, "spdk_json_decode_object failed\n");
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
"Invalid parameters");
- free(ctx);
- 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));
- free(ctx);
- goto end;
+ goto cleanup;
}
- ctx->core_name = vbdev->core.name;
- ctx->request = request;
- ocf_mngt_cache_read_lock(vbdev->ocf_cache, rpc_bdev_ocf_get_stats_cmpl, ctx);
+ vbdev_ocf_core_add(req.core_name, req.base_name, req.cache_name, rpc_bdev_ocf_add_core_cb, request);
-end:
- free_rpc_bdev_ocf_name(&req);
+cleanup:
+ free_rpc_bdev_ocf_add_core(&req);
}
-SPDK_RPC_REGISTER("bdev_ocf_get_stats", rpc_bdev_ocf_get_stats, SPDK_RPC_RUNTIME)
+SPDK_RPC_REGISTER("bdev_ocf_add_core", rpc_bdev_ocf_add_core, SPDK_RPC_RUNTIME)
+
+struct rpc_bdev_ocf_remove_core {
+ char *core_name;
+};
static void
-rpc_bdev_ocf_reset_stats_cmpl(ocf_cache_t cache, void *priv, int error)
+free_rpc_bdev_ocf_remove_core(struct rpc_bdev_ocf_remove_core *r)
{
- struct get_ocf_stats_ctx *ctx = (struct get_ocf_stats_ctx *) priv;
-
- if (error) {
- goto end;
- }
+ free(r->core_name);
+}
- error = vbdev_ocf_stats_reset(cache, ctx->core_name);
+static const struct spdk_json_object_decoder rpc_bdev_ocf_remove_core_decoders[] = {
+ {"core_name", offsetof(struct rpc_bdev_ocf_remove_core, core_name), spdk_json_decode_string},
+};
- ocf_mngt_cache_read_unlock(cache);
+static void
+rpc_bdev_ocf_remove_core_cb(const char *bdev_name, void *cb_arg, int error)
+{
+ struct spdk_jsonrpc_request *request = cb_arg;
-end:
if (error) {
- spdk_jsonrpc_send_error_response_fmt(ctx->request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
- "Could not reset stats: %s",
- spdk_strerror(-error));
- } else {
- spdk_jsonrpc_send_bool_response(ctx->request, true);
+ spdk_jsonrpc_send_error_response_fmt(request, error,
+ "Failed to remove core '%s' from OCF cache: %s",
+ bdev_name, spdk_strerror(-error));
+ return;
}
- free(ctx);
+
+ spdk_jsonrpc_send_bool_response(request, true);
}
static void
-rpc_bdev_ocf_reset_stats(struct spdk_jsonrpc_request *request,
- const struct spdk_json_val *params)
+rpc_bdev_ocf_remove_core(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
{
- struct rpc_bdev_ocf_name req = {NULL};
- struct vbdev_ocf *vbdev;
- struct get_ocf_stats_ctx *ctx;
+ struct rpc_bdev_ocf_remove_core req = {};
- 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;
- }
-
- if (spdk_json_decode_object(params, rpc_bdev_ocf_name_decoders,
- SPDK_COUNTOF(rpc_bdev_ocf_name_decoders),
+ if (spdk_json_decode_object(params, rpc_bdev_ocf_remove_core_decoders,
+ SPDK_COUNTOF(rpc_bdev_ocf_remove_core_decoders),
&req)) {
+ SPDK_DEBUGLOG(vbdev_ocf_rpc, "spdk_json_decode_object failed\n");
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
"Invalid parameters");
- free(ctx);
- goto end;
+ goto cleanup;
}
- 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));
- free(ctx);
- goto end;
- }
+ vbdev_ocf_core_remove(req.core_name, rpc_bdev_ocf_remove_core_cb, request);
- ctx->core_name = vbdev->core.name;
- ctx->request = request;
- ocf_mngt_cache_read_lock(vbdev->ocf_cache, rpc_bdev_ocf_reset_stats_cmpl, ctx);
-
-end:
- free_rpc_bdev_ocf_name(&req);
+cleanup:
+ free_rpc_bdev_ocf_remove_core(&req);
}
-SPDK_RPC_REGISTER("bdev_ocf_reset_stats", rpc_bdev_ocf_reset_stats, SPDK_RPC_RUNTIME)
+SPDK_RPC_REGISTER("bdev_ocf_remove_core", rpc_bdev_ocf_remove_core, SPDK_RPC_RUNTIME)
-/* Structure to decode the input parameters for this RPC method. */
-static const struct spdk_json_object_decoder rpc_bdev_ocf_get_bdevs_decoders[] = {
- {"name", offsetof(struct rpc_bdev_ocf_name, name), spdk_json_decode_string, true},
+struct rpc_bdev_ocf_set_cachemode {
+ char *cache_name;
+ char *cache_mode;
};
-struct bdev_get_bdevs_ctx {
- char *name;
- struct spdk_json_write_ctx *w;
+static void
+free_rpc_bdev_ocf_set_cachemode(struct rpc_bdev_ocf_set_cachemode *r)
+{
+ free(r->cache_name);
+ free(r->cache_mode);
+}
+
+static const struct spdk_json_object_decoder rpc_bdev_ocf_set_cachemode_decoders[] = {
+ {"cache_name", offsetof(struct rpc_bdev_ocf_set_cachemode, cache_name), spdk_json_decode_string},
+ {"cache_mode", offsetof(struct rpc_bdev_ocf_set_cachemode, cache_mode), spdk_json_decode_string},
};
static void
-bdev_get_bdevs_fn(struct vbdev_ocf *vbdev, void *ctx)
+rpc_bdev_ocf_set_cachemode_cb(const char *bdev_name, void *cb_arg, int error)
{
- struct bdev_get_bdevs_ctx *cctx = ctx;
- struct spdk_json_write_ctx *w = cctx->w;
+ struct spdk_jsonrpc_request *request = cb_arg;
- if (cctx->name != NULL &&
- strcmp(vbdev->name, cctx->name) &&
- strcmp(vbdev->cache.name, cctx->name) &&
- strcmp(vbdev->core.name, cctx->name)) {
+ if (error) {
+ spdk_jsonrpc_send_error_response_fmt(request, error,
+ "Failed to change cache mode on OCF cache '%s': %s",
+ bdev_name, spdk_strerror(-error));
return;
}
- spdk_json_write_object_begin(w);
- spdk_json_write_named_string(w, "name", vbdev->name);
- spdk_json_write_named_bool(w, "started", vbdev->state.started);
-
- spdk_json_write_named_object_begin(w, "cache");
- spdk_json_write_named_string(w, "name", vbdev->cache.name);
- spdk_json_write_named_bool(w, "attached", vbdev->cache.attached);
- spdk_json_write_object_end(w);
-
- spdk_json_write_named_object_begin(w, "core");
- spdk_json_write_named_string(w, "name", vbdev->core.name);
- spdk_json_write_named_bool(w, "attached", vbdev->core.attached);
- spdk_json_write_object_end(w);
-
- spdk_json_write_object_end(w);
+ spdk_jsonrpc_send_bool_response(request, true);
}
static void
-rpc_bdev_ocf_get_bdevs(struct spdk_jsonrpc_request *request,
- const struct spdk_json_val *params)
+rpc_bdev_ocf_set_cachemode(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
{
- struct spdk_json_write_ctx *w;
- struct rpc_bdev_ocf_name req = {NULL};
- struct bdev_get_bdevs_ctx cctx;
+ struct rpc_bdev_ocf_set_cachemode req = {};
- if (params && spdk_json_decode_object(params, rpc_bdev_ocf_get_bdevs_decoders,
- SPDK_COUNTOF(rpc_bdev_ocf_get_bdevs_decoders),
- &req)) {
+ if (spdk_json_decode_object(params, rpc_bdev_ocf_set_cachemode_decoders,
+ SPDK_COUNTOF(rpc_bdev_ocf_set_cachemode_decoders),
+ &req)) {
+ SPDK_DEBUGLOG(vbdev_ocf_rpc, "spdk_json_decode_object failed\n");
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
"Invalid parameters");
- goto end;
- }
-
- if (req.name) {
- if (!(vbdev_ocf_get_by_name(req.name) || vbdev_ocf_get_base_by_name(req.name))) {
- spdk_jsonrpc_send_error_response(request,
- SPDK_JSONRPC_ERROR_INVALID_PARAMS,
- spdk_strerror(ENODEV));
- goto end;
- }
+ goto cleanup;
}
- w = spdk_jsonrpc_begin_result(request);
-
- cctx.name = req.name;
- cctx.w = w;
-
- spdk_json_write_array_begin(w);
- vbdev_ocf_foreach(bdev_get_bdevs_fn, &cctx);
- spdk_json_write_array_end(w);
- spdk_jsonrpc_end_result(request, w);
+ vbdev_ocf_set_cachemode(req.cache_name, req.cache_mode, rpc_bdev_ocf_set_cachemode_cb, request);
-end:
- free_rpc_bdev_ocf_name(&req);
+cleanup:
+ free_rpc_bdev_ocf_set_cachemode(&req);
}
-SPDK_RPC_REGISTER("bdev_ocf_get_bdevs", rpc_bdev_ocf_get_bdevs, SPDK_RPC_RUNTIME)
+SPDK_RPC_REGISTER("bdev_ocf_set_cachemode", rpc_bdev_ocf_set_cachemode, SPDK_RPC_RUNTIME)
-/* Structure to hold the parameters for this RPC method. */
-struct rpc_bdev_ocf_set_cache_mode {
- char *name; /* main vbdev name */
- char *mode; /* OCF cache mode to switch to */
+struct rpc_bdev_ocf_set_promotion {
+ char *cache_name;
+ char *policy;
+ int32_t nhit_insertion_threshold;
+ int32_t nhit_trigger_threshold;
};
static void
-free_rpc_bdev_ocf_set_cache_mode(struct rpc_bdev_ocf_set_cache_mode *r)
+free_rpc_bdev_ocf_set_promotion(struct rpc_bdev_ocf_set_promotion *r)
{
- free(r->name);
- free(r->mode);
+ free(r->cache_name);
+ free(r->policy);
}
-/* Structure to decode the input parameters for this RPC method. */
-static const struct spdk_json_object_decoder rpc_bdev_ocf_set_cache_mode_decoders[] = {
- {"name", offsetof(struct rpc_bdev_ocf_set_cache_mode, name), spdk_json_decode_string},
- {"mode", offsetof(struct rpc_bdev_ocf_set_cache_mode, mode), spdk_json_decode_string},
+static const struct spdk_json_object_decoder rpc_bdev_ocf_set_promotion_decoders[] = {
+ {"cache_name", offsetof(struct rpc_bdev_ocf_set_promotion, cache_name), spdk_json_decode_string},
+ {"policy", offsetof(struct rpc_bdev_ocf_set_promotion, policy), spdk_json_decode_string, true},
+ {"nhit_insertion_threshold", offsetof(struct rpc_bdev_ocf_set_promotion, nhit_insertion_threshold), spdk_json_decode_int32, true},
+ {"nhit_trigger_threshold", offsetof(struct rpc_bdev_ocf_set_promotion, nhit_trigger_threshold), spdk_json_decode_int32, true},
};
static void
-cache_mode_cb(int status, struct vbdev_ocf *vbdev, void *cb_arg)
+rpc_bdev_ocf_set_promotion_cb(const char *bdev_name, void *cb_arg, int error)
{
struct spdk_jsonrpc_request *request = cb_arg;
- struct spdk_json_write_ctx *w;
- if (status) {
- spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
- "Could not change OCF vbdev cache mode: %d",
- status);
- } else {
- w = spdk_jsonrpc_begin_result(request);
- spdk_json_write_string(w, ocf_get_cache_modename(
- ocf_cache_get_mode(vbdev->ocf_cache)));
- spdk_jsonrpc_end_result(request, w);
+ if (error) {
+ spdk_jsonrpc_send_error_response_fmt(request, error,
+ "Failed to set promotion params on OCF cache '%s': %s",
+ bdev_name, spdk_strerror(-error));
+ return;
}
+
+ spdk_jsonrpc_send_bool_response(request, true);
}
static void
-rpc_bdev_ocf_set_cache_mode(struct spdk_jsonrpc_request *request,
- const struct spdk_json_val *params)
+rpc_bdev_ocf_set_promotion(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
{
- struct rpc_bdev_ocf_set_cache_mode req = {NULL};
- struct vbdev_ocf *vbdev;
- int status;
+ struct rpc_bdev_ocf_set_promotion req = {};
- status = spdk_json_decode_object(params, rpc_bdev_ocf_set_cache_mode_decoders,
- SPDK_COUNTOF(rpc_bdev_ocf_set_cache_mode_decoders),
- &req);
- if (status) {
+ if (spdk_json_decode_object(params, rpc_bdev_ocf_set_promotion_decoders,
+ SPDK_COUNTOF(rpc_bdev_ocf_set_promotion_decoders),
+ &req)) {
+ SPDK_DEBUGLOG(vbdev_ocf_rpc, "spdk_json_decode_object failed\n");
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
"Invalid parameters");
- goto end;
+ goto cleanup;
}
- 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_promotion(req.cache_name, req.policy, req.nhit_insertion_threshold,
+ req.nhit_trigger_threshold, rpc_bdev_ocf_set_promotion_cb, request);
- vbdev_ocf_set_cache_mode(vbdev, req.mode, cache_mode_cb, request);
+cleanup:
+ free_rpc_bdev_ocf_set_promotion(&req);
+}
+SPDK_RPC_REGISTER("bdev_ocf_set_promotion", rpc_bdev_ocf_set_promotion, SPDK_RPC_RUNTIME)
+
+struct rpc_bdev_ocf_set_cleaning {
+ char *cache_name;
+ char *policy;
+ int32_t acp_wake_up_time;
+ int32_t acp_flush_max_buffers;
+ int32_t alru_wake_up_time;
+ int32_t alru_flush_max_buffers;
+ int32_t alru_staleness_time;
+ int32_t alru_activity_threshold;
+ int32_t alru_dirty_ratio_threshold;
+ int32_t alru_dirty_ratio_inertia;
+};
-end:
- free_rpc_bdev_ocf_set_cache_mode(&req);
+static void
+free_rpc_bdev_ocf_set_cleaning(struct rpc_bdev_ocf_set_cleaning *r)
+{
+ free(r->cache_name);
+ free(r->policy);
}
-SPDK_RPC_REGISTER("bdev_ocf_set_cache_mode", rpc_bdev_ocf_set_cache_mode, SPDK_RPC_RUNTIME)
+
+static const struct spdk_json_object_decoder rpc_bdev_ocf_set_cleaning_decoders[] = {
+ {"cache_name", offsetof(struct rpc_bdev_ocf_set_cleaning, cache_name), spdk_json_decode_string},
+ {"policy", offsetof(struct rpc_bdev_ocf_set_cleaning, policy), spdk_json_decode_string, true},
+ {"acp_wake_up_time", offsetof(struct rpc_bdev_ocf_set_cleaning, acp_wake_up_time), spdk_json_decode_int32, true},
+ {"acp_flush_max_buffers", offsetof(struct rpc_bdev_ocf_set_cleaning, acp_flush_max_buffers), spdk_json_decode_int32, true},
+ {"alru_wake_up_time", offsetof(struct rpc_bdev_ocf_set_cleaning, alru_wake_up_time), spdk_json_decode_int32, true},
+ {"alru_flush_max_buffers", offsetof(struct rpc_bdev_ocf_set_cleaning, alru_flush_max_buffers), spdk_json_decode_int32, true},
+ {"alru_staleness_time", offsetof(struct rpc_bdev_ocf_set_cleaning, alru_staleness_time), spdk_json_decode_int32, true},
+ {"alru_activity_threshold", offsetof(struct rpc_bdev_ocf_set_cleaning, alru_activity_threshold), spdk_json_decode_int32, true},
+ {"alru_dirty_ratio_threshold", offsetof(struct rpc_bdev_ocf_set_cleaning, alru_dirty_ratio_threshold), spdk_json_decode_int32, true},
+ {"alru_dirty_ratio_inertia", offsetof(struct rpc_bdev_ocf_set_cleaning, alru_dirty_ratio_inertia), spdk_json_decode_int32, true},
+};
static void
-seqcutoff_cb(int status, void *cb_arg)
+rpc_bdev_ocf_set_cleaning_cb(const char *bdev_name, void *cb_arg, int error)
{
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 sequential cutoff parameters: %d", status);
- } else {
- spdk_jsonrpc_send_bool_response(request, true);
+ if (error) {
+ spdk_jsonrpc_send_error_response_fmt(request, error,
+ "Failed to set cleaning params on OCF cache '%s': %s",
+ bdev_name, spdk_strerror(-error));
+ return;
}
+
+ spdk_jsonrpc_send_bool_response(request, true);
}
-/* Structure to hold the parameters for this RPC method. */
+static void
+rpc_bdev_ocf_set_cleaning(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
+{
+ struct rpc_bdev_ocf_set_cleaning req = {};
+
+ if (spdk_json_decode_object(params, rpc_bdev_ocf_set_cleaning_decoders,
+ SPDK_COUNTOF(rpc_bdev_ocf_set_cleaning_decoders),
+ &req)) {
+ SPDK_DEBUGLOG(vbdev_ocf_rpc, "spdk_json_decode_object failed\n");
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "Invalid parameters");
+ goto cleanup;
+ }
+
+ vbdev_ocf_set_cleaning(req.cache_name, req.policy, req.acp_wake_up_time,
+ req.acp_flush_max_buffers, req.alru_wake_up_time,
+ req.alru_flush_max_buffers, req.alru_staleness_time,
+ req.alru_activity_threshold, req.alru_dirty_ratio_threshold,
+ req.alru_dirty_ratio_inertia, rpc_bdev_ocf_set_cleaning_cb, request);
+
+cleanup:
+ free_rpc_bdev_ocf_set_cleaning(&req);
+}
+SPDK_RPC_REGISTER("bdev_ocf_set_cleaning", rpc_bdev_ocf_set_cleaning, SPDK_RPC_RUNTIME)
+
struct rpc_bdev_ocf_set_seqcutoff {
- char *name; /* main vbdev name */
+ char *bdev_name;
char *policy;
- uint32_t threshold;
- uint32_t promotion_count;
+ int32_t threshold;
+ int32_t promotion_count;
+ int32_t promote_on_threshold;
};
static void
free_rpc_bdev_ocf_set_seqcutoff(struct rpc_bdev_ocf_set_seqcutoff *r)
{
- free(r->name);
+ free(r->bdev_name);
free(r->policy);
}
-/* Structure to decode the input parameters for this RPC method. */
static const struct spdk_json_object_decoder rpc_bdev_ocf_set_seqcutoff_decoders[] = {
- {"name", offsetof(struct rpc_bdev_ocf_set_seqcutoff, name), spdk_json_decode_string},
- {"policy", offsetof(struct rpc_bdev_ocf_set_seqcutoff, policy), spdk_json_decode_string},
- {"threshold", offsetof(struct rpc_bdev_ocf_set_seqcutoff, threshold), spdk_json_decode_uint32, true},
- {"promotion_count", offsetof(struct rpc_bdev_ocf_set_seqcutoff, promotion_count), spdk_json_decode_uint32, true},
+ {"bdev_name", offsetof(struct rpc_bdev_ocf_set_seqcutoff, bdev_name), spdk_json_decode_string},
+ {"policy", offsetof(struct rpc_bdev_ocf_set_seqcutoff, policy), spdk_json_decode_string, true},
+ {"threshold", offsetof(struct rpc_bdev_ocf_set_seqcutoff, threshold), spdk_json_decode_int32, true},
+ {"promotion_count", offsetof(struct rpc_bdev_ocf_set_seqcutoff, promotion_count), spdk_json_decode_int32, true},
+ {"promote_on_threshold", offsetof(struct rpc_bdev_ocf_set_seqcutoff, promote_on_threshold), spdk_json_decode_int32, true},
};
static void
-rpc_bdev_ocf_set_seqcutoff(struct spdk_jsonrpc_request *request,
- const struct spdk_json_val *params)
+rpc_bdev_ocf_set_seqcutoff_cb(const char *bdev_name, void *cb_arg, int error)
{
- struct rpc_bdev_ocf_set_seqcutoff req = {NULL};
- struct vbdev_ocf *vbdev;
- int ret;
+ struct spdk_jsonrpc_request *request = cb_arg;
- ret = spdk_json_decode_object(params, rpc_bdev_ocf_set_seqcutoff_decoders,
- SPDK_COUNTOF(rpc_bdev_ocf_set_seqcutoff_decoders), &req);
- if (ret) {
- spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
- "Invalid parameters");
- goto end;
+ if (error) {
+ spdk_jsonrpc_send_error_response_fmt(request, error,
+ "Failed to set sequential cut-off params on OCF cache or core '%s': %s",
+ bdev_name, spdk_strerror(-error));
+ return;
}
- vbdev = vbdev_ocf_get_by_name(req.name);
- if (vbdev == NULL) {
+ spdk_jsonrpc_send_bool_response(request, true);
+}
+
+static void
+rpc_bdev_ocf_set_seqcutoff(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
+{
+ struct rpc_bdev_ocf_set_seqcutoff req = {};
+
+ if (spdk_json_decode_object(params, rpc_bdev_ocf_set_seqcutoff_decoders,
+ SPDK_COUNTOF(rpc_bdev_ocf_set_seqcutoff_decoders),
+ &req)) {
+ SPDK_DEBUGLOG(vbdev_ocf_rpc, "spdk_json_decode_object failed\n");
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
- spdk_strerror(ENODEV));
- goto end;
+ "Invalid parameters");
+ goto cleanup;
}
- vbdev_ocf_set_seqcutoff(vbdev, req.policy, req.threshold, req.promotion_count, seqcutoff_cb,
- request);
+ vbdev_ocf_set_seqcutoff(req.bdev_name, req.policy, req.threshold, req.promotion_count,
+ req.promote_on_threshold, rpc_bdev_ocf_set_seqcutoff_cb, request);
-end:
+cleanup:
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;
+struct rpc_bdev_ocf_flush_start {
+ char *bdev_name;
};
static void
-rpc_bdev_ocf_flush_start_cmpl(ocf_cache_t cache, void *priv, int error)
+free_rpc_bdev_ocf_flush_start(struct rpc_bdev_ocf_flush_start *r)
{
- 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);
+ free(r->bdev_name);
}
+static const struct spdk_json_object_decoder rpc_bdev_ocf_flush_start_decoders[] = {
+ {"bdev_name", offsetof(struct rpc_bdev_ocf_flush_start, bdev_name), spdk_json_decode_string},
+};
+
static void
-rpc_bdev_ocf_flush_start_lock_cmpl(ocf_cache_t cache, void *priv, int error)
+rpc_bdev_ocf_flush_start_cb(const char *bdev_name, void *cb_arg, int error)
{
- struct get_ocf_flush_start_ctx *ctx = priv;
+ struct spdk_jsonrpc_request *request = cb_arg;
if (error) {
- spdk_jsonrpc_send_error_response_fmt(ctx->request,
- SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
- "Could not lock cache: %d", error);
- free(ctx);
+ spdk_jsonrpc_send_error_response_fmt(request, error,
+ "Failed to start flushing OCF cache or core '%s': %s",
+ bdev_name, spdk_strerror(-error));
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);
+ spdk_jsonrpc_send_bool_response(request, true);
}
static void
-rpc_bdev_ocf_flush_start(struct spdk_jsonrpc_request *request,
- const struct spdk_json_val *params)
+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;
+ struct rpc_bdev_ocf_flush_start req = {};
- ctx = calloc(1, sizeof(*ctx));
- if (!ctx) {
+ if (spdk_json_decode_object(params, rpc_bdev_ocf_flush_start_decoders,
+ SPDK_COUNTOF(rpc_bdev_ocf_flush_start_decoders),
+ &req)) {
+ SPDK_DEBUGLOG(vbdev_ocf_rpc, "spdk_json_decode_object failed\n");
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
- "Not enough memory to process request");
- goto end;
+ "Invalid parameters");
+ goto cleanup;
}
- status = spdk_json_decode_object(params, rpc_bdev_ocf_name_decoders,
- SPDK_COUNTOF(rpc_bdev_ocf_name_decoders),
- &req);
- if (status) {
+ vbdev_ocf_flush_start(req.bdev_name, rpc_bdev_ocf_flush_start_cb, request);
+
+cleanup:
+ free_rpc_bdev_ocf_flush_start(&req);
+}
+SPDK_RPC_REGISTER("bdev_ocf_flush_start", rpc_bdev_ocf_flush_start, SPDK_RPC_RUNTIME)
+
+struct rpc_bdev_ocf_get_stats {
+ char *bdev_name;
+};
+
+static void
+free_rpc_bdev_ocf_get_stats(struct rpc_bdev_ocf_get_stats *r)
+{
+ free(r->bdev_name);
+}
+
+static const struct spdk_json_object_decoder rpc_bdev_ocf_get_stats_decoders[] = {
+ {"bdev_name", offsetof(struct rpc_bdev_ocf_get_stats, bdev_name), spdk_json_decode_string},
+};
+
+static void
+rpc_bdev_ocf_get_stats_cb(void *cb_arg1, void *cb_arg2)
+{
+ struct spdk_json_write_ctx *w = cb_arg1;
+ struct spdk_jsonrpc_request *request = cb_arg2;
+
+ spdk_json_write_object_end(w);
+ spdk_jsonrpc_end_result(request, w);
+}
+
+static void
+rpc_bdev_ocf_get_stats(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
+{
+ struct rpc_bdev_ocf_get_stats req = {};
+ struct spdk_json_write_ctx *w;
+
+ if (spdk_json_decode_object(params, rpc_bdev_ocf_get_stats_decoders,
+ SPDK_COUNTOF(rpc_bdev_ocf_get_stats_decoders),
+ &req)) {
+ SPDK_DEBUGLOG(vbdev_ocf_rpc, "spdk_json_decode_object failed\n");
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
"Invalid parameters");
- free(ctx);
- goto end;
+ goto cleanup;
}
- 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;
+ w = spdk_jsonrpc_begin_result(request);
+ spdk_json_write_object_begin(w);
+
+ vbdev_ocf_get_stats(req.bdev_name, rpc_bdev_ocf_get_stats_cb, w, request);
+
+cleanup:
+ free_rpc_bdev_ocf_get_stats(&req);
+}
+SPDK_RPC_REGISTER("bdev_ocf_get_stats", rpc_bdev_ocf_get_stats, SPDK_RPC_RUNTIME)
+
+struct rpc_bdev_ocf_reset_stats {
+ char *bdev_name;
+};
+
+static void
+free_rpc_bdev_ocf_reset_stats(struct rpc_bdev_ocf_reset_stats *r)
+{
+ free(r->bdev_name);
+}
+
+static const struct spdk_json_object_decoder rpc_bdev_ocf_reset_stats_decoders[] = {
+ {"bdev_name", offsetof(struct rpc_bdev_ocf_reset_stats, bdev_name), spdk_json_decode_string},
+};
+
+static void
+rpc_bdev_ocf_reset_stats_cb(const char *bdev_name, void *cb_arg, int error)
+{
+ struct spdk_jsonrpc_request *request = cb_arg;
+
+ if (error) {
+ spdk_jsonrpc_send_error_response_fmt(request, error,
+ "Failed to reset statistics on OCF cache or core '%s': %s",
+ bdev_name, spdk_strerror(-error));
+ return;
}
- if (!ctx->vbdev->ocf_cache) {
+ spdk_jsonrpc_send_bool_response(request, true);
+}
+
+static void
+rpc_bdev_ocf_reset_stats(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
+{
+ struct rpc_bdev_ocf_reset_stats req = {};
+
+ if (spdk_json_decode_object(params, rpc_bdev_ocf_reset_stats_decoders,
+ SPDK_COUNTOF(rpc_bdev_ocf_reset_stats_decoders),
+ &req)) {
+ SPDK_DEBUGLOG(vbdev_ocf_rpc, "spdk_json_decode_object failed\n");
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
- "Couldn't flush cache: device not attached");
- free(ctx);
- goto end;
+ "Invalid parameters");
+ goto cleanup;
}
- ctx->request = request;
- ocf_mngt_cache_read_lock(ctx->vbdev->ocf_cache, rpc_bdev_ocf_flush_start_lock_cmpl, ctx);
+ vbdev_ocf_reset_stats(req.bdev_name, rpc_bdev_ocf_reset_stats_cb, request);
-end:
- free_rpc_bdev_ocf_name(&req);
+cleanup:
+ free_rpc_bdev_ocf_reset_stats(&req);
+}
+SPDK_RPC_REGISTER("bdev_ocf_reset_stats", rpc_bdev_ocf_reset_stats, SPDK_RPC_RUNTIME)
+
+struct rpc_bdev_ocf_get_bdevs {
+ char *bdev_name;
+};
+
+static void
+free_rpc_bdev_ocf_get_bdevs(struct rpc_bdev_ocf_get_bdevs *r)
+{
+ free(r->bdev_name);
+}
+
+static const struct spdk_json_object_decoder rpc_bdev_ocf_get_bdevs_decoders[] = {
+ {"bdev_name", offsetof(struct rpc_bdev_ocf_get_bdevs, bdev_name), spdk_json_decode_string, true},
+};
+
+static void
+rpc_bdev_ocf_get_bdevs_cb(void *cb_arg1, void *cb_arg2)
+{
+ struct spdk_json_write_ctx *w = cb_arg1;
+ struct spdk_jsonrpc_request *request = cb_arg2;
+
+ spdk_json_write_object_end(w);
+ spdk_jsonrpc_end_result(request, w);
}
-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)
+rpc_bdev_ocf_get_bdevs(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
{
- struct rpc_bdev_ocf_name req = {NULL};
+ struct rpc_bdev_ocf_get_bdevs req = {};
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) {
+ if (params && spdk_json_decode_object(params, rpc_bdev_ocf_get_bdevs_decoders,
+ SPDK_COUNTOF(rpc_bdev_ocf_get_bdevs_decoders),
+ &req)) {
+ SPDK_DEBUGLOG(vbdev_ocf_rpc, "spdk_json_decode_object failed\n");
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;
+ goto cleanup;
}
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);
+ vbdev_ocf_get_bdevs(params ? req.bdev_name : NULL, rpc_bdev_ocf_get_bdevs_cb, w, request);
-end:
- free_rpc_bdev_ocf_name(&req);
+cleanup:
+ free_rpc_bdev_ocf_get_bdevs(&req);
}
-SPDK_RPC_REGISTER("bdev_ocf_flush_status", rpc_bdev_ocf_flush_status, SPDK_RPC_RUNTIME)
+SPDK_RPC_REGISTER("bdev_ocf_get_bdevs", rpc_bdev_ocf_get_bdevs, SPDK_RPC_RUNTIME)
+
+SPDK_LOG_REGISTER_COMPONENT(vbdev_ocf_rpc)
diff --git a/module/bdev/ocf/volume.c b/module/bdev/ocf/volume.c
index ea4b54d16f4..b7f27faa0e8 100644
--- a/module/bdev/ocf/volume.c
+++ b/module/bdev/ocf/volume.c
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (C) 2019 Intel Corporation.
+ * Copyright (C) 2025 Huawei Technologies
* All rights reserved.
*/
@@ -7,30 +8,26 @@
#include "spdk/bdev_module.h"
#include "spdk/env.h"
-#include "spdk/thread.h"
#include "spdk/log.h"
+#include "spdk/string.h"
+#include "spdk/thread.h"
+#include "vbdev_ocf_core.h"
#include "data.h"
#include "volume.h"
#include "ctx.h"
-#include "vbdev_ocf.h"
+/* TODO?: refactor (like ocf_core_volume in ocf_core.c ?) */
static int
vbdev_ocf_volume_open(ocf_volume_t volume, void *opts)
{
struct vbdev_ocf_base **priv = ocf_volume_get_priv(volume);
- struct vbdev_ocf_base *base;
if (opts) {
- base = opts;
- } else {
- base = vbdev_ocf_get_base_by_name(ocf_volume_get_uuid(volume)->data);
- if (base == NULL) {
- return -ENODEV;
- }
+ *priv = opts;
}
- *priv = base;
+ assert(*priv);
return 0;
}
@@ -125,18 +122,18 @@ vbdev_forward_get_channel(ocf_volume_t volume, ocf_forward_token_t token)
*((struct vbdev_ocf_base **)
ocf_volume_get_priv(volume));
ocf_queue_t queue = ocf_forward_get_io_queue(token);
- struct vbdev_ocf_qctx *qctx;
+ struct vbdev_ocf_core_io_channel_ctx *ch_ctx;
if (unlikely(ocf_queue_is_mngt(queue))) {
- return base->management_channel;
+ return base->mngt_ch;
}
- qctx = ocf_queue_get_priv(queue);
- if (unlikely(qctx == NULL)) {
+ ch_ctx = ocf_queue_get_priv(queue);
+ if (unlikely(ch_ctx == NULL)) {
return NULL;
}
- return (base->is_cache) ? qctx->cache_ch : qctx->core_ch;
+ return (base->is_cache) ? ch_ctx->cache_ch : ch_ctx->core_ch;
}
static void
@@ -147,13 +144,19 @@ vbdev_forward_io(ocf_volume_t volume, ocf_forward_token_t token,
struct vbdev_ocf_base *base =
*((struct vbdev_ocf_base **)
ocf_volume_get_priv(volume));
- struct bdev_ocf_data *data = ocf_forward_get_data(token);
+ struct vbdev_ocf_data *data = ocf_forward_get_data(token);
struct spdk_io_channel *ch;
spdk_bdev_io_completion_cb cb = vbdev_forward_io_cb;
bool iovs_allocated = false;
int iovcnt, skip, status = -1;
struct iovec *iovs;
+ if (!base->attached) {
+ SPDK_ERRLOG("Base bdev '%s' not attached\n", base->name);
+ ocf_forward_end(token, -ENXIO);
+ return;
+ }
+
ch = vbdev_forward_get_channel(volume, token);
if (unlikely(ch == NULL)) {
ocf_forward_end(token, -EFAULT);
@@ -215,6 +218,12 @@ vbdev_forward_flush(ocf_volume_t volume, ocf_forward_token_t token)
uint64_t bytes = base->bdev->blockcnt * base->bdev->blocklen;
int status;
+ /* If base device doesn't support flush just ignore it and exit. */
+ if (unlikely(!spdk_bdev_io_type_supported(base->bdev, SPDK_BDEV_IO_TYPE_FLUSH))) {
+ ocf_forward_end(token, 0);
+ return;
+ }
+
ch = vbdev_forward_get_channel(volume, token);
if (unlikely(ch == NULL)) {
ocf_forward_end(token, -EFAULT);
@@ -282,7 +291,7 @@ vbdev_forward_io_simple(ocf_volume_t volume, ocf_forward_token_t token,
struct vbdev_ocf_base *base =
*((struct vbdev_ocf_base **)
ocf_volume_get_priv(volume));
- struct bdev_ocf_data *data = ocf_forward_get_data(token);
+ struct vbdev_ocf_data *data = ocf_forward_get_data(token);
struct vbdev_forward_io_simple_ctx *ctx;
int status = -1;
@@ -339,6 +348,35 @@ static struct ocf_volume_properties vbdev_volume_props = {
},
};
+static void
+_base_detach(void *ctx)
+{
+ struct vbdev_ocf_base *base = ctx;
+
+ spdk_put_io_channel(base->mngt_ch);
+ spdk_bdev_close(base->desc);
+}
+
+void
+vbdev_ocf_base_detach(struct vbdev_ocf_base *base)
+{
+ int rc;
+
+ if (base->thread && base->thread != spdk_get_thread()) {
+ if ((rc = spdk_thread_send_msg(base->thread, _base_detach, base))) {
+ SPDK_ERRLOG("OCF '%s': failed to send message to thread (name: %s, id: %ld): %s\n",
+ base->name,
+ spdk_thread_get_name(base->thread),
+ spdk_thread_get_id(base->thread),
+ spdk_strerror(-rc));
+ assert(false);
+ }
+ } else {
+ _base_detach(base);
+ }
+ base->attached = false;
+}
+
int
vbdev_ocf_volume_init(void)
{
diff --git a/module/bdev/ocf/volume.h b/module/bdev/ocf/volume.h
index 7e6629d4715..3545293e26a 100644
--- a/module/bdev/ocf/volume.h
+++ b/module/bdev/ocf/volume.h
@@ -1,16 +1,25 @@
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (C) 2018 Intel Corporation.
+ * Copyright (C) 2025 Huawei Technologies
* All rights reserved.
*/
-#ifndef VBDEV_OCF_DOBJ_H
-#define VBDEV_OCF_DOBJ_H
+#ifndef VBDEV_OCF_VOLUME_H
+#define VBDEV_OCF_VOLUME_H
-#include
+#define VBDEV_OCF_BDEV_NAME_SIZE spdk_max(OCF_CACHE_NAME_SIZE, OCF_CORE_NAME_SIZE)
-#include "ctx.h"
-#include "data.h"
+struct vbdev_ocf_base {
+ char name[VBDEV_OCF_BDEV_NAME_SIZE];
+ bool is_cache;
+ bool attached;
+ struct spdk_bdev *bdev;
+ struct spdk_bdev_desc *desc;
+ struct spdk_io_channel *mngt_ch;
+ struct spdk_thread *thread;
+};
+void vbdev_ocf_base_detach(struct vbdev_ocf_base *base);
int vbdev_ocf_volume_init(void);
void vbdev_ocf_volume_cleanup(void);
diff --git a/ocf b/ocf
index 6ad1007e6fa..6b433080dc4 160000
--- a/ocf
+++ b/ocf
@@ -1 +1 @@
-Subproject commit 6ad1007e6fa47ff6468eaba0b304d77334c34d4a
+Subproject commit 6b433080dc4e0038bcc50e243105fb7430e7de54
diff --git a/python/spdk/cli/bdev.py b/python/spdk/cli/bdev.py
index b52b18917e1..551c60f8f47 100644
--- a/python/spdk/cli/bdev.py
+++ b/python/spdk/cli/bdev.py
@@ -103,91 +103,214 @@ def bdev_crypto_delete(args):
p.add_argument('name', help='crypto bdev name')
p.set_defaults(func=bdev_crypto_delete)
- def bdev_ocf_create(args):
- print_json(args.client.bdev_ocf_create(
- name=args.name,
- mode=args.mode,
- cache_line_size=args.cache_line_size,
- cache_bdev_name=args.cache_bdev_name,
- core_bdev_name=args.core_bdev_name))
- p = subparsers.add_parser('bdev_ocf_create', help='Add an OCF block device')
- p.add_argument('name', help='Name of resulting OCF bdev')
- p.add_argument('mode', help='OCF cache mode', choices=['wb', 'wt', 'pt', 'wa', 'wi', 'wo'])
- p.add_argument(
- '--cache-line-size',
- help='OCF cache line size. The unit is KiB',
- type=int,
- choices=[4, 8, 16, 32, 64]
- )
- p.add_argument('cache_bdev_name', help='Name of underlying cache bdev')
- p.add_argument('core_bdev_name', help='Name of underlying core bdev')
- p.set_defaults(func=bdev_ocf_create)
-
- def bdev_ocf_delete(args):
- args.client.bdev_ocf_delete(name=args.name)
-
- p = subparsers.add_parser('bdev_ocf_delete', help='Delete an OCF block device')
- p.add_argument('name', help='Name of OCF bdev')
- p.set_defaults(func=bdev_ocf_delete)
-
- def bdev_ocf_get_stats(args):
- print_dict(args.client.bdev_ocf_get_stats(name=args.name))
- p = subparsers.add_parser('bdev_ocf_get_stats', help='Get statistics of chosen OCF block device')
- p.add_argument('name', help='Name of OCF bdev')
- p.set_defaults(func=bdev_ocf_get_stats)
-
- def bdev_ocf_reset_stats(args):
- print_dict(args.client.bdev_ocf_reset_stats(name=args.name))
- p = subparsers.add_parser('bdev_ocf_reset_stats', help='Reset statistics of chosen OCF block device')
- p.add_argument('name', help='Name of OCF bdev')
- p.set_defaults(func=bdev_ocf_reset_stats)
-
- def bdev_ocf_get_bdevs(args):
- print_dict(args.client.bdev_ocf_get_bdevs(name=args.name))
- p = subparsers.add_parser('bdev_ocf_get_bdevs', help='Get list of OCF devices including unregistered ones')
- p.add_argument('name', nargs='?', help='name of OCF vbdev or name of cache device or name of core device (optional)')
- p.set_defaults(func=bdev_ocf_get_bdevs)
-
- def bdev_ocf_set_cache_mode(args):
- print_json(args.client.bdev_ocf_set_cache_mode(
- name=args.name,
- mode=args.mode))
- p = subparsers.add_parser('bdev_ocf_set_cache_mode',
- help='Set cache mode of OCF block device')
- p.add_argument('name', help='Name of OCF bdev')
- 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_start_cache(args):
+ print_json(args.client.bdev_ocf_start_cache(
+ cache_name=args.cache_name,
+ base_name=args.base_name,
+ cache_mode=args.cache_mode,
+ cache_line_size=args.cache_line_size,
+ no_load=args.no_load))
+ p = subparsers.add_parser('bdev_ocf_start_cache', help='Start OCF cache instance')
+ p.add_argument('cache_name', help='name for the new OCF cache vbdev')
+ p.add_argument('base_name', help='name of the base bdev to use as cache')
+ p.add_argument('-m', '--cache-mode',
+ help='choose between {wt|wb|wa|wo|wi|pt}; default wt (Write-Through)',
+ choices=['wt', 'wb', 'wa', 'wo', 'wi', 'pt'])
+ p.add_argument('-l', '--cache-line-size',
+ help='choose between {4|8|16|32|64} [KiB]; default 4',
+ type=int,
+ choices=[4, 8, 16, 32, 64])
+ p.add_argument('-n', '--no-load',
+ action='store_true',
+ help='do not load previous cache instance from metadata and force starting a \
+ new one instead (WARNING: all cache metadata will be discarded!)')
+ p.set_defaults(func=bdev_ocf_start_cache)
+
+ def bdev_ocf_stop_cache(args):
+ args.client.bdev_ocf_stop_cache(cache_name=args.cache_name)
+ p = subparsers.add_parser('bdev_ocf_stop_cache', help='Stop OCF cache instance')
+ p.add_argument('cache_name', help='name of the cache vbdev to stop')
+ p.set_defaults(func=bdev_ocf_stop_cache)
+
+ def bdev_ocf_detach_cache(args):
+ args.client.bdev_ocf_detach_cache(cache_name=args.cache_name)
+ p = subparsers.add_parser('bdev_ocf_detach_cache', help='Detach caching device from OCF cache')
+ p.add_argument('cache_name', help='name of the cache vbdev to detach device from')
+ p.set_defaults(func=bdev_ocf_detach_cache)
+
+ def bdev_ocf_attach_cache(args):
+ args.client.bdev_ocf_attach_cache(
+ cache_name=args.cache_name,
+ base_name=args.base_name,
+ force=args.force)
+ p = subparsers.add_parser('bdev_ocf_attach_cache', help='Attach caching device to OCF cache')
+ p.add_argument('cache_name', help='name of the cache vbdev to attach device to')
+ p.add_argument('base_name', help='name of the base bdev to use as caching device')
+ p.add_argument('-f', '--force', action='store_true', help='discard cache metadata if present on device')
+ p.set_defaults(func=bdev_ocf_attach_cache)
+
+ def bdev_ocf_add_core(args):
+ print_json(args.client.bdev_ocf_add_core(
+ core_name=args.core_name,
+ base_name=args.base_name,
+ cache_name=args.cache_name))
+ p = subparsers.add_parser('bdev_ocf_add_core', help='Add core (backend device) to OCF cache')
+ p.add_argument('core_name', help='name for the new OCF core vbdev')
+ p.add_argument('base_name', help='name of the base bdev to use as core')
+ p.add_argument('cache_name', help='name of the cache vbdev to add this core to')
+ p.set_defaults(func=bdev_ocf_add_core)
+
+ def bdev_ocf_remove_core(args):
+ args.client.bdev_ocf_remove_core(core_name=args.core_name)
+ p = subparsers.add_parser('bdev_ocf_remove_core', help='Remove core (backend device) from OCF cache')
+ p.add_argument('core_name', help='name of the core vbdev to remove from OCF cache instance')
+ p.set_defaults(func=bdev_ocf_remove_core)
+
+ def bdev_ocf_set_cachemode(args):
+ args.client.bdev_ocf_set_cachemode(
+ cache_name=args.cache_name,
+ cache_mode=args.cache_mode)
+ p = subparsers.add_parser('bdev_ocf_set_cachemode', help='Set cache mode of OCF cache')
+ p.add_argument('cache_name', help='name of the cache vbdev')
+ p.add_argument('cache_mode',
+ help='choose between {wt|wb|wa|wo|wi|pt} (Write-Through, Write-Back, \
+ Write-Around, Write-Only, Write-Invalidate, Pass-Through); default "wt"',
+ choices=['wt', 'wb', 'wa', 'wo', 'wi', 'pt'])
+ p.set_defaults(func=bdev_ocf_set_cachemode)
+
+ def bdev_ocf_set_promotion(args):
+ args.client.bdev_ocf_set_promotion(
+ cache_name=args.cache_name,
+ policy=args.policy,
+ nhit_insertion_threshold=args.nhit_insertion_threshold,
+ nhit_trigger_threshold=args.nhit_trigger_threshold)
+ p = subparsers.add_parser('bdev_ocf_set_promotion', help='Set promotion parameters for OCF cache')
+ p.add_argument('cache_name', help='name of the cache vbdev')
+ p.add_argument('-p', '--policy',
+ help='promotion policy (choose between {always|nhit}; default "always")',
+ choices=['always', 'nhit'],
+ default='none')
+ p.add_argument('-i', '--nhit-insertion-threshold',
+ help='number of requests for given core line after which NHIT policy \
+ allows insertion into cache (range <2-1000>; default 3)',
+ type=int,
+ default=-1)
+ p.add_argument('-t', '--nhit-trigger-threshold',
+ help='cache occupancy value over which NHIT promotion is active (range <0-100> [%%]; default 80)',
+ type=int,
+ default=-1)
+ p.set_defaults(func=bdev_ocf_set_promotion)
+
+ def bdev_ocf_set_cleaning(args):
+ args.client.bdev_ocf_set_cleaning(
+ cache_name=args.cache_name,
+ policy=args.policy,
+ acp_wake_up_time=args.acp_wake_up_time,
+ acp_flush_max_buffers=args.acp_flush_max_buffers,
+ alru_wake_up_time=args.alru_wake_up_time,
+ alru_flush_max_buffers=args.alru_flush_max_buffers,
+ alru_staleness_time=args.alru_staleness_time,
+ alru_activity_threshold=args.alru_activity_threshold,
+ alru_dirty_ratio_threshold=args.alru_dirty_ratio_threshold,
+ alru_dirty_ratio_inertia=args.alru_dirty_ratio_inertia)
+ p = subparsers.add_parser('bdev_ocf_set_cleaning', help='Set cleaning parameters for OCF cache')
+ p.add_argument('cache_name', help='name of the cache vbdev')
+ p.add_argument('-p', '--policy',
+ help='cleaning policy (choose between {acp|alru|nop}; default "alru")',
+ choices=['acp', 'alru', 'nop'],
+ default='none')
+ p.add_argument('-u', '--acp-wake-up-time',
+ help='time between ACP cleaning thread iterations (range <0-10000> [ms]; default 10)',
+ type=int,
+ default=-1)
+ p.add_argument('-m', '--acp-flush-max-buffers',
+ help='number of dirty cache lines to be flushed in a single ACP \
+ cleaning thread iteration (range <1-10000>; default 128)',
+ type=int,
+ default=-1)
+ p.add_argument('-v', '--alru-wake-up-time',
+ help='cleaning thread sleep time after an idle wake up (range <0-3600> [s]; default 20)',
+ type=int,
+ default=-1)
+ p.add_argument('-n', '--alru-flush-max-buffers',
+ help='number of dirty cache lines to be flushed in one cleaning cycle (range <1-10000>; default 100)',
+ type=int,
+ default=-1)
+ p.add_argument('-s', '--alru-staleness-time',
+ help='time that has to pass from the last write operation before a dirty cache line \
+ can be scheduled to be flushed (range <1-3600> [s]; default 120)',
+ type=int,
+ default=-1)
+ p.add_argument('-t', '--alru-activity-threshold',
+ help='cache idle time before flushing thread can start (range <0-1000000> [ms]; default 10000)',
+ type=int,
+ default=-1)
+ p.add_argument('-d', '--alru-dirty-ratio-threshold',
+ help='dirty ratio of the cache device at which cleaning will be triggered (range <0-100> [%%]; default 100)',
+ type=int,
+ default=-1)
+ p.add_argument('-i', '--alru-dirty-ratio-inertia',
+ help='inertia for dirty ratio - cleaning triggered by exceeding dirty ratio threshold will stop \
+ when dirty ratio drops below (threshold - inertia) (range <0-4095> [MiB]; default 128)',
+ type=int,
+ default=-1)
+ p.set_defaults(func=bdev_ocf_set_cleaning)
def bdev_ocf_set_seqcutoff(args):
args.client.bdev_ocf_set_seqcutoff(
- name=args.name,
+ bdev_name=args.bdev_name,
policy=args.policy,
threshold=args.threshold,
- promotion_count=args.promotion_count)
- p = subparsers.add_parser('bdev_ocf_set_seqcutoff',
- help='Set sequential cutoff parameters on all cores for the given OCF cache device')
- p.add_argument('name', help='Name of OCF cache bdev')
- p.add_argument('-t', '--threshold', type=int,
- help='Activation threshold [KiB]')
- p.add_argument('-c', '--promotion-count', type=int,
- help='Promotion request count')
- p.add_argument('-p', '--policy', choices=['always', 'full', 'never'], required=True,
- help='Sequential cutoff policy')
+ promotion_count=args.promotion_count,
+ promote_on_threshold=args.promote_on_threshold)
+ p = subparsers.add_parser('bdev_ocf_set_seqcutoff', help='Set sequential cut-off parameters for OCF core or all cores in given cache')
+ p.add_argument('bdev_name', help='name of OCF vbdev')
+ p.add_argument('-p', '--policy',
+ help='sequential cut-off policy (choose between {always|full|never}; default "full")',
+ choices=['always', 'full', 'never'],
+ default='none')
+ p.add_argument('-t', '--threshold',
+ help='activation threshold (range <1-4194181> [KiB]; default 1)',
+ type=int,
+ default=-1)
+ p.add_argument('-c', '--promotion-count',
+ help='request count threshold for cutting off the sequential stream (range <1-65535>; default 8)',
+ type=int,
+ default=-1)
+ p.add_argument('-o', '--promote-on-threshold',
+ help='whether to promote core sequential cut-off stream to global structures \
+ when threshold is reached (choose between {0|1}; default 0)',
+ choices=[0, 1],
+ type=int,
+ default=-1)
p.set_defaults(func=bdev_ocf_set_seqcutoff)
def bdev_ocf_flush_start(args):
- args.client.bdev_ocf_flush_start(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')
+ args.client.bdev_ocf_flush_start(bdev_name=args.bdev_name)
+ p = subparsers.add_parser('bdev_ocf_flush_start', help='Flush all dirty data on the given OCF device \
+ (from cache to all of its cores or to particular core only). Note that this call only starts \
+ the flushing process which will be running in background and may take some time depending on \
+ the underlying device size and speed. You can check flushing status using the bdev_ocf_get_bdevs call.')
+ p.add_argument('bdev_name', help='name of OCF vbdev to flush')
p.set_defaults(func=bdev_ocf_flush_start)
- def bdev_ocf_flush_status(args):
- print_json(args.client.bdev_ocf_flush_status(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_ocf_get_stats(args):
+ print_dict(args.client.bdev_ocf_get_stats(bdev_name=args.bdev_name))
+ p = subparsers.add_parser('bdev_ocf_get_stats', help='Get statistics of OCF vbdev')
+ p.add_argument('bdev_name', help='name of OCF vbdev')
+ p.set_defaults(func=bdev_ocf_get_stats)
+
+ def bdev_ocf_reset_stats(args):
+ args.client.bdev_ocf_reset_stats(bdev_name=args.bdev_name)
+ p = subparsers.add_parser('bdev_ocf_reset_stats', help='Reset statistics of OCF vbdev')
+ p.add_argument('bdev_name', help='name of OCF vbdev')
+ p.set_defaults(func=bdev_ocf_reset_stats)
+
+ def bdev_ocf_get_bdevs(args):
+ print_dict(args.client.bdev_ocf_get_bdevs(bdev_name=args.bdev_name))
+ p = subparsers.add_parser('bdev_ocf_get_bdevs', help='Get detailed info about OCF vbdevs')
+ p.add_argument('bdev_name', nargs='?', help='optional name of specific OCF vbdev (shows all by default)')
+ p.set_defaults(func=bdev_ocf_get_bdevs)
def bdev_malloc_create(args):
num_blocks = (args.total_size * 1024 * 1024) // args.block_size
diff --git a/test/ocf/common.sh b/test/ocf/common.sh
index 44ffbf86a87..af6e1e5cd41 100644
--- a/test/ocf/common.sh
+++ b/test/ocf/common.sh
@@ -1,24 +1,890 @@
+#
# SPDX-License-Identifier: BSD-3-Clause
-# Copyright (C) 2020 Intel Corporation
+# Copyright (C) 2025 Huawei Technologies
# All rights reserved.
#
-source $rootdir/scripts/common.sh
-source $rootdir/test/common/autotest_common.sh
+source "$rootdir/test/common/autotest_common.sh"
+
+rpc_py="$rootdir/scripts/rpc.py"
+bdevperf_py="$rootdir/examples/bdev/bdevperf/bdevperf.py"
+
+cache_line_sizes=(4 8 16 32 64)
+cache_modes=("wt" "wb" "wa" "wo" "wi" "pt")
+promotion_policies=("always" "nhit")
+promotion_nhit_insertion_threshold_range=(2 1000)
+promotion_nhit_trigger_threshold_range=(0 100)
+cleaning_policies=("alru" "acp" "nop")
+cleaning_acp_wake_up_time_range=(0 10000)
+cleaning_acp_flush_max_buffers_range=(1 10000)
+cleaning_alru_wake_up_time_range=(0 3600)
+cleaning_alru_flush_max_buffers_range=(1 10000)
+cleaning_alru_staleness_time_range=(1 3600)
+cleaning_alru_activity_threshold_range=(0 1000000)
+cleaning_alru_dirty_ratio_threshold_range=(0 100)
+cleaning_alru_dirty_ratio_inertia_range=(0 4095)
+seqcutoff_policies=("always" "full" "never")
+seqcutoff_policy_default="full"
+seqcutoff_threshold_range=(1 4194181)
+seqcutoff_threshold_default=1
+seqcutoff_promotion_count_range=(1 65535)
+seqcutoff_promotion_count_default=8
+seqcutoff_promote_on_threshold_range=(0 1)
+seqcutoff_promote_on_threshold_default=false
+
+###### Change those depending on persistent storage availability:
+persistent_cache_addr=(05:00.0 06:00.0 08:00.0)
+
+persistent_cache_create() {
+ # $1: name of bdev to create
+ # $2: address/path/identifier of specific device
+
+ if [ $# -ne 2 ]; then
+ echo >&2 "invalid number of arguments"
+ exit 1
+ fi
+
+ local name=$1
+ local addr=$2
+
+ $rpc_py bdev_virtio_attach_controller -t pci -a $addr -d blk $name
+}
+
+persistent_cache_destroy() {
+ # $1: name of bdev to destroy
+
+ if [ $# -ne 1 ]; then
+ echo >&2 "invalid number of arguments"
+ exit 1
+ fi
+
+ local name=$1
+
+ $rpc_py bdev_virtio_detach_controller $name
+}
+######
+
+malloc_cache_create() {
+ # $1: name of bdev to create
+
+ if [ $# -ne 1 ]; then
+ echo >&2 "invalid number of arguments"
+ exit 1
+ fi
+
+ local name=$1
+
+ $rpc_py bdev_malloc_create -b $name 100 512
+}
+
+malloc_cache_destroy() {
+ # $1: name of bdev to destroy
+
+ if [ $# -ne 1 ]; then
+ echo >&2 "invalid number of arguments"
+ exit 1
+ fi
-rpc_py=$rootdir/scripts/rpc.py
+ local name=$1
-function clear_nvme() {
- mapfile -t bdf < <(get_first_nvme_bdf)
+ $rpc_py bdev_malloc_delete $name
+}
- # Clear metadata on NVMe device
- $rootdir/scripts/setup.sh reset
+malloc_core_create() {
+ # $1: name of bdev to create
- name=$(get_nvme_name_from_bdf "${bdf[0]}")
- mountpoints=$(lsblk /dev/$name --output MOUNTPOINT -n | wc -w)
- if [ "$mountpoints" != "0" ]; then
+ if [ $# -ne 1 ]; then
+ echo >&2 "invalid number of arguments"
exit 1
fi
- dd if=/dev/zero of=/dev/$name bs=1M count=1000 oflag=direct
- $rootdir/scripts/setup.sh
+
+ local name=$1
+
+ $rpc_py bdev_malloc_create -b $name 200 512
+}
+
+malloc_core_destroy() {
+ malloc_cache_destroy "$@"
+}
+
+start_spdk() {
+ "$SPDK_BIN_DIR/spdk_tgt" -L vbdev_ocf "$@" &
+ spdk_pid=$!
+ trap 'killprocess $spdk_pid; exit 1' SIGINT SIGTERM EXIT
+ waitforlisten $spdk_pid
+}
+
+stop_spdk() {
+ trap - SIGINT SIGTERM EXIT
+ killprocess $spdk_pid
+}
+
+start_bdevperf() {
+ "$SPDK_EXAMPLE_DIR/bdevperf" -z -L vbdev_ocf "$@" &
+ bdevperf_pid=$!
+ trap 'killprocess $bdevperf_pid; exit 1' SIGINT SIGTERM EXIT
+ waitforlisten $bdevperf_pid
+}
+
+stop_bdevperf() {
+ trap - SIGINT SIGTERM EXIT
+ killprocess $bdevperf_pid
+}
+
+ocf_settled() {
+ # Check if there are no caches currently busy (under a lock).
+ # Instead of the usual cache info, an empty JSON object is printed for such cache.
+ if ! ./scripts/rpc.py bdev_ocf_get_bdevs | jq -e \
+ '.caches | any(. == {}) | not'; then
+
+ return 1
+ fi
+
+ # By using array arithmetic check if there is no attached
+ # cores in wait list that belong to any attached cache.
+ # Those cores should be automatically added to their caches, so if there
+ # are any cores left it means that the adding process did not finish yet.
+ # Such condition can happen when base bdevs were found for started caches
+ # (or added cores in wait list) and then attaching caches and moving cores
+ # from wait list is done in background.
+ if ! ./scripts/rpc.py bdev_ocf_get_bdevs | jq -e \
+ '([.cores_waitlist[] | select(.base_attached).cache_name] | unique) -
+ (([.cores_waitlist[] | select(.base_attached).cache_name] | unique) -
+ ([.caches[] | select(.base_attached).name])) == []'; then
+
+ return 1
+ fi
+}
+
+create_caches_do() {
+ for i in {1..3}; do
+ malloc_cache_create Cache_dev$i
+ done
+}
+
+create_caches() {
+ create_caches_do
+
+ # Give it some time to settle before returning, as there might be
+ # some cores in wait list that need to be moved to attached caches.
+ waitforcondition ocf_settled
+
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.'
+}
+
+create_caches_with_metadata() {
+ create_caches
+ start_caches
+ stop_caches
+}
+
+create_caches_with_metadata_with_cores() {
+ create_caches
+ create_cores
+ start_caches
+ add_cores
+ stop_caches
+ destroy_cores
+}
+
+destroy_caches_do() {
+ for i in {1..3}; do
+ malloc_cache_destroy Cache_dev$i
+ done
+}
+
+destroy_caches() {
+ destroy_caches_do
+
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.'
+}
+
+create_caches_only_first() {
+ malloc_cache_create Cache_dev1
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.'
+}
+
+destroy_caches_only_first() {
+ malloc_cache_destroy Cache_dev1
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.'
+}
+
+create_caches_all_but_first() {
+ malloc_cache_create Cache_dev2
+ malloc_cache_create Cache_dev3
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.'
+}
+
+destroy_caches_all_but_first() {
+ malloc_cache_destroy Cache_dev2
+ malloc_cache_destroy Cache_dev3
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.'
+}
+
+create_caches_persistent_do() {
+ for i in {1..3}; do
+ persistent_cache_create Cache_dev$i "${persistent_cache_addr[i - 1]}"
+ done
+}
+
+create_caches_persistent() {
+ create_caches_persistent_do
+
+ # Give it a bit more time to settle, because this might be called after
+ # caches load with attached cores in wait list and it may take more time
+ # to load caches configuration and then move all cores to loaded caches.
+ waitforcondition ocf_settled 20
+
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.'
+}
+
+create_caches_persistent_with_metadata() {
+ create_caches_persistent
+ start_caches
+ stop_caches
+}
+
+create_caches_persistent_with_metadata_with_cores() {
+ create_caches_persistent
+ create_cores
+ start_caches
+ add_cores
+ stop_caches
+ destroy_cores
+}
+
+destroy_caches_persistent() {
+ for i in {1..3}; do
+ persistent_cache_destroy Cache_dev$i
+ done
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.'
+}
+
+create_cores_do() {
+ for i in {1..3}; do
+ for j in {1..3}; do
+ malloc_core_create Core_dev$i-$j
+ done
+ done
+}
+
+create_cores() {
+ create_cores_do
+
+ # Give it some time to settle before returning, as there might be some cores
+ # in wait list that need to be moved to their caches after attaching bases.
+ waitforcondition ocf_settled
+
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.'
+}
+
+destroy_cores_do() {
+ for i in {1..3}; do
+ for j in {1..3}; do
+ malloc_core_destroy Core_dev$i-$j
+ done
+ done
+}
+
+destroy_cores() {
+ destroy_cores_do
+
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.'
+}
+
+create_cores_only_first() {
+ for i in {1..3}; do
+ malloc_core_create Core_dev$i-1
+ done
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.'
+}
+
+destroy_cores_only_first() {
+ for i in {1..3}; do
+ malloc_core_destroy Core_dev$i-1
+ done
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.'
+}
+
+create_cores_all_but_first() {
+ for i in {1..3}; do
+ malloc_core_create Core_dev$i-2
+ malloc_core_create Core_dev$i-3
+ done
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.'
+}
+
+destroy_cores_all_but_first() {
+ for i in {1..3}; do
+ malloc_core_destroy Core_dev$i-2
+ malloc_core_destroy Core_dev$i-3
+ done
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.'
+}
+
+start_caches_do() {
+ for i in {1..3}; do
+ $rpc_py bdev_ocf_start_cache Ocf_cache$i Cache_dev$i "$@"
+ done
+}
+
+start_caches() {
+ start_caches_do --no-load
+
+ # Give it some time to settle before returning, as there might be
+ # some cores in wait list that need to be moved to attached caches.
+ waitforcondition ocf_settled
+
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.'
+}
+
+start_caches_try_load() {
+ start_caches_do
+
+ # Give it some time to settle before returning, as there might be
+ # some cores in wait list that need to be moved to attached caches.
+ waitforcondition ocf_settled
+
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.'
+}
+
+start_caches_with_cache_line_size() {
+ if [ $# -ne 1 ]; then
+ echo >&2 "invalid number of arguments"
+ exit 1
+ fi
+
+ local cache_line_size=$1
+
+ start_caches_do --cache-line-size $cache_line_size --no-load
+
+ # Give it some time to settle before returning, as there might be
+ # some cores in wait list that need to be moved to attached caches.
+ waitforcondition ocf_settled
+
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.'
+}
+
+stop_caches() {
+ for i in {1..3}; do
+ $rpc_py bdev_ocf_stop_cache Ocf_cache$i
+ done
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.'
+}
+
+detach_caches_do() {
+ for i in {1..3}; do
+ $rpc_py bdev_ocf_detach_cache Ocf_cache$i
+ done
+}
+
+detach_caches() {
+ detach_caches_do
+
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.'
+}
+
+attach_caches_do() {
+ for i in {1..3}; do
+ $rpc_py bdev_ocf_attach_cache Ocf_cache$i Cache_dev$i
+ done
+}
+
+attach_caches() {
+ attach_caches_do
+
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.'
+}
+
+add_cores_do() {
+ for i in {1..3}; do
+ for j in {1..3}; do
+ $rpc_py bdev_ocf_add_core Ocf_core$i-$j Core_dev$i-$j Ocf_cache$i
+ done
+ done
+}
+
+add_cores() {
+ add_cores_do
+
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.'
+}
+
+remove_cores_do() {
+ for i in {1..3}; do
+ for j in {1..3}; do
+ $rpc_py bdev_ocf_remove_core Ocf_core$i-$j
+ done
+ done
+}
+
+remove_cores() {
+ remove_cores_do
+
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.'
+}
+
+set_cache_mode() {
+ if [ $# -ne 1 ]; then
+ echo >&2 "invalid number of arguments"
+ exit 1
+ fi
+
+ local cache_mode=$1
+
+ for i in {1..3}; do
+ $rpc_py bdev_ocf_set_cachemode Ocf_cache$i $cache_mode
+ done
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.'
+}
+
+set_promotion_always_params() {
+ for i in {1..3}; do
+ $rpc_py bdev_ocf_set_promotion Ocf_cache$i \
+ --policy always
+ done
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.'
+}
+
+set_promotion_nhit_params() {
+ if [ $# -ne 2 ]; then
+ echo >&2 "invalid number of arguments"
+ exit 1
+ fi
+
+ local insertion_threshold=$1
+ local trigger_threshold=$2
+
+ for i in {1..3}; do
+ $rpc_py bdev_ocf_set_promotion Ocf_cache$i \
+ --policy nhit \
+ --nhit-insertion-threshold $insertion_threshold \
+ --nhit-trigger-threshold $trigger_threshold
+ done
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.'
+}
+
+set_cleaning_alru_params() {
+ if [ $# -ne 6 ]; then
+ echo >&2 "invalid number of arguments"
+ exit 1
+ fi
+
+ local wake_up_time=$1
+ local flush_max_buffers=$2
+ local staleness_time=$3
+ local activity_threshold=$4
+ local dirty_ratio_threshold=$5
+ local dirty_ratio_inertia=$6
+
+ for i in {1..3}; do
+ $rpc_py bdev_ocf_set_cleaning Ocf_cache$i \
+ --policy alru \
+ --alru-wake-up-time $wake_up_time \
+ --alru-flush-max-buffers $flush_max_buffers \
+ --alru-staleness-time $staleness_time \
+ --alru-activity-threshold $activity_threshold \
+ --alru-dirty-ratio-threshold $dirty_ratio_threshold \
+ --alru-dirty-ratio-inertia $dirty_ratio_inertia
+ done
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.'
+}
+
+set_cleaning_acp_params() {
+ if [ $# -ne 2 ]; then
+ echo >&2 "invalid number of arguments"
+ exit 1
+ fi
+
+ local wake_up_time=$1
+ local flush_max_buffers=$2
+
+ for i in {1..3}; do
+ $rpc_py bdev_ocf_set_cleaning Ocf_cache$i \
+ --policy acp \
+ --acp-wake-up-time $wake_up_time \
+ --acp-flush-max-buffers $flush_max_buffers
+ done
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.'
+}
+
+set_cleaning_nop_params() {
+ for i in {1..3}; do
+ $rpc_py bdev_ocf_set_cleaning Ocf_cache$i \
+ --policy nop
+ done
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.'
+}
+
+set_seqcutoff_params() {
+ if [ $# -ne 4 ]; then
+ echo >&2 "invalid number of arguments"
+ exit 1
+ fi
+
+ local policy=$1
+ local threshold=$2
+ local promotion_count=$3
+ local promote_on_threshold=$4
+
+ for i in {1..3}; do
+ $rpc_py bdev_ocf_set_seqcutoff Ocf_core$i-1 \
+ --policy $policy \
+ --threshold $threshold \
+ --promotion-count $promotion_count \
+ --promote-on-threshold $promote_on_threshold
+ done
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.'
+}
+
+set_seqcutoff_params_all() {
+ if [ $# -ne 4 ]; then
+ echo >&2 "invalid number of arguments"
+ exit 1
+ fi
+
+ local policy=$1
+ local threshold=$2
+ local promotion_count=$3
+ local promote_on_threshold=$4
+
+ for i in {1..3}; do
+ $rpc_py bdev_ocf_set_seqcutoff Ocf_cache$i \
+ --policy $policy \
+ --threshold $threshold \
+ --promotion-count $promotion_count \
+ --promote-on-threshold $promote_on_threshold
+ done
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.'
+}
+
+__check_caches_base_claimed() {
+ $rpc_py bdev_get_bdevs | jq -e '[.[] | select(.name | test("Cache_dev"))] | all(.claimed)'
+}
+
+__check_caches_base_not_claimed() {
+ $rpc_py bdev_get_bdevs | jq -e '[.[] | select(.name | test("Cache_dev"))] | any(.claimed) | not'
+}
+
+__check_caches_empty() {
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches | length == 0'
+}
+
+__check_caches_attached() {
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches | length == 3'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches | all(.base_attached)'
+}
+
+__check_caches_detached() {
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches | length == 3'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches | any(.base_attached) | not'
+}
+
+__check_caches_detached_only_first() {
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches | length == 3'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches[0].base_attached | not'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches[1].base_attached'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches[2].base_attached'
+}
+
+__check_caches_detached_all_but_first() {
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches | length == 3'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches[0].base_attached'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches[1].base_attached | not'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches[2].base_attached | not'
+}
+
+__check_cores_base_claimed() {
+ $rpc_py bdev_get_bdevs | jq -e '[.[] | select(.name | test("Core_dev"))] | all(.claimed)'
+}
+
+__check_cores_base_not_claimed() {
+ $rpc_py bdev_get_bdevs | jq -e '[.[] | select(.name | test("Core_dev"))] | any(.claimed) | not'
+}
+
+__check_cores_empty() {
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores | length] | all(. == 0)'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores_count] | all(. == 0)'
+}
+
+__check_cores_loading() {
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores | length] | all(. == 3)'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores_count] | all(. == 3)'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores[]] | any(.base_attached) | not'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores[]] | all(.loading)'
+}
+
+__check_cores_attached() {
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores | length] | all(. == 3)'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores_count] | all(. == 3)'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores[]] | all(.base_attached)'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores[]] | any(.loading) | not'
+}
+
+__check_cores_detached() {
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores | length] | all(. == 3)'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores_count] | all(. == 3)'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores[]] | any(.base_attached) | not'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores[]] | any(.loading) | not'
+}
+
+__check_cores_detached_only_first() {
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores | length] | all(. == 3)'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores_count] | all(. == 3)'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores[0]] | any(.base_attached) | not'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores[0]] | any(.loading) | not'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores[1]] | all(.base_attached)'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores[1]] | any(.loading) | not'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores[2]] | all(.base_attached)'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores[2]] | any(.loading) | not'
+}
+
+__check_cores_detached_all_but_first() {
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores | length] | all(. == 3)'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores_count] | all(. == 3)'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores[0]] | all(.base_attached)'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores[0]] | any(.loading) | not'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores[1]] | any(.base_attached) | not'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores[1]] | any(.loading) | not'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores[2]] | any(.base_attached) | not'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores[2]] | any(.loading) | not'
+}
+
+__check_cores_waitlist_empty() {
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.cores_waitlist | length == 0'
+}
+
+__check_cores_waitlist_attached() {
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.cores_waitlist | length == 9'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.cores_waitlist | all(.base_attached)'
+}
+
+__check_cores_waitlist_detached() {
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.cores_waitlist | length == 9'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.cores_waitlist | any(.base_attached) | not'
+}
+
+__check_cores_waitlist_detached_only_first() {
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.cores_waitlist | length == 9'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.cores_waitlist[0].base_attached | not'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.cores_waitlist[1].base_attached'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.cores_waitlist[2].base_attached'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.cores_waitlist[3].base_attached | not'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.cores_waitlist[4].base_attached'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.cores_waitlist[5].base_attached'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.cores_waitlist[6].base_attached | not'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.cores_waitlist[7].base_attached'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.cores_waitlist[8].base_attached'
+}
+
+__check_cores_waitlist_detached_all_but_first() {
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.cores_waitlist | length == 9'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.cores_waitlist[0].base_attached'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.cores_waitlist[1].base_attached | not'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.cores_waitlist[2].base_attached | not'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.cores_waitlist[3].base_attached'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.cores_waitlist[4].base_attached | not'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.cores_waitlist[5].base_attached | not'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.cores_waitlist[6].base_attached'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.cores_waitlist[7].base_attached | not'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.cores_waitlist[8].base_attached | not'
+}
+
+__check_setup_completed() {
+ for i in {1..3}; do
+ for j in {1..3}; do
+ waitforbdev Core_dev$i-$j 20000 > /dev/null
+ done
+ done
+
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.'
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_base_claimed
+ __check_cores_waitlist_empty
+ __check_cores_attached
+}
+
+__check_cache_line_size() {
+ if [ $# -ne 1 ]; then
+ echo >&2 "invalid number of arguments"
+ exit 1
+ fi
+
+ local cache_line_size=$1
+
+ $rpc_py bdev_ocf_get_bdevs | jq -e --argjson cache_line_size $cache_line_size \
+ '.caches | all(.cache_line_size == $cache_line_size * 1024)'
+}
+
+__check_cache_mode() {
+ if [ $# -ne 1 ]; then
+ echo >&2 "invalid number of arguments"
+ exit 1
+ fi
+
+ local cache_mode=$1
+
+ $rpc_py bdev_ocf_get_bdevs | jq -e --arg cache_mode $cache_mode \
+ '.caches | all(.cache_mode == $cache_mode)'
+}
+
+__check_promotion_always_params() {
+ $rpc_py bdev_ocf_get_bdevs | jq -e \
+ '.caches | all(.promotion.policy == "always")'
+}
+
+__check_promotion_nhit_params() {
+ if [ $# -ne 2 ]; then
+ echo >&2 "invalid number of arguments"
+ exit 1
+ fi
+
+ local insertion_threshold=$1
+ local trigger_threshold=$2
+
+ $rpc_py bdev_ocf_get_bdevs | jq -e \
+ --argjson insertion_threshold $insertion_threshold \
+ --argjson trigger_threshold $trigger_threshold \
+ '[.caches[].promotion] | all(
+ (.policy == "nhit") and
+ (.insertion_threshold == $insertion_threshold) and
+ (.trigger_threshold == $trigger_threshold))'
+}
+
+__check_cleaning_alru_params() {
+ if [ $# -ne 6 ]; then
+ echo >&2 "invalid number of arguments"
+ exit 1
+ fi
+
+ local wake_up_time=$1
+ local flush_max_buffers=$2
+ local staleness_time=$3
+ local activity_threshold=$4
+ local dirty_ratio_threshold=$5
+ local dirty_ratio_inertia=$6
+
+ $rpc_py bdev_ocf_get_bdevs | jq -e \
+ --argjson wake_up_time $wake_up_time \
+ --argjson flush_max_buffers $flush_max_buffers \
+ --argjson staleness_time $staleness_time \
+ --argjson activity_threshold $activity_threshold \
+ --argjson dirty_ratio_threshold $dirty_ratio_threshold \
+ --argjson dirty_ratio_inertia $dirty_ratio_inertia \
+ '[.caches[].cleaning] | all(
+ (.policy == "alru") and
+ (.wake_up_time == $wake_up_time) and
+ (.flush_max_buffers == $flush_max_buffers) and
+ (.staleness_time == $staleness_time) and
+ (.activity_threshold == $activity_threshold) and
+ (.dirty_ratio_threshold == $dirty_ratio_threshold) and
+ (.dirty_ratio_inertia == $dirty_ratio_inertia * 1024 * 1024))'
+}
+
+__check_cleaning_acp_params() {
+ if [ $# -ne 2 ]; then
+ echo >&2 "invalid number of arguments"
+ exit 1
+ fi
+
+ local wake_up_time=$1
+ local flush_max_buffers=$2
+
+ $rpc_py bdev_ocf_get_bdevs | jq -e \
+ --argjson wake_up_time $wake_up_time \
+ --argjson flush_max_buffers $flush_max_buffers \
+ '[.caches[].cleaning] | all(
+ (.policy == "acp") and
+ (.wake_up_time == $wake_up_time) and
+ (.flush_max_buffers == $flush_max_buffers))'
+}
+
+__check_cleaning_nop_params() {
+ $rpc_py bdev_ocf_get_bdevs | jq -e \
+ '.caches | all(.cleaning.policy == "nop")'
+}
+
+__check_seqcutoff_params() {
+ if [ $# -ne 4 ]; then
+ echo >&2 "invalid number of arguments"
+ exit 1
+ fi
+
+ local policy=$1
+ local threshold=$2
+ local promotion_count=$3
+ if [ $4 -eq 0 ]; then
+ local promote_on_threshold=false
+ elif [ $4 -eq 1 ]; then
+ local promote_on_threshold=true
+ fi
+
+ $rpc_py bdev_ocf_get_bdevs | jq -e \
+ --arg policy $policy \
+ --argjson threshold $threshold \
+ --argjson promotion_count $promotion_count \
+ --argjson promote_on_threshold $promote_on_threshold \
+ '[.caches[].cores[0].seq_cutoff] | all(
+ (.policy == $policy) and
+ (.threshold == $threshold * 1024) and
+ (.promotion_count == $promotion_count) and
+ (.promote_on_threshold == $promote_on_threshold))'
+
+ $rpc_py bdev_ocf_get_bdevs | jq -e \
+ --arg policy $seqcutoff_policy_default \
+ --argjson threshold $seqcutoff_threshold_default \
+ --argjson promotion_count $seqcutoff_promotion_count_default \
+ --argjson promote_on_threshold $seqcutoff_promote_on_threshold_default \
+ '[.caches[].cores[1].seq_cutoff] | all(
+ (.policy == $policy) and
+ (.threshold == $threshold * 1024) and
+ (.promotion_count == $promotion_count) and
+ (.promote_on_threshold == $promote_on_threshold))'
+}
+
+__check_seqcutoff_params_all() {
+ if [ $# -ne 4 ]; then
+ echo >&2 "invalid number of arguments"
+ exit 1
+ fi
+
+ local policy=$1
+ local threshold=$2
+ local promotion_count=$3
+ if [ $4 -eq 0 ]; then
+ local promote_on_threshold=false
+ elif [ $4 -eq 1 ]; then
+ local promote_on_threshold=true
+ fi
+
+ $rpc_py bdev_ocf_get_bdevs | jq -e \
+ --arg policy $policy \
+ --argjson threshold $threshold \
+ --argjson promotion_count $promotion_count \
+ --argjson promote_on_threshold $promote_on_threshold \
+ '[.caches[].cores[].seq_cutoff] | all(
+ (.policy == $policy) and
+ (.threshold == $threshold * 1024) and
+ (.promotion_count == $promotion_count) and
+ (.promote_on_threshold == $promote_on_threshold))'
+}
+
+# Generate random number between range.
+random_number() {
+ # $1: lower range (inclusive)
+ # $2: upper range (inclusive)
+ # stdout: random number between range
+
+ echo $(($1 + RANDOM % ($2 - $1 + 1)))
+}
+
+# Convert an array of items to comma separated list of items.
+array_to_comma_list() {
+ # $1: name of array variable
+ # stdout: string with comma separated elements
+
+ IFS=","
+ declare -n array="$1"
+ echo "${array[*]}"
}
diff --git a/test/ocf/integrity/bdev_config.json b/test/ocf/integrity/bdev_config.json
new file mode 100644
index 00000000000..22bc8f2251d
--- /dev/null
+++ b/test/ocf/integrity/bdev_config.json
@@ -0,0 +1,62 @@
+{
+ "subsystems": [
+ {
+ "subsystem": "bdev",
+ "config": [
+ {
+ "method": "bdev_malloc_create",
+ "params": {
+ "name": "Mal_cache1",
+ "num_blocks": 409600,
+ "block_size": 512
+ }
+ },
+ {
+ "method": "bdev_malloc_create",
+ "params": {
+ "name": "Mal_core1-1",
+ "num_blocks": 819200,
+ "block_size": 512
+ }
+ },
+ {
+ "method": "bdev_malloc_create",
+ "params": {
+ "name": "Mal_core1-2",
+ "num_blocks": 819200,
+ "block_size": 512
+ }
+ },
+ {
+ "method": "bdev_ocf_start_cache",
+ "params": {
+ "cache_name": "Ocf_cache1",
+ "base_name": "Mal_cache1",
+ "cache_mode": "wt",
+ "cache_line_size": 4096,
+ "no_load": true
+ }
+ },
+ {
+ "method": "bdev_ocf_add_core",
+ "params": {
+ "core_name": "Ocf_core1-1",
+ "base_name": "Mal_core1-1",
+ "cache_name": "Ocf_cache1"
+ }
+ },
+ {
+ "method": "bdev_ocf_add_core",
+ "params": {
+ "core_name": "Ocf_core1-2",
+ "base_name": "Mal_core1-2",
+ "cache_name": "Ocf_cache1"
+ }
+ },
+ {
+ "method": "bdev_wait_for_examine"
+ }
+ ]
+ }
+ ]
+}
diff --git a/test/ocf/integrity/bdevperf-iotypes.sh b/test/ocf/integrity/bdevperf-iotypes.sh
deleted file mode 100755
index 4d159c5c36c..00000000000
--- a/test/ocf/integrity/bdevperf-iotypes.sh
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright (C) 2019 Intel Corporation
-# All rights reserved.
-#
-curdir=$(dirname $(readlink -f "${BASH_SOURCE[0]}"))
-rootdir=$(readlink -f $curdir/../../..)
-source $rootdir/test/common/autotest_common.sh
-
-bdevperf=$rootdir/build/examples/bdevperf
-
-source "$curdir/mallocs.conf"
-$bdevperf --json <(gen_malloc_ocf_json) -q 128 -o 4096 -t 4 -w flush
-$bdevperf --json <(gen_malloc_ocf_json) -q 128 -o 4096 -t 4 -w unmap
-$bdevperf --json <(gen_malloc_ocf_json) -q 128 -o 4096 -t 4 -w write
diff --git a/test/ocf/integrity/fio-modes.sh b/test/ocf/integrity/fio-modes.sh
deleted file mode 100755
index ca930b7ff2c..00000000000
--- a/test/ocf/integrity/fio-modes.sh
+++ /dev/null
@@ -1,103 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright (C) 2019 Intel Corporation
-# All rights reserved.
-#
-curdir=$(dirname $(readlink -f "${BASH_SOURCE[0]}"))
-rootdir=$(readlink -f $curdir/../../..)
-
-source $rootdir/test/ocf/common.sh
-
-function fio_verify() {
- fio_bdev $curdir/test.fio --aux-path=/tmp/ --ioengine=spdk_bdev "$@"
-}
-
-function cleanup() {
- rm -f $curdir/modes.conf
-}
-
-# Clear nvme device which we will use in test
-clear_nvme
-
-trap "cleanup; exit 1" SIGINT SIGTERM EXIT
-
-# Building config is not backtrace worthy ...
-xtrace_disable
-
-config=() ocf_names=() ocf_modes=()
-
-ocf_names[1]=PT_Nvme ocf_modes[1]=pt
-ocf_names[2]=WT_Nvme ocf_modes[2]=wt
-ocf_names[3]=WB_Nvme0 ocf_modes[3]=wb
-ocf_names[4]=WB_Nvme1 ocf_modes[4]=wb
-
-mapfile -t config < <("$rootdir/scripts/gen_nvme.sh")
-
-# Drop anything from last closing ] so we can inject our own config pieces ...
-config=("${config[@]::${#config[@]}-2}")
-# ... and now convert entire array to a single string item
-config=("${config[*]}")
-
-config+=(
- "$(
- cat <<- JSON
- {
- "method": "bdev_split_create",
- "params": {
- "base_bdev": "Nvme0n1",
- "split_count": 8,
- "split_size_mb": 101
- }
- }
- JSON
- )"
-)
-
-for ((d = 0, c = 1; d <= ${#ocf_names[@]} + 2; d += 2, c++)); do
- config+=(
- "$(
- cat <<- JSON
- {
- "method": "bdev_ocf_create",
- "params": {
- "name": "${ocf_names[c]}",
- "mode": "${ocf_modes[c]}",
- "cache_bdev_name": "Nvme0n1p$d",
- "core_bdev_name": "Nvme0n1p$((d + 1))"
- }
- }
- JSON
- )"
- )
-done
-
-config+=(
- "$(
- cat <<- JSON
- {
- "method": "bdev_wait_for_examine"
- }
- JSON
- )"
-)
-
-# First ']}' closes our config and bdev subsystem blocks
-cat <<- CONFIG > "$curdir/modes.conf"
- {"subsystems":[
- $(
- IFS=","
- printf '%s\n' "${config[*]}"
- )
- ]}]}
-CONFIG
-
-# Format the config nicely and dump it to stdout for everyone to marvel at it ...
-jq . "$curdir/modes.conf"
-
-# ... and now back to our regularly scheduled program
-xtrace_restore
-
-fio_verify --filename=PT_Nvme:WT_Nvme:WB_Nvme0:WB_Nvme1 --spdk_json_conf="$curdir/modes.conf" --thread=1
-
-trap - SIGINT SIGTERM EXIT
-cleanup
diff --git a/test/ocf/integrity/flush.sh b/test/ocf/integrity/flush.sh
index 29b1c7c82cf..30d044be93a 100755
--- a/test/ocf/integrity/flush.sh
+++ b/test/ocf/integrity/flush.sh
@@ -1,84 +1,50 @@
#!/usr/bin/env bash
+
+#
# SPDX-License-Identifier: BSD-3-Clause
-# Copyright (C) 2021 Intel Corporation.
+# Copyright (C) 2025 Huawei Technologies
# All rights reserved.
+#
curdir=$(dirname $(readlink -f "${BASH_SOURCE[0]}"))
-rootdir=$(readlink -f $curdir/../../..)
-source $rootdir/test/common/autotest_common.sh
+rootdir=$(readlink -f "$curdir/../../..")
+source "$rootdir/test/ocf/common.sh"
-bdevperf=$rootdir/build/examples/bdevperf
-rpc_py="$rootdir/scripts/rpc.py -s /var/tmp/spdk.sock"
+devices=("Ocf_cache1" "Ocf_core1-1" "Ocf_core1-2")
+cache_modes=("wb" "wo")
+io_patterns=("write" "rw" "randwrite" "randrw")
+io_depth=128
+io_size=4096
+rw_mix=50
+runtime=10
check_flush_in_progress() {
- $rpc_py bdev_ocf_flush_status MalCache0 \
- | jq -e '.in_progress' > /dev/null
+ $rpc_py bdev_ocf_get_bdevs $1 | jq -e '.flush.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
+start_bdevperf -c "$curdir/bdev_config.json"
+$rpc_py bdev_ocf_set_seqcutoff Ocf_cache1 --policy never
+$rpc_py bdev_ocf_set_promotion Ocf_cache1 --policy always
+$rpc_py bdev_ocf_set_cleaning Ocf_cache1 --policy nop
+
+for cache_mode in "${cache_modes[@]}"; do
+ $rpc_py bdev_ocf_set_cachemode Ocf_cache1 $cache_mode
+ for ip in "${io_patterns[@]}"; do
+ for dev in "${devices[@]}"; do
+ $bdevperf_py perform_tests -t $runtime -q $io_depth -o $io_size \
+ -w $ip $([[ $ip =~ rw$ ]] && echo "-M $rw_mix")
+ for dv in "${devices[@]}"; do
+ $rpc_py bdev_ocf_get_stats $dv | jq -e '.usage | .dirty.count > .clean.count'
+ done
+
+ $rpc_py bdev_ocf_flush_start $dev
+ while check_flush_in_progress $dev; do
+ sleep 1
+ done
+
+ $rpc_py bdev_ocf_get_bdevs $dev | jq -e '.flush.error == 0'
+ $rpc_py bdev_ocf_get_stats $dev | jq -e '.usage | .dirty.count == 0'
+ done
+ done
done
-$rpc_py bdev_ocf_flush_status MalCache0 | jq -e '.status == 0'
+stop_bdevperf
diff --git a/test/ocf/integrity/io_stats.sh b/test/ocf/integrity/io_stats.sh
new file mode 100755
index 00000000000..779fc49deb3
--- /dev/null
+++ b/test/ocf/integrity/io_stats.sh
@@ -0,0 +1,80 @@
+#!/usr/bin/env bash
+
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (C) 2025 Huawei Technologies
+# All rights reserved.
+#
+
+curdir=$(dirname $(readlink -f "${BASH_SOURCE[0]}"))
+rootdir=$(readlink -f "$curdir/../../..")
+source "$rootdir/test/ocf/common.sh"
+
+io_patterns=("read" "write" "rw" "randread" "randwrite" "randrw" "unmap" "flush")
+io_depth=(1 16 128)
+io_size=(512 4096)
+rw_mix=50
+runtime=10
+cpu_mask=0xF
+
+start_bdevperf -c "$curdir/bdev_config.json" -m $cpu_mask -C
+for cache_mode in "${cache_modes[@]}"; do
+ $rpc_py bdev_ocf_set_cachemode Ocf_cache1 $cache_mode
+ for id in "${io_depth[@]}"; do
+ for is in "${io_size[@]}"; do
+ for ip in "${io_patterns[@]}"; do
+ $rpc_py bdev_ocf_reset_stats Ocf_cache1
+ test_results=$($bdevperf_py perform_tests -t $runtime -q $id \
+ -o $is -w $ip $([[ $ip =~ rw$ ]] && echo "-M $rw_mix"))
+ stats_core1=$($rpc_py bdev_ocf_get_stats Ocf_core1-1)
+ stats_core2=$($rpc_py bdev_ocf_get_stats Ocf_core1-2)
+
+ xtrace_disable
+ reqs_sent_core1=$(echo $test_results | jq -e \
+ '[.results[] | select(.job == "Ocf_core1-1") |
+ .runtime * .iops] | add | round')
+ reqs_sent_core2=$(echo $test_results | jq -e \
+ '[.results[] | select(.job == "Ocf_core1-2") |
+ .runtime * .iops] | add | round')
+ reqs_recv_rd_core1=$(echo $stats_core1 | jq -e \
+ '.requests | .rd_total.count + .rd_pt.count')
+ reqs_recv_rd_core2=$(echo $stats_core2 | jq -e \
+ '.requests | .rd_total.count + .rd_pt.count')
+ reqs_recv_wr_core1=$(echo $stats_core1 | jq -e \
+ '.requests | .wr_total.count + .wr_pt.count')
+ reqs_recv_wr_core2=$(echo $stats_core2 | jq -e \
+ '.requests | .wr_total.count + .wr_pt.count')
+ reqs_recv_total_core1=$(echo $stats_core1 | jq -e '.requests.total.count')
+ reqs_recv_total_core2=$(echo $stats_core2 | jq -e '.requests.total.count')
+ xtrace_restore
+
+ if [[ $ip == "unmap" || $ip == "flush" ]]; then
+ continue
+ elif [[ $ip =~ read$ ]]; then
+ [ $reqs_recv_rd_core1 -eq $reqs_sent_core1 ]
+ [ $reqs_recv_rd_core2 -eq $reqs_sent_core2 ]
+ [ $reqs_recv_wr_core1 -eq 0 ]
+ [ $reqs_recv_wr_core2 -eq 0 ]
+ elif [[ $ip =~ write$ ]]; then
+ [ $reqs_recv_rd_core1 -eq 0 ]
+ [ $reqs_recv_rd_core2 -eq 0 ]
+ [ $reqs_recv_wr_core1 -eq $reqs_sent_core1 ]
+ [ $reqs_recv_wr_core2 -eq $reqs_sent_core2 ]
+ elif [[ $ip =~ rw$ ]]; then
+ [ $reqs_recv_rd_core1 -gt 0 ]
+ [ $reqs_recv_rd_core2 -gt 0 ]
+ [ $reqs_recv_wr_core1 -gt 0 ]
+ [ $reqs_recv_wr_core2 -gt 0 ]
+ reqs_rw_recv_core1=$((reqs_recv_rd_core1 + reqs_recv_wr_core1))
+ reqs_rw_recv_core2=$((reqs_recv_rd_core2 + reqs_recv_wr_core2))
+ [ $reqs_rw_recv_core1 -eq $reqs_sent_core1 ]
+ [ $reqs_rw_recv_core2 -eq $reqs_sent_core2 ]
+ fi
+
+ [ $reqs_recv_total_core1 -eq $reqs_sent_core1 ]
+ [ $reqs_recv_total_core2 -eq $reqs_sent_core2 ]
+ done
+ done
+ done
+done
+stop_bdevperf
diff --git a/test/ocf/integrity/mallocs.conf b/test/ocf/integrity/mallocs.conf
deleted file mode 100644
index 0e9b7993e22..00000000000
--- a/test/ocf/integrity/mallocs.conf
+++ /dev/null
@@ -1,62 +0,0 @@
-gen_malloc_ocf_json () {
- local size=300 # MB
- local block_size=512
- local config
-
- local malloc malloc_devs=3
- for (( malloc = 0; malloc < malloc_devs; malloc++ )); do
- config+=(
- "$(
- cat <<-JSON
- {
- "method": "bdev_malloc_create",
- "params": {
- "name": "Malloc$malloc",
- "num_blocks": $(( (size << 20) / block_size )),
- "block_size": 512
- }
- }
- JSON
- )"
- )
- done
-
- local ocfs ocf ocf_mode ocf_cache ocf_core
- ocfs=(1 2)
- ocf_mode[1]=wt ocf_cache[1]=Malloc0 ocf_core[1]=Malloc1
- ocf_mode[2]=pt ocf_cache[2]=Malloc0 ocf_core[2]=Malloc2
-
- for ocf in "${ocfs[@]}"; do
- config+=(
- "$(
- cat <<-JSON
- {
- "method": "bdev_ocf_create",
- "params": {
- "name": "MalCache$ocf",
- "mode": "${ocf_mode[ocf]}",
- "cache_bdev_name": "${ocf_cache[ocf]}",
- "core_bdev_name": "${ocf_core[ocf]}"
- }
- }
- JSON
- )"
- )
- done
-
- jq . <<-JSON
- {
- "subsystems": [
- {
- "subsystem": "bdev",
- "config": [
- $(IFS=","; printf '%s\n' "${config[*]}"),
- {
- "method": "bdev_wait_for_examine"
- }
- ]
- }
- ]
- }
- JSON
-}
diff --git a/test/ocf/integrity/mngt_during_io.sh b/test/ocf/integrity/mngt_during_io.sh
new file mode 100755
index 00000000000..240ecf3d924
--- /dev/null
+++ b/test/ocf/integrity/mngt_during_io.sh
@@ -0,0 +1,123 @@
+#!/usr/bin/env bash
+
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (C) 2025 Huawei Technologies
+# All rights reserved.
+#
+
+curdir=$(dirname $(readlink -f "${BASH_SOURCE[0]}"))
+rootdir=$(readlink -f "$curdir/../../..")
+source "$rootdir/test/ocf/common.sh"
+
+# change cache parameters and state during IO:
+
+for cache_mode in "${cache_modes[@]}"; do
+ start_bdevperf -c "$curdir/bdev_config.json"
+ $bdevperf_py perform_tests -t 600 -q 128 -o 4096 -w randrw -M 50 &
+ sleep 1
+
+ $rpc_py bdev_ocf_set_cachemode Ocf_cache1 $cache_mode
+ sleep 1
+
+ nhit_insertion_threshold=$(random_number ${promotion_nhit_insertion_threshold_range[0]} \
+ ${promotion_nhit_insertion_threshold_range[1]})
+ nhit_trigger_threshold=$(random_number ${promotion_nhit_trigger_threshold_range[0]} \
+ ${promotion_nhit_trigger_threshold_range[1]})
+ $rpc_py bdev_ocf_set_promotion Ocf_cache1 \
+ --policy nhit \
+ --nhit-insertion-threshold $nhit_insertion_threshold \
+ --nhit-trigger-threshold $nhit_trigger_threshold
+ sleep 1
+ $rpc_py bdev_ocf_set_promotion Ocf_cache1 --policy always
+ sleep 1
+
+ acp_wake_up_time=$(random_number ${cleaning_acp_wake_up_time_range[0]} \
+ ${cleaning_acp_wake_up_time_range[1]})
+ acp_flush_max_buffers=$(random_number ${cleaning_acp_flush_max_buffers_range[0]} \
+ ${cleaning_acp_flush_max_buffers_range[1]})
+ $rpc_py bdev_ocf_set_cleaning Ocf_cache1 \
+ --policy acp \
+ --acp-wake-up-time $acp_wake_up_time \
+ --acp-flush-max-buffers $acp_flush_max_buffers
+ sleep 1
+ $rpc_py bdev_ocf_set_cleaning Ocf_cache1 --policy alru
+ sleep 1
+
+ seqcutoff_threshold=$(random_number ${seqcutoff_threshold_range[0]} \
+ ${seqcutoff_threshold_range[1]})
+ seqcutoff_promotion_count=$(random_number ${seqcutoff_promotion_count_range[0]} \
+ ${seqcutoff_promotion_count_range[1]})
+ seqcutoff_promote_on_threshold=$(random_number ${seqcutoff_promote_on_threshold_range[0]} \
+ ${seqcutoff_promote_on_threshold_range[1]})
+ $rpc_py bdev_ocf_set_seqcutoff Ocf_cache1 \
+ --policy always \
+ --threshold $seqcutoff_threshold \
+ --promotion-count $seqcutoff_promotion_count \
+ --promote-on-threshold $seqcutoff_promote_on_threshold
+ sleep 1
+ $rpc_py bdev_ocf_set_seqcutoff Ocf_cache1 --policy never
+ sleep 1
+
+ $rpc_py bdev_ocf_detach_cache Ocf_cache1
+ sleep 1
+ $rpc_py bdev_ocf_attach_cache Ocf_cache1 Mal_cache1 -f
+ sleep 1
+
+ $rpc_py bdev_ocf_remove_core Ocf_core1-1
+ sleep 1
+ $rpc_py bdev_ocf_add_core Ocf_core1-1 Mal_core1-1 Ocf_cache1
+ sleep 1
+
+ $rpc_py bdev_ocf_get_stats Ocf_cache1
+ sleep 1
+ $rpc_py bdev_ocf_reset_stats Ocf_cache1
+ sleep 1
+ $rpc_py bdev_ocf_flush_start Ocf_cache1
+ sleep 1
+ $rpc_py bdev_ocf_get_bdevs
+ sleep 1
+ $rpc_py bdev_ocf_stop_cache Ocf_cache1
+ sleep 1
+
+ stop_bdevperf
+done
+
+# hot remove and examine during IO:
+
+for cache_mode in "${cache_modes[@]}"; do
+ start_bdevperf -c "$curdir/bdev_config.json"
+ $bdevperf_py perform_tests -t 600 -q 128 -o 4096 -w randrw -M 50 &
+ bpy_pid=$!
+ sleep 1
+
+ $rpc_py bdev_ocf_set_cachemode Ocf_cache1 $cache_mode
+ sleep 1
+
+ $rpc_py bdev_malloc_delete Mal_cache1
+ sleep 1
+ $rpc_py bdev_malloc_create -b Mal_cache1 200 512
+ sleep 1
+
+ $rpc_py bdev_malloc_delete Mal_core1-1
+ sleep 1
+ $rpc_py bdev_malloc_create -b Mal_core1-1 400 512
+ sleep 1
+
+ $rpc_py bdev_malloc_delete Mal_cache1
+ sleep 1
+ $rpc_py bdev_malloc_create -b Mal_cache1 200 512
+ sleep 1
+
+ $rpc_py bdev_malloc_delete Mal_core1-2
+ sleep 1
+ $rpc_py bdev_malloc_create -b Mal_core1-2 400 512
+ sleep 1
+
+ killprocess $bpy_pid
+ sleep 1
+ $bdevperf_py perform_tests -t 600 -q 128 -o 4096 -w randrw -M 50 &
+ sleep 5
+
+ stop_bdevperf
+done
diff --git a/test/ocf/integrity/run.sh b/test/ocf/integrity/run.sh
new file mode 100755
index 00000000000..86889add488
--- /dev/null
+++ b/test/ocf/integrity/run.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (C) 2025 Huawei Technologies
+# All rights reserved.
+#
+
+curdir=$(dirname $(readlink -f "${BASH_SOURCE[0]}"))
+rootdir=$(readlink -f "$curdir/../../..")
+
+source "$rootdir/test/common/autotest_common.sh"
+
+run_test "io_stats" "$curdir/io_stats.sh"
+run_test "mngt_during_io" "$curdir/mngt_during_io.sh"
+run_test "flush" "$curdir/flush.sh"
+run_test "stress" "$curdir/stress.sh"
diff --git a/test/ocf/integrity/stats.sh b/test/ocf/integrity/stats.sh
deleted file mode 100755
index 85177dde5e5..00000000000
--- a/test/ocf/integrity/stats.sh
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright (C) 2019 Intel Corporation
-# All rights reserved.
-#
-curdir=$(dirname $(readlink -f "${BASH_SOURCE[0]}"))
-rootdir=$(readlink -f $curdir/../../..)
-source $rootdir/test/common/autotest_common.sh
-
-bdevperf=$rootdir/build/examples/bdevperf
-
-source "$curdir/mallocs.conf"
-$bdevperf --json <(gen_malloc_ocf_json) -q 128 -o 4096 -w write -t 120 -r /var/tmp/spdk.sock &
-bdev_perf_pid=$!
-waitforlisten $bdev_perf_pid
-sleep 1
-$rpc_py bdev_ocf_get_stats MalCache1
-kill -9 $bdev_perf_pid
-wait $bdev_perf_pid || true
diff --git a/test/ocf/integrity/stress.sh b/test/ocf/integrity/stress.sh
new file mode 100755
index 00000000000..c4c85d27877
--- /dev/null
+++ b/test/ocf/integrity/stress.sh
@@ -0,0 +1,301 @@
+#!/usr/bin/env bash
+
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (C) 2025 Huawei Technologies
+# All rights reserved.
+#
+
+curdir=$(dirname $(readlink -f "${BASH_SOURCE[0]}"))
+rootdir=$(readlink -f "$curdir/../../..")
+source "$rootdir/test/ocf/common.sh"
+
+app_pid=
+bg_pids=()
+
+start_spdk_() {
+ "$SPDK_BIN_DIR/spdk_tgt" -L vbdev_ocf "$@" &
+ app_pid=$!
+ waitforlisten $app_pid
+}
+
+stop_spdk_() {
+ killprocess $app_pid
+}
+
+start_bdevperf_() {
+ "$SPDK_EXAMPLE_DIR/bdevperf" -z -L vbdev_ocf -c "$curdir/bdev_config.json" &
+ app_pid=$!
+ waitforlisten $app_pid
+ $bdevperf_py perform_tests -t 600 -q 128 -o 4096 -w randrw -M 50 &
+}
+
+stop_bdevperf_() {
+ killprocess $app_pid
+}
+
+run_in_bg() {
+ xtrace_disable
+ (while true; do
+ "$@" &> /dev/null || true
+ sleep 0.1
+ done) &
+ bg_pids+=($!)
+ xtrace_restore
+}
+
+cleanup() {
+ for pid in "${bg_pids[@]}"; do
+ killprocess $pid || true
+ done
+ killprocess $app_pid
+}
+
+trap 'cleanup; exit 1' SIGINT SIGTERM EXIT
+
+run_in_bg $rpc_py bdev_ocf_get_bdevs
+run_in_bg $rpc_py bdev_ocf_get_bdevs Ocf_cache1
+run_in_bg $rpc_py bdev_ocf_get_bdevs Ocf_core1-1
+run_in_bg $rpc_py bdev_ocf_get_bdevs Ocf_core1-2
+run_in_bg $rpc_py bdev_ocf_get_bdevs Ocf_core1-3
+run_in_bg $rpc_py bdev_ocf_get_bdevs Ocf_cache3
+run_in_bg $rpc_py bdev_ocf_get_bdevs Ocf_core3-1
+run_in_bg $rpc_py bdev_ocf_get_bdevs Ocf_core3-2
+run_in_bg $rpc_py bdev_ocf_get_bdevs Ocf_core3-3
+run_in_bg $rpc_py bdev_ocf_get_stats Ocf_cache1
+run_in_bg $rpc_py bdev_ocf_get_stats Ocf_core1-1
+run_in_bg $rpc_py bdev_ocf_get_stats Ocf_core1-2
+run_in_bg $rpc_py bdev_ocf_get_stats Ocf_core1-3
+run_in_bg $rpc_py bdev_ocf_get_stats Ocf_cache3
+run_in_bg $rpc_py bdev_ocf_get_stats Ocf_core3-1
+run_in_bg $rpc_py bdev_ocf_get_stats Ocf_core3-2
+run_in_bg $rpc_py bdev_ocf_get_stats Ocf_core3-3
+
+# Test fast setup under the load of multiple info dumping RPC calls.
+
+# basic:
+
+start_spdk_
+create_caches_do
+create_cores_do
+start_caches_do
+add_cores_do
+__check_setup_completed
+stop_spdk_
+
+start_spdk_
+create_caches_do
+create_cores_do
+add_cores_do
+start_caches_do
+__check_setup_completed
+stop_spdk_
+
+start_spdk_
+create_cores_do
+create_caches_do
+start_caches_do
+add_cores_do
+__check_setup_completed
+stop_spdk_
+
+start_spdk_
+create_cores_do
+create_caches_do
+add_cores_do
+start_caches_do
+__check_setup_completed
+stop_spdk_
+
+# examine:
+
+start_spdk_
+start_caches_do
+add_cores_do
+create_caches_do
+create_cores_do
+__check_setup_completed
+stop_spdk_
+
+start_spdk_
+start_caches_do
+add_cores_do
+create_cores_do
+create_caches_do
+__check_setup_completed
+stop_spdk_
+
+start_spdk_
+add_cores_do
+start_caches_do
+create_caches_do
+create_cores_do
+__check_setup_completed
+stop_spdk_
+
+start_spdk_
+add_cores_do
+start_caches_do
+create_cores_do
+create_caches_do
+__check_setup_completed
+stop_spdk_
+
+# load:
+
+start_spdk_
+create_caches_persistent_with_metadata_with_cores
+destroy_caches_persistent
+start_caches_do
+add_cores_do
+create_caches_persistent_do
+create_cores_do
+__check_setup_completed
+stop_spdk_
+
+start_spdk_
+create_caches_persistent_with_metadata_with_cores
+destroy_caches_persistent
+start_caches_do
+add_cores_do
+create_cores_do
+create_caches_persistent_do
+__check_setup_completed
+stop_spdk_
+
+start_spdk_
+create_caches_persistent_with_metadata_with_cores
+destroy_caches_persistent
+add_cores_do
+start_caches_do
+create_caches_persistent_do
+create_cores_do
+__check_setup_completed
+stop_spdk_
+
+start_spdk_
+create_caches_persistent_with_metadata_with_cores
+destroy_caches_persistent
+add_cores_do
+start_caches_do
+create_cores_do
+create_caches_persistent_do
+__check_setup_completed
+stop_spdk_
+
+# detach-attach:
+
+start_spdk_
+create_caches_do
+create_cores_do
+start_caches_do
+add_cores_do
+detach_caches_do
+attach_caches_do
+__check_setup_completed
+stop_spdk_
+
+start_spdk_
+create_caches_do
+create_cores_do
+start_caches_do
+detach_caches_do
+add_cores_do
+attach_caches_do
+__check_setup_completed
+stop_spdk_
+
+start_spdk_
+create_caches_do
+create_cores_do
+start_caches_do
+add_cores_do
+detach_caches_do
+remove_cores_do
+attach_caches_do
+add_cores_do
+__check_setup_completed
+stop_spdk_
+
+# hotremove:
+
+start_spdk_
+create_caches_do
+start_caches_do
+destroy_caches_do
+stop_spdk_
+
+start_spdk_
+create_cores_do
+add_cores_do
+destroy_cores_do
+stop_spdk_
+
+start_spdk_
+create_caches_do
+create_cores_do
+start_caches_do
+add_cores_do
+destroy_caches_do
+create_caches_do
+__check_setup_completed
+stop_spdk_
+
+start_spdk_
+create_caches_do
+create_cores_do
+start_caches_do
+add_cores_do
+destroy_cores_do
+create_cores_do
+__check_setup_completed
+stop_spdk_
+
+start_spdk_
+create_caches_do
+create_cores_do
+start_caches_do
+add_cores_do
+destroy_caches_do
+destroy_cores_do
+create_caches_do
+create_cores_do
+__check_setup_completed
+stop_spdk_
+
+# management during IO:
+
+start_bdevperf_
+for cache_mode in "${cache_modes[@]}"; do
+ $rpc_py bdev_ocf_set_cachemode Ocf_cache1 $cache_mode
+ $rpc_py bdev_ocf_set_promotion Ocf_cache1 \
+ --policy nhit \
+ --nhit-insertion-threshold ${promotion_nhit_insertion_threshold_range[1]} \
+ --nhit-trigger-threshold ${promotion_nhit_trigger_threshold_range[1]}
+ $rpc_py bdev_ocf_set_promotion Ocf_cache1 --policy always
+ $rpc_py bdev_ocf_set_cleaning Ocf_cache1 \
+ --policy acp \
+ --acp-wake-up-time ${cleaning_acp_wake_up_time_range[1]} \
+ --acp-flush-max-buffers ${cleaning_acp_flush_max_buffers_range[1]}
+ $rpc_py bdev_ocf_set_cleaning Ocf_cache1 --policy alru
+ $rpc_py bdev_ocf_set_seqcutoff Ocf_cache1 \
+ --policy always \
+ --threshold ${seqcutoff_threshold_range[1]} \
+ --promotion-count ${seqcutoff_promotion_count_range[1]} \
+ --promote-on-threshold ${seqcutoff_promote_on_threshold_range[1]}
+ $rpc_py bdev_ocf_set_seqcutoff Ocf_cache1 --policy never
+ $rpc_py bdev_ocf_detach_cache Ocf_cache1
+ $rpc_py bdev_ocf_attach_cache Ocf_cache1 Mal_cache1 -f
+ $rpc_py bdev_ocf_remove_core Ocf_core1-1
+ $rpc_py bdev_ocf_add_core Ocf_core1-1 Mal_core1-1 Ocf_cache1
+ $rpc_py bdev_ocf_reset_stats Ocf_cache1
+ $rpc_py bdev_ocf_flush_start Ocf_cache1
+ $rpc_py bdev_malloc_delete Mal_cache1
+ $rpc_py bdev_malloc_create -b Mal_cache1 200 512
+ $rpc_py bdev_malloc_delete Mal_core1-1
+ $rpc_py bdev_malloc_create -b Mal_core1-1 400 512
+done
+stop_bdevperf_
+
+trap - SIGINT SIGTERM EXIT
+cleanup
diff --git a/test/ocf/integrity/test.fio b/test/ocf/integrity/test.fio
deleted file mode 100644
index e45d1b95865..00000000000
--- a/test/ocf/integrity/test.fio
+++ /dev/null
@@ -1,40 +0,0 @@
-[global]
-thread=1
-group_reporting=1
-direct=1
-serialize_overlap=1
-time_based=1
-do_verify=1
-verify=md5
-verify_backlog=1024
-iodepth=128
-bs=4K
-runtime=10
-verify_state_save=0
-
-size=20%
-
-[job_1]
-offset=0
-rw=randwrite
-name=randwrite
-
-[job_2]
-offset=20%
-rw=randrw
-name=randrw
-
-[job_3]
-offset=40%
-rw=write
-name=write
-
-[job_4]
-offset=60%
-rw=rw
-name=rw
-
-[job_5]
-offset=80%
-rw=randwrite
-name=randwrite
diff --git a/test/ocf/management/config_change.sh b/test/ocf/management/config_change.sh
new file mode 100755
index 00000000000..4d9464ee069
--- /dev/null
+++ b/test/ocf/management/config_change.sh
@@ -0,0 +1,149 @@
+#!/usr/bin/env bash
+
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (C) 2025 Huawei Technologies
+# All rights reserved.
+#
+
+curdir=$(dirname $(readlink -f "${BASH_SOURCE[0]}"))
+rootdir=$(readlink -f "$curdir/../../..")
+source "$rootdir/test/ocf/common.sh"
+
+# cache line sizes and cache modes:
+
+for cache_line_size in "${cache_line_sizes[@]}"; do
+ for add_cores in false true; do
+ for create_caches in false true; do
+ for stop_caches in false true; do
+ start_spdk
+ start_caches_with_cache_line_size $cache_line_size
+ if [ $create_caches == true ]; then
+ create_caches
+ __check_caches_attached
+ if [ $add_cores == true ]; then
+ create_cores
+ add_cores
+ __check_cores_attached
+ fi
+ else
+ __check_caches_detached
+ fi
+ __check_cache_line_size $cache_line_size
+ for cache_mode in "${cache_modes[@]}"; do
+ set_cache_mode $cache_mode
+ __check_cache_mode $cache_mode
+ done
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_empty
+ fi
+ stop_spdk
+ done
+ done
+ done
+done
+
+# promotion params:
+
+for promotion_policy in "${promotion_policies[@]}"; do
+ for add_cores in false true; do
+ for stop_caches in false true; do
+ start_spdk
+ create_caches
+ start_caches
+ __check_caches_attached
+ if [ $add_cores == true ]; then
+ create_cores
+ add_cores
+ __check_cores_attached
+ fi
+ if [ $promotion_policy == always ]; then
+ set_promotion_always_params
+ __check_promotion_always_params
+ elif [ $promotion_policy == nhit ]; then
+ insertion_threshold=$(random_number ${promotion_nhit_insertion_threshold_range[0]} ${promotion_nhit_insertion_threshold_range[1]})
+ trigger_threshold=$(random_number ${promotion_nhit_trigger_threshold_range[0]} ${promotion_nhit_trigger_threshold_range[1]})
+ set_promotion_nhit_params $insertion_threshold $trigger_threshold
+ __check_promotion_nhit_params $insertion_threshold $trigger_threshold
+ fi
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_empty
+ fi
+ stop_spdk
+ done
+ done
+done
+
+# cleaning params:
+
+for cleaning_policy in "${cleaning_policies[@]}"; do
+ for add_cores in false true; do
+ for stop_caches in false true; do
+ start_spdk
+ create_caches
+ start_caches
+ __check_caches_attached
+ if [ $add_cores == true ]; then
+ create_cores
+ add_cores
+ __check_cores_attached
+ fi
+ if [ $cleaning_policy == alru ]; then
+ wake_up_time=$(random_number ${cleaning_alru_wake_up_time_range[0]} ${cleaning_alru_wake_up_time_range[1]})
+ flush_max_buffers=$(random_number ${cleaning_alru_flush_max_buffers_range[0]} ${cleaning_alru_flush_max_buffers_range[1]})
+ staleness_time=$(random_number ${cleaning_alru_staleness_time_range[0]} ${cleaning_alru_staleness_time_range[1]})
+ activity_threshold=$(random_number ${cleaning_alru_activity_threshold_range[0]} ${cleaning_alru_activity_threshold_range[1]})
+ dirty_ratio_threshold=$(random_number ${cleaning_alru_dirty_ratio_threshold_range[0]} ${cleaning_alru_dirty_ratio_threshold_range[1]})
+ dirty_ratio_inertia=$(random_number ${cleaning_alru_dirty_ratio_inertia_range[0]} ${cleaning_alru_dirty_ratio_inertia_range[1]})
+ set_cleaning_alru_params $wake_up_time $flush_max_buffers $staleness_time $activity_threshold $dirty_ratio_threshold $dirty_ratio_inertia
+ __check_cleaning_alru_params $wake_up_time $flush_max_buffers $staleness_time $activity_threshold $dirty_ratio_threshold $dirty_ratio_inertia
+ elif [ $cleaning_policy == acp ]; then
+ wake_up_time=$(random_number ${cleaning_acp_wake_up_time_range[0]} ${cleaning_acp_wake_up_time_range[1]})
+ flush_max_buffers=$(random_number ${cleaning_acp_flush_max_buffers_range[0]} ${cleaning_acp_flush_max_buffers_range[1]})
+ set_cleaning_acp_params $wake_up_time $flush_max_buffers
+ __check_cleaning_acp_params $wake_up_time $flush_max_buffers
+ elif [ $cleaning_policy == nop ]; then
+ set_cleaning_nop_params
+ __check_cleaning_nop_params
+ fi
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_empty
+ fi
+ stop_spdk
+ done
+ done
+done
+
+# sequential cut-off params:
+
+for device in core cache; do
+ for seqcutoff_policy in "${seqcutoff_policies[@]}"; do
+ for stop_caches in false true; do
+ start_spdk
+ create_caches
+ create_cores
+ start_caches
+ add_cores
+ __check_caches_attached
+ __check_cores_attached
+ threshold=$(random_number ${seqcutoff_threshold_range[0]} ${seqcutoff_threshold_range[1]})
+ promotion_count=$(random_number ${seqcutoff_promotion_count_range[0]} ${seqcutoff_promotion_count_range[1]})
+ promote_on_threshold=$(random_number ${seqcutoff_promote_on_threshold_range[0]} ${seqcutoff_promote_on_threshold_range[1]})
+ if [ $device == core ]; then
+ set_seqcutoff_params $seqcutoff_policy $threshold $promotion_count $promote_on_threshold
+ __check_seqcutoff_params $seqcutoff_policy $threshold $promotion_count $promote_on_threshold
+ elif [ $device == cache ]; then
+ set_seqcutoff_params_all $seqcutoff_policy $threshold $promotion_count $promote_on_threshold
+ __check_seqcutoff_params_all $seqcutoff_policy $threshold $promotion_count $promote_on_threshold
+ fi
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_empty
+ fi
+ stop_spdk
+ done
+ done
+done
diff --git a/test/ocf/management/configuration-change.sh b/test/ocf/management/configuration-change.sh
deleted file mode 100755
index b9e517033e8..00000000000
--- a/test/ocf/management/configuration-change.sh
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright (C) 2021 Intel Corporation
-# All rights reserved.
-#
-curdir=$(dirname $(readlink -f "${BASH_SOURCE[0]}"))
-rootdir=$(readlink -f $curdir/../../..)
-source $rootdir/test/common/autotest_common.sh
-
-rpc_py=$rootdir/scripts/rpc.py
-cache_line_sizes=(4 8 16 32 64)
-cache_modes=(wt wb pt wa wi wo)
-
-$SPDK_BIN_DIR/iscsi_tgt &
-spdk_pid=$!
-
-waitforlisten $spdk_pid
-
-# Create OCF cache with different cache line sizes
-for cache_line_size in "${cache_line_sizes[@]}"; do
- $rpc_py bdev_malloc_create 101 512 -b Malloc0
- $rpc_py bdev_malloc_create 101 512 -b Malloc1
- $rpc_py bdev_ocf_create Cache0 wt Malloc0 Malloc1 --cache-line-size $cache_line_size
-
- $rpc_py bdev_ocf_get_bdevs | jq -e \
- '.[0] | .started and .cache.attached and .core.attached'
-
- # Check if cache line size values are reported correctly
- $rpc_py bdev_get_bdevs -b Cache0 | jq -e \
- ".[0] | .driver_specific.cache_line_size == $cache_line_size"
- $rpc_py save_subsystem_config -n bdev | jq -e \
- ".config | .[] | select(.method == \"bdev_ocf_create\") | .params.cache_line_size == $cache_line_size"
-
- $rpc_py bdev_ocf_delete Cache0
- $rpc_py bdev_malloc_delete Malloc0
- $rpc_py bdev_malloc_delete Malloc1
-done
-
-# Prepare OCF cache for dynamic configuration switching
-$rpc_py bdev_malloc_create 101 512 -b Malloc0
-$rpc_py bdev_malloc_create 101 512 -b Malloc1
-$rpc_py bdev_ocf_create Cache0 wt Malloc0 Malloc1
-
-$rpc_py bdev_ocf_get_bdevs | jq -e \
- '.[0] | .started and .cache.attached and .core.attached'
-
-# Change cache mode
-for cache_mode in "${cache_modes[@]}"; do
- $rpc_py bdev_ocf_set_cache_mode Cache0 $cache_mode
-
- # Check if cache mode values are reported correctly
- $rpc_py bdev_get_bdevs -b Cache0 | jq -e \
- ".[0] | .driver_specific.mode == \"$cache_mode\""
- $rpc_py save_subsystem_config -n bdev | jq -e \
- ".config | .[] | select(.method == \"bdev_ocf_create\") | .params.mode == \"$cache_mode\""
-done
-
-# Change sequential cutoff
-$rpc_py bdev_ocf_set_seqcutoff Cache0 -p always -t 64
-$rpc_py bdev_ocf_set_seqcutoff Cache0 -p never -t 16
-
-trap - SIGINT SIGTERM EXIT
-killprocess $spdk_pid
diff --git a/test/ocf/management/create-destruct.sh b/test/ocf/management/create-destruct.sh
deleted file mode 100755
index 839b56945dc..00000000000
--- a/test/ocf/management/create-destruct.sh
+++ /dev/null
@@ -1,91 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright (C) 2019 Intel Corporation
-# All rights reserved.
-#
-curdir=$(dirname $(readlink -f "${BASH_SOURCE[0]}"))
-rootdir=$(readlink -f $curdir/../../..)
-source $rootdir/test/common/autotest_common.sh
-
-rpc_py=$rootdir/scripts/rpc.py
-
-function bdev_check_claimed() {
- if [ "$($rpc_py bdev_get_bdevs -b "$@" | jq '.[0].claimed')" = "true" ]; then
- return 0
- else
- return 1
- fi
-}
-
-$SPDK_BIN_DIR/iscsi_tgt &
-spdk_pid=$!
-
-trap 'killprocess $spdk_pid; exit 1' SIGINT SIGTERM EXIT
-
-waitforlisten $spdk_pid
-
-$rpc_py bdev_malloc_create 101 512 -b Malloc0
-$rpc_py bdev_malloc_create 101 512 -b Malloc1
-
-$rpc_py bdev_ocf_create PartCache wt Malloc0 NonExisting
-
-$rpc_py bdev_ocf_get_bdevs PartCache | jq -e \
- '.[0] | .started == false and .cache.attached and .core.attached == false'
-
-$rpc_py bdev_ocf_get_bdevs NonExisting | jq -e \
- '.[0] | .name == "PartCache"'
-
-if ! bdev_check_claimed Malloc0; then
- echo >&2 "Base device expected to be claimed now"
- exit 1
-fi
-
-$rpc_py bdev_ocf_delete PartCache
-if bdev_check_claimed Malloc0; then
- echo >&2 "Base device is not expected to be claimed now"
- exit 1
-fi
-
-$rpc_py bdev_ocf_create FullCache wt Malloc0 Malloc1
-
-$rpc_py bdev_ocf_get_bdevs FullCache | jq -e \
- '.[0] | .started and .cache.attached and .core.attached'
-
-if ! (bdev_check_claimed Malloc0 && bdev_check_claimed Malloc1); then
- echo >&2 "Base devices expected to be claimed now"
- exit 1
-fi
-
-$rpc_py bdev_ocf_delete FullCache
-if bdev_check_claimed Malloc0 && bdev_check_claimed Malloc1; then
- echo >&2 "Base devices are not expected to be claimed now"
- exit 1
-fi
-
-$rpc_py bdev_ocf_create HotCache wt Malloc0 Malloc1
-
-if ! (bdev_check_claimed Malloc0 && bdev_check_claimed Malloc1); then
- echo >&2 "Base devices expected to be claimed now"
- exit 1
-fi
-
-$rpc_py bdev_malloc_delete Malloc0
-
-if bdev_check_claimed Malloc1; then
- echo >&2 "Base device is not expected to be claimed now"
- exit 1
-fi
-
-status=$($rpc_py bdev_get_bdevs)
-gone=$(echo $status | jq 'map(select(.name == "HotCache")) == []')
-if [[ $gone == false ]]; then
- echo >&2 "OCF bdev is expected to unregister"
- exit 1
-fi
-
-# check if shutdown of running CAS bdev is ok
-$rpc_py bdev_ocf_create PartCache wt NonExisting Malloc1
-
-trap - SIGINT SIGTERM EXIT
-
-killprocess $spdk_pid
diff --git a/test/ocf/management/detach-attach.sh b/test/ocf/management/detach-attach.sh
new file mode 100755
index 00000000000..210953ee51a
--- /dev/null
+++ b/test/ocf/management/detach-attach.sh
@@ -0,0 +1,180 @@
+#!/usr/bin/env bash
+
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (C) 2025 Huawei Technologies
+# All rights reserved.
+#
+
+curdir=$(dirname $(readlink -f "${BASH_SOURCE[0]}"))
+rootdir=$(readlink -f "$curdir/../../..")
+source "$rootdir/test/ocf/common.sh"
+
+# caches only:
+
+for attach_caches in false true; do
+ for stop_caches in false true; do
+ start_spdk
+ create_caches
+ start_caches
+ __check_caches_attached
+ detach_caches
+ __check_caches_detached
+ if [ $attach_caches == true ]; then
+ attach_caches
+ __check_caches_attached
+ fi
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_empty
+ fi
+ stop_spdk
+ done
+done
+
+# add cores before detaching caches:
+
+for attach_caches in false true; do
+ for stop_caches in false true; do
+ start_spdk
+ create_caches
+ create_cores
+ start_caches
+ add_cores
+ __check_caches_attached
+ __check_cores_attached
+ detach_caches
+ __check_caches_detached
+ __check_cores_attached
+ if [ $attach_caches == true ]; then
+ attach_caches
+ __check_caches_attached
+ __check_cores_attached
+ fi
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_empty
+ fi
+ stop_spdk
+ done
+done
+
+# add cores after detaching caches:
+
+for attach_caches in false true; do
+ for stop_caches in false true; do
+ start_spdk
+ create_caches
+ create_cores
+ start_caches
+ __check_caches_attached
+ detach_caches
+ __check_caches_detached
+ add_cores
+ __check_caches_detached
+ __check_cores_waitlist_attached
+ if [ $attach_caches == true ]; then
+ attach_caches
+ __check_caches_attached
+ __check_cores_attached
+ __check_cores_waitlist_empty
+ fi
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_empty
+ fi
+ stop_spdk
+ done
+done
+
+# add cores before and remove after detaching caches:
+
+for attach_caches in false true; do
+ for stop_caches in false true; do
+ start_spdk
+ create_caches
+ create_cores
+ start_caches
+ add_cores
+ __check_caches_attached
+ __check_cores_attached
+ detach_caches
+ __check_caches_detached
+ __check_cores_attached
+ remove_cores
+ __check_caches_detached
+ __check_cores_empty
+ if [ $attach_caches == true ]; then
+ attach_caches
+ __check_caches_attached
+ __check_cores_empty
+ fi
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_empty
+ fi
+ stop_spdk
+ done
+done
+
+# add cores after detaching caches and remove before/without attaching:
+
+for attach_caches in false true; do
+ for stop_caches in false true; do
+ start_spdk
+ create_caches
+ create_cores
+ start_caches
+ __check_caches_attached
+ detach_caches
+ __check_caches_detached
+ add_cores
+ __check_caches_detached
+ __check_cores_empty
+ __check_cores_waitlist_attached
+ remove_cores
+ __check_caches_detached
+ __check_cores_empty
+ __check_cores_waitlist_empty
+ if [ $attach_caches == true ]; then
+ attach_caches
+ __check_caches_attached
+ __check_cores_empty
+ __check_cores_waitlist_empty
+ fi
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_empty
+ fi
+ stop_spdk
+ done
+done
+
+# add cores after detaching caches and remove after attaching:
+
+for stop_caches in false true; do
+ start_spdk
+ create_caches
+ create_cores
+ start_caches
+ __check_caches_attached
+ detach_caches
+ __check_caches_detached
+ add_cores
+ __check_caches_detached
+ __check_cores_empty
+ __check_cores_waitlist_attached
+ attach_caches
+ __check_caches_attached
+ __check_cores_attached
+ __check_cores_waitlist_empty
+ remove_cores
+ __check_caches_attached
+ __check_cores_empty
+ __check_cores_waitlist_empty
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_empty
+ fi
+ stop_spdk
+done
diff --git a/test/ocf/management/hotremove.sh b/test/ocf/management/hotremove.sh
new file mode 100755
index 00000000000..0fa0210526a
--- /dev/null
+++ b/test/ocf/management/hotremove.sh
@@ -0,0 +1,429 @@
+#!/usr/bin/env bash
+
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (C) 2025 Huawei Technologies
+# All rights reserved.
+#
+
+curdir=$(dirname $(readlink -f "${BASH_SOURCE[0]}"))
+rootdir=$(readlink -f "$curdir/../../..")
+source "$rootdir/test/ocf/common.sh"
+
+# cores in waitlist:
+
+for remove_cores in false true; do
+ for create_cores in false true; do
+ start_spdk
+ create_cores
+ add_cores
+ __check_cores_waitlist_attached
+ destroy_cores
+ __check_cores_waitlist_detached
+ if [ $create_cores == true ]; then
+ create_cores
+ __check_cores_waitlist_attached
+ fi
+ if [ $remove_cores == true ]; then
+ remove_cores
+ __check_cores_waitlist_empty
+ fi
+ stop_spdk
+ done
+done
+
+# cores in waitlist (destroying only first):
+
+for remove_cores in false true; do
+ for create_cores in false true; do
+ start_spdk
+ create_cores
+ add_cores
+ __check_cores_waitlist_attached
+ destroy_cores_only_first
+ __check_cores_waitlist_detached_only_first
+ if [ $create_cores == true ]; then
+ create_cores_only_first
+ __check_cores_waitlist_attached
+ fi
+ if [ $remove_cores == true ]; then
+ remove_cores
+ __check_cores_waitlist_empty
+ fi
+ stop_spdk
+ done
+done
+
+# cores in waitlist (destroying all but first):
+
+for remove_cores in false true; do
+ for create_cores in false true; do
+ start_spdk
+ create_cores
+ add_cores
+ __check_cores_waitlist_attached
+ destroy_cores_all_but_first
+ __check_cores_waitlist_detached_all_but_first
+ if [ $create_cores == true ]; then
+ create_cores_all_but_first
+ __check_cores_waitlist_attached
+ fi
+ if [ $remove_cores == true ]; then
+ remove_cores
+ __check_cores_waitlist_empty
+ fi
+ stop_spdk
+ done
+done
+
+# caches only:
+
+for create_caches in false true; do
+ for stop_caches in false true; do
+ start_spdk
+ create_caches
+ start_caches
+ __check_caches_attached
+ destroy_caches
+ __check_caches_detached
+ if [ $create_caches == true ]; then
+ create_caches
+ __check_caches_attached
+ fi
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_empty
+ fi
+ stop_spdk
+ done
+done
+
+# caches only (destroying only first):
+
+for create_caches in false true; do
+ for stop_caches in false true; do
+ start_spdk
+ create_caches
+ start_caches
+ __check_caches_attached
+ destroy_caches_only_first
+ __check_caches_detached_only_first
+ if [ $create_caches == true ]; then
+ create_caches_only_first
+ __check_caches_attached
+ fi
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_empty
+ fi
+ stop_spdk
+ done
+done
+
+# caches only (destroying all but first):
+
+for create_caches in false true; do
+ for stop_caches in false true; do
+ start_spdk
+ create_caches
+ start_caches
+ __check_caches_attached
+ destroy_caches_all_but_first
+ __check_caches_detached_all_but_first
+ if [ $create_caches == true ]; then
+ create_caches_all_but_first
+ __check_caches_attached
+ fi
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_empty
+ fi
+ stop_spdk
+ done
+done
+
+# hotremove caches:
+
+for remove_cores in false true; do
+ for create_caches in false true; do
+ for stop_caches in false true; do
+ start_spdk
+ create_caches
+ create_cores
+ start_caches
+ add_cores
+ __check_caches_attached
+ __check_cores_attached
+ destroy_caches
+ __check_caches_detached
+ __check_cores_attached
+ if [ $remove_cores == true ]; then
+ remove_cores
+ __check_caches_detached
+ __check_cores_empty
+ fi
+ if [ $create_caches == true ]; then
+ create_caches
+ __check_caches_attached
+ fi
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_empty
+ fi
+ stop_spdk
+ done
+ done
+done
+
+# hotremove caches (destroying only first):
+
+for remove_cores in false true; do
+ for create_caches in false true; do
+ for stop_caches in false true; do
+ start_spdk
+ create_caches
+ create_cores
+ start_caches
+ add_cores
+ __check_caches_attached
+ __check_cores_attached
+ destroy_caches_only_first
+ __check_caches_detached_only_first
+ __check_cores_attached
+ if [ $remove_cores == true ]; then
+ remove_cores
+ __check_caches_detached_only_first
+ __check_cores_empty
+ fi
+ if [ $create_caches == true ]; then
+ create_caches_only_first
+ __check_caches_attached
+ fi
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_empty
+ fi
+ stop_spdk
+ done
+ done
+done
+
+# hotremove caches (destroying all but first):
+
+for remove_cores in false true; do
+ for create_caches in false true; do
+ for stop_caches in false true; do
+ start_spdk
+ create_caches
+ create_cores
+ start_caches
+ add_cores
+ __check_caches_attached
+ __check_cores_attached
+ destroy_caches_all_but_first
+ __check_caches_detached_all_but_first
+ __check_cores_attached
+ if [ $remove_cores == true ]; then
+ remove_cores
+ __check_caches_detached_all_but_first
+ __check_cores_empty
+ fi
+ if [ $create_caches == true ]; then
+ create_caches_all_but_first
+ __check_caches_attached
+ fi
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_empty
+ fi
+ stop_spdk
+ done
+ done
+done
+
+# hotremove cores:
+
+for create_cores in false true; do
+ for remove_cores in false true; do
+ for stop_caches in false true; do
+ start_spdk
+ create_caches
+ create_cores
+ start_caches
+ add_cores
+ __check_caches_attached
+ __check_cores_attached
+ destroy_cores
+ __check_caches_attached
+ __check_cores_detached
+ if [ $create_cores == true ]; then
+ create_cores
+ __check_caches_attached
+ __check_cores_attached
+ fi
+ if [ $remove_cores == true ]; then
+ remove_cores
+ __check_caches_attached
+ __check_cores_empty
+ fi
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_empty
+ fi
+ stop_spdk
+ done
+ done
+done
+
+# hotremove cores (destroying only first):
+
+for create_cores in false true; do
+ for remove_cores in false true; do
+ for stop_caches in false true; do
+ start_spdk
+ create_caches
+ create_cores
+ start_caches
+ add_cores
+ __check_caches_attached
+ __check_cores_attached
+ destroy_cores_only_first
+ __check_caches_attached
+ __check_cores_detached_only_first
+ if [ $create_cores == true ]; then
+ create_cores_only_first
+ __check_caches_attached
+ __check_cores_attached
+ fi
+ if [ $remove_cores == true ]; then
+ remove_cores
+ __check_caches_attached
+ __check_cores_empty
+ fi
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_empty
+ fi
+ stop_spdk
+ done
+ done
+done
+
+# hotremove cores (destroying all but first):
+
+for create_cores in false true; do
+ for remove_cores in false true; do
+ for stop_caches in false true; do
+ start_spdk
+ create_caches
+ create_cores
+ start_caches
+ add_cores
+ __check_caches_attached
+ __check_cores_attached
+ destroy_cores_all_but_first
+ __check_caches_attached
+ __check_cores_detached_all_but_first
+ if [ $create_cores == true ]; then
+ create_cores_all_but_first
+ __check_caches_attached
+ __check_cores_attached
+ fi
+ if [ $remove_cores == true ]; then
+ remove_cores
+ __check_caches_attached
+ __check_cores_empty
+ fi
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_empty
+ fi
+ stop_spdk
+ done
+ done
+done
+
+# hotremove caches and cores:
+
+for create_caches_and_cores in false true; do
+ for stop_caches in false true; do
+ start_spdk
+ create_caches
+ create_cores
+ start_caches
+ add_cores
+ __check_caches_attached
+ __check_cores_attached
+ destroy_caches
+ __check_caches_detached
+ destroy_cores
+ __check_cores_detached
+ if [ $create_caches_and_cores == true ]; then
+ create_caches
+ __check_caches_attached
+ create_cores
+ __check_cores_attached
+ fi
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_empty
+ fi
+ stop_spdk
+ done
+done
+
+# hotremove caches and cores, then manually remove cores:
+
+for create_caches in false true; do
+ for stop_caches in false true; do
+ start_spdk
+ create_caches
+ create_cores
+ start_caches
+ add_cores
+ __check_caches_attached
+ __check_cores_attached
+ destroy_caches
+ __check_caches_detached
+ destroy_cores
+ __check_cores_detached
+ remove_cores
+ __check_caches_detached
+ __check_cores_empty
+ if [ $create_caches == true ]; then
+ create_caches
+ __check_caches_attached
+ __check_cores_empty
+ fi
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_empty
+ fi
+ stop_spdk
+ done
+done
+
+# hotremove caches and cores (inverse create after destroy order):
+
+for stop_caches in false true; do
+ start_spdk
+ create_caches
+ create_cores
+ start_caches
+ add_cores
+ __check_caches_attached
+ __check_cores_attached
+ destroy_caches
+ __check_caches_detached
+ destroy_cores
+ __check_cores_detached
+ create_cores
+ __check_cores_attached
+ create_caches
+ __check_caches_attached
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_empty
+ fi
+ stop_spdk
+done
diff --git a/test/ocf/management/info_dump.sh b/test/ocf/management/info_dump.sh
new file mode 100755
index 00000000000..f9c56b06626
--- /dev/null
+++ b/test/ocf/management/info_dump.sh
@@ -0,0 +1,57 @@
+#!/usr/bin/env bash
+
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (C) 2025 Huawei Technologies
+# All rights reserved.
+#
+
+curdir=$(dirname $(readlink -f "${BASH_SOURCE[0]}"))
+rootdir=$(readlink -f "$curdir/../../..")
+source "$rootdir/test/ocf/common.sh"
+
+start_spdk
+create_caches
+$rpc_py bdev_ocf_start_cache Ocf_cache1 Cache_dev1 --no-load
+$rpc_py bdev_ocf_start_cache Ocf_cache2 Cache_dev2 --no-load
+$rpc_py bdev_ocf_start_cache Ocf_cache3 Cache_dev3 --no-load --cache-mode pt --cache-line-size 64
+create_cores
+add_cores
+
+# first cache detached
+destroy_caches_only_first
+# first core in each cache detached
+destroy_cores_only_first
+# first core in wait list detached
+for i in {2..3}; do
+ $rpc_py bdev_malloc_create -b Core_dev_waitlist$i 200 512
+done
+for i in {1..3}; do
+ $rpc_py bdev_ocf_add_core Ocf_core_waitlist$i Core_dev_waitlist$i Ocf_cache_none
+done
+$rpc_py bdev_ocf_set_cachemode Ocf_cache2 wb
+
+$rpc_py bdev_ocf_get_bdevs | jq -e .
+
+# Test OCF statistics:
+$rpc_py bdev_ocf_get_stats Ocf_cache1 | jq -e \
+ '([.requests[], .blocks[], .errors[] | .count] | any(. != 0)) and
+ ([.requests[], .blocks[], .errors[] | .percentage] | any(. != "0.0"))'
+$rpc_py bdev_ocf_reset_stats Ocf_cache1
+$rpc_py bdev_ocf_get_stats Ocf_cache1 | jq -e \
+ '[.requests[], .blocks[], .errors[]] | all(.count == 0 and .percentage == "0.0")'
+
+# Test OCF get bdevs:
+# remove UUID fields before comparison as they are changing each run
+diff <($rpc_py bdev_ocf_get_bdevs | sed '/"uuid": /d' | jq -e .) \
+ <(sed '/"uuid": /d' "$curdir/info_dump_get_bdevs.json" | jq -e .)
+
+# Test general get bdevs driver specific info:
+diff <($rpc_py bdev_get_bdevs | jq -e '.[].driver_specific.ocf | select(. != null)') \
+ <(jq -e . "$curdir/info_dump_driver_specific.json")
+
+# Test current config dump:
+diff <($rpc_py save_subsystem_config -n bdev | jq -e '.config[] | select(.method | test("bdev_ocf_"))') \
+ <(jq -e . "$curdir/info_dump_save_config.json")
+
+stop_spdk
diff --git a/test/ocf/management/info_dump_driver_specific.json b/test/ocf/management/info_dump_driver_specific.json
new file mode 100644
index 00000000000..42d45cffb9b
--- /dev/null
+++ b/test/ocf/management/info_dump_driver_specific.json
@@ -0,0 +1,60 @@
+{
+ "name": "Ocf_core1-2",
+ "base_name": "Core_dev1-2",
+ "cache": {
+ "name": "Ocf_cache1",
+ "base_name": "Cache_dev1",
+ "cache_mode": "wt",
+ "cache_line_size": 4096
+ }
+}
+{
+ "name": "Ocf_core1-3",
+ "base_name": "Core_dev1-3",
+ "cache": {
+ "name": "Ocf_cache1",
+ "base_name": "Cache_dev1",
+ "cache_mode": "wt",
+ "cache_line_size": 4096
+ }
+}
+{
+ "name": "Ocf_core2-2",
+ "base_name": "Core_dev2-2",
+ "cache": {
+ "name": "Ocf_cache2",
+ "base_name": "Cache_dev2",
+ "cache_mode": "wb",
+ "cache_line_size": 4096
+ }
+}
+{
+ "name": "Ocf_core2-3",
+ "base_name": "Core_dev2-3",
+ "cache": {
+ "name": "Ocf_cache2",
+ "base_name": "Cache_dev2",
+ "cache_mode": "wb",
+ "cache_line_size": 4096
+ }
+}
+{
+ "name": "Ocf_core3-2",
+ "base_name": "Core_dev3-2",
+ "cache": {
+ "name": "Ocf_cache3",
+ "base_name": "Cache_dev3",
+ "cache_mode": "pt",
+ "cache_line_size": 65536
+ }
+}
+{
+ "name": "Ocf_core3-3",
+ "base_name": "Core_dev3-3",
+ "cache": {
+ "name": "Ocf_cache3",
+ "base_name": "Cache_dev3",
+ "cache_mode": "pt",
+ "cache_line_size": 65536
+ }
+}
diff --git a/test/ocf/management/info_dump_get_bdevs.json b/test/ocf/management/info_dump_get_bdevs.json
new file mode 100644
index 00000000000..d66dbc8a1da
--- /dev/null
+++ b/test/ocf/management/info_dump_get_bdevs.json
@@ -0,0 +1,294 @@
+{
+ "cores_waitlist": [
+ {
+ "name": "Ocf_core_waitlist1",
+ "cache_name": "Ocf_cache_none",
+ "base_name": "Core_dev_waitlist1",
+ "base_attached": false,
+ "size": null,
+ "block_size": null
+ },
+ {
+ "name": "Ocf_core_waitlist2",
+ "cache_name": "Ocf_cache_none",
+ "base_name": "Core_dev_waitlist2",
+ "base_attached": true,
+ "size": 209715200,
+ "block_size": 512
+ },
+ {
+ "name": "Ocf_core_waitlist3",
+ "cache_name": "Ocf_cache_none",
+ "base_name": "Core_dev_waitlist3",
+ "base_attached": true,
+ "size": 209715200,
+ "block_size": 512
+ }
+ ],
+ "caches": [
+ {
+ "name": "Ocf_cache1",
+ "base_name": "Cache_dev1",
+ "base_attached": false,
+ "size": null,
+ "block_size": null,
+ "cache_line_size": 4096,
+ "cache_mode": "wt",
+ "promotion": {
+ "policy": "always"
+ },
+ "cleaning": {
+ "policy": "alru",
+ "wake_up_time": 20,
+ "flush_max_buffers": 100,
+ "staleness_time": 120,
+ "activity_threshold": 10000,
+ "dirty_ratio_threshold": 100,
+ "dirty_ratio_inertia": 134217728
+ },
+ "flush": {
+ "in_progress": false,
+ "error": 0
+ },
+ "cores_count": 3,
+ "cores": [
+ {
+ "name": "Ocf_core1-1",
+ "uuid": "0be66590-b2b0-58cf-8dde-aa8ed86ee223",
+ "cache_name": "Ocf_cache1",
+ "base_name": "Core_dev1-1",
+ "base_attached": false,
+ "size": null,
+ "block_size": null,
+ "loading": false,
+ "seq_cutoff": {
+ "policy": "full",
+ "threshold": 1024,
+ "promotion_count": 8,
+ "promote_on_threshold": false
+ },
+ "flush": {
+ "in_progress": false,
+ "error": 0
+ }
+ },
+ {
+ "name": "Ocf_core1-2",
+ "uuid": "7ae5d5b1-f2b8-56e6-838e-d6c3e4e4a52d",
+ "cache_name": "Ocf_cache1",
+ "base_name": "Core_dev1-2",
+ "base_attached": true,
+ "size": 209715200,
+ "block_size": 512,
+ "loading": false,
+ "seq_cutoff": {
+ "policy": "full",
+ "threshold": 1024,
+ "promotion_count": 8,
+ "promote_on_threshold": false
+ },
+ "flush": {
+ "in_progress": false,
+ "error": 0
+ }
+ },
+ {
+ "name": "Ocf_core1-3",
+ "uuid": "6d587297-39c4-5e4b-899b-faa488c5a976",
+ "cache_name": "Ocf_cache1",
+ "base_name": "Core_dev1-3",
+ "base_attached": true,
+ "size": 209715200,
+ "block_size": 512,
+ "loading": false,
+ "seq_cutoff": {
+ "policy": "full",
+ "threshold": 1024,
+ "promotion_count": 8,
+ "promote_on_threshold": false
+ },
+ "flush": {
+ "in_progress": false,
+ "error": 0
+ }
+ }
+ ]
+ },
+ {
+ "name": "Ocf_cache2",
+ "base_name": "Cache_dev2",
+ "base_attached": true,
+ "size": 104857600,
+ "block_size": 512,
+ "cache_line_size": 4096,
+ "cache_mode": "wb",
+ "promotion": {
+ "policy": "always"
+ },
+ "cleaning": {
+ "policy": "alru",
+ "wake_up_time": 20,
+ "flush_max_buffers": 100,
+ "staleness_time": 120,
+ "activity_threshold": 10000,
+ "dirty_ratio_threshold": 100,
+ "dirty_ratio_inertia": 134217728
+ },
+ "flush": {
+ "in_progress": false,
+ "error": 0
+ },
+ "cores_count": 3,
+ "cores": [
+ {
+ "name": "Ocf_core2-1",
+ "uuid": "a3dbab70-2366-5a7b-a22d-a5f27f958fde",
+ "cache_name": "Ocf_cache2",
+ "base_name": "Core_dev2-1",
+ "base_attached": false,
+ "size": null,
+ "block_size": null,
+ "loading": false,
+ "seq_cutoff": {
+ "policy": "full",
+ "threshold": 1024,
+ "promotion_count": 8,
+ "promote_on_threshold": false
+ },
+ "flush": {
+ "in_progress": false,
+ "error": 0
+ }
+ },
+ {
+ "name": "Ocf_core2-2",
+ "uuid": "089e4715-908c-5635-8ff7-e45ce42f0b2b",
+ "cache_name": "Ocf_cache2",
+ "base_name": "Core_dev2-2",
+ "base_attached": true,
+ "size": 209715200,
+ "block_size": 512,
+ "loading": false,
+ "seq_cutoff": {
+ "policy": "full",
+ "threshold": 1024,
+ "promotion_count": 8,
+ "promote_on_threshold": false
+ },
+ "flush": {
+ "in_progress": false,
+ "error": 0
+ }
+ },
+ {
+ "name": "Ocf_core2-3",
+ "uuid": "373d6301-3d56-5261-b0bd-dd959c48aa3e",
+ "cache_name": "Ocf_cache2",
+ "base_name": "Core_dev2-3",
+ "base_attached": true,
+ "size": 209715200,
+ "block_size": 512,
+ "loading": false,
+ "seq_cutoff": {
+ "policy": "full",
+ "threshold": 1024,
+ "promotion_count": 8,
+ "promote_on_threshold": false
+ },
+ "flush": {
+ "in_progress": false,
+ "error": 0
+ }
+ }
+ ]
+ },
+ {
+ "name": "Ocf_cache3",
+ "base_name": "Cache_dev3",
+ "base_attached": true,
+ "size": 104857600,
+ "block_size": 512,
+ "cache_line_size": 65536,
+ "cache_mode": "pt",
+ "promotion": {
+ "policy": "always"
+ },
+ "cleaning": {
+ "policy": "alru",
+ "wake_up_time": 20,
+ "flush_max_buffers": 100,
+ "staleness_time": 120,
+ "activity_threshold": 10000,
+ "dirty_ratio_threshold": 100,
+ "dirty_ratio_inertia": 134217728
+ },
+ "flush": {
+ "in_progress": false,
+ "error": 0
+ },
+ "cores_count": 3,
+ "cores": [
+ {
+ "name": "Ocf_core3-1",
+ "uuid": "b3c4e428-b6cb-572d-98bf-33d192ba4a62",
+ "cache_name": "Ocf_cache3",
+ "base_name": "Core_dev3-1",
+ "base_attached": false,
+ "size": null,
+ "block_size": null,
+ "loading": false,
+ "seq_cutoff": {
+ "policy": "full",
+ "threshold": 1024,
+ "promotion_count": 8,
+ "promote_on_threshold": false
+ },
+ "flush": {
+ "in_progress": false,
+ "error": 0
+ }
+ },
+ {
+ "name": "Ocf_core3-2",
+ "uuid": "7c321a44-f6b1-5af3-9ce3-2088fa529566",
+ "cache_name": "Ocf_cache3",
+ "base_name": "Core_dev3-2",
+ "base_attached": true,
+ "size": 209715200,
+ "block_size": 512,
+ "loading": false,
+ "seq_cutoff": {
+ "policy": "full",
+ "threshold": 1024,
+ "promotion_count": 8,
+ "promote_on_threshold": false
+ },
+ "flush": {
+ "in_progress": false,
+ "error": 0
+ }
+ },
+ {
+ "name": "Ocf_core3-3",
+ "uuid": "5cfe8234-1fd5-5b73-9026-9214652a4475",
+ "cache_name": "Ocf_cache3",
+ "base_name": "Core_dev3-3",
+ "base_attached": true,
+ "size": 209715200,
+ "block_size": 512,
+ "loading": false,
+ "seq_cutoff": {
+ "policy": "full",
+ "threshold": 1024,
+ "promotion_count": 8,
+ "promote_on_threshold": false
+ },
+ "flush": {
+ "in_progress": false,
+ "error": 0
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/test/ocf/management/info_dump_save_config.json b/test/ocf/management/info_dump_save_config.json
new file mode 100644
index 00000000000..8083ad721f8
--- /dev/null
+++ b/test/ocf/management/info_dump_save_config.json
@@ -0,0 +1,123 @@
+{
+ "method": "bdev_ocf_add_core",
+ "params": {
+ "core_name": "Ocf_core_waitlist1",
+ "base_name": "Core_dev_waitlist1",
+ "cache_name": "Ocf_cache_none"
+ }
+}
+{
+ "method": "bdev_ocf_add_core",
+ "params": {
+ "core_name": "Ocf_core_waitlist2",
+ "base_name": "Core_dev_waitlist2",
+ "cache_name": "Ocf_cache_none"
+ }
+}
+{
+ "method": "bdev_ocf_add_core",
+ "params": {
+ "core_name": "Ocf_core_waitlist3",
+ "base_name": "Core_dev_waitlist3",
+ "cache_name": "Ocf_cache_none"
+ }
+}
+{
+ "method": "bdev_ocf_start_cache",
+ "params": {
+ "cache_name": "Ocf_cache1",
+ "base_name": "Cache_dev1",
+ "cache_mode": "wt",
+ "cache_line_size": 4096
+ }
+}
+{
+ "method": "bdev_ocf_add_core",
+ "params": {
+ "core_name": "Ocf_core1-1",
+ "base_name": "Core_dev1-1",
+ "cache_name": "Ocf_cache1"
+ }
+}
+{
+ "method": "bdev_ocf_add_core",
+ "params": {
+ "core_name": "Ocf_core1-2",
+ "base_name": "Core_dev1-2",
+ "cache_name": "Ocf_cache1"
+ }
+}
+{
+ "method": "bdev_ocf_add_core",
+ "params": {
+ "core_name": "Ocf_core1-3",
+ "base_name": "Core_dev1-3",
+ "cache_name": "Ocf_cache1"
+ }
+}
+{
+ "method": "bdev_ocf_start_cache",
+ "params": {
+ "cache_name": "Ocf_cache2",
+ "base_name": "Cache_dev2",
+ "cache_mode": "wb",
+ "cache_line_size": 4096
+ }
+}
+{
+ "method": "bdev_ocf_add_core",
+ "params": {
+ "core_name": "Ocf_core2-1",
+ "base_name": "Core_dev2-1",
+ "cache_name": "Ocf_cache2"
+ }
+}
+{
+ "method": "bdev_ocf_add_core",
+ "params": {
+ "core_name": "Ocf_core2-2",
+ "base_name": "Core_dev2-2",
+ "cache_name": "Ocf_cache2"
+ }
+}
+{
+ "method": "bdev_ocf_add_core",
+ "params": {
+ "core_name": "Ocf_core2-3",
+ "base_name": "Core_dev2-3",
+ "cache_name": "Ocf_cache2"
+ }
+}
+{
+ "method": "bdev_ocf_start_cache",
+ "params": {
+ "cache_name": "Ocf_cache3",
+ "base_name": "Cache_dev3",
+ "cache_mode": "pt",
+ "cache_line_size": 65536
+ }
+}
+{
+ "method": "bdev_ocf_add_core",
+ "params": {
+ "core_name": "Ocf_core3-1",
+ "base_name": "Core_dev3-1",
+ "cache_name": "Ocf_cache3"
+ }
+}
+{
+ "method": "bdev_ocf_add_core",
+ "params": {
+ "core_name": "Ocf_core3-2",
+ "base_name": "Core_dev3-2",
+ "cache_name": "Ocf_cache3"
+ }
+}
+{
+ "method": "bdev_ocf_add_core",
+ "params": {
+ "core_name": "Ocf_core3-3",
+ "base_name": "Core_dev3-3",
+ "cache_name": "Ocf_cache3"
+ }
+}
diff --git a/test/ocf/management/multicore.sh b/test/ocf/management/multicore.sh
deleted file mode 100755
index ce1ff5950c2..00000000000
--- a/test/ocf/management/multicore.sh
+++ /dev/null
@@ -1,85 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright (C) 2019 Intel Corporation
-# All rights reserved.
-#
-curdir=$(dirname $(readlink -f "${BASH_SOURCE[0]}"))
-rootdir=$(readlink -f $curdir/../../..)
-source $rootdir/test/common/autotest_common.sh
-
-rpc_py=$rootdir/scripts/rpc.py
-
-spdk_pid='?'
-function start_spdk() {
- $SPDK_BIN_DIR/iscsi_tgt &
- spdk_pid=$!
- trap 'killprocess $spdk_pid; exit 1' SIGINT SIGTERM EXIT
- waitforlisten $spdk_pid
-}
-function stop_spdk() {
- killprocess $spdk_pid
- trap - SIGINT SIGTERM EXIT
-}
-
-start_spdk
-
-# Hotplug case
-
-$rpc_py bdev_malloc_create 1 512 -b Core0
-$rpc_py bdev_malloc_create 1 512 -b Core1
-
-$rpc_py bdev_ocf_create C1 wt Cache Core0
-$rpc_py bdev_ocf_create C2 wt Cache Core1
-
-$rpc_py bdev_ocf_get_bdevs | jq -e \
- 'any(select(.started)) == false'
-
-$rpc_py bdev_malloc_create 101 512 -b Cache
-
-$rpc_py bdev_ocf_get_bdevs | jq -e \
- 'all(select(.started)) == true'
-
-#Be sure that we will not fail delete because examine is still in progress
-waitforbdev C2
-
-# Detaching cores
-
-$rpc_py bdev_ocf_delete C2
-
-$rpc_py bdev_ocf_get_bdevs C1 | jq -e \
- '.[0] | .started'
-
-$rpc_py bdev_ocf_create C2 wt Cache Core1
-
-$rpc_py bdev_ocf_get_bdevs C2 | jq -e \
- '.[0] | .started'
-
-# Normal shutdown
-
-stop_spdk
-
-# Hotremove case
-start_spdk
-
-$rpc_py bdev_malloc_create 101 512 -b Cache
-$rpc_py bdev_malloc_create 101 512 -b Malloc
-$rpc_py bdev_malloc_create 1 512 -b Core
-
-$rpc_py bdev_ocf_create C1 wt Cache Malloc
-$rpc_py bdev_ocf_create C2 wt Cache Core
-
-$rpc_py bdev_ocf_get_bdevs Cache | jq \
- 'length == 2'
-
-$rpc_py bdev_malloc_delete Cache
-
-$rpc_py bdev_ocf_get_bdevs | jq -e \
- '. == []'
-
-# Not fully initialized shutdown
-
-$rpc_py bdev_ocf_create C1 wt Malloc NonExisting
-$rpc_py bdev_ocf_create C2 wt Malloc NonExisting
-$rpc_py bdev_ocf_create C3 wt Malloc Core
-
-stop_spdk
diff --git a/test/ocf/management/remove.sh b/test/ocf/management/remove.sh
deleted file mode 100755
index 17795411b6d..00000000000
--- a/test/ocf/management/remove.sh
+++ /dev/null
@@ -1,87 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright (C) 2019 Intel Corporation
-# All rights reserved.
-#
-curdir=$(dirname $(readlink -f "${BASH_SOURCE[0]}"))
-rootdir=$(readlink -f $curdir/../../..)
-source $rootdir/test/common/autotest_common.sh
-
-rpc_py=$rootdir/scripts/rpc.py
-
-rm -f aio*
-truncate -s 128M aio0
-truncate -s 128M aio1
-
-jq . <<- JSON > "$curdir/config"
- {
- "subsystems": [
- {
- "subsystem": "bdev",
- "config": [
- {
- "method": "bdev_aio_create",
- "params": {
- "name": "ai0",
- "block_size": 512,
- "filename": "./aio0"
- }
- },
- {
- "method": "bdev_aio_create",
- "params": {
- "name": "aio1",
- "block_size": 512,
- "filename": "./aio1"
- }
- },
- {
- "method": "bdev_wait_for_examine"
- }
- ]
- }
- ]
- }
-JSON
-
-"$SPDK_BIN_DIR/iscsi_tgt" --json "$curdir/config" &
-spdk_pid=$!
-
-waitforlisten $spdk_pid
-
-# Create ocf on persistent storage
-
-$rpc_py bdev_ocf_create ocfWT wt aio0 aio1
-
-# Check that ocfWT was created properly
-
-$rpc_py bdev_ocf_get_bdevs | jq -r '.[] .name' | grep -qw ocfWT
-
-# Remove ocfWT, after delete via rpc ocf bdev should not load on next app start
-
-$rpc_py bdev_ocf_delete ocfWT
-
-# Check that ocfWT was deleted properly
-
-[[ -z $("$rpc_py" bdev_ocf_get_bdevs | jq -r '.[] | select(.name == "ocfWT") | .name') ]]
-
-trap - SIGINT SIGTERM EXIT
-
-killprocess $spdk_pid
-
-# Check for ocfWT was deleted permanently
-"$SPDK_BIN_DIR/iscsi_tgt" --json "$curdir/config" &
-spdk_pid=$!
-
-trap 'killprocess $spdk_pid; rm -f aio* $curdir/config ocf_bdevs ocf_bdevs_verify; exit 1' SIGINT SIGTERM EXIT
-
-waitforlisten $spdk_pid
-
-# Check that ocfWT was not loaded on app start
-
-(($("$rpc_py" bdev_ocf_get_bdevs | jq 'length') == 0))
-
-trap - SIGINT SIGTERM EXIT
-
-killprocess $spdk_pid
-rm -f aio* $curdir/config ocf_bdevs ocf_bdevs_verify
diff --git a/test/ocf/management/run.sh b/test/ocf/management/run.sh
new file mode 100755
index 00000000000..701a6f2e2fa
--- /dev/null
+++ b/test/ocf/management/run.sh
@@ -0,0 +1,18 @@
+#!/usr/bin/env bash
+
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (C) 2025 Huawei Technologies
+# All rights reserved.
+#
+
+curdir=$(dirname $(readlink -f "${BASH_SOURCE[0]}"))
+rootdir=$(readlink -f "$curdir/../../..")
+
+source "$rootdir/test/common/autotest_common.sh"
+
+run_test "start-stop" "$curdir/start-stop/run.sh"
+run_test "info_dump" "$curdir/info_dump.sh"
+run_test "detach-attach" "$curdir/detach-attach.sh"
+run_test "hotremove" "$curdir/hotremove.sh"
+run_test "config_change" "$curdir/config_change.sh"
diff --git a/test/ocf/management/start-stop/basic.sh b/test/ocf/management/start-stop/basic.sh
new file mode 100755
index 00000000000..5f851a1c838
--- /dev/null
+++ b/test/ocf/management/start-stop/basic.sh
@@ -0,0 +1,236 @@
+#!/usr/bin/env bash
+
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (C) 2025 Huawei Technologies
+# All rights reserved.
+#
+
+curdir=$(dirname $(readlink -f "${BASH_SOURCE[0]}"))
+rootdir=$(readlink -f "$curdir/../../../..")
+source "$rootdir/test/ocf/common.sh"
+
+# start caches:
+
+for stop_caches in false true; do
+ start_spdk
+ create_caches
+ __check_caches_base_not_claimed
+ __check_caches_empty
+ __check_cores_waitlist_empty
+ start_caches
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_waitlist_empty
+ __check_cores_empty
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_base_not_claimed
+ __check_caches_empty
+ __check_cores_waitlist_empty
+ fi
+ stop_spdk
+done
+
+# add cores:
+
+for remove_cores in false true; do
+ start_spdk
+ create_cores
+ __check_caches_empty
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ add_cores
+ __check_caches_empty
+ __check_cores_base_claimed
+ __check_cores_waitlist_attached
+ if [ $remove_cores == true ]; then
+ remove_cores
+ __check_caches_empty
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ fi
+ stop_spdk
+done
+
+# start caches, then add cores:
+
+for stop_caches in false true; do
+ start_spdk
+ create_caches
+ create_cores
+ __check_caches_base_not_claimed
+ __check_caches_empty
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ start_caches
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ __check_cores_empty
+ add_cores
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_base_claimed
+ __check_cores_waitlist_empty
+ __check_cores_attached
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_base_not_claimed
+ __check_caches_empty
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ fi
+ stop_spdk
+done
+
+# add cores, then start caches:
+
+for stop_caches in false true; do
+ start_spdk
+ create_caches
+ create_cores
+ __check_caches_base_not_claimed
+ __check_caches_empty
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ add_cores
+ __check_caches_base_not_claimed
+ __check_caches_empty
+ __check_cores_base_claimed
+ __check_cores_waitlist_attached
+ start_caches
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_base_claimed
+ __check_cores_waitlist_empty
+ __check_cores_attached
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_base_not_claimed
+ __check_caches_empty
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ fi
+ stop_spdk
+done
+
+# restart all caches with cores:
+
+for stop_caches in false true; do
+ start_spdk
+ create_caches
+ create_cores
+ start_caches
+ add_cores
+ stop_caches
+ __check_caches_base_not_claimed
+ __check_caches_empty
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ start_caches
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ add_cores
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_base_claimed
+ __check_cores_waitlist_empty
+ __check_cores_attached
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_base_not_claimed
+ __check_caches_empty
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ fi
+ stop_spdk
+done
+
+# remove cores:
+
+for stop_caches in false true; do
+ start_spdk
+ create_caches
+ create_cores
+ start_caches
+ add_cores
+ remove_cores
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ __check_cores_empty
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_base_not_claimed
+ __check_caches_empty
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ fi
+ stop_spdk
+done
+
+# remove first core:
+
+for stop_caches in false true; do
+ start_spdk
+ create_caches
+ create_cores
+ start_caches
+ add_cores
+ for i in {1..3}; do
+ $rpc_py bdev_ocf_remove_core Ocf_core$i-1
+ done
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_waitlist_empty
+ $rpc_py bdev_get_bdevs | jq -e '[.[] | select(.name | test("Core_dev.-1"))] | any(.claimed) | not'
+ $rpc_py bdev_get_bdevs | jq -e '[.[] | select(.name | test("Core_dev.-[23]"))] | all(.claimed)'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores | length] | all(. == 2)'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores_count] | all(. == 2)'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores[]] | all(.base_attached)'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores[]] | any(.loading) | not'
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_base_not_claimed
+ __check_caches_empty
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ fi
+ stop_spdk
+done
+
+# remove all cores but first:
+
+for stop_caches in false true; do
+ start_spdk
+ create_caches
+ create_cores
+ start_caches
+ add_cores
+ for i in {1..3}; do
+ $rpc_py bdev_ocf_remove_core Ocf_core$i-2
+ $rpc_py bdev_ocf_remove_core Ocf_core$i-3
+ done
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_waitlist_empty
+ $rpc_py bdev_get_bdevs | jq -e '[.[] | select(.name | test("Core_dev.-1"))] | all(.claimed)'
+ $rpc_py bdev_get_bdevs | jq -e '[.[] | select(.name | test("Core_dev.-[23]"))] | any(.claimed) | not'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores | length] | all(. == 1)'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores_count] | all(. == 1)'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores[]] | all(.base_attached)'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '[.caches[].cores[]] | any(.loading) | not'
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_base_not_claimed
+ __check_caches_empty
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ fi
+ stop_spdk
+done
diff --git a/test/ocf/management/start-stop/examine.sh b/test/ocf/management/start-stop/examine.sh
new file mode 100755
index 00000000000..f56862317be
--- /dev/null
+++ b/test/ocf/management/start-stop/examine.sh
@@ -0,0 +1,372 @@
+#!/usr/bin/env bash
+
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (C) 2025 Huawei Technologies
+# All rights reserved.
+#
+
+curdir=$(dirname $(readlink -f "${BASH_SOURCE[0]}"))
+rootdir=$(readlink -f "$curdir/../../../..")
+source "$rootdir/test/ocf/common.sh"
+
+# found cache device (cache only):
+
+for stop_caches in false true; do
+ start_spdk
+ start_caches
+ create_caches
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_waitlist_empty
+ __check_cores_empty
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_base_not_claimed
+ __check_caches_empty
+ __check_cores_waitlist_empty
+ fi
+ stop_spdk
+done
+
+# found cache device (cache with core):
+
+for stop_caches in false true; do
+ start_spdk
+ start_caches
+ add_cores
+ create_caches
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_waitlist_detached
+ __check_cores_empty
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_base_not_claimed
+ __check_caches_empty
+ __check_cores_waitlist_detached
+ fi
+ stop_spdk
+done
+
+# found core device (cores only):
+
+for remove_cores in false true; do
+ start_spdk
+ add_cores
+ create_cores
+ __check_caches_empty
+ __check_cores_base_claimed
+ __check_cores_waitlist_attached
+ if [ $remove_cores == true ]; then
+ remove_cores
+ __check_caches_empty
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ fi
+ stop_spdk
+done
+
+# found core device (cache with core):
+
+for stop_caches in false true; do
+ start_spdk
+ start_caches
+ add_cores
+ create_cores
+ __check_caches_detached
+ __check_cores_base_claimed
+ __check_cores_waitlist_attached
+ __check_cores_empty
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_empty
+ __check_cores_base_claimed
+ __check_cores_waitlist_attached
+ fi
+ stop_spdk
+done
+
+# found core device (cache with core, inverse add order):
+
+for stop_caches in false true; do
+ start_spdk
+ add_cores
+ start_caches
+ create_cores
+ __check_caches_detached
+ __check_cores_base_claimed
+ __check_cores_waitlist_attached
+ __check_cores_empty
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_empty
+ __check_cores_base_claimed
+ __check_cores_waitlist_attached
+ fi
+ stop_spdk
+done
+
+# found cache and then core device:
+
+for stop_caches in false true; do
+ start_spdk
+ start_caches
+ add_cores
+ create_caches
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_waitlist_detached
+ __check_cores_empty
+ create_cores
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_base_claimed
+ __check_cores_waitlist_empty
+ __check_cores_attached
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_base_not_claimed
+ __check_caches_empty
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ fi
+ stop_spdk
+done
+
+# found core and then cache device:
+
+for stop_caches in false true; do
+ start_spdk
+ start_caches
+ add_cores
+ create_cores
+ __check_caches_detached
+ __check_cores_base_claimed
+ __check_cores_waitlist_attached
+ __check_cores_empty
+ create_caches
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_base_claimed
+ __check_cores_waitlist_empty
+ __check_cores_attached
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_base_not_claimed
+ __check_caches_empty
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ fi
+ stop_spdk
+done
+
+# found cache and then core device (inverse add order):
+
+for stop_caches in false true; do
+ start_spdk
+ add_cores
+ start_caches
+ create_caches
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_waitlist_detached
+ __check_cores_empty
+ create_cores
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_base_claimed
+ __check_cores_waitlist_empty
+ __check_cores_attached
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_base_not_claimed
+ __check_caches_empty
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ fi
+ stop_spdk
+done
+
+# found core and then cache device (inverse add order):
+
+for stop_caches in false true; do
+ start_spdk
+ add_cores
+ start_caches
+ create_cores
+ __check_caches_detached
+ __check_cores_base_claimed
+ __check_cores_waitlist_attached
+ __check_cores_empty
+ create_caches
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_base_claimed
+ __check_cores_waitlist_empty
+ __check_cores_attached
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_base_not_claimed
+ __check_caches_empty
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ fi
+ stop_spdk
+done
+
+# found core device, then start cache:
+
+for remove_cores in false true; do
+ for stop_caches in false true; do
+ start_spdk
+ add_cores
+ create_cores
+ __check_caches_empty
+ __check_cores_base_claimed
+ __check_cores_waitlist_attached
+ start_caches
+ __check_caches_detached
+ __check_cores_base_claimed
+ __check_cores_waitlist_attached
+ __check_cores_empty
+ if [ $remove_cores == true ]; then
+ remove_cores
+ __check_caches_detached
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ __check_cores_empty
+ fi
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_empty
+ if [ $remove_cores == true ]; then
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ else
+ __check_cores_base_claimed
+ __check_cores_waitlist_attached
+ fi
+ fi
+ stop_spdk
+ done
+done
+
+# found core device, then start cache, then found cache device:
+
+for remove_cores in false true; do
+ for stop_caches in false true; do
+ start_spdk
+ add_cores
+ create_cores
+ __check_caches_empty
+ __check_cores_base_claimed
+ __check_cores_waitlist_attached
+ start_caches
+ __check_caches_detached
+ __check_cores_base_claimed
+ __check_cores_waitlist_attached
+ __check_cores_empty
+ create_caches
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_base_claimed
+ __check_cores_waitlist_empty
+ __check_cores_attached
+ if [ $remove_cores == true ]; then
+ remove_cores
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ __check_cores_empty
+ fi
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_base_not_claimed
+ __check_caches_empty
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ fi
+ stop_spdk
+ done
+done
+
+# found core device (not yet added) on detached cache:
+
+for stop_caches in false true; do
+ start_spdk
+ create_caches
+ start_caches
+ add_cores
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_waitlist_detached
+ __check_cores_empty
+ detach_caches
+ __check_caches_base_not_claimed
+ __check_caches_detached
+ __check_cores_waitlist_detached
+ __check_cores_empty
+ create_cores
+ __check_caches_base_not_claimed
+ __check_caches_detached
+ __check_cores_base_claimed
+ __check_cores_waitlist_attached
+ __check_cores_empty
+ attach_caches
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_base_claimed
+ __check_cores_waitlist_empty
+ __check_cores_attached
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_base_not_claimed
+ __check_caches_empty
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ fi
+ stop_spdk
+done
+
+# found core device (hotremoved) on detached cache:
+
+for stop_caches in false true; do
+ start_spdk
+ create_caches
+ create_cores
+ start_caches
+ add_cores
+ detach_caches
+ __check_caches_base_not_claimed
+ __check_caches_detached
+ __check_cores_base_claimed
+ __check_cores_waitlist_empty
+ __check_cores_attached
+ destroy_cores
+ __check_caches_base_not_claimed
+ __check_caches_detached
+ __check_cores_waitlist_empty
+ __check_cores_detached
+ create_cores
+ __check_caches_base_not_claimed
+ __check_caches_detached
+ __check_cores_base_claimed
+ __check_cores_waitlist_empty
+ __check_cores_attached
+ attach_caches
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_base_claimed
+ __check_cores_waitlist_empty
+ __check_cores_attached
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_base_not_claimed
+ __check_caches_empty
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ fi
+ stop_spdk
+done
diff --git a/test/ocf/management/start-stop/incomplete.sh b/test/ocf/management/start-stop/incomplete.sh
new file mode 100755
index 00000000000..cae743409f6
--- /dev/null
+++ b/test/ocf/management/start-stop/incomplete.sh
@@ -0,0 +1,159 @@
+#!/usr/bin/env bash
+
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (C) 2025 Huawei Technologies
+# All rights reserved.
+#
+
+curdir=$(dirname $(readlink -f "${BASH_SOURCE[0]}"))
+rootdir=$(readlink -f "$curdir/../../../..")
+source "$rootdir/test/ocf/common.sh"
+
+# incomplete caches:
+
+for stop_caches in false true; do
+ start_spdk
+ start_caches
+ __check_caches_detached
+ __check_cores_waitlist_empty
+ __check_cores_empty
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_empty
+ __check_cores_waitlist_empty
+ fi
+ stop_spdk
+done
+
+# incomplete cores:
+
+for remove_cores in false true; do
+ start_spdk
+ add_cores
+ __check_caches_empty
+ __check_cores_waitlist_detached
+ if [ $remove_cores == true ]; then
+ remove_cores
+ __check_caches_empty
+ __check_cores_waitlist_empty
+ fi
+ stop_spdk
+done
+
+# incomplete caches and incomplete cores:
+
+for inverse in false true; do
+ for remove_cores in false true; do
+ for stop_caches in false true; do
+ start_spdk
+ if [ $inverse == false ]; then
+ start_caches
+ add_cores
+ else
+ add_cores
+ start_caches
+ fi
+ __check_caches_detached
+ __check_cores_waitlist_detached
+ __check_cores_empty
+ if [ $remove_cores == true ]; then
+ remove_cores
+ __check_caches_detached
+ __check_cores_waitlist_empty
+ __check_cores_empty
+ fi
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_empty
+ if [ $remove_cores == true ]; then
+ __check_cores_waitlist_empty
+ else
+ __check_cores_waitlist_detached
+ fi
+ fi
+ stop_spdk
+ done
+ done
+done
+
+# complete caches and incomplete cores:
+
+for inverse in false true; do
+ for remove_cores in false true; do
+ for stop_caches in false true; do
+ start_spdk
+ create_caches
+ if [ $inverse == false ]; then
+ start_caches
+ add_cores
+ else
+ add_cores
+ start_caches
+ fi
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_waitlist_detached
+ __check_cores_empty
+ if [ $remove_cores == true ]; then
+ remove_cores
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_waitlist_empty
+ __check_cores_empty
+ fi
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_base_not_claimed
+ __check_caches_empty
+ if [ $remove_cores == true ]; then
+ __check_cores_waitlist_empty
+ else
+ __check_cores_waitlist_detached
+ fi
+ fi
+ stop_spdk
+ done
+ done
+done
+
+# incomplete caches and complete cores:
+
+for inverse in false true; do
+ for remove_cores in false true; do
+ for stop_caches in false true; do
+ start_spdk
+ create_cores
+ if [ $inverse == false ]; then
+ start_caches
+ add_cores
+ else
+ add_cores
+ start_caches
+ fi
+ __check_caches_detached
+ __check_cores_base_claimed
+ __check_cores_waitlist_attached
+ __check_cores_empty
+ if [ $remove_cores == true ]; then
+ remove_cores
+ __check_caches_detached
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ __check_cores_empty
+ fi
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_empty
+ if [ $remove_cores == true ]; then
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ else
+ __check_cores_base_claimed
+ __check_cores_waitlist_attached
+ fi
+ fi
+ stop_spdk
+ done
+ done
+done
diff --git a/test/ocf/management/start-stop/load.sh b/test/ocf/management/start-stop/load.sh
new file mode 100755
index 00000000000..291b6994676
--- /dev/null
+++ b/test/ocf/management/start-stop/load.sh
@@ -0,0 +1,481 @@
+#!/usr/bin/env bash
+
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (C) 2025 Huawei Technologies
+# All rights reserved.
+#
+
+curdir=$(dirname $(readlink -f "${BASH_SOURCE[0]}"))
+rootdir=$(readlink -f "$curdir/../../../..")
+source "$rootdir/test/ocf/common.sh"
+
+# load caches without cores in metadata:
+
+for add_cores in false true; do
+ for stop_caches in false true; do
+ start_spdk
+ create_caches_with_metadata
+ __check_caches_base_not_claimed
+ create_cores
+ __check_cores_base_not_claimed
+ start_caches_try_load
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ __check_cores_empty
+ if [ $add_cores == true ]; then
+ add_cores
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_base_claimed
+ __check_cores_waitlist_empty
+ __check_cores_attached
+ fi
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_base_not_claimed
+ __check_caches_empty
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ fi
+ stop_spdk
+ done
+done
+
+# load caches with cores in metadata:
+
+for add_cores in false true; do
+ for stop_caches in false true; do
+ start_spdk
+ create_caches_with_metadata_with_cores
+ __check_caches_base_not_claimed
+ create_cores
+ __check_cores_base_not_claimed
+ start_caches_try_load
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ __check_cores_loading
+ if [ $add_cores == true ]; then
+ add_cores
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_base_claimed
+ __check_cores_waitlist_empty
+ __check_cores_attached
+ fi
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_base_not_claimed
+ __check_caches_empty
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ fi
+ stop_spdk
+ done
+done
+
+# load caches with cores in metadata (add cores first):
+
+for create_cores in false true; do
+ for stop_caches in false true; do
+ start_spdk
+ create_caches_with_metadata_with_cores
+ add_cores
+ __check_caches_base_not_claimed
+ __check_caches_empty
+ __check_cores_waitlist_detached
+ start_caches_try_load
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_waitlist_detached
+ __check_cores_loading
+ if [ $create_cores == true ]; then
+ create_cores
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_base_claimed
+ __check_cores_waitlist_empty
+ __check_cores_attached
+ fi
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_base_not_claimed
+ __check_caches_empty
+ if [ $create_cores == true ]; then
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ else
+ __check_cores_waitlist_detached
+ fi
+ fi
+ stop_spdk
+ done
+done
+
+# examine found cache device with metadata (without cores in metadata):
+
+for stop_caches in false true; do
+ start_spdk
+ create_caches_persistent_with_metadata
+ __check_caches_base_not_claimed
+ destroy_caches_persistent
+ start_caches_try_load
+ __check_caches_detached
+ __check_cores_waitlist_empty
+ create_caches_persistent
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_waitlist_empty
+ __check_cores_empty
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_base_not_claimed
+ __check_caches_empty
+ __check_cores_waitlist_empty
+ fi
+ stop_spdk
+done
+
+# examine found cache device with metadata (with cores in metadata):
+
+for stop_caches in false true; do
+ start_spdk
+ create_caches_persistent_with_metadata_with_cores
+ __check_caches_base_not_claimed
+ create_cores
+ __check_cores_base_not_claimed
+ destroy_caches_persistent
+ __check_cores_base_not_claimed
+ start_caches_try_load
+ __check_caches_detached
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ __check_cores_empty
+ create_caches_persistent
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ __check_cores_loading
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_base_not_claimed
+ __check_caches_empty
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ fi
+ stop_spdk
+done
+
+# examine found cache device with metadata (with cores in metadata, added at different stages):
+
+for stop_caches in false true; do
+ start_spdk
+ create_caches_persistent_with_metadata_with_cores
+ __check_caches_base_not_claimed
+ create_cores
+ __check_cores_base_not_claimed
+ destroy_caches_persistent
+ __check_cores_base_not_claimed
+ start_caches_try_load
+ __check_caches_detached
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ __check_cores_empty
+ create_caches_persistent
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ __check_cores_loading
+ add_cores
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_base_claimed
+ __check_cores_waitlist_empty
+ __check_cores_attached
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_base_not_claimed
+ __check_caches_empty
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ fi
+ stop_spdk
+done
+
+for stop_caches in false true; do
+ start_spdk
+ create_caches_persistent_with_metadata_with_cores
+ __check_caches_base_not_claimed
+ create_cores
+ __check_cores_base_not_claimed
+ destroy_caches_persistent
+ __check_cores_base_not_claimed
+ start_caches_try_load
+ __check_caches_detached
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ __check_cores_empty
+ add_cores
+ __check_caches_detached
+ __check_cores_base_claimed
+ __check_cores_waitlist_attached
+ __check_cores_empty
+ create_caches_persistent
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_base_claimed
+ __check_cores_waitlist_empty
+ __check_cores_attached
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_base_not_claimed
+ __check_caches_empty
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ fi
+ stop_spdk
+done
+
+for stop_caches in false true; do
+ start_spdk
+ create_caches_persistent_with_metadata_with_cores
+ __check_caches_base_not_claimed
+ create_cores
+ __check_cores_base_not_claimed
+ destroy_caches_persistent
+ __check_cores_base_not_claimed
+ add_cores
+ __check_caches_empty
+ __check_cores_base_claimed
+ __check_cores_waitlist_attached
+ start_caches_try_load
+ __check_caches_detached
+ __check_cores_base_claimed
+ __check_cores_waitlist_attached
+ __check_cores_empty
+ create_caches_persistent
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_base_claimed
+ __check_cores_waitlist_empty
+ __check_cores_attached
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_base_not_claimed
+ __check_caches_empty
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ fi
+ stop_spdk
+done
+
+# examine found core device after cache load (without cores in metadata):
+
+for stop_caches in false true; do
+ start_spdk
+ create_caches_with_metadata
+ __check_caches_base_not_claimed
+ start_caches_try_load
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_waitlist_empty
+ __check_cores_empty
+ add_cores
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_waitlist_detached
+ __check_cores_empty
+ create_cores
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_base_claimed
+ __check_cores_waitlist_empty
+ __check_cores_attached
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_base_not_claimed
+ __check_caches_empty
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ fi
+ stop_spdk
+done
+
+# examine found core device after cache load (with cores in metadata):
+
+for stop_caches in false true; do
+ start_spdk
+ create_caches_with_metadata_with_cores
+ __check_caches_base_not_claimed
+ start_caches_try_load
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_waitlist_empty
+ __check_cores_loading
+ add_cores
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_waitlist_detached
+ __check_cores_loading
+ create_cores
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_base_claimed
+ __check_cores_waitlist_empty
+ __check_cores_attached
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_base_not_claimed
+ __check_caches_empty
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ fi
+ stop_spdk
+done
+
+# examine found cache with metadata first and then core devices after cache load:
+
+for stop_caches in false true; do
+ start_spdk
+ create_caches_persistent_with_metadata_with_cores
+ __check_caches_base_not_claimed
+ destroy_caches_persistent
+ add_cores
+ __check_caches_empty
+ __check_cores_waitlist_detached
+ start_caches_try_load
+ __check_caches_detached
+ __check_cores_waitlist_detached
+ __check_cores_empty
+ create_caches_persistent
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_waitlist_detached
+ __check_cores_loading
+ create_cores
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_base_claimed
+ __check_cores_waitlist_empty
+ __check_cores_attached
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_base_not_claimed
+ __check_caches_empty
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ fi
+ stop_spdk
+done
+
+# examine found core devices first and then cache with metadata after cache load:
+
+for stop_caches in false true; do
+ start_spdk
+ create_caches_persistent_with_metadata_with_cores
+ __check_caches_base_not_claimed
+ destroy_caches_persistent
+ add_cores
+ __check_caches_empty
+ __check_cores_waitlist_detached
+ start_caches_try_load
+ __check_caches_detached
+ __check_cores_waitlist_detached
+ __check_cores_empty
+ create_cores
+ __check_caches_detached
+ __check_cores_base_claimed
+ __check_cores_waitlist_attached
+ __check_cores_empty
+ create_caches_persistent
+ __check_caches_base_claimed
+ __check_caches_attached
+ __check_cores_base_claimed
+ __check_cores_waitlist_empty
+ __check_cores_attached
+ if [ $stop_caches == true ]; then
+ stop_caches
+ __check_caches_base_not_claimed
+ __check_caches_empty
+ __check_cores_base_not_claimed
+ __check_cores_waitlist_empty
+ fi
+ stop_spdk
+done
+
+# partially found cache and core devices:
+
+for stop_caches in false true; do
+ start_spdk
+ create_caches_persistent_with_metadata_with_cores
+ __check_caches_base_not_claimed
+ destroy_caches_persistent
+ add_cores
+ __check_caches_empty
+ __check_cores_waitlist_detached
+ start_caches_try_load
+ __check_caches_detached
+ __check_cores_waitlist_detached
+ __check_cores_empty
+
+ persistent_cache_create Cache_dev2 "${persistent_cache_addr[1]}"
+ persistent_cache_create Cache_dev3 "${persistent_cache_addr[2]}"
+ waitforcondition ocf_settled
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.'
+ $rpc_py bdev_get_bdevs | jq -e '[.[] | select(.name | test("Cache_dev[23]"))] | all(.claimed)'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches | length == 3'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches[0].base_attached | not'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches[0].cores | length == 0'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches[0].cores_count == 0'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches[1].base_attached'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches[1].cores | length == 3'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches[1].cores_count == 3'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches[1].cores | any(.base_attached) | not'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches[1].cores | all(.loading)'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches[2].base_attached'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches[2].cores | length == 3'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches[2].cores_count == 3'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches[2].cores | any(.base_attached) | not'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches[2].cores | all(.loading)'
+ __check_cores_waitlist_detached
+
+ for i in {1..3}; do
+ $rpc_py bdev_malloc_create -b Core_dev2-$i 200 512
+ done
+ waitforcondition ocf_settled
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.'
+ $rpc_py bdev_get_bdevs | jq -e '[.[] | select(.name | test("Cache_dev[23]"))] | all(.claimed)'
+ $rpc_py bdev_get_bdevs | jq -e '[.[] | select(.name | test("Core_dev2-."))] | all(.claimed)'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches | length == 3'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches[0].base_attached | not'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches[0].cores | length == 0'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches[0].cores_count == 0'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches[1].base_attached'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches[1].cores | length == 3'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches[1].cores_count == 3'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches[1].cores | all(.base_attached)'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches[1].cores | any(.loading) | not'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches[2].base_attached'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches[2].cores | length == 3'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches[2].cores_count == 3'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches[2].cores | any(.base_attached) | not'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.caches[2].cores | all(.loading)'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.cores_waitlist | length == 6'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.cores_waitlist | any(.base_attached) | not'
+
+ if [ $stop_caches == true ]; then
+ stop_caches
+ $rpc_py bdev_get_bdevs | jq -e '[.[] | select(.name | test("Cache_dev[23]"))] | any(.claimed) | not'
+ $rpc_py bdev_get_bdevs | jq -e '[.[] | select(.name | test("Core_dev2-."))] | any(.claimed) | not'
+ __check_caches_empty
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.cores_waitlist | length == 6'
+ $rpc_py bdev_ocf_get_bdevs | jq -e '.cores_waitlist | any(.base_attached) | not'
+ fi
+ stop_spdk
+done
diff --git a/test/ocf/management/start-stop/run.sh b/test/ocf/management/start-stop/run.sh
new file mode 100755
index 00000000000..502cb08cc5f
--- /dev/null
+++ b/test/ocf/management/start-stop/run.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright (C) 2025 Huawei Technologies
+# All rights reserved.
+#
+
+curdir=$(dirname $(readlink -f "${BASH_SOURCE[0]}"))
+rootdir=$(readlink -f "$curdir/../../../..")
+
+source "$rootdir/test/common/autotest_common.sh"
+
+run_test "basic" "$curdir/basic.sh"
+run_test "incomplete" "$curdir/incomplete.sh"
+run_test "examine" "$curdir/examine.sh"
+run_test "load" "$curdir/load.sh"
diff --git a/test/ocf/ocf.sh b/test/ocf/ocf.sh
index 4d01a42bb38..4d304993151 100755
--- a/test/ocf/ocf.sh
+++ b/test/ocf/ocf.sh
@@ -1,18 +1,16 @@
#!/usr/bin/env bash
+
+#
# SPDX-License-Identifier: BSD-3-Clause
# Copyright (C) 2018 Intel Corporation
+# Copyright (C) 2025 Huawei Technologies
# All rights reserved.
#
-testdir=$(readlink -f $(dirname $0))
-rootdir=$(readlink -f $testdir/../..)
-source $rootdir/test/common/autotest_common.sh
+curdir=$(dirname $(readlink -f "${BASH_SOURCE[0]}"))
+rootdir=$(readlink -f "$curdir/../..")
+
+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_remove" "$testdir/management/remove.sh"
-run_test "ocf_configuration_change" "$testdir/management/configuration-change.sh"
+run_test "management" "$curdir/management/run.sh"
+run_test "integrity" "$curdir/integrity/run.sh"