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"