diff --git a/src/engine/engine_d2c.c b/src/engine/engine_d2c.c index e55320f..2a6b86a 100644 --- a/src/engine/engine_d2c.c +++ b/src/engine/engine_d2c.c @@ -1,6 +1,6 @@ /* * Copyright(c) 2012-2022 Intel Corporation - * Copyright(c) 2024 Huawei Technologies + * Copyright(c) 2024-2025 Huawei Technologies * SPDX-License-Identifier: BSD-3-Clause */ #include "ocf/ocf.h" @@ -33,7 +33,10 @@ int ocf_d2c_io_fast(struct ocf_request *req) { OCF_DEBUG_TRACE(req->cache); - /* Get OCF request - increase reference counter */ + /* Increase reference counter - once for submission and second time to + * avoid freeing the request before updating stats + */ + ocf_req_get(req); ocf_req_get(req); ocf_engine_forward_core_io_req(req, _ocf_d2c_completion); @@ -46,6 +49,8 @@ int ocf_d2c_io_fast(struct ocf_request *req) ocf_core_stats_request_pt_update(req->core, req->part_id, req->rw, req->info.hit_no, req->core_line_count); + ocf_req_put(req); + return 0; } diff --git a/src/mngt/ocf_mngt_cache.c b/src/mngt/ocf_mngt_cache.c index 145d333..20e3fd4 100644 --- a/src/mngt/ocf_mngt_cache.c +++ b/src/mngt/ocf_mngt_cache.c @@ -3860,14 +3860,14 @@ static void ocf_mngt_cache_detach_finish(ocf_pipeline_t pipeline, } _ocf_mngt_cache_set_detached(cache); + + ocf_pipeline_destroy(cache->stop_pipeline); + cache->stop_pipeline = NULL; } else { ocf_cache_log(cache, log_err, "Detaching device failed\n"); } - ocf_pipeline_destroy(cache->stop_pipeline); - cache->stop_pipeline = NULL; - context->cmpl(cache, context->priv, error ?: context->cache_write_error); diff --git a/src/ocf_request.c b/src/ocf_request.c index 9b7ce4d..4e3abe9 100644 --- a/src/ocf_request.c +++ b/src/ocf_request.c @@ -170,11 +170,14 @@ static inline struct ocf_request *ocf_req_new_d2c(ocf_queue_t queue, { ocf_cache_t cache = ocf_core_get_cache(core); struct ocf_request *req; + uint32_t request_size = 1; - req = env_mpool_new(cache->owner->resources.req, 1); + req = env_mpool_new(cache->owner->resources.req, request_size); if (unlikely(!req)) return NULL; + req->alloc_core_line_count = request_size; + ocf_req_init(req, cache, queue, core, addr, bytes, rw); req->d2c = true; diff --git a/tests/functional/pyocf/types/core.py b/tests/functional/pyocf/types/core.py index d774ba1..9cce29f 100644 --- a/tests/functional/pyocf/types/core.py +++ b/tests/functional/pyocf/types/core.py @@ -1,6 +1,6 @@ # # Copyright(c) 2019-2022 Intel Corporation -# Copyright(c) 2024 Huawei Technologies +# Copyright(c) 2024-2025 Huawei Technologies # SPDX-License-Identifier: BSD-3-Clause # @@ -182,6 +182,17 @@ class Core: if c.results["error"]: raise OcfError("Couldn't flush cache", c.results["error"]) + def detach(self): + self.cache.write_lock() + + c = OcfCompletion([("priv", c_void_p), ("error", c_int)]) + self.cache.owner.lib.ocf_mngt_cache_detach_core(self.handle, c, None) + c.wait() + self.cache.write_unlock() + + if c.results["error"]: + raise OcfError("Couldn't detach core", c.results["error"]) + def reset_stats(self): lib.ocf_core_stats_initialize(self.handle) @@ -207,3 +218,5 @@ lib.ocf_core_stats_initialize.argtypes = [c_void_p] lib.ocf_core_stats_initialize.restype = c_void_p lib.ocf_mngt_core_flush.argtypes = [c_void_p, c_void_p, c_void_p] lib.ocf_mngt_core_flush.restype = c_void_p +lib.ocf_mngt_cache_detach_core.argtypes = [c_void_p, c_void_p, c_void_p] +lib.ocf_mngt_cache_detach_core.restype = c_void_p diff --git a/tests/functional/tests/management/test_add_remove.py b/tests/functional/tests/management/test_add_remove.py index 1d43dae..40698ec 100644 --- a/tests/functional/tests/management/test_add_remove.py +++ b/tests/functional/tests/management/test_add_remove.py @@ -1,6 +1,6 @@ # # Copyright(c) 2019-2022 Intel Corporation -# Copyright(c) 2024 Huawei Technologies +# Copyright(c) 2024-2025 Huawei Technologies # SPDX-License-Identifier: BSD-3-Clause # @@ -8,7 +8,7 @@ import pytest from ctypes import c_int from random import randint -from pyocf.types.cache import Cache, CacheMode +from pyocf.types.cache import Cache, CacheMode, CleaningPolicy, PromotionPolicy from pyocf.types.core import Core from pyocf.types.volume import RamVolume, Volume from pyocf.types.volume_core import CoreVolume @@ -16,7 +16,7 @@ from pyocf.types.data import Data from pyocf.types.io import IoDir, Sync from pyocf.types.queue import Queue from pyocf.utils import Size as S -from pyocf.types.shared import OcfError, OcfCompletion, CacheLineSize +from pyocf.types.shared import OcfError, OcfErrorCode, OcfCompletion, CacheLineSize @pytest.mark.parametrize("cache_mode", CacheMode) @@ -89,6 +89,190 @@ def test_remove_dirty_no_flush(pyocf_ctx, cache_mode, cls): cache.remove_core(core) +@pytest.mark.parametrize("cleaning_policy", CleaningPolicy) +@pytest.mark.parametrize("promotion_policy", PromotionPolicy) +def test_detach_core_detach_cache_cleaning(pyocf_ctx, cleaning_policy, promotion_policy): + cache_device = RamVolume(S.from_MiB(100)) + core_device_1 = RamVolume(S.from_MiB(10)) + core_device_2 = RamVolume(S.from_MiB(10)) + + cache = Cache.start_on_device(cache_device, cache_mode=CacheMode.WB) + core_1 = Core.using_device(core_device_1, name="core_1") + core_2 = Core.using_device(core_device_2, name="core_2") + + cache.add_core(core_1) + cache.add_core(core_2) + + cache.set_cleaning_policy(cleaning_policy) + cache.set_promotion_policy(promotion_policy) + + core_1.detach() + + with pytest.raises(OcfError, match="OCF_ERR_CACHE_IN_INCOMPLETE_STATE"): + cache.detach_device() + + +@pytest.mark.parametrize("cleaning_policy", CleaningPolicy) +@pytest.mark.parametrize("promotion_policy", PromotionPolicy) +def test_detach_core_stop_cache_cleaning(pyocf_ctx, cleaning_policy, promotion_policy): + cache_device = RamVolume(S.from_MiB(100)) + core_device_1 = RamVolume(S.from_MiB(10)) + core_device_2 = RamVolume(S.from_MiB(10)) + + cache = Cache.start_on_device(cache_device, cache_mode=CacheMode.WB) + core_1 = Core.using_device(core_device_1, name="core_1") + core_2 = Core.using_device(core_device_2, name="core_2") + + cache.add_core(core_1) + cache.add_core(core_2) + + cache.set_cleaning_policy(cleaning_policy) + cache.set_promotion_policy(promotion_policy) + + core_1.detach() + + cache.stop() + + +@pytest.mark.parametrize("cleaning_policy", CleaningPolicy) +@pytest.mark.parametrize("promotion_policy", PromotionPolicy) +def test_detach_cache_detach_core_cleaning(pyocf_ctx, cleaning_policy, promotion_policy): + cache_device = RamVolume(S.from_MiB(100)) + core_device_1 = RamVolume(S.from_MiB(10)) + core_device_2 = RamVolume(S.from_MiB(10)) + + cache = Cache.start_on_device(cache_device, cache_mode=CacheMode.WB) + core_1 = Core.using_device(core_device_1, name="core_1") + core_2 = Core.using_device(core_device_2, name="core_2") + + cache.add_core(core_1) + cache.add_core(core_2) + + cache.set_cleaning_policy(cleaning_policy) + cache.set_promotion_policy(promotion_policy) + + for core in [core_1, core_2]: + vol = CoreVolume(core) + queue = core.cache.get_default_queue() + + core_size = core.get_stats()["size"] + data = Data(core_size.B) + + _io_to_core(vol, queue, data) + + core_1.detach() + + cache.stop() + + +@pytest.mark.parametrize("cleaning_policy", CleaningPolicy) +@pytest.mark.parametrize("promotion_policy", PromotionPolicy) +def test_detach_cache_retach_core_cleaning(pyocf_ctx, cleaning_policy, promotion_policy): + cache_device = RamVolume(S.from_MiB(100)) + core_device_1 = RamVolume(S.from_MiB(10)) + core_device_2 = RamVolume(S.from_MiB(10)) + + cache = Cache.start_on_device(cache_device, cache_mode=CacheMode.WB) + core_1 = Core.using_device(core_device_1, name="core_1") + core_2 = Core.using_device(core_device_2, name="core_2") + + def _write_cores(cores_list): + for core in cores_list: + vol = CoreVolume(core) + queue = core.cache.get_default_queue() + + core_size = core.get_stats()["size"] + data = Data(core_size.B) + + _io_to_core(vol, queue, data) + + cache.add_core(core_1) + cache.add_core(core_2) + + cache.set_cleaning_policy(cleaning_policy) + cache.set_promotion_policy(promotion_policy) + + cache.detach_device() + + core_1.detach() + + _write_cores([core_2]) + + cache.attach_device(cache_device) + + _write_cores([core_2]) + + cache.add_core(core_1, try_add=True) + + _write_cores([core_1, core_2]) + + cache.stop() + + +@pytest.mark.parametrize("cleaning_policy", CleaningPolicy) +@pytest.mark.parametrize("promotion_policy", PromotionPolicy) +def test_reattach_cache_reattach_core_cleaning(pyocf_ctx, cleaning_policy, promotion_policy): + cache_device = RamVolume(S.from_MiB(100)) + core_device_1 = RamVolume(S.from_MiB(10)) + core_device_2 = RamVolume(S.from_MiB(10)) + + cache = Cache.start_on_device(cache_device, cache_mode=CacheMode.WB) + core_1 = Core.using_device(core_device_1, name="core_1") + core_2 = Core.using_device(core_device_2, name="core_2") + + def _write_cores(cores_list): + for core in cores_list: + vol = CoreVolume(core) + queue = core.cache.get_default_queue() + + core_size = core.get_stats()["size"] + data = Data(core_size.B) + + _io_to_core(vol, queue, data) + + cache.add_core(core_1) + cache.add_core(core_2) + + cache.set_cleaning_policy(cleaning_policy) + cache.set_promotion_policy(promotion_policy) + + cache.detach_device() + + core_1.detach() + + cache.add_core(core_1, try_add=True) + + cache.attach_device(cache_device) + + _write_cores([core_1, core_2]) + + cache.stop() + + +@pytest.mark.parametrize("cleaning_policy", CleaningPolicy) +@pytest.mark.parametrize("promotion_policy", PromotionPolicy) +def test_detach_cache_detach_core_load_cleaning(pyocf_ctx, cleaning_policy, promotion_policy): + cache_device = RamVolume(S.from_MiB(100)) + core_device_1 = RamVolume(S.from_MiB(10)) + core_device_2 = RamVolume(S.from_MiB(10)) + + cache = Cache.start_on_device(cache_device, cache_mode=CacheMode.WB) + core_1 = Core.using_device(core_device_1, name="core_1") + core_2 = Core.using_device(core_device_2, name="core_2") + + cache.add_core(core_1) + cache.add_core(core_2) + + cache.set_cleaning_policy(cleaning_policy) + cache.set_promotion_policy(promotion_policy) + + core_1.detach() + + cache.stop() + + cache = Cache.load_from_device(cache_device) + + def test_30add_remove(pyocf_ctx): # Start cache device cache_device = RamVolume(S.from_MiB(50)) diff --git a/tests/functional/tests/management/test_attach_cache.py b/tests/functional/tests/management/test_attach_cache.py index 6593fab..e995268 100644 --- a/tests/functional/tests/management/test_attach_cache.py +++ b/tests/functional/tests/management/test_attach_cache.py @@ -85,6 +85,30 @@ def test_detach_cache_with_cleaning(pyocf_ctx, cleaning_policy): cache.stop() +def test_d2c_io(pyocf_ctx): + cache_device = RamVolume(Size.from_MiB(100)) + core_device_1 = RamVolume(Size.from_MiB(10)) + + cache = Cache.start_on_device(cache_device, cache_mode=CacheMode.WB) + core_1 = Core.using_device(core_device_1, name="core_1") + + cache.add_core(core_1) + + cache.detach_device() + + vol = CoreVolume(core_1) + queue = core_1.cache.get_default_queue() + data = Data(4096) + vol.open() + io = vol.new_io(queue, 0, data.size, IoDir.WRITE, 0, 0) + io.set_data(data) + + completion = Sync(io).submit() + + vol.close() + assert completion.results["err"] == 0 + + def test_detach_cache_zero_superblock(pyocf_ctx): """Check if superblock is zeroed after detach and the cache device can be reattached without --force option.