From 5f9c2db6f37d93335ce2ddc3af5c0c2acfcfd20d Mon Sep 17 00:00:00 2001 From: gabrpham Date: Wed, 4 Dec 2024 11:46:59 -0600 Subject: [PATCH] [SWDEV-484382] Added new command `amd-smi set -c/--clk-level` Signed-off-by: gabrpham Change-Id: If45152e3a3c94f65b6a8a960601b9ed16fa3d0d7 --- CHANGELOG.md | 12 ++++++ amdsmi_cli/amdsmi_commands.py | 73 +++++++++++++++++++++++++++++--- amdsmi_cli/amdsmi_parser.py | 33 +++++++++++++++ docs/how-to/amdsmi-cli-tool.md | 2 + py-interface/amdsmi_interface.py | 18 ++++++-- 5 files changed, 127 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6749e6226..f450b505e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,18 @@ Full documentation for amd_smi_lib is available at [https://rocm.docs.amd.com/pr ### Added +- **Added new command `amd-smi set -c/--clock-level`**. + This new command sets the performance level of the selected clock on the desired GPUs. The command can accept a range of acceptable levels, but will not set the level when a level is beyond the number of frequency levels as show in `amd-smi static -C/--clock` + +```shell +sudo amd-smi set -c sclk 5 6 +GPU: 0 + CLK_LEVEL: Successfully changed sclk perf level(s) to 5, 6 + +GPU: 1 + CLK_LEVEL: level(s) 5, 6 is/are greater than performance levels supported for device +``` + - **Added new command `amd-smi static -C/--clock`**. This new command displays the clock frequency performance levels for the selected GPUs and clocks. diff --git a/amdsmi_cli/amdsmi_commands.py b/amdsmi_cli/amdsmi_commands.py index 9262e02ca7..9073594e99 100644 --- a/amdsmi_cli/amdsmi_commands.py +++ b/amdsmi_cli/amdsmi_commands.py @@ -31,7 +31,7 @@ import os from _version import __version__ from amdsmi_helpers import AMDSMIHelpers from amdsmi_logger import AMDSMILogger -from amdsmi_cli_exceptions import AmdSmiRequiredCommandException, AmdSmiInvalidParameterValueException +from amdsmi_cli_exceptions import AmdSmiRequiredCommandException, AmdSmiInvalidParameterException from rocm_version import get_rocm_version from amdsmi import amdsmi_interface from amdsmi import amdsmi_exception @@ -871,7 +871,7 @@ class AMDSMICommands(): else: clk_type_conversion = "N/A" output_format = self.helpers.get_output_format() - raise AmdSmiInvalidParameterValueException(clk_type, output_format) # clk type given is bad + raise AmdSmiInvalidParameterException(clk_type, output_format) # clk type given is bad try: frequencies = amdsmi_interface.amdsmi_get_clk_freq(args.gpu, clk_type_conversion) @@ -3897,7 +3897,7 @@ class AMDSMICommands(): def set_gpu(self, args, multiple_devices=False, gpu=None, fan=None, perf_level=None, profile=None, perf_determinism=None, compute_partition=None, memory_partition=None, power_cap=None, soc_pstate=None, xgmi_plpd = None, - process_isolation=None, clk_limit=None): + process_isolation=None, clk_limit=None, clk_level=None): """Issue reset commands to target gpu(s) Args: @@ -3946,6 +3946,8 @@ class AMDSMICommands(): args.process_isolation = process_isolation if clk_limit: args.clk_limit = clk_limit + if clk_level: + args.clk_level = clk_level # Handle No GPU passed if args.gpu == None: @@ -3969,6 +3971,7 @@ class AMDSMICommands(): args.power_cap is not None, args.soc_pstate is not None, args.xgmi_plpd is not None, + args.clk_level is not None, args.clk_limit is not None, args.process_isolation is not None]): command = " ".join(sys.argv[1:]) @@ -4220,6 +4223,62 @@ class AMDSMICommands(): raise PermissionError('Command requires elevation') from e raise ValueError(f"Unable to set XGMI policy to {args.xgmi_plpd} on {gpu_string}") from e self.logger.store_output(args.gpu, 'xgmiplpd', f"Successfully set per-link power down policy to id {args.xgmi_plpd}") + if isinstance(args.clk_level, tuple): + clk_type = args.clk_level.clk_type + perf_levels = args.clk_level.perf_levels + + # check if perf levels are all valid levels + try: + if clk_type == "sclk": + clk_type_conversion = amdsmi_interface.AmdSmiClkType.SYS + elif clk_type == "mclk": + clk_type_conversion = amdsmi_interface.AmdSmiClkType.MEM + elif clk_type == "pcie": + clk_type_conversion = amdsmi_interface.AmdSmiClkType.PCIE + elif clk_type == "fclk": + clk_type_conversion = amdsmi_interface.AmdSmiClkType.DF + elif clk_type == "socclk": + clk_type_conversion = amdsmi_interface.AmdSmiClkType.SOC + else: + clk_type_conversion = "N/A" + frequencies = amdsmi_interface.amdsmi_get_clk_freq(args.gpu, clk_type_conversion) + num_supported = frequencies['num_supported'] + except amdsmi_exception.AmdSmiLibraryException as e: + pass + + # convert perf_levels given to freq bit mask + freq_mask = 0 + valid_level = True + invalid_levels = [] + for level in perf_levels: + if level < num_supported: + freq_mask += 2**level + else: + # cancel this instance since the level should not be possible + invalid_levels.append(level) + valid_level = False + + if valid_level: + perf_levels_str = str(perf_levels).strip('[]') + if clk_type.lower() == "pcie": + try: + amdsmi_interface.amdsmi_set_gpu_pci_bandwidth(args.gpu, freq_mask) + except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_NO_PERM: + raise PermissionError('Command requires elevation') from e + raise ValueError(f"Unable to set pcie bandwidth to perf level(s) {perf_levels_str} on {gpu_string}") from e + else: + try: + amdsmi_interface.amdsmi_set_clk_freq(args.gpu, clk_type, freq_mask) + except amdsmi_exception.AmdSmiLibraryException as e: + if e.get_error_code() == amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_NO_PERM: + raise PermissionError('Command requires elevation') from e + raise ValueError(f"Unable to set {clk_type} to perf level(s) {perf_levels_str} on {gpu_string}") from e + self.logger.store_output(args.gpu, 'clk_level', f"Successfully changed {clk_type} perf level(s) to {perf_levels_str}") + else: + invalid_levels_str = str(invalid_levels).strip('[]') + self.logger.store_output(args.gpu, 'clk_level', f"level(s) {invalid_levels_str} is/are greater than performance levels supported for device") + if isinstance(args.clk_limit, tuple): clk_type = args.clk_limit.clk_type lim_type = args.clk_limit.lim_type @@ -4294,7 +4353,7 @@ class AMDSMICommands(): cpu_pwr_eff_mode=None, cpu_gmi3_link_width=None, cpu_pcie_link_rate=None, cpu_df_pstate_range=None, cpu_enable_apb=None, cpu_disable_apb=None, soc_boost_limit=None, core=None, core_boost_limit=None, soc_pstate=None, xgmi_plpd=None, - process_isolation=None, clk_limit=None): + process_isolation=None, clk_limit=None, clk_level=None): """Issue reset commands to target gpu(s) Args: @@ -4346,7 +4405,7 @@ class AMDSMICommands(): gpu_args_enabled = False gpu_attributes = ["fan", "perf_level", "profile", "perf_determinism", "compute_partition", "memory_partition", "power_cap", "soc_pstate", "xgmi_plpd", - "process_isolation", "clk_limit"] + "process_isolation", "clk_limit", "clk_level"] for attr in gpu_attributes: if hasattr(args, attr): if getattr(args, attr) is not None: @@ -4414,7 +4473,7 @@ class AMDSMICommands(): self.set_gpu(args, multiple_devices, gpu, fan, perf_level, profile, perf_determinism, compute_partition, memory_partition, power_cap, soc_pstate, xgmi_plpd, - process_isolation, clk_limit) + process_isolation, clk_limit, clk_level) elif self.helpers.is_amd_hsmp_initialized(): # Only CPU is initialized if args.cpu == None and args.core == None: raise ValueError('No CPU or CORE provided, specific target(s) are needed') @@ -4434,7 +4493,7 @@ class AMDSMICommands(): self.set_gpu(args, multiple_devices, gpu, fan, perf_level, profile, perf_determinism, compute_partition, memory_partition, power_cap, soc_pstate, xgmi_plpd, - process_isolation, clk_limit) + process_isolation, clk_limit, clk_level) def reset(self, args, multiple_devices=False, gpu=None, gpureset=None, diff --git a/amdsmi_cli/amdsmi_parser.py b/amdsmi_cli/amdsmi_parser.py index 21aec247cb..a6fbbc0c15 100644 --- a/amdsmi_cli/amdsmi_parser.py +++ b/amdsmi_cli/amdsmi_parser.py @@ -203,6 +203,37 @@ class AMDSMIParser(argparse.ArgumentParser): return AMDSMILimitArgs + def _level_select(self): + """Custom action for setting clock frequencies to particular performance level""" + output_format = self.helpers.get_output_format() + + class AMDSMIFreqArgs(argparse.Action): + def __call__(self, parser: AMDSMIParser, namespace: argparse.Namespace, + values: list, option_string: Optional[str] = None) -> None: + # valid values + valid_clk_types = ('sclk', 'mclk', 'pcie', 'fclk', 'socclk') + clk_type = values[0] + perf_levels_str = values[1:] + + # Check if the sclk and mclk parameters are valid + if clk_type not in valid_clk_types: + raise amdsmi_cli_exceptions.AmdSmiInvalidParameterException(clk_type, output_format) + + perf_levels = [] + # Check if every item in perf level is valid + for level in perf_levels_str: + if not level.isdigit(): + raise amdsmi_cli_exceptions.AmdSmiInvalidParameterValueException(level, output_format) + level = int(level) + if level < 0: + raise amdsmi_cli_exceptions.AmdSmiInvalidParameterValueException(level, output_format) + perf_levels.append(level) + + clk_level_args = collections.namedtuple('clk_level_args', ['clk_type', 'perf_levels']) + setattr(namespace, self.dest, clk_level_args(clk_type, perf_levels)) + return AMDSMIFreqArgs + + def _check_output_file_path(self): """ Argument action validator: Returns a path to a file from the output file path provided. @@ -1052,6 +1083,7 @@ class AMDSMIParser(argparse.ArgumentParser): set_soc_pstate_help = "Set the GPU soc pstate policy using policy id\n" set_xgmi_plpd_help = "Set the GPU XGMI per-link power down policy using policy id\n" set_clk_limit_help = "Sets the sclk (aka gfxclk) or mclk minimum and maximum frequencies:\n\tamd-smi set -L (sclk | mclk) (min | max) value" + set_clock_freq_help = "Set the sclk (aka gfxclk), mclk, fclk, pcie, or socclk frequency performance level.\nCan take range of acceptable levels." set_process_isolation_help = "Enable or disable the GPU process isolation on a per partition basis:\n\t0 for disable and 1 for enable.\n" # Help text for CPU set options @@ -1091,6 +1123,7 @@ class AMDSMIParser(argparse.ArgumentParser): set_value_parser.add_argument('-o', '--power-cap', action='store', type=self._positive_int, required=False, help=set_power_cap_help, metavar='WATTS') set_value_parser.add_argument('-p', '--soc-pstate', action='store', required=False, type=self._not_negative_int, help=set_soc_pstate_help, metavar='POLICY_ID') set_value_parser.add_argument('-x', '--xgmi-plpd', action='store', required=False, type=self._not_negative_int, help=set_xgmi_plpd_help, metavar='POLICY_ID') + set_value_parser.add_argument('-c', '--clk-level', action=self._level_select(), nargs='+', required=False, help=set_clock_freq_help, metavar=('CLK_TYPE', 'PERF_LEVELS')) set_value_parser.add_argument('-L', '--clk-limit', action=self._limit_select(), nargs=3, required=False, help=set_clk_limit_help, metavar=('CLK_TYPE', 'LIM_TYPE', 'VALUE')) set_value_parser.add_argument('-R', '--process-isolation', action='store', choices=[0,1], type=self._not_negative_int, required=False, help=set_process_isolation_help, metavar='STATUS') diff --git a/docs/how-to/amdsmi-cli-tool.md b/docs/how-to/amdsmi-cli-tool.md index daaf17056e..1f3589dec2 100644 --- a/docs/how-to/amdsmi-cli-tool.md +++ b/docs/how-to/amdsmi-cli-tool.md @@ -540,6 +540,8 @@ Set Arguments: -o, --power-cap WATTS Set power capacity limit -p, --soc-pstate POLICY_ID Set the GPU soc pstate policy using policy id -x, --xgmi-plpd POLICY_ID Set the GPU XGMI per-link power down policy using policy id + -c, --clk-level CLK_TYPE [PERF_LEVELS ...] Set the sclk (aka gfxclk), mclk, fclk, pcie, or socclk frequency performance level. + Can take range of acceptable levels. -L, --clk-limit CLK_TYPE LIM_TYPE VALUE Sets the sclk (aka gfxclk) or mclk minimum and maximum frequencies amd-smi set -L (sclk | mclk) (min | max) value -R, --process-isolation STATUS Enable or disable the GPU process isolation: diff --git a/py-interface/amdsmi_interface.py b/py-interface/amdsmi_interface.py index 95abd19238..1a809a0fe6 100644 --- a/py-interface/amdsmi_interface.py +++ b/py-interface/amdsmi_interface.py @@ -2956,21 +2956,31 @@ def amdsmi_reset_gpu_fan( def amdsmi_set_clk_freq( processor_handle: amdsmi_wrapper.amdsmi_processor_handle, - clk_type: AmdSmiClkType, + clk_type: str, freq_bitmask: int, ): if not isinstance(processor_handle, amdsmi_wrapper.amdsmi_processor_handle): raise AmdSmiParameterException( processor_handle, amdsmi_wrapper.amdsmi_processor_handle ) - if not isinstance(clk_type, AmdSmiClkType): - raise AmdSmiParameterException(clk_type, AmdSmiParameterException) + if clk_type.lower() == "sclk": + clk_type_conversion = AmdSmiClkType.SYS + elif clk_type.lower() == "mclk": + clk_type_conversion = AmdSmiClkType.MEM + elif clk_type.lower() == "fclk": + clk_type_conversion = AmdSmiClkType.DF + elif clk_type.lower() == "socclk": + clk_type_conversion = AmdSmiClkType.SOC + else: + clk_type_conversion = "N/A" + if not isinstance(clk_type_conversion, AmdSmiClkType): + raise AmdSmiParameterException(clk_type_conversion, AmdSmiClkType) if not isinstance(freq_bitmask, int): raise AmdSmiParameterException(freq_bitmask, int) freq_bitmask = ctypes.c_uint64(freq_bitmask) _check_res( amdsmi_wrapper.amdsmi_set_clk_freq( - processor_handle, clk_type, freq_bitmask + processor_handle, clk_type_conversion, freq_bitmask ) )