[SWDEV-488276] Add partition 2.0 functionality (#44)
Changes:
* CLI:
- Updated amd-smi partition
- Updated amd-smi partition -c
- Updated amd-smi partition -m
- Updated amd-smi partition -a
- Updated amd-smi set -M <NPS1/NPS2/NPS4/NPS8>
- Updated amd-smi set -C <SPX/DPX/QPX/TPX/CPX>
- Updated amd-smi set -C <ACCELERATOR_TYPE> or <PROFILE_INDEX>
Where PROFILE_INDEX = available ACCELERATOR_TYPES
- Updated amd-smi set --help, now includes more detail for
amd-smi set -C <ACCELERATOR_TYPE> or <PROFILE_INDEX>
* API:
- Added amdsmi_get_gpu_memory_partition_config
- Added amdsmi_set_gpu_memory_partition_mode
- Added amdsmi_get_gpu_accelerator_partition_profile_config
- Updated amdsmi_get_gpu_accelerator_partition_profile_config
- Added amdsmi_set_gpu_accelerator_partition_profile
Signed-off-by: Charis Poag <Charis.Poag@amd.com>
[ROCm/amdsmi commit: c1cd2b46ef]
Bu işleme şunda yer alıyor:
işlemeyi yapan:
Maisam Arif
ebeveyn
8f203f8bca
işleme
fa81bcb513
@@ -4156,14 +4156,35 @@ class AMDSMICommands():
|
||||
|
||||
self.logger.store_output(args.gpu, 'perfdeterminism', f"Successfully enabled performance determinism and set GFX clock frequency to {args.perf_determinism}")
|
||||
if args.compute_partition:
|
||||
compute_partition = amdsmi_interface.AmdSmiComputePartitionType[args.compute_partition]
|
||||
try:
|
||||
amdsmi_interface.amdsmi_set_gpu_compute_partition(args.gpu, compute_partition)
|
||||
(accelerator_set_choices, accelerator_profiles) = self.helpers.get_accelerator_choices_types_indices()
|
||||
logging.debug("args.compute_partition: %s; Accelerator_set_choices: %s", str(args.compute_partition), str(json.dumps(accelerator_set_choices, indent=4)))
|
||||
if args.compute_partition in accelerator_profiles['profile_types']:
|
||||
compute_partition = amdsmi_interface.AmdSmiComputePartitionType[args.compute_partition]
|
||||
index = accelerator_profiles['profile_types'].index(args.compute_partition)
|
||||
attempted_to_set = f"Attempted to set accelerator partition to {args.compute_partition} (profile #{accelerator_profiles['profile_indices'][int(index)]} on {gpu_string}"
|
||||
amdsmi_interface.amdsmi_set_gpu_compute_partition(args.gpu, compute_partition)
|
||||
self.logger.store_output(args.gpu, 'accelerator_partition', f"Successfully set accelerator partition to {args.compute_partition} (profile #{accelerator_profiles['profile_indices'][int(index)]})")
|
||||
elif args.compute_partition in accelerator_profiles['profile_indices']:
|
||||
compute_partition = int(args.compute_partition)
|
||||
index = accelerator_profiles['profile_indices'].index(args.compute_partition)
|
||||
attempted_to_set = f"Attempted to set accelerator partition to {accelerator_profiles['profile_types'][int(index)]} (profile #{args.compute_partition}) on {gpu_string}"
|
||||
amdsmi_interface.amdsmi_set_gpu_accelerator_partition_profile(args.gpu, compute_partition)
|
||||
self.logger.store_output(args.gpu, 'accelerator_partition', f"Successfully set accelerator partition to {accelerator_profiles['profile_types'][int(index)]} (profile #{args.compute_partition})")
|
||||
else:
|
||||
raise ValueError(f"Invalid accelerator configuration {args.compute_partition} on {gpu_string}")
|
||||
|
||||
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 compute partition to {args.compute_partition} on {gpu_string}") from e
|
||||
self.logger.store_output(args.gpu, 'computepartition', f"Successfully set compute partition to {args.compute_partition}")
|
||||
elif e.get_error_code() == amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_SETTING_UNAVAILABLE:
|
||||
print(f"\n{attempted_to_set}\n"
|
||||
f"\n[AMDSMI_STATUS_SETTING_UNAVAILABLE] Please check amd-smi partition --memory --accelerator for available profiles.\n"
|
||||
"Users may need to switch memory partition to another mode in order to enable the desired accelerator partition.\n")
|
||||
raise ValueError(f"[AMDSMI_STATUS_SETTING_UNAVAILABLE] Unable to set accelerator partition to {args.compute_partition} on {gpu_string}") from e
|
||||
else:
|
||||
raise ValueError(f"Unable to set accelerator partition to {args.compute_partition} on {gpu_string}") from e
|
||||
|
||||
if args.memory_partition:
|
||||
lock = multiprocessing.Lock()
|
||||
lock.acquire()
|
||||
@@ -4172,49 +4193,18 @@ class AMDSMICommands():
|
||||
# Info used if AMDSMI_STATUS_INVAL is caught & to set progress bar #
|
||||
####################################################################
|
||||
try:
|
||||
memory_partition = amdsmi_interface.amdsmi_get_gpu_memory_partition(args.gpu) # this info likely actually comes from different apis than used here
|
||||
memory_dict = {'caps': "N/A", 'current': "N/A"}
|
||||
memory_partition_config = amdsmi_interface.amdsmi_get_gpu_memory_partition_config(args.gpu)
|
||||
memory_dict['caps'] = str(memory_partition_config['partition_caps']).replace("]", "").replace("[", "").replace("\'", "").replace(" ", "")
|
||||
memory_dict['current'] = memory_partition_config['mp_mode']
|
||||
except amdsmi_exception.AmdSmiLibraryException as e:
|
||||
memory_partition = "N/A"
|
||||
logging.debug("Failed to get current memory partition for GPU %s | %s", gpu_id, e.get_error_info())
|
||||
try:
|
||||
mem_caps_str = "N/A"
|
||||
partition_dict = amdsmi_interface.amdsmi_get_gpu_accelerator_partition_profile(args.gpu)
|
||||
temp_mem_caps = partition_dict['partition_profile']['memory_caps']
|
||||
mem_caps = temp_mem_caps.nps_cap_mask
|
||||
if temp_mem_caps.amdsmi_nps_flags_t == None:
|
||||
mem_caps_list = []
|
||||
if mem_caps & 1 == 1:
|
||||
mem_caps_list.append("NPS1")
|
||||
if mem_caps & 2 == 2:
|
||||
mem_caps_list.append("NPS2")
|
||||
if mem_caps & 4 == 4:
|
||||
mem_caps_list.append("NPS4")
|
||||
if mem_caps & 8 == 8:
|
||||
mem_caps_list.append("NPS8")
|
||||
mem_caps_str = str(mem_caps_list).replace("]", "").replace("[", "")
|
||||
else:
|
||||
mem_caps = temp_mem_caps.amdsmi_nps_flags_t
|
||||
mem_caps_list = []
|
||||
if mem_caps.nps1_cap == 1:
|
||||
mem_caps_list.append("NPS1")
|
||||
if mem_caps.nps2_cap == 1:
|
||||
mem_caps_list.append("NPS2")
|
||||
if mem_caps.nps4_cap == 1:
|
||||
mem_caps_list.append("NPS4")
|
||||
if mem_caps.nps8_cap == 1:
|
||||
mem_caps_list.append("NPS8")
|
||||
mem_caps_str = str(mem_caps_list).replace("]", "").replace("[", "").replace("\'", "")
|
||||
if mem_caps_str == "":
|
||||
mem_caps_str = "N/A"
|
||||
except amdsmi_exception.AmdSmiLibraryException as e:
|
||||
logging.debug("Failed to get accelerator partition profile for GPU %s | %s", gpu_id, e.get_error_info())
|
||||
memory_dict = {'caps': mem_caps_str, 'current': memory_partition}
|
||||
|
||||
###############################################################
|
||||
# memory partition set starts here #
|
||||
###############################################################
|
||||
showProgressBar = False
|
||||
if ((str(memory_dict['current']) != "N/A") and (str(args.memory_partition) in mem_caps_str)
|
||||
if ((str(memory_dict['current']) != "N/A") and (str(args.memory_partition) in memory_dict['caps'])
|
||||
and ((str(memory_dict['current']) != str(args.memory_partition)))):
|
||||
showProgressBar = True # Only show progress bar if
|
||||
# 1) Device can set memory partition modes
|
||||
@@ -4259,7 +4249,7 @@ class AMDSMICommands():
|
||||
raise PermissionError('Command requires elevation') from e
|
||||
if e.get_error_code() == amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_INVAL:
|
||||
out = f"[AMDSMI_STATUS_INVAL] Unable to set memory partition to {args.memory_partition} on {gpu_string}"
|
||||
print(f"Valid Memory partition Modes: {mem_caps_str}\n")
|
||||
print(f"Valid Memory partition Modes: {memory_dict['caps']}\n")
|
||||
self.logger.store_output(args.gpu, 'memory_partition', out)
|
||||
self.logger.print_output()
|
||||
self.logger.clear_multiple_devices_ouput()
|
||||
@@ -5711,15 +5701,21 @@ class AMDSMICommands():
|
||||
if accelerator:
|
||||
args.accelerator = accelerator
|
||||
|
||||
###########################################
|
||||
# amd-smi partition (no args) #
|
||||
###########################################
|
||||
# if no args are present, then everything should be displayed
|
||||
if not args.current and not args.memory and not args.accelerator:
|
||||
args.current = True
|
||||
args.memory = True
|
||||
args.accelerator = True
|
||||
|
||||
###########################################
|
||||
# amd-smi partition --current #
|
||||
###########################################
|
||||
if args.current:
|
||||
self.logger.table_header = ''.rjust(7)
|
||||
current_header = "GPU_ID".ljust(13) + \
|
||||
current_header = "GPU_ID".ljust(8) + \
|
||||
"MEMORY".ljust(8) + \
|
||||
"ACCELERATOR_TYPE".ljust(18) + \
|
||||
"ACCELERATOR_PROFILE_INDEX".ljust(27) + \
|
||||
@@ -5733,11 +5729,11 @@ class AMDSMICommands():
|
||||
partition_dict = amdsmi_interface.amdsmi_get_gpu_accelerator_partition_profile(gpu)
|
||||
profile_type = partition_dict['partition_profile']['profile_type']
|
||||
profile_index = partition_dict['partition_profile']['profile_index']
|
||||
partition_id = partition_dict['partition_id']
|
||||
partition_id = str(partition_dict['partition_id']).replace("[", "").replace("]", "").replace(" ", "")
|
||||
except amdsmi_exception.AmdSmiLibraryException as e:
|
||||
profile_type = "N/A"
|
||||
profile_index = "N/A"
|
||||
partition_id = "N/A"
|
||||
partition_id = "0"
|
||||
logging.debug("Failed to get accelerator partition profile for GPU %s | %s", gpu_id, e.get_error_info())
|
||||
try:
|
||||
current_mem_cap = amdsmi_interface.amdsmi_get_gpu_memory_partition(gpu)
|
||||
@@ -5756,65 +5752,52 @@ class AMDSMICommands():
|
||||
tabular_output.append(tabular_output_dict)
|
||||
|
||||
self.logger.multiple_device_output = tabular_output
|
||||
self.logger.table_title = "CURRENT_PARTITION"
|
||||
self.logger.print_output(multiple_device_enabled=True, tabular=True)
|
||||
self.logger.table_title = "\nCURRENT_PARTITION"
|
||||
self.logger.print_output(multiple_device_enabled=True, tabular=True, dynamic=True)
|
||||
self.logger.clear_multiple_devices_ouput()
|
||||
|
||||
###########################################
|
||||
# amd-smi partition --memory #
|
||||
###########################################
|
||||
if args.memory:
|
||||
tabular_output = []
|
||||
self.logger.table_header = ''.rjust(7)
|
||||
current_header = "GPU_ID".ljust(8) + \
|
||||
"MEMORY_PARTITION_CAPS".ljust(23) + \
|
||||
"CURRENT_MEMORY_PARTITION".ljust(26)
|
||||
self.logger.table_header = current_header + self.logger.table_header.strip()
|
||||
|
||||
for gpu in args.gpu:
|
||||
gpu_id = self.helpers.get_gpu_id_from_device_handle(gpu)
|
||||
mem_caps_str = "N/A"
|
||||
current_memory_partition = "N/A"
|
||||
try:
|
||||
memory_partition = amdsmi_interface.amdsmi_get_gpu_memory_partition(gpu) # this info likely actually comes from different apis than used here
|
||||
memory_partition_config = amdsmi_interface.amdsmi_get_gpu_memory_partition_config(gpu)
|
||||
mem_caps_str = str(memory_partition_config['partition_caps']).replace("]", "").replace("[", "").replace("\'", "").replace(" ", "")
|
||||
current_memory_partition = memory_partition_config['mp_mode']
|
||||
except amdsmi_exception.AmdSmiLibraryException as e:
|
||||
memory_partition = "N/A"
|
||||
logging.debug("Failed to get current memory partition for GPU %s | %s", gpu_id, e.get_error_info())
|
||||
try:
|
||||
partition_dict = amdsmi_interface.amdsmi_get_gpu_accelerator_partition_profile(gpu)
|
||||
temp_mem_caps = partition_dict['partition_profile']['memory_caps']
|
||||
|
||||
if temp_mem_caps.amdsmi_nps_flags_t == None:
|
||||
mem_caps = temp_mem_caps.nps_cap_mask
|
||||
mem_caps_list = []
|
||||
if mem_caps & 1 == 1:
|
||||
mem_caps_list.append("NPS1")
|
||||
if mem_caps & 2 == 2:
|
||||
mem_caps_list.append("NPS2")
|
||||
if mem_caps & 4 == 4:
|
||||
mem_caps_list.append("NPS4")
|
||||
if mem_caps & 8 == 8:
|
||||
mem_caps_list.append("NPS8")
|
||||
mem_caps_str = str(mem_caps_list).replace("]", "").replace("[", "")
|
||||
else:
|
||||
mem_caps = temp_mem_caps.amdsmi_nps_flags_t
|
||||
mem_caps_list = []
|
||||
if mem_caps.nps1_cap == 1:
|
||||
mem_caps_list.append("NPS1")
|
||||
if mem_caps.nps2_cap == 1:
|
||||
mem_caps_list.append("NPS2")
|
||||
if mem_caps.nps4_cap == 1:
|
||||
mem_caps_list.append("NPS4")
|
||||
if mem_caps.nps8_cap == 1:
|
||||
mem_caps_list.append("NPS8")
|
||||
mem_caps_str = str(mem_caps_list).replace("]", "").replace("[", "").replace("\'", "")
|
||||
if mem_caps_str == "":
|
||||
mem_caps_str = "N/A"
|
||||
except amdsmi_exception.AmdSmiLibraryException as e:
|
||||
mem_caps_str = "N/A"
|
||||
logging.debug("Failed to get accelerator partition profile for GPU %s | %s", gpu_id, e.get_error_info())
|
||||
tabular_output_dict = {"gpu_id": gpu_id,
|
||||
"memory_partition_caps": mem_caps_str,
|
||||
"current_memory_partition": current_memory_partition}
|
||||
tabular_output.append(tabular_output_dict)
|
||||
|
||||
memory_dict = {'caps': mem_caps_str, 'current': memory_partition}
|
||||
self.logger.store_output(gpu, 'memory_partition', memory_dict)
|
||||
self.logger.store_multiple_device_output()
|
||||
self.logger.print_output(multiple_device_enabled=True)
|
||||
self.logger.multiple_device_output = tabular_output
|
||||
self.logger.table_title = "\nMEMORY_PARTITION"
|
||||
self.logger.print_output(multiple_device_enabled=True, tabular=True, dynamic=True)
|
||||
self.logger.clear_multiple_devices_ouput()
|
||||
|
||||
###########################################
|
||||
# amd-smi partition --accelerator #
|
||||
###########################################
|
||||
if args.accelerator:
|
||||
self.logger.table_header = ''.rjust(7)
|
||||
current_header = "GPU_ID".ljust(13) + \
|
||||
current_header = "GPU_ID".ljust(8) + \
|
||||
"PROFILE_INDEX".ljust(15) + \
|
||||
"MEMORY_PARTITION_CAPS".ljust(23) + \
|
||||
"ACCELERATOR_TYPE".ljust(18) + \
|
||||
"PARTITION_ID".ljust(14) + \
|
||||
"PARTITION_ID".ljust(17) + \
|
||||
"NUM_PARTITIONS".ljust(16) + \
|
||||
"NUM_RESOURCES".ljust(15) + \
|
||||
"RESOURCE_INDEX".ljust(16) + \
|
||||
@@ -5824,74 +5807,184 @@ class AMDSMICommands():
|
||||
self.logger.table_header = current_header + self.logger.table_header.strip()
|
||||
|
||||
tabular_output = []
|
||||
prev_gpu_id = "N/A"
|
||||
for gpu in args.gpu:
|
||||
gpu_id = self.helpers.get_gpu_id_from_device_handle(gpu)
|
||||
tabular_output_dict = {"gpu_id": "N/A",
|
||||
"profile_index": "N/A",
|
||||
"memory_partition_caps": "N/A",
|
||||
"accelerator_type": "N/A",
|
||||
"partition_id": "0",
|
||||
"num_partitions": "N/A",
|
||||
"num_resources": "N/A",
|
||||
"resource_index": "N/A",
|
||||
"resource_type": "N/A",
|
||||
"resource_instances": "N/A",
|
||||
"resources_shared": "N/A"}
|
||||
try:
|
||||
partition_dict = amdsmi_interface.amdsmi_get_gpu_accelerator_partition_profile(gpu)
|
||||
profile_type = partition_dict['partition_profile']['profile_type']
|
||||
profile_index = partition_dict['partition_profile']['profile_index']
|
||||
temp_mem_caps = partition_dict['partition_profile']['memory_caps']
|
||||
parition_id = partition_dict['partition_id']
|
||||
num_resources = partition_dict['partition_profile']['num_resources']
|
||||
resources = partition_dict['partition_profile']['resources']
|
||||
partition_id = str(partition_dict['partition_id']).replace("[", "").replace("]", "").replace(" ", "")
|
||||
current_accelerator_type = partition_dict['partition_profile']['profile_type']
|
||||
|
||||
# save only the primary GPU node's partition_id (the 1st listed device; non N/A one)
|
||||
# else keep current_partition_id unchanged for displaying in accelerator resource's output
|
||||
if partition_id != "N/A":
|
||||
current_partition_id = partition_id
|
||||
|
||||
if temp_mem_caps.amdsmi_nps_flags_t == None:
|
||||
mem_caps = temp_mem_caps.nps_cap_mask
|
||||
mem_caps_list = []
|
||||
if mem_caps & 1 == 1:
|
||||
mem_caps_list.append("NPS1")
|
||||
if mem_caps & 2 == 2:
|
||||
mem_caps_list.append("NPS2")
|
||||
if mem_caps & 4 == 4:
|
||||
mem_caps_list.append("NPS4")
|
||||
if mem_caps & 8 == 8:
|
||||
mem_caps_list.append("NPS8")
|
||||
mem_caps_str = str(mem_caps_list).replace("]", "").replace("[", "").replace("\'", "")
|
||||
else:
|
||||
mem_caps = temp_mem_caps.amdsmi_nps_flags_t
|
||||
mem_caps_list = []
|
||||
if mem_caps.nps1_cap == 1:
|
||||
mem_caps_list.append("NPS1")
|
||||
if mem_caps.nps2_cap == 1:
|
||||
mem_caps_list.append("NPS2")
|
||||
if mem_caps.nps4_cap == 1:
|
||||
mem_caps_list.append("NPS4")
|
||||
if mem_caps.nps8_cap == 1:
|
||||
mem_caps_list.append("NPS8")
|
||||
mem_caps_str = str(mem_caps_list).replace("]", "").replace("[", "").replace("\'", "")
|
||||
if mem_caps_str == "":
|
||||
mem_caps_str = "N/A"
|
||||
except amdsmi_exception.AmdSmiLibraryException as e:
|
||||
profile_type = "N/A"
|
||||
profile_index = "N/A"
|
||||
temp_mem_caps = "N/A"
|
||||
parition_id = "N/A"
|
||||
num_resources = "N/A"
|
||||
resources = "N/A"
|
||||
partition_id = "0"
|
||||
mem_caps_str = "N/A"
|
||||
num_partitions = 0
|
||||
current_accelerator_type = "N/A"
|
||||
logging.debug("Failed to get accelerator partition profile for GPU %s | %s", gpu_id, e.get_error_info())
|
||||
|
||||
if profile_type == 0:
|
||||
profile_type = "N/A"
|
||||
try:
|
||||
partition_config_dict = amdsmi_interface.amdsmi_get_gpu_accelerator_partition_profile_config(gpu)
|
||||
logging.debug("amdsmi_commands.py | partition_config_dict: " + str(json.dumps(partition_config_dict, indent=4)))
|
||||
num_profiles = partition_config_dict['num_profiles']
|
||||
num_resource_profiles = partition_config_dict['num_resource_profiles']
|
||||
|
||||
tabular_output_dict = {"gpu_id": gpu_id,
|
||||
resource_index = 0
|
||||
prev_accelerator_type = "N/A"
|
||||
for p in range(0, num_profiles):
|
||||
accelerator_type = partition_config_dict['profiles'][p]['profile_type']
|
||||
profile_index = partition_config_dict['profiles'][p]['profile_index']
|
||||
num_partitions = partition_config_dict['profiles'][p]['num_partitions']
|
||||
mem_caps_str = str(partition_config_dict['profiles'][p]['memory_caps']).replace("]", "").replace("[", "").replace("\'", "").replace(" ", "")
|
||||
# 2 modifications based on the current accelerator type:
|
||||
# 1) display a * for the current accelerator type, otherwise display as normal
|
||||
# 2) display partition id only for the current accelerator profile (the *'d one)
|
||||
if current_accelerator_type == accelerator_type:
|
||||
accelerator_type = accelerator_type + "*"
|
||||
partition_id = current_partition_id
|
||||
else:
|
||||
partition_id = "N/A"
|
||||
# only display the first instance of the gpu_id, rest are empty strings
|
||||
if prev_gpu_id != gpu_id:
|
||||
tabular_gpu_id = gpu_id
|
||||
prev_gpu_id = gpu_id
|
||||
else:
|
||||
tabular_gpu_id = ""
|
||||
logging.debug("amdsmi_commands.py | tabular_gpu_id: " + str(tabular_gpu_id))
|
||||
|
||||
if num_resource_profiles == 0:
|
||||
if prev_accelerator_type != accelerator_type: # only print the first instance of the resources
|
||||
tabular_output_dict = {"gpu_id": tabular_gpu_id,
|
||||
"profile_index": profile_index,
|
||||
"memory_partition_caps": mem_caps_str,
|
||||
"accelerator_type": profile_type,
|
||||
"partition_id": parition_id,
|
||||
"num_partitions": 0,
|
||||
"num_resources": num_resources,
|
||||
"resource_index": resources,
|
||||
"resource_type": resources,
|
||||
"resource_instances": resources,
|
||||
"resources_shared": resources}
|
||||
tabular_output.append(tabular_output_dict)
|
||||
"accelerator_type": accelerator_type,
|
||||
"partition_id": partition_id,
|
||||
"num_partitions": num_partitions,
|
||||
"num_resources": num_resource_profiles,
|
||||
"resource_index": "N/A",
|
||||
"resource_type": "N/A",
|
||||
"resource_instances": "N/A",
|
||||
"resources_shared": "N/A"}
|
||||
prev_accelerator_type = accelerator_type
|
||||
tabular_output.append(tabular_output_dict)
|
||||
continue
|
||||
|
||||
for r in range(0, num_resource_profiles):
|
||||
logging.debug("amdsmi_commands.py | p: " + str(p) + "; r: " + str(r)
|
||||
+ "; accelerator_type: " + str(accelerator_type))
|
||||
resource_type = partition_config_dict['profiles'][p]['resources'][r]['resource_type']
|
||||
resource_instances = partition_config_dict['profiles'][p]['resources'][r]['partition_resource']
|
||||
resources_shared = partition_config_dict['profiles'][p]['resources'][r]['num_partitions_share_resource']
|
||||
if prev_accelerator_type != accelerator_type: # only print the first instance of the resources
|
||||
tabular_output_dict = {"gpu_id": tabular_gpu_id,
|
||||
"profile_index": profile_index,
|
||||
"memory_partition_caps": mem_caps_str,
|
||||
"accelerator_type": accelerator_type,
|
||||
"partition_id": partition_id,
|
||||
"num_partitions": num_partitions,
|
||||
"num_resources": num_resource_profiles,
|
||||
"resource_index": resource_index,
|
||||
"resource_type": resource_type,
|
||||
"resource_instances": resource_instances,
|
||||
"resources_shared": resources_shared}
|
||||
prev_accelerator_type = accelerator_type
|
||||
else:
|
||||
tabular_output_dict = {"gpu_id": "",
|
||||
"profile_index": "",
|
||||
"memory_partition_caps": "",
|
||||
"accelerator_type": "",
|
||||
"partition_id": "",
|
||||
"num_partitions": "",
|
||||
"num_resources": "",
|
||||
"resource_index": resource_index,
|
||||
"resource_type": resource_type,
|
||||
"resource_instances": resource_instances,
|
||||
"resources_shared": resources_shared}
|
||||
resource_index += 1
|
||||
tabular_output.append(tabular_output_dict)
|
||||
except amdsmi_exception.AmdSmiLibraryException as e:
|
||||
tabular_output.append(tabular_output_dict)
|
||||
|
||||
self.logger.multiple_device_output = tabular_output
|
||||
self.logger.table_title = "ACCELERATOR_PARTITION_PROFILES"
|
||||
self.logger.print_output(multiple_device_enabled=True, tabular=True)
|
||||
self.logger.table_title = "\nACCELERATOR_PARTITION_PROFILES"
|
||||
self.logger.print_output(multiple_device_enabled=True, tabular=True, dynamic=True)
|
||||
self.logger.clear_multiple_devices_ouput()
|
||||
|
||||
#########################################
|
||||
# print accelerator partition resources #
|
||||
#########################################
|
||||
self.logger.table_header = ''.rjust(7)
|
||||
current_header = "RESOURCE_INDEX".ljust(16) + \
|
||||
"RESOURCE_TYPE".ljust(15) + \
|
||||
"RESOURCE_INSTANCES".ljust(20) + \
|
||||
"RESOURCES_SHARED".ljust(18)
|
||||
self.logger.table_header = current_header + self.logger.table_header.strip()
|
||||
|
||||
tabular_output = []
|
||||
for gpu in args.gpu:
|
||||
gpu_id = self.helpers.get_gpu_id_from_device_handle(gpu)
|
||||
tabular_output_dict = {"resource_index": "N/A",
|
||||
"resource_type": "N/A",
|
||||
"resource_instances": "N/A",
|
||||
"resources_shared": "N/A"}
|
||||
try:
|
||||
partition_config_dict = amdsmi_interface.amdsmi_get_gpu_accelerator_partition_profile_config(gpu)
|
||||
logging.debug("amdsmi_commands.py | partition_config_dict: " + str(json.dumps(partition_config_dict, indent=4)))
|
||||
num_profiles = partition_config_dict['num_profiles']
|
||||
num_resource_profiles = partition_config_dict['num_resource_profiles']
|
||||
|
||||
if num_resource_profiles == 0:
|
||||
tabular_output.append(tabular_output_dict)
|
||||
continue
|
||||
|
||||
resource_index = 0
|
||||
for p in range(0, num_profiles):
|
||||
for r in range(0, num_resource_profiles):
|
||||
resource_type = partition_config_dict['profiles'][p]['resources'][r]['resource_type']
|
||||
resource_instances = partition_config_dict['profiles'][p]['resources'][r]['partition_resource']
|
||||
resources_shared = partition_config_dict['profiles'][p]['resources'][r]['num_partitions_share_resource']
|
||||
tabular_output_dict = {
|
||||
"resource_index": resource_index,
|
||||
"resource_type": resource_type,
|
||||
"resource_instances": resource_instances,
|
||||
"resources_shared": resources_shared}
|
||||
resource_index += 1
|
||||
tabular_output.append(tabular_output_dict)
|
||||
except amdsmi_exception.AmdSmiLibraryException as e:
|
||||
tabular_output.append(tabular_output_dict)
|
||||
|
||||
self.logger.multiple_device_output = tabular_output
|
||||
self.logger.table_title = "\nACCELERATOR_PARTITION_RESOURCES"
|
||||
self.logger.print_output(multiple_device_enabled=True, tabular=True, dynamic=True)
|
||||
self.logger.clear_multiple_devices_ouput()
|
||||
|
||||
# print legend
|
||||
legend_parts = [
|
||||
"\n\nLegend:",
|
||||
" * = Current mode"]
|
||||
legend_output = "\n".join(legend_parts)
|
||||
if self.logger.destination == 'stdout':
|
||||
print(legend_output)
|
||||
else:
|
||||
with self.logger.destination.open('a', encoding="utf-8") as output_file:
|
||||
output_file.write(legend_output + '\n')
|
||||
|
||||
def _event_thread(self, commands, i):
|
||||
devices = commands.device_handles
|
||||
|
||||
@@ -27,6 +27,7 @@ import sys
|
||||
import time
|
||||
import re
|
||||
import multiprocessing
|
||||
import json
|
||||
|
||||
from typing import List, Union
|
||||
from enum import Enum
|
||||
@@ -681,12 +682,30 @@ class AMDSMIHelpers():
|
||||
perf_levels_int = list(set(clock.value for clock in amdsmi_interface.AmdSmiDevPerfLevel))
|
||||
return perf_levels_str, perf_levels_int
|
||||
|
||||
def get_accelerator_partition_profile_config(self):
|
||||
device_handles = amdsmi_interface.amdsmi_get_processor_handles()
|
||||
accelerator_partition_profiles = {'profile_indices':[], 'profile_types':[], 'memory_caps': []}
|
||||
for dev in device_handles:
|
||||
try:
|
||||
profile = amdsmi_interface.amdsmi_get_gpu_accelerator_partition_profile_config(dev)
|
||||
num_profiles = profile['num_profiles']
|
||||
for p in range(num_profiles):
|
||||
accelerator_partition_profiles['profile_indices'].append(str(profile['profiles'][p]['profile_index']))
|
||||
accelerator_partition_profiles['profile_types'].append(profile['profiles'][p]['profile_type'])
|
||||
accelerator_partition_profiles['memory_caps'].append(profile['profiles'][p]['memory_caps'])
|
||||
break # Only need to get the profiles for one device
|
||||
except amdsmi_interface.AmdSmiLibraryException as e:
|
||||
break
|
||||
return accelerator_partition_profiles
|
||||
|
||||
def get_compute_partition_types(self):
|
||||
compute_partitions_str = [partition.name for partition in amdsmi_interface.AmdSmiComputePartitionType]
|
||||
if 'INVALID' in compute_partitions_str:
|
||||
compute_partitions_str.remove('INVALID')
|
||||
return compute_partitions_str
|
||||
def get_accelerator_choices_types_indices(self):
|
||||
return_val = ("N/A", {'profile_indices':[], 'profile_types':[]})
|
||||
accelerator_partition_profiles = self.get_accelerator_partition_profile_config()
|
||||
if len(accelerator_partition_profiles['profile_types']) != 0:
|
||||
compute_partitions_str = accelerator_partition_profiles['profile_types'] + accelerator_partition_profiles['profile_indices']
|
||||
accelerator_choices = ", ".join(compute_partitions_str)
|
||||
return_val = (accelerator_choices, accelerator_partition_profiles)
|
||||
return return_val
|
||||
|
||||
def get_memory_partition_types(self):
|
||||
memory_partitions_str = [partition.name for partition in amdsmi_interface.AmdSmiMemoryPartitionType]
|
||||
|
||||
@@ -102,14 +102,24 @@ class AMDSMILogger():
|
||||
return output_dict
|
||||
|
||||
|
||||
def _convert_json_to_tabular(self, json_object: Dict[str, any]):
|
||||
# TODO make dynamic
|
||||
def _convert_json_to_tabular(self, json_object: Dict[str, any], dynamic=False):
|
||||
# TODO make dynamic - convert other python CLI outputs to use (as needed)
|
||||
# Update: using dynamic=true provides dynamic re-sizing based on key name length
|
||||
|
||||
table_values = ''
|
||||
stored_gpu = ''
|
||||
stored_timestamp = ''
|
||||
for key, value in json_object.items():
|
||||
string_value = str(value)
|
||||
if key == 'gpu':
|
||||
if key == 'partition_id':
|
||||
# Special case for partition_id: 8 partitions + 7 comma + 2 spaces = 17
|
||||
table_values += string_value.ljust(17)
|
||||
continue
|
||||
key_length = len(key) + 2
|
||||
if dynamic and len(key) > 0:
|
||||
stored_gpu = string_value
|
||||
table_values += string_value.ljust(key_length)
|
||||
elif key == 'gpu':
|
||||
stored_gpu = string_value
|
||||
table_values += string_value.rjust(3)
|
||||
elif key == 'timestamp':
|
||||
@@ -144,30 +154,6 @@ class AMDSMILogger():
|
||||
elif key == "link_status":
|
||||
for i in value:
|
||||
table_values += str(i).ljust(3)
|
||||
elif key == "memory":
|
||||
table_values += string_value.ljust(8)
|
||||
elif key == "accelerator_type":
|
||||
table_values += string_value.ljust(18)
|
||||
elif key == "partition_id":
|
||||
table_values += string_value.ljust(14)
|
||||
elif key == "accelerator_profile_index":
|
||||
table_values += string_value.ljust(27)
|
||||
elif key == "profile_index":
|
||||
table_values += string_value.ljust(15)
|
||||
elif key == "memory_partition_caps":
|
||||
table_values += string_value.ljust(23)
|
||||
elif key == "num_partitions":
|
||||
table_values += string_value.ljust(16)
|
||||
elif key == "num_resources":
|
||||
table_values += string_value.ljust(15)
|
||||
elif key == "resource_index":
|
||||
table_values += string_value.ljust(16)
|
||||
elif key == "resource_type":
|
||||
table_values += string_value.ljust(15)
|
||||
elif key == "resource_instances":
|
||||
table_values += string_value.ljust(20)
|
||||
elif key == "resources_shared":
|
||||
table_values += string_value.ljust(18)
|
||||
elif key == "RW":
|
||||
table_values += string_value.ljust(57)
|
||||
elif key in ('pviol', 'tviol'):
|
||||
@@ -494,12 +480,14 @@ class AMDSMILogger():
|
||||
self.output = {}
|
||||
|
||||
|
||||
def print_output(self, multiple_device_enabled=False, watching_output=False, tabular=False, dual_csv_output=False):
|
||||
def print_output(self, multiple_device_enabled=False, watching_output=False, tabular=False, dual_csv_output=False, dynamic=False):
|
||||
""" Print current output acording to format and then destination
|
||||
params:
|
||||
multiple_device_enabled (bool) - True if printing output from
|
||||
multiple devices
|
||||
watching_output (bool) - True if printing watch output
|
||||
dynamic (bool) - Defaults to False. True turns on dynamic resizing for
|
||||
left justified table output
|
||||
return:
|
||||
Nothing
|
||||
"""
|
||||
@@ -516,7 +504,7 @@ class AMDSMILogger():
|
||||
elif self.is_human_readable_format():
|
||||
# If tabular output is enabled, redirect to _print_tabular_output
|
||||
if tabular:
|
||||
self._print_tabular_output(multiple_device_enabled=multiple_device_enabled, watching_output=watching_output)
|
||||
self._print_tabular_output(multiple_device_enabled=multiple_device_enabled, watching_output=watching_output, dynamic=dynamic)
|
||||
else:
|
||||
self._print_human_readable_output(multiple_device_enabled=multiple_device_enabled,
|
||||
watching_output=watching_output)
|
||||
@@ -788,7 +776,7 @@ class AMDSMILogger():
|
||||
output_file.write(human_readable_output + '\n')
|
||||
|
||||
|
||||
def _print_tabular_output(self, multiple_device_enabled=False, watching_output=False):
|
||||
def _print_tabular_output(self, multiple_device_enabled=False, watching_output=False, dynamic=False):
|
||||
primary_table = ''
|
||||
secondary_table = ''
|
||||
|
||||
@@ -808,7 +796,7 @@ class AMDSMILogger():
|
||||
for key, value in device_output.items():
|
||||
if key != 'process_list':
|
||||
primary_table_output[key] = value
|
||||
primary_table += self._convert_json_to_tabular(primary_table_output) + '\n'
|
||||
primary_table += self._convert_json_to_tabular(primary_table_output, dynamic=dynamic) + '\n'
|
||||
else: # Single device output
|
||||
if 'process_list' in self.output:
|
||||
process_table_dict = {}
|
||||
@@ -822,7 +810,7 @@ class AMDSMILogger():
|
||||
for key, value in self.output.items():
|
||||
if key != 'process_list':
|
||||
primary_table_output[key] = value
|
||||
primary_table += self._convert_json_to_tabular(primary_table_output) + '\n'
|
||||
primary_table += self._convert_json_to_tabular(primary_table_output, dynamic=dynamic) + '\n'
|
||||
primary_table = primary_table.rstrip()
|
||||
secondary_table = secondary_table.rstrip()
|
||||
|
||||
@@ -879,7 +867,7 @@ class AMDSMILogger():
|
||||
for key, value in device_output.items():
|
||||
if key != 'process_list':
|
||||
primary_table_output[key] = value
|
||||
primary_table += self._convert_json_to_tabular(primary_table_output) + '\n'
|
||||
primary_table += self._convert_json_to_tabular(primary_table_output, dynamic=dynamic) + '\n'
|
||||
primary_table = primary_table.rstrip() # Remove trailing new line
|
||||
secondary_table = secondary_table.rstrip()
|
||||
|
||||
|
||||
@@ -173,6 +173,14 @@ class AMDSMIParser(argparse.ArgumentParser):
|
||||
else:
|
||||
raise amdsmi_cli_exceptions.AmdSmiInvalidParameterValueException(string_value, outputformat)
|
||||
|
||||
def _is_command_supported(self, user_input, acceptable_values, command_name):
|
||||
if acceptable_values == "N/A":
|
||||
raise amdsmi_cli_exceptions.AmdSmiCommandNotSupportedException(command_name, self.helpers.get_output_format())
|
||||
elif str(user_input).upper() not in acceptable_values:
|
||||
print(f"Valid inputs are {acceptable_values}")
|
||||
raise amdsmi_cli_exceptions.AmdSmiInvalidParameterValueException(str(user_input).upper(), self.helpers.get_output_format())
|
||||
else:
|
||||
return str(user_input).upper()
|
||||
|
||||
def _limit_select(self):
|
||||
"""Custom action for setting clock limits"""
|
||||
@@ -401,7 +409,7 @@ class AMDSMIParser(argparse.ArgumentParser):
|
||||
return _CoreSelectAction
|
||||
|
||||
|
||||
def _add_command_modifiers(self, subcommand_parser):
|
||||
def _add_command_modifiers(self, subcommand_parser: argparse.ArgumentParser):
|
||||
json_help = "Displays output in JSON format (human readable by default)."
|
||||
csv_help = "Displays output in CSV format (human readable by default)."
|
||||
file_help = "Saves output into a file on the provided path (stdout by default)."
|
||||
@@ -460,7 +468,7 @@ class AMDSMIParser(argparse.ArgumentParser):
|
||||
return value
|
||||
|
||||
|
||||
def _add_device_arguments(self, subcommand_parser, required=False):
|
||||
def _add_device_arguments(self, subcommand_parser: argparse.ArgumentParser, required=False):
|
||||
# Device arguments help text
|
||||
gpu_help = f"Select a GPU ID, BDF, or UUID from the possible choices:\n{self.gpu_choices_str}"
|
||||
vf_help = "Gets general information about the specified VF (timeslice, fb info, …).\
|
||||
@@ -583,7 +591,7 @@ class AMDSMIParser(argparse.ArgumentParser):
|
||||
return _ValidateOverdrivePercent
|
||||
|
||||
|
||||
def _add_version_parser(self, subparsers, func):
|
||||
def _add_version_parser(self, subparsers: argparse._SubParsersAction, func):
|
||||
# Subparser help text
|
||||
version_help = "Display version information"
|
||||
|
||||
@@ -597,7 +605,7 @@ class AMDSMIParser(argparse.ArgumentParser):
|
||||
self._add_command_modifiers(version_parser)
|
||||
|
||||
|
||||
def _add_list_parser(self, subparsers, func):
|
||||
def _add_list_parser(self, subparsers: argparse._SubParsersAction, func):
|
||||
if not self.helpers.is_amdgpu_initialized():
|
||||
# The list subcommand is only applicable to systems with amdgpu initialized
|
||||
return
|
||||
@@ -619,7 +627,7 @@ class AMDSMIParser(argparse.ArgumentParser):
|
||||
self._add_device_arguments(list_parser, required=False)
|
||||
|
||||
|
||||
def _add_static_parser(self, subparsers, func):
|
||||
def _add_static_parser(self, subparsers: argparse._SubParsersAction, func):
|
||||
# Subparser help text
|
||||
static_help = "Gets static information about the specified GPU"
|
||||
static_subcommand_help = "If no GPU is specified, returns static information for all GPUs on the system.\
|
||||
@@ -925,7 +933,7 @@ class AMDSMIParser(argparse.ArgumentParser):
|
||||
self._add_command_modifiers(metric_parser)
|
||||
|
||||
|
||||
def _add_process_parser(self, subparsers, func):
|
||||
def _add_process_parser(self, subparsers: argparse._SubParsersAction, func):
|
||||
if self.helpers.is_hypervisor():
|
||||
# Don't add this subparser on Hypervisors
|
||||
# This subparser is only available to Guest and Baremetal systems
|
||||
@@ -969,7 +977,7 @@ class AMDSMIParser(argparse.ArgumentParser):
|
||||
process_parser.add_argument('-n', '--name', action='store', type=lambda value: self._is_valid_string(value, '--name'), required=False, help=name_help)
|
||||
|
||||
|
||||
def _add_profile_parser(self, subparsers, func):
|
||||
def _add_profile_parser(self, subparsers: argparse._SubParsersAction, func):
|
||||
if not (self.helpers.is_windows() and self.helpers.is_hypervisor()):
|
||||
# This subparser only applies to Hypervisors
|
||||
return
|
||||
@@ -990,7 +998,7 @@ class AMDSMIParser(argparse.ArgumentParser):
|
||||
self._add_device_arguments(profile_parser, required=False)
|
||||
|
||||
|
||||
def _add_event_parser(self, subparsers, func):
|
||||
def _add_event_parser(self, subparsers: argparse._SubParsersAction, func):
|
||||
if not self.helpers.is_amdgpu_initialized():
|
||||
# The event subcommand is only applicable to systems with amdgpu initialized
|
||||
return
|
||||
@@ -1011,7 +1019,7 @@ class AMDSMIParser(argparse.ArgumentParser):
|
||||
self._add_device_arguments(event_parser, required=False)
|
||||
|
||||
|
||||
def _add_topology_parser(self, subparsers, func):
|
||||
def _add_topology_parser(self, subparsers: argparse._SubParsersAction, func):
|
||||
if not(self.helpers.is_baremetal() and self.helpers.is_linux()):
|
||||
# This subparser is only applicable to Baremetal Linux
|
||||
return
|
||||
@@ -1059,7 +1067,7 @@ class AMDSMIParser(argparse.ArgumentParser):
|
||||
topology_parser.add_argument('-z', '--bi-dir', action='store_true', required=False, help=bi_dir_help)
|
||||
|
||||
|
||||
def _add_set_value_parser(self, subparsers, func):
|
||||
def _add_set_value_parser(self, subparsers: argparse._SubParsersAction, func):
|
||||
if not self.helpers.is_linux():
|
||||
# This subparser is only applicable to Linux
|
||||
return
|
||||
@@ -1078,9 +1086,9 @@ class AMDSMIParser(argparse.ArgumentParser):
|
||||
set_profile_help = f"Set power profile level (#) or choose one of available profiles:\n\t{power_profile_choices_str}"
|
||||
perf_det_choices_str = ", ".join(self.helpers.get_perf_det_levels())
|
||||
set_perf_det_help = f"Set performance determinism and select one of the corresponding performance levels:\n\t{perf_det_choices_str}"
|
||||
compute_partition_choices_str = ", ".join(self.helpers.get_compute_partition_types())
|
||||
(accelerator_set_choices, _) = self.helpers.get_accelerator_choices_types_indices()
|
||||
memory_partition_choices_str = ", ".join(self.helpers.get_memory_partition_types())
|
||||
set_compute_partition_help = f"Set one of the following the compute partition modes:\n\t{compute_partition_choices_str}"
|
||||
set_compute_partition_help = f"Set one of the following the accelerator type or profile index:\n\t{accelerator_set_choices}.\n\tUse `sudo amd-smi partition --accelerator` to find acceptable values."
|
||||
set_memory_partition_help = f"Set one of the following the memory partition modes:\n\t{memory_partition_choices_str}"
|
||||
power_cap_min, power_cap_max = self.helpers.get_power_caps()
|
||||
power_cap_max = self.helpers.convert_SI_unit(power_cap_max, AMDSMIHelpers.SI_Unit.MICRO)
|
||||
@@ -1128,7 +1136,7 @@ class AMDSMIParser(argparse.ArgumentParser):
|
||||
set_value_exclusive_group.add_argument('-l', '--perf-level', action='store', choices=self.helpers.get_perf_levels()[0], type=str.upper, required=False, help=set_perf_level_help, metavar='LEVEL')
|
||||
set_value_exclusive_group.add_argument('-P', '--profile', action='store', required=False, help=set_profile_help, metavar='SETPROFILE')
|
||||
set_value_exclusive_group.add_argument('-d', '--perf-determinism', action='store', type=lambda value: self._not_negative_int(value, '--perf-determinism'), required=False, help=set_perf_det_help, metavar='SCLKMAX')
|
||||
set_value_exclusive_group.add_argument('-C', '--compute-partition', action='store', choices=self.helpers.get_compute_partition_types(), type=str.upper, required=False, help=set_compute_partition_help, metavar='PARTITION')
|
||||
set_value_exclusive_group.add_argument('-C', '--compute-partition', action='store', choices=accelerator_set_choices, type=lambda value: self._is_command_supported(value, accelerator_set_choices, '--compute-partition'), required=False, help=set_compute_partition_help, metavar='<ACCELERATOR_TYPE> or <PROFILE_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')
|
||||
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('-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')
|
||||
@@ -1162,7 +1170,7 @@ class AMDSMIParser(argparse.ArgumentParser):
|
||||
self._add_command_modifiers(set_value_parser)
|
||||
|
||||
|
||||
def _add_reset_parser(self, subparsers, func):
|
||||
def _add_reset_parser(self, subparsers: argparse._SubParsersAction, func):
|
||||
if not self.helpers.is_linux():
|
||||
# This subparser is only applicable to Linux
|
||||
return
|
||||
@@ -1215,7 +1223,7 @@ class AMDSMIParser(argparse.ArgumentParser):
|
||||
reset_exclusive_group.add_argument('-l', '--clean-local-data', action='store_true', required=False, help=reset_gpu_clean_local_data_help)
|
||||
|
||||
|
||||
def _add_monitor_parser(self, subparsers, func):
|
||||
def _add_monitor_parser(self, subparsers: argparse._SubParsersAction, func):
|
||||
if not self.helpers.is_linux():
|
||||
# This subparser is only applicable to Linux
|
||||
return
|
||||
@@ -1314,7 +1322,7 @@ class AMDSMIParser(argparse.ArgumentParser):
|
||||
rocm_smi_parser.add_argument('-f', '--showclkfrq', action='store_true', required=False, help=showclkfrq_help)
|
||||
|
||||
|
||||
def _add_xgmi_parser(self, subparsers, func):
|
||||
def _add_xgmi_parser(self, subparsers: argparse._SubParsersAction, func):
|
||||
if not self.helpers.is_amdgpu_initialized():
|
||||
# The xgmi subcommand is only applicable to systems with amdgpu initialized
|
||||
return
|
||||
@@ -1344,7 +1352,7 @@ class AMDSMIParser(argparse.ArgumentParser):
|
||||
xgmi_parser.add_argument('-l', '--link-status', action='store_true', required=False, help=xgmi_link_status_help)
|
||||
|
||||
|
||||
def _add_partition_parser(self, subparsers, func):
|
||||
def _add_partition_parser(self, subparsers: argparse._SubParsersAction, func):
|
||||
if not self.helpers.is_amdgpu_initialized():
|
||||
# The partition subcommand is only applicable to systems with amdgpu initialized
|
||||
return
|
||||
|
||||
Yeni konuda referans
Bir kullanıcı engelle