diff --git a/casadm/cas_lib.c b/casadm/cas_lib.c index bc9acea0b..25bfbaeb9 100644 --- a/casadm/cas_lib.c +++ b/casadm/cas_lib.c @@ -2524,7 +2524,7 @@ int partition_set_config(struct kcas_io_classes *cnfg) return result; } -int partition_setup(unsigned int cache_id, const char *file) +int partition_setup(unsigned int cache_id, bool reclassify, const char *file) { int result = 0; CSVFILE *in; @@ -2556,6 +2556,7 @@ int partition_setup(unsigned int cache_id, const char *file) } if (0 == partition_get_config(in, cnfg, cache_id)) { + cnfg->repart_all = reclassify; result = partition_set_config(cnfg); } else { result = FAILURE; diff --git a/casadm/cas_lib.h b/casadm/cas_lib.h index 9d8c48d99..a09bce949 100644 --- a/casadm/cas_lib.h +++ b/casadm/cas_lib.h @@ -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 */ @@ -279,7 +279,7 @@ int flush_core(unsigned int cache_id, unsigned int core_id); int check_cache_device(const char *device_path); int partition_list(unsigned int cache_id, unsigned int output_format); -int partition_setup(unsigned int cache_id, const char *file); +int partition_setup(unsigned int cache_id, bool reclassify, const char *file); int partition_is_name_valid(const char *name); int cas_module_version(char *buff, int size); diff --git a/casadm/cas_main.c b/casadm/cas_main.c index f41fb5a4e..b8e674df0 100644 --- a/casadm/cas_main.c +++ b/casadm/cas_main.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 */ @@ -1312,6 +1312,7 @@ enum { io_class_opt_cache_id, io_class_opt_cache_file_load, + io_class_opt_keep_classification, io_class_opt_output_format, io_class_opt_io_class_id, @@ -1369,6 +1370,14 @@ static cli_option io_class_params_options[] = { .priv = (1 << io_class_opt_subcmd_configure) | (1 << io_class_opt_flag_required) }, + [io_class_opt_keep_classification] = { + .short_name = 'k', + .long_name = "keep-classification", + .desc = "Prevents reclassification of data in IO classes with IDs matching previous configuration", + .args_count = 0, + .arg = NULL, + .priv = (1 << io_class_opt_subcmd_configure) + }, [io_class_opt_output_format] = { .short_name = 'o', .long_name = "output-format", @@ -1434,6 +1443,7 @@ struct { int io_class_id; int cache_mode; int io_class_prio; + bool reclassify; int output_format; uint32_t min; uint32_t max; @@ -1443,6 +1453,7 @@ struct { .subcmd = io_class_opt_subcmd_unknown, .cache_id = 0, .file = "", + .reclassify = true, .output_format = OUTPUT_FORMAT_DEFAULT }; @@ -1479,6 +1490,10 @@ int io_class_handle_option(char *opt, const char **arg) return FAILURE; io_class_params_options[io_class_opt_output_format].priv |= (1 << io_class_opt_flag_set); + } else if (!strcmp(opt, "keep-classification")) { + io_class_params.reclassify = false; + + io_class_params_options[io_class_opt_keep_classification].priv |= (1 << io_class_opt_flag_set); } return 0; @@ -1534,8 +1549,8 @@ int io_class_handle() { switch (io_class_params.subcmd) { case io_class_opt_subcmd_configure: - return partition_setup(io_class_params.cache_id, - io_class_params.file); + return partition_setup(io_class_params.cache_id, + io_class_params.reclassify, io_class_params.file); case io_class_opt_subcmd_list: return partition_list(io_class_params.cache_id, io_class_params.output_format); diff --git a/modules/cas_cache/layer_cache_management.c b/modules/cas_cache/layer_cache_management.c index ec78435ea..7ac01513e 100644 --- a/modules/cas_cache/layer_cache_management.c +++ b/modules/cas_cache/layer_cache_management.c @@ -1746,7 +1746,7 @@ int cache_mngt_set_partitions(const char *cache_name, size_t name_len, result = ocf_mngt_cache_io_classes_configure(cache, io_class_cfg); if (result == -OCF_ERR_IO_CLASS_NOT_EXIST) result = 0; - if(result) + if (result) goto out_configure; result = _cache_mngt_save_sync(cache); @@ -1762,6 +1762,9 @@ int cache_mngt_set_partitions(const char *cache_name, size_t name_len, if (result) { while (class_id--) cas_cls_rule_destroy(cache, cls_rule[class_id]); + } else { + /* repartition without management lock */ + ocf_repart_to_default(cache, io_class_cfg, cfg->repart_all); } out_not_running: ocf_mngt_cache_put(cache); diff --git a/modules/include/cas_ioctl_codes.h b/modules/include/cas_ioctl_codes.h index bdfad29e0..2a74f2967 100644 --- a/modules/include/cas_ioctl_codes.h +++ b/modules/include/cas_ioctl_codes.h @@ -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 */ @@ -250,6 +250,7 @@ struct kcas_io_class { struct kcas_io_classes { /** Cache ID */ uint16_t cache_id; + bool repart_all; int ext_err_code; diff --git a/test/functional/api/cas/cache.py b/test/functional/api/cas/cache.py index 939f3231e..7ca296e96 100644 --- a/test/functional/api/cas/cache.py +++ b/test/functional/api/cas/cache.py @@ -164,8 +164,8 @@ def reset_counters(self) -> Output: def set_cache_mode(self, cache_mode: CacheMode, flush=None) -> Output: return casadm.set_cache_mode(cache_mode, self.cache_id, flush) - def load_io_class(self, file_path: str) -> Output: - return casadm.load_io_classes(self.cache_id, file_path) + def load_io_class(self, file_path: str, keep_classification: bool = False) -> Output: + return casadm.load_io_classes(self.cache_id, file_path, keep_classification) def list_io_classes(self) -> list: return get_io_class_list(self.cache_id) diff --git a/test/functional/api/cas/casadm.py b/test/functional/api/cas/casadm.py index b0cbef575..89aa9d831 100644 --- a/test/functional/api/cas/casadm.py +++ b/test/functional/api/cas/casadm.py @@ -490,9 +490,9 @@ def flush_core(cache_id: int, core_id: int, shortcut: bool = False) -> Output: return output -def load_io_classes(cache_id: int, file: str, shortcut: bool = False) -> Output: +def load_io_classes(cache_id: int, file: str, keep_classification: bool = False, shortcut: bool = False) -> Output: output = TestRun.executor.run( - load_io_classes_cmd(cache_id=str(cache_id), file=file, shortcut=shortcut) + load_io_classes_cmd(cache_id=str(cache_id), file=file, keep_classification=keep_classification, shortcut=shortcut) ) if output.exit_code != 0: raise CmdException("Load IO class command failed.", output) diff --git a/test/functional/api/cas/cli.py b/test/functional/api/cas/cli.py index 0c8c52e1c..264f91fbe 100644 --- a/test/functional/api/cas/cli.py +++ b/test/functional/api/cas/cli.py @@ -1,6 +1,6 @@ # # Copyright(c) 2019-2022 Intel Corporation -# Copyright(c) 2024 Huawei Technologies Co., Ltd. +# Copyright(c) 2024-2025 Huawei Technologies Co., Ltd. # SPDX-License-Identifier: BSD-3-Clause # @@ -338,10 +338,12 @@ def flush_core_cmd(cache_id: str, core_id: str, shortcut: bool = False) -> str: return casadm_bin + command -def load_io_classes_cmd(cache_id: str, file: str, shortcut: bool = False) -> str: +def load_io_classes_cmd(cache_id: str, file: str, keep_classification: bool = False, shortcut: bool = False) -> str: command = " -C -C" if shortcut else " --io-class --load-config" command += (" -i " if shortcut else " --cache-id ") + cache_id command += (" -f " if shortcut else " --file ") + file + if keep_classification: + command += (" -k" if shortcut else " --keep-classification") return casadm_bin + command diff --git a/test/functional/api/cas/cli_help_messages.py b/test/functional/api/cas/cli_help_messages.py index a99f6c834..0def9b05b 100644 --- a/test/functional/api/cas/cli_help_messages.py +++ b/test/functional/api/cas/cli_help_messages.py @@ -259,6 +259,7 @@ r"Options that are valid with --load-config \(-C\) are:", r"-i --cache-id \ Identifier of cache instance \<1-16384\>", r"-f --file \ Configuration file containing IO class definition", + r"-k --keep-classification Prevents reclassification of data in IO classes with IDs matching previous configuration", r"Lists currently configured IO classes:", r"Usage: casadm --io-class --list --cache-id \ \[option\.\.\.\]", r"Options that are valid with --list \(-L\) are:", diff --git a/test/functional/tests/security/fuzzy/kernel/fuzzy_with_io/fuzzy_io_class/test_fuzzy_io_class_load_config_flags.py b/test/functional/tests/security/fuzzy/kernel/fuzzy_with_io/fuzzy_io_class/test_fuzzy_io_class_load_config_flags.py new file mode 100644 index 000000000..df73db767 --- /dev/null +++ b/test/functional/tests/security/fuzzy/kernel/fuzzy_with_io/fuzzy_io_class/test_fuzzy_io_class_load_config_flags.py @@ -0,0 +1,91 @@ +# +# Copyright(c) 2025 Huawei Technologies Co., Ltd. +# SPDX-License-Identifier: BSD-3-Clause +# + +import pytest + +from api.cas.cache_config import ( + CacheMode, + CacheLineSize, + CleaningPolicy, + UnalignedIo, + KernelParameters, + UseIoScheduler, +) +from api.cas.cli import load_io_classes_cmd +from core.test_run import TestRun +from storage_devices.disk import DiskType, DiskTypeSet, DiskTypeLowerThan +from test_tools.peach_fuzzer.peach_fuzzer import PeachFuzzer +from tests.security.fuzzy.kernel.common.common import ( + prepare_cas_instance, + get_fuzz_config, + run_cmd_and_validate, +) +from tests.security.fuzzy.kernel.fuzzy_with_io.common.common import ( + get_basic_workload, + mount_point, +) + + +@pytest.mark.require_disk("cache", DiskTypeSet([DiskType.optane, DiskType.nand])) +@pytest.mark.require_disk("core", DiskTypeLowerThan("cache")) +@pytest.mark.parametrizex("cache_mode", CacheMode) +@pytest.mark.parametrizex("cache_line_size", CacheLineSize) +@pytest.mark.parametrizex("cleaning_policy", CleaningPolicy) +@pytest.mark.parametrizex("unaligned_io", UnalignedIo) +@pytest.mark.parametrizex("use_io_scheduler", UseIoScheduler) +def test_fuzzy_io_class_load_config_flags( + cache_mode, cache_line_size, cleaning_policy, unaligned_io, use_io_scheduler +): + """ + title: Fuzzy test for casadm 'load IO class configuration' command – flags. + description: | + Using Peach Fuzzer check Open CAS ability of handling wrong flags in + 'load IO class configuration' command. + pass_criteria: + - System did not crash + - Open CAS still works. + """ + with TestRun.step( + "Start cache with configuration and add core device, make filesystem and mount it" + ): + cache_disk = TestRun.disks["cache"] + core_disk = TestRun.disks["core"] + cache, core = prepare_cas_instance( + cache_device=cache_disk, + core_device=core_disk, + cache_mode=cache_mode, + cache_line_size=cache_line_size, + kernel_params=KernelParameters(unaligned_io, use_io_scheduler), + cleaning_policy=cleaning_policy, + mount_point=mount_point, + ) + + with TestRun.step("Run fio in background"): + fio = get_basic_workload(mount_point) + fio_pid = fio.run_in_background() + if not TestRun.executor.check_if_process_exists(fio_pid): + raise Exception("Fio is not running.") + + with TestRun.step("Prepare PeachFuzzer"): + valid_values = ["", "-k", "--keep-classification"] + valid_values = [v.encode("ascii") for v in valid_values] + PeachFuzzer.generate_config(get_fuzz_config("flags.yml")) + base_cmd = load_io_classes_cmd(cache_id=str(cache.cache_id), file="/etc/opencas/ioclass-config.csv") + " {param}" + commands = PeachFuzzer.get_fuzzed_command( + command_template=base_cmd, count=TestRun.usr.fuzzy_iter_count + ) + + for index, cmd in TestRun.iteration( + enumerate(commands), f"Run command {TestRun.usr.fuzzy_iter_count} times" + ): + with TestRun.step(f"Iteration {index + 1}"): + if not TestRun.executor.check_if_process_exists(fio_pid): + raise Exception("Fio is not running.") + + run_cmd_and_validate( + cmd=cmd, + value_name="Flag", + is_valid=cmd.param in valid_values, + )