Added support for configuring PPT1 power cap

- Updated python integration test to account for PPT1 support changes
  - Updated set/reset power-cap input format
  - Adjusted python API and updated C++ API test

Signed-off-by: gabrpham_amdeng <Gabriel.Pham@amd.com>
Change-Id: Ia9d02868b6e91c88c10a9772d9e2d9f37c3c352f
This commit is contained in:
gabrpham_amdeng
2025-10-30 09:48:35 -05:00
committed by Arif, Maisam
parent 4dfe74eb72
commit 18faddf6f3
17 changed files with 591 additions and 245 deletions
+95 -41
View File
@@ -606,20 +606,36 @@ class AMDSMICommands():
if 'limit' in current_platform_args:
if args.limit:
# Power limits
power_limit_types = {}
for power_type in amdsmi_interface.AmdSmiPowerCapType:
# Strip 'AMDSMI_POWER_CAP_TYPE_' prefix and convert to lowercase
key = power_type.name.replace('AMDSMI_POWER_CAP_TYPE_', '').lower()
power_limit_types[key] = "N/A"
try:
power_limit_error = False
power_cap_info = amdsmi_interface.amdsmi_get_power_cap_info(args.gpu)
max_power_limit = power_cap_info['max_power_cap']
max_power_limit = self.helpers.convert_SI_unit(max_power_limit, AMDSMIHelpers.SI_Unit.MICRO)
min_power_limit = power_cap_info['min_power_cap']
min_power_limit = self.helpers.convert_SI_unit(min_power_limit, AMDSMIHelpers.SI_Unit.MICRO)
socket_power_limit = power_cap_info['power_cap']
socket_power_limit = self.helpers.convert_SI_unit(socket_power_limit, AMDSMIHelpers.SI_Unit.MICRO)
power_cap_types = amdsmi_interface.amdsmi_get_supported_power_cap(args.gpu)
for sensor in power_cap_types['sensor_inds']:
power_cap_info = amdsmi_interface.amdsmi_get_power_cap_info(args.gpu, sensor)
max_power_limit = power_cap_info['max_power_cap']
max_power_limit = self.helpers.convert_SI_unit(max_power_limit, AMDSMIHelpers.SI_Unit.MICRO)
min_power_limit = power_cap_info['min_power_cap']
min_power_limit = self.helpers.convert_SI_unit(min_power_limit, AMDSMIHelpers.SI_Unit.MICRO)
socket_power_limit = power_cap_info['power_cap']
socket_power_limit = self.helpers.convert_SI_unit(socket_power_limit, AMDSMIHelpers.SI_Unit.MICRO)
ppt = {
"max_power_limit" : max_power_limit,
"min_power_limit" : min_power_limit,
"socket_power_limit" : socket_power_limit
}
sensor_name = power_cap_types['sensor_types'][sensor]
# Strip 'AMDSMI_POWER_CAP_TYPE_' prefix and convert to lowercase
sensor_key = sensor_name.name.replace('AMDSMI_POWER_CAP_TYPE_', '').lower()
power_limit_types[sensor_key] = ppt
except amdsmi_exception.AmdSmiLibraryException as e:
power_limit_error = True
max_power_limit = "N/A"
min_power_limit = "N/A"
socket_power_limit = "N/A"
logging.debug("Failed to get power cap info for gpu %s | %s", gpu_id, e.get_error_info())
# Edge temperature limits
@@ -740,9 +756,8 @@ class AMDSMICommands():
limit_info = {}
# Power limits
limit_info['max_power'] = max_power_limit
limit_info['min_power'] = min_power_limit
limit_info['socket_power'] = socket_power_limit
limit_info['ppt0'] = power_limit_types['ppt0']
limit_info['ppt1'] = power_limit_types['ppt1']
# Shutdown limits
limit_info['slowdown_edge_temperature'] = slowdown_temp_edge_limit
@@ -4909,9 +4924,13 @@ class AMDSMICommands():
self.logger.clear_multiple_devices_output()
return
# Universal args
if isinstance(args.power_cap, int):
if isinstance(args.power_cap, tuple):
pwr_type = args.power_cap.pwr_type
pwr_type_as_int = (0 if pwr_type == "ppt0" else 1 if pwr_type == "ppt1" else None)
pwr_type = pwr_type.upper()
requested_power_cap = args.power_cap.watts
try:
power_cap_info = amdsmi_interface.amdsmi_get_power_cap_info(args.gpu)
power_cap_info = amdsmi_interface.amdsmi_get_power_cap_info(args.gpu, pwr_type_as_int)
logging.debug(f"Power cap info for gpu {gpu_id} | {power_cap_info}")
min_power_cap = power_cap_info["min_power_cap"]
min_power_cap = self.helpers.convert_SI_unit(min_power_cap, AMDSMIHelpers.SI_Unit.MICRO)
@@ -4923,29 +4942,29 @@ class AMDSMICommands():
min_power_cap = "N/A"
max_power_cap = "N/A"
current_power_cap = "N/A"
self.logger.store_output(args.gpu, 'powercap', f"[{e.get_error_info(detailed=False)}] Unable to set power cap to {args.power_cap}W")
self.logger.store_output(args.gpu, 'powercap', f"[{e.get_error_info(detailed=False)}] Unable to set {pwr_type} power cap to {requested_power_cap}W")
self.logger.print_output()
self.logger.clear_multiple_devices_output()
return
if args.power_cap == current_power_cap:
self.logger.store_output(args.gpu, 'powercap', f"Power cap is already set to {args.power_cap}W")
if requested_power_cap == current_power_cap:
self.logger.store_output(args.gpu, 'powercap', f"{pwr_type} power cap is already set to {requested_power_cap}W")
elif current_power_cap == 0:
self.logger.store_output(args.gpu, 'powercap', f"Unable to set power cap to {args.power_cap}W, current value is {current_power_cap}W")
elif args.power_cap >= min_power_cap and args.power_cap <= max_power_cap:
self.logger.store_output(args.gpu, 'powercap', f"Unable to set {pwr_type} power cap to {requested_power_cap}W, current value is {current_power_cap}W")
elif requested_power_cap >= min_power_cap and requested_power_cap <= max_power_cap and requested_power_cap > 0:
try:
new_power_cap = self.helpers.convert_SI_unit(args.power_cap, AMDSMIHelpers.SI_Unit.BASE,
new_power_cap = self.helpers.convert_SI_unit(requested_power_cap, AMDSMIHelpers.SI_Unit.BASE,
AMDSMIHelpers.SI_Unit.MICRO)
amdsmi_interface.amdsmi_set_power_cap(args.gpu, 0, new_power_cap)
amdsmi_interface.amdsmi_set_power_cap(args.gpu, pwr_type_as_int, new_power_cap)
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
self.logger.store_output(args.gpu, 'powercap', f"[{e.get_error_info(detailed=False)}] Unable to set power cap to {args.power_cap}W")
self.logger.store_output(args.gpu, 'powercap', f"[{e.get_error_info(detailed=False)}] Unable to set {pwr_type} power cap to {requested_power_cap}W")
self.logger.print_output()
self.logger.clear_multiple_devices_output()
return
self.logger.store_output(args.gpu, 'powercap', f"Successfully set power cap to {args.power_cap}W")
self.logger.store_output(args.gpu, 'powercap', f"Successfully set {pwr_type} power cap to {requested_power_cap}W")
else:
# setting power cap to 0 will return the current power cap so the technical minimum value is 1
if min_power_cap == 0:
@@ -5469,32 +5488,66 @@ class AMDSMICommands():
self.logger.clear_multiple_devices_output()
return
if args.power_cap:
power_limit_types = {}
for power_type in amdsmi_interface.AmdSmiPowerCapType:
# Strip 'AMDSMI_POWER_CAP_TYPE_' prefix and convert to lowercase
key = power_type.name.replace('AMDSMI_POWER_CAP_TYPE_', '').lower()
power_limit_types[key] = "N/A"
try:
power_cap_info = amdsmi_interface.amdsmi_get_power_cap_info(args.gpu)
logging.debug(f"Power cap info for gpu {gpu_id} | {power_cap_info}")
default_power_cap_in_w = power_cap_info["default_power_cap"]
default_power_cap_in_w = self.helpers.convert_SI_unit(default_power_cap_in_w, AMDSMIHelpers.SI_Unit.MICRO)
current_power_cap_in_w = power_cap_info["power_cap"]
current_power_cap_in_w = self.helpers.convert_SI_unit(current_power_cap_in_w, AMDSMIHelpers.SI_Unit.MICRO)
power_cap_types = amdsmi_interface.amdsmi_get_supported_power_cap(args.gpu)
for sensor in power_cap_types['sensor_inds']:
power_cap_info = amdsmi_interface.amdsmi_get_power_cap_info(args.gpu, sensor)
logging.debug(f"Power cap info for gpu {gpu_id} ppt{sensor} | {power_cap_info}")
default_power_cap_in_w = power_cap_info["default_power_cap"]
default_power_cap_in_w = self.helpers.convert_SI_unit(default_power_cap_in_w, AMDSMIHelpers.SI_Unit.MICRO)
current_power_cap_in_w = power_cap_info["power_cap"]
current_power_cap_in_w = self.helpers.convert_SI_unit(current_power_cap_in_w, AMDSMIHelpers.SI_Unit.MICRO)
sensor_name = power_cap_types['sensor_types'][sensor]
# Strip 'AMDSMI_POWER_CAP_TYPE_' prefix and convert to lowercase
sensor_key = sensor_name.name.replace('AMDSMI_POWER_CAP_TYPE_', '').lower()
power_limit_types[sensor_key] = (default_power_cap_in_w, current_power_cap_in_w)
except amdsmi_exception.AmdSmiLibraryException as e:
self.logger.store_output(args.gpu, 'powercap', f"[{e.get_error_info(detailed=False)}] Unable to reset power cap to default")
self.logger.print_output()
self.logger.clear_multiple_devices_output()
return
if current_power_cap_in_w == default_power_cap_in_w:
self.logger.store_output(args.gpu, 'powercap', f"Power cap is already set to {default_power_cap_in_w}W")
# TODO Make agnostic to number of power cap types
final_output = {"ppt0": "", "ppt1": ""}
if power_limit_types['ppt0'] == "N/A":
final_output['ppt0'] = f"PPT0 Power cap information is not available"
elif power_limit_types['ppt0'][1] == power_limit_types['ppt0'][0]:
final_output['ppt0'] = f"PPT0 Power cap is already set to {power_limit_types['ppt0'][0]}W"
else:
try:
default_power_cap_in_uw = self.helpers.convert_SI_unit(default_power_cap_in_w,
AMDSMIHelpers.SI_Unit.BASE,
AMDSMIHelpers.SI_Unit.MICRO)
amdsmi_interface.amdsmi_set_power_cap(args.gpu, 0, default_power_cap_in_uw)
default_ppt0_power_cap_in_uw = self.helpers.convert_SI_unit(power_limit_types['ppt0'][0],
AMDSMIHelpers.SI_Unit.BASE,
AMDSMIHelpers.SI_Unit.MICRO)
amdsmi_interface.amdsmi_set_power_cap(args.gpu, 0, default_ppt0_power_cap_in_uw)
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 reset power cap to {default_power_cap_in_w} on GPU {gpu_id}") from e
self.logger.store_output(args.gpu, 'powercap', f"Successfully set power cap to {default_power_cap_in_w}W")
raise ValueError(f"Unable to reset PPT0 power cap to {power_limit_types['ppt0'][0]} on GPU {gpu_id}") from e
final_output['ppt0'] = f"Successfully reset PPT0 power cap to {power_limit_types['ppt0'][0]}W"
if power_limit_types['ppt1'] == "N/A":
final_output['ppt1'] = f"PPT1 Power cap information is not available"
elif power_limit_types['ppt1'][1] == power_limit_types['ppt1'][0]:
final_output['ppt1'] = f"PPT1 Power cap is already set to {power_limit_types['ppt1'][0]}W"
else:
try:
default_ppt1_power_cap_in_uw = self.helpers.convert_SI_unit(power_limit_types['ppt1'][0],
AMDSMIHelpers.SI_Unit.BASE,
AMDSMIHelpers.SI_Unit.MICRO)
amdsmi_interface.amdsmi_set_power_cap(args.gpu, 1, default_ppt1_power_cap_in_uw)
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 reset PPT1 power cap to {power_limit_types['ppt1'][0]} on GPU {gpu_id}") from e
final_output['ppt1'] = f"Successfully reset PPT1 power cap to {power_limit_types['ppt1'][0]}W"
self.logger.store_output(args.gpu, 'powercap', final_output)
self.logger.print_output()
self.logger.clear_multiple_devices_output()
return
@@ -5822,7 +5875,8 @@ class AMDSMICommands():
if args.power_usage and not args.default_output:
# Get Current Power Cap
try:
power_cap_info = amdsmi_interface.amdsmi_get_power_cap_info(args.gpu)
# assume that we're always asking for ppt0 for quick checks like this
power_cap_info = amdsmi_interface.amdsmi_get_power_cap_info(args.gpu, 0)
monitor_values['max_power'] = power_cap_info['power_cap'] # Get current power cap (`power_cap`) socket is set to
# `max_power_cap`, is the maximum value it can be set to
monitor_values['max_power'] = self.helpers.convert_SI_unit(monitor_values['max_power'], AMDSMIHelpers.SI_Unit.MICRO)
@@ -7340,9 +7394,9 @@ class AMDSMICommands():
gpu_info_dict.update({"temp": temperature})
# rest of power usage info
# rest of power usage info; Will assume we're always trying to get PPT0 for now
try:
power_cap_info = amdsmi_interface.amdsmi_get_power_cap_info(processor)
power_cap_info = amdsmi_interface.amdsmi_get_power_cap_info(processor, 0)
socket_power_limit = self.helpers.convert_SI_unit(power_cap_info['power_cap'], AMDSMIHelpers.SI_Unit.MICRO)
power_usage = {"current_power": current_power, "power_limit": socket_power_limit}
except amdsmi_exception.AmdSmiLibraryException as e:
+37 -13
View File
@@ -815,26 +815,50 @@ class AMDSMIHelpers():
def get_power_caps(self):
device_handles = amdsmi_interface.amdsmi_get_processor_handles()
power_cap_min = amdsmi_interface.MaxUIntegerTypes.UINT64_T # start out at max and min and then find real min and max
power_cap_max = 0
power_limit_types = {
'ppt0': {
'power_cap_min': amdsmi_interface.MaxUIntegerTypes.UINT64_T,
'power_cap_max': 0
},
'ppt1': {
'power_cap_min': amdsmi_interface.MaxUIntegerTypes.UINT64_T,
'power_cap_max': 0
}
}
for dev in device_handles:
try:
power_cap_info = amdsmi_interface.amdsmi_get_power_cap_info(dev)
if power_cap_info['max_power_cap'] > power_cap_max:
power_cap_max = power_cap_info['max_power_cap']
if power_cap_info['min_power_cap'] < power_cap_max:
power_cap_min = power_cap_info['min_power_cap']
except amdsmi_interface.AmdSmiLibraryException as e:
power_cap_types = amdsmi_interface.amdsmi_get_supported_power_cap(dev)
for sensor in power_cap_types['sensor_inds']:
power_cap_info = amdsmi_interface.amdsmi_get_power_cap_info(dev, sensor)
if power_cap_info['max_power_cap'] > power_limit_types[f'ppt{sensor}']['power_cap_max']:
power_limit_types[f'ppt{sensor}']['power_cap_max'] = power_cap_info['max_power_cap']
if power_cap_info['min_power_cap'] < power_limit_types[f'ppt{sensor}']['power_cap_min']:
power_limit_types[f'ppt{sensor}']['power_cap_min'] = power_cap_info['min_power_cap']
except (amdsmi_interface.AmdSmiLibraryException, KeyError) as e:
logging.debug(f"AMDSMIHelpers.get_power_caps - Unable to get power cap info for device {dev}: {str(e)}")
continue
# If we never found a real min or max, set them to N/A
if power_cap_min == amdsmi_interface.MaxUIntegerTypes.UINT64_T:
power_cap_min = "N/A"
if power_cap_max == 0:
power_cap_max = "N/A"
for ppt_key in ['ppt0', 'ppt1']:
if power_limit_types[ppt_key]['power_cap_min'] == amdsmi_interface.MaxUIntegerTypes.UINT64_T:
power_limit_types[ppt_key]['power_cap_min'] = "N/A"
if power_limit_types[ppt_key]['power_cap_max'] == 0:
power_limit_types[ppt_key]['power_cap_max'] = "N/A"
return (power_cap_min, power_cap_max)
ppt0_power_cap_max = self.format_power_cap(power_limit_types['ppt0']['power_cap_min'])
ppt0_power_cap_min = self.format_power_cap(power_limit_types['ppt0']['power_cap_max'])
ppt1_power_cap_max = self.format_power_cap(power_limit_types['ppt1']['power_cap_max'])
ppt1_power_cap_min = self.format_power_cap(power_limit_types['ppt1']['power_cap_min'])
return (ppt0_power_cap_min, ppt0_power_cap_min, ppt1_power_cap_max, ppt1_power_cap_min)
def format_power_cap(self, value):
if value != "N/A":
converted = self.convert_SI_unit(value, AMDSMIHelpers.SI_Unit.MICRO)
return f"{converted} W"
return value
def get_soc_pstates(self):
+28 -10
View File
@@ -288,6 +288,30 @@ class AMDSMIParser(argparse.ArgumentParser):
return AMDSMIFreqArgs
def _power_cap_options(self):
"""Custom action for setting power cap options"""
output_format = self.helpers.get_output_format()
class AMDSMIPowerCapArgs(argparse.Action):
def __call__(self, parser: AMDSMIParser, namespace: argparse.Namespace,
values: list, option_string: Optional[str] = None) -> None:
if len(values) != 2:
raise amdsmi_cli_exceptions.AmdSmiInvalidParameterException(sys.argv[1], values, output_format)
power_cap_type = values[0]
power_cap_value = values[1]
if power_cap_type not in ['ppt0', 'ppt1']:
raise amdsmi_cli_exceptions.AmdSmiInvalidParameterException(sys.argv[1], power_cap_type, output_format)
if not power_cap_value.isdigit():
raise amdsmi_cli_exceptions.AmdSmiInvalidParameterValueException(sys.argv[1], power_cap_value, output_format)
power_cap_args = collections.namedtuple('power_cap_args', ['pwr_type', 'watts'])
setattr(namespace, self.dest, power_cap_args(power_cap_type, int(power_cap_value)))
return AMDSMIPowerCapArgs
def _check_folder_path(self):
""" Argument action validator:
Returns a path to folder from the folder path provided.
@@ -1235,14 +1259,8 @@ class AMDSMIParser(argparse.ArgumentParser):
xgmi_plpd_help_info = ", ".join(self.helpers.get_xgmi_plpd_policies())
set_xgmi_plpd_help = f"Set the GPU XGMI per-link power down policy using policy id, an integer. Valid id's include:\n\t{xgmi_plpd_help_info}"
set_clock_freq_help = "Set one or more sclk (aka gfxclk), mclk, fclk, pcie, or socclk frequency levels.\n\tUse `amd-smi static --clock` to find acceptable levels.\n\tUse `amd-smi static --bus` to find acceptable pcie levels."
power_cap_min, power_cap_max = self.helpers.get_power_caps()
if power_cap_max != "N/A":
power_cap_max = self.helpers.convert_SI_unit(power_cap_max, AMDSMIHelpers.SI_Unit.MICRO)
power_cap_max = str(power_cap_max) + ' W'
if power_cap_min != "N/A":
power_cap_min = self.helpers.convert_SI_unit(power_cap_min, AMDSMIHelpers.SI_Unit.MICRO)
power_cap_min = str(power_cap_min) + ' W'
set_power_cap_help = f"Set power capacity limit:\n\tmin cap: {power_cap_min}, max cap: {power_cap_max}"
ppt0_power_cap_min, ppt0_power_cap_max, ppt1_power_cap_min, ppt1_power_cap_max = self.helpers.get_power_caps()
set_power_cap_help = f"Set either PPT0 or PPT1 power capacity limit:\n\tex: amd-smi set -o ppt0 1300\n\tPPT0 min cap: {ppt0_power_cap_min}, PPT0 max cap: {ppt0_power_cap_max}\n\tPPT1 min cap: {ppt1_power_cap_min}, PPT1 max cap: {ppt1_power_cap_max}"
set_clk_limit_help = "Sets the sclk (aka gfxclk) or mclk minimum and maximum frequencies. \n\tex: amd-smi set -L (sclk | mclk) (min | max) value"
set_process_isolation_help = "Enable or disable the GPU process isolation on a per partition basis:\n 0 for disable and 1 for enable.\n"
@@ -1281,7 +1299,7 @@ class AMDSMIParser(argparse.ArgumentParser):
required=False, help=set_compute_partition_help, metavar=('TYPE/INDEX'))
set_value_exclusive_group.add_argument('-M', '--memory-partition', action='store', choices=self.helpers.get_memory_partition_types(), type=str.upper, required=False, help=set_memory_partition_help, metavar='PARTITION')
# Power cap is enabled on guest, maintain order
set_value_exclusive_group.add_argument('-o', '--power-cap', action='store', type=lambda value: self._positive_int(value, '--power-cap'), required=False, help=set_power_cap_help, metavar='WATTS')
set_value_exclusive_group.add_argument('-o', '--power-cap', action=self._power_cap_options(), nargs=2, required=False, help=set_power_cap_help, metavar=('PWR_TYPE', 'WATTS'))
if self.helpers.is_baremetal():
set_value_exclusive_group.add_argument('-p', '--soc-pstate', action='store', required=False, type=lambda value: self._not_negative_int(value, '--soc-pstate'), help=set_soc_pstate_help, metavar='POLICY_ID')
set_value_exclusive_group.add_argument('-x', '--xgmi-plpd', action='store', required=False, type=lambda value: self._not_negative_int(value, '--xgmi-plpd'), help=set_xgmi_plpd_help, metavar='POLICY_ID')
@@ -1338,7 +1356,7 @@ class AMDSMIParser(argparse.ArgumentParser):
reset_profile_help = "Reset power profile back to default"
reset_xgmierr_help = "Reset XGMI error counts"
reset_perf_det_help = "Disable performance determinism"
reset_power_cap_help = "Reset power capacity limit to max capable"
reset_power_cap_help = "Reset the PPT0 and PPT1 power capacity limit to max capable"
reset_gpu_clean_local_data_help = "Clean up local data in LDS/GPRs on a per partition basis"
reset_gpu_driver_help = "Reset (reload) AMD GPU driver"