diff --git a/projects/amdsmi/amdsmi_cli/amdsmi_cli.py b/projects/amdsmi/amdsmi_cli/amdsmi_cli.py index b7ec6b5efb2..16025699ca2 100755 --- a/projects/amdsmi/amdsmi_cli/amdsmi_cli.py +++ b/projects/amdsmi/amdsmi_cli/amdsmi_cli.py @@ -20,6 +20,7 @@ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +import functools import logging import sys import os diff --git a/projects/amdsmi/amdsmi_cli/amdsmi_commands.py b/projects/amdsmi/amdsmi_cli/amdsmi_commands.py index f17249f4d78..023109b2717 100644 --- a/projects/amdsmi/amdsmi_cli/amdsmi_commands.py +++ b/projects/amdsmi/amdsmi_cli/amdsmi_commands.py @@ -20,6 +20,7 @@ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import argparse +import functools import json import logging import multiprocessing @@ -64,6 +65,7 @@ def __init__(self, format='human_readable', destination='stdout', helpers=None) if self.helpers.is_amdgpu_initialized(): try: self.device_handles = amdsmi_interface.amdsmi_get_processor_handles() + self.device_handles_gpus = amdsmi_interface.get_gpu_handles() except amdsmi_exception.AmdSmiLibraryException as e: if e.err_code in (amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_NOT_INIT, amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_DRIVER_NOT_LOADED): @@ -76,6 +78,20 @@ def __init__(self, format='human_readable', destination='stdout', helpers=None) logging.error('Unable to detect any GPU devices, check amdgpu version and module status (sudo modprobe amdgpu)') exit_flag = True + if self.helpers.is_ainic_initialized(): + try: + self.device_handles_brcm_nics = amdsmi_interface.get_nic_handles() + self.device_handles_ainics = amdsmi_interface.get_ainic_handles() + if len(self.device_handles_gpus) == 0: + self.device_handles_gpus = amdsmi_interface.get_gpu_handles() + self.device_handles_switchs = amdsmi_interface.get_switch_handles() + except amdsmi_exception.AmdSmiLibraryException as e: + if e.err_code in (amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_NOT_INIT, + amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_DRIVER_NOT_LOADED): + logging.error('Unable to get devices, driver not initialized (BRCMNIC not found in modules)') + else: + raise e + # Resolve the node handle. for dev in self.device_handles: try: @@ -134,7 +150,7 @@ def __init__(self, format='human_readable', destination='stdout', helpers=None) sys.exit(-1) - def version(self, args, gpu_version=None, cpu_version=None): + def version(self, args, gpu_version=None, cpu_version=None, nic_version=None): """Print Version String Args: @@ -145,10 +161,13 @@ def version(self, args, gpu_version=None, cpu_version=None): args.gpu_version = gpu_version if cpu_version: args.cpu_version = cpu_version + if nic_version: + args.nic_version = nic_version # if no args are given, display everything - if args.gpu_version is None and args.cpu_version is None: + if args.gpu_version is None and args.cpu_version is None and args.nic_version is None: args.gpu_version = True args.cpu_version = True + args.nic_version = True try: amdsmi_lib_version = amdsmi_interface.amdsmi_get_lib_version() @@ -195,6 +214,19 @@ def version(self, args, gpu_version=None, cpu_version=None): cpu_version_str = "N/A" self.logger.output['amd_hsmp_driver_version'] = cpu_version_str + nic_version_str = "N/A" + if args.nic_version: + try: + ainic_device_handles = amdsmi_interface.get_ainic_handles() + for nic_id, device_handle in enumerate(ainic_device_handles): + nic_info = amdsmi_interface.amdsmi_get_ainic_info(device_handle, True) + if nic_version_str != "": + nic_version_str += ", " + nic_version_str += nic_info['DRIVER']['NAME'] + "." + nic_info['DRIVER']['VERSION'] + except amdsmi_exception.AmdSmiLibraryException as e: + nic_version_str = e.get_error_info() + self.logger.output['nic_driver_version'] = nic_version_str + if self.logger.is_human_readable_format(): human_readable_output = f"AMDSMI Tool: {__version__} | " \ f"AMDSMI Library version: {amdsmi_lib_version_str} | " \ @@ -203,6 +235,8 @@ def version(self, args, gpu_version=None, cpu_version=None): human_readable_output = human_readable_output + f" | amdgpu version: {gpu_version_str}" if args.cpu_version: human_readable_output = human_readable_output + f" | hsmp version: {cpu_version_str}" + if args.nic_version: + human_readable_output = human_readable_output + f" | AINIC version: {nic_version_str}" # Custom human readable handling for version if self.logger.destination == 'stdout': print(human_readable_output) @@ -213,7 +247,7 @@ def version(self, args, gpu_version=None, cpu_version=None): self.logger.print_output() - def list(self, args, multiple_devices=False, gpu=None): + def list_gpu(self, args, multiple_devices=False, gpu=None): """List information for target gpu Args: @@ -246,7 +280,7 @@ def list(self, args, multiple_devices=False, gpu=None): self.group_check_printed = True # Handle multiple GPUs - handled_multiple_gpus, device_handle = self.helpers.handle_gpus(args, self.logger, self.list) + handled_multiple_gpus, device_handle = self.helpers.handle_gpus(args, self.logger, self.list_gpu) if handled_multiple_gpus: return # This function is recursive @@ -321,6 +355,296 @@ def list(self, args, multiple_devices=False, gpu=None): self.logger.print_output() + def list_brcm_nic(self, args, multiple_devices=False, nic=None): + """List information for target nic + + Args: + args (Namespace): Namespace containing the parsed CLI args + multiple_devices (bool, optional): True if checking for multiple devices. Defaults to False. + nic (device_handle, optional): device_handle for target device. Defaults to None. + + Raises: + IndexError: Index error if nic list is empty + + Returns: + None: Print output via AMDSMILogger to destination + """ + # Set args.* to passed in arguments + if nic: + args.nic = nic + + if not self.group_check_printed: + self.helpers.check_required_groups() + self.group_check_printed = True + + # Handle multiple NICs + handled_multiple_nics, device_handle = self.helpers.handle_brcm_nics(args, self.logger, self.list_brcm_nic) + if handled_multiple_nics: + return # This function is recursive + + args.nic = device_handle + + # Get nic_id for logging + nic_id = self.helpers.get_nic_id_from_device_handle(args.nic) + + # Get nic info for logging + try: + nic_info = amdsmi_interface.amdsmi_get_nic_info(args.nic) + if nic_info: + bdf = nic_info['bdf'] + uuid = nic_info['UUID'] + device_name = nic_info['Device Name'] + part_number = nic_info['Part Number'] + firmware_version = nic_info['Firmware_Version'] + else: + bdf = uuid = device_name = part_number = firmware_version = "N/A" + + except amdsmi_exception.AmdSmiLibraryException as e: + bdf = uuid = device_name = part_number = firmware_version = "N/A" + logging.debug("Failed to get info for nic %s | %s", nic_id, e.get_error_info()) + + # CSV format is intentionally aligned with Host + if self.logger.is_csv_format(): + self.logger.store_nic_output(args.nic, 'nic_bdf', bdf) + self.logger.store_nic_output(args.nic, 'permanent_address', uuid) + self.logger.store_nic_output(args.nic, 'device_name', device_name) + self.logger.store_nic_output(args.nic, 'part_number', part_number) + self.logger.store_nic_output(args.nic, 'firmware_version', firmware_version) + else: + self.logger.store_nic_output(args.nic, 'bdf', bdf) + self.logger.store_nic_output(args.nic, 'permanent_address', uuid) + self.logger.store_nic_output(args.nic, 'device_name', device_name) + self.logger.store_nic_output(args.nic, 'part_number', part_number) + self.logger.store_nic_output(args.nic, 'firmware_version', firmware_version) + + if multiple_devices: + self.logger.store_multiple_device_output() + return # Skip printing when there are multiple devices + + self.logger.print_output() + + def list_ainic(self, args, multiple_devices=False, nic=None): + """List information for target ainic + + Args: + args (Namespace): Namespace containing the parsed CLI args + multiple_devices (bool, optional): True if checking for multiple devices. Defaults to False. + nic (device_handle, optional): device_handle for target device. Defaults to None. + + Raises: + IndexError: Index error if nic list is empty + + Returns: + None: Print output via AMDSMILogger to destination + """ + # Set args.* to passed in arguments + if nic: + args.nic = nic + + if not self.group_check_printed: + self.helpers.check_required_groups() + self.group_check_printed = True + + # Handle multiple NICs + handled_multiple_nics, device_handle = self.helpers.handle_ainics(args, self.logger, self.list_ainic) + if handled_multiple_nics: + return # This function is recursive + + args.nic = device_handle + + # Get nic_id for logging + nic_id = self.helpers.get_ainic_id_from_device_handle(args.nic) + + # Get nic info for logging + try: + ainic_info = amdsmi_interface.amdsmi_get_ainic_info(args.nic) + except amdsmi_exception.AmdSmiLibraryException as e: + bdf = uuid = device_name = part_number = firmware_version = "N/A" + logging.debug("Failed to get info for nic %s | %s", nic_id, e.get_error_info()) + + # CSV format is intentionally aligned with Host + if self.logger.is_csv_format(): + self.logger.store_ainic_output(args.nic, 'nic_bdf', ainic_info['bdf']) + self.logger.store_ainic_output(args.nic, 'permanent_address', ainic_info['Permanent Address']) + self.logger.store_ainic_output(args.nic, 'product_name', ainic_info['Product Name']) + self.logger.store_ainic_output(args.nic, 'part_number', ainic_info['Part Number']) + self.logger.store_ainic_output(args.nic, 'serial_number', ainic_info['Serial Number']) + self.logger.store_ainic_output(args.nic, 'vendor_name', ainic_info['Vendor Name']) + else: + self.logger.store_ainic_output(args.nic, 'bdf', ainic_info['bdf']) + self.logger.store_ainic_output(args.nic, 'permanent_address', ainic_info['Permanent Address']) + self.logger.store_ainic_output(args.nic, 'product_name', ainic_info['Product Name']) + self.logger.store_ainic_output(args.nic, 'part_number', ainic_info['Part Number']) + self.logger.store_ainic_output(args.nic, 'serial_number', ainic_info['Serial Number']) + self.logger.store_ainic_output(args.nic, 'vendor_name', ainic_info['Vendor Name']) + + if multiple_devices: + self.logger.store_multiple_device_output() + return # Skip printing when there are multiple devices + + self.logger.print_output() + + def list_nics(self, args): + if not self.helpers.is_ainic_initialized() and not self.helpers.is_brcm_nic_initialized(): + return False + if args.nic == None: + args.nic = self.device_handles_ainics + args.nic.extend(self.device_handles_brcm_nics) + return False + if not isinstance(args.nic, list): + return False + nicCount = len(args.nic) + self.logger.output = {} + self.logger.clear_multiple_devices_output() + if nicCount <= 0: + return False + nics,ainics = self._get_nics_from_args(args) + if len(nics) > 0: + self.list_brcm_nic(args, False, nic=nics) + if len(ainics) > 0: + self.list_ainic(args, False, nic=ainics) + return True + return False + + def list_switch(self, args, multiple_devices=False, switch=None): + """List information for target switch + + Args: + args (Namespace): Namespace containing the parsed CLI args + multiple_devices (bool, optional): True if checking for multiple devices. Defaults to False. + switch (device_handle, optional): device_handle for target device. Defaults to None. + + Raises: + IndexError: Index error if switch list is empty + + Returns: + None: Print output via AMDSMILogger to destination + """ + # Set args.* to passed in arguments + if switch: + args.switch = switch + + if not self.group_check_printed: + self.helpers.check_required_groups() + self.group_check_printed = True + + # Handle multiple Switchs + handled_multiple_switchs, device_handle = self.helpers.handle_switchs(args, self.logger, self.list_switch) + if handled_multiple_switchs: + return # This function is recursive + + args.switch = device_handle + + try: + bdf = amdsmi_interface.amdsmi_get_switch_device_bdf(args.switch) + except amdsmi_exception.AmdSmiLibraryException as e: + bdf = e.get_error_info() + + try: + uuid = amdsmi_interface.amdsmi_get_switch_device_uuid(args.switch) + except amdsmi_exception.AmdSmiLibraryException as e: + uuid = e.get_error_info() + + # CSV format is intentionally aligned with Host + if self.logger.is_csv_format(): + self.logger.store_switch_output(args.switch, 'switch_bdf', bdf) + self.logger.store_switch_output(args.switch, 'switch_uuid', uuid) + else: + self.logger.store_switch_output(args.switch, 'bdf', bdf) + self.logger.store_switch_output(args.switch, 'uuid', uuid) + + if multiple_devices: + self.logger.store_multiple_device_output() + return # Skip printing when there are multiple devices + + self.logger.print_output() + + def list_switchs(self, args): + if not self.helpers.is_brcm_switch_initialized(): + return False + if args.switch == None: + args.switch = self.device_handles_switchs + if isinstance(args.switch, list): + switchCount = len(args.switch) + return False + if isinstance(args.switch, list): + switchCount = len(args.switch) + self.logger.output = {} + self.logger.clear_multiple_devices_output() + if switchCount > 0: + self.list_switch(args, False, switch=args.switch) + return True + return False + + def _get_nics_from_args(self, args): + nics = [] + ainics = [] + for nic in args.nic: + for nic_ptr in self.device_handles_brcm_nics: + if nic_ptr.value == nic.value: + nics.append(nic) + for nic_ptr in self.device_handles_ainics: + if nic_ptr.value == nic.value: + ainics.append(nic) + return nics, ainics + + def list(self, args, multiple_devices=False, gpu=None, nic=None, switch=None): + + if gpu: + args.gpu = gpu + if nic: + args.nic = nic + if switch: + args.switch = switch + + gpuCount = 0 + nicCount = 0 + switchCount = 0 + + # Handle No GPU passed + if args.gpu == None: + args.gpu = self.device_handles_gpus + if isinstance(args.gpu, list): + gpuCount = len(args.gpu) + else: + if isinstance(args.gpu, list): + gpuCount = len(args.gpu) + self.logger.output = {} + self.logger.clear_multiple_devices_output() + + if gpuCount > 0: + self.list_gpu(args, False, gpu=args.gpu) + return + + if self.list_nics(args): + return + if self.list_switchs(args): + return + + self.logger.output = {} + self.logger.clear_multiple_devices_output() + + if gpuCount > 0: + self.list_gpu(args, False, gpu=args.gpu) + + self.logger.output = {} + self.logger.clear_multiple_devices_output() + + if self.helpers.is_ainic_initialized() or self.helpers.is_brcm_nic_initialized(): + nics,ainics = self._get_nics_from_args(args) + if len(nics) > 0: + self.list_brcm_nic(args, False, nic=ainics) + if len(ainics) > 0: + self.list_ainic(args, False, nic=ainics) + + self.logger.output = {} + self.logger.clear_multiple_devices_output() + + if self.helpers.is_brcm_switch_initialized(): + self.list_switch(args, False, switch=args.switch) + + self.logger.output = {} + self.logger.clear_multiple_devices_output() def static_cpu(self, args, multiple_devices=False, cpu=None, interface_ver=None): """Get Static information for target cpu @@ -1287,10 +1611,188 @@ def static_gpu(self, args, multiple_devices=False, gpu=None, asic=None, bus=None if not self.logger.is_json_format(): self.logger.print_output(multiple_device_enabled=multiple_devices_csv_override) + def _filter_nics_from_args(subcommand): + @functools.wraps(subcommand) + def wrapper(self, *args, **kwargs): + original_nic = None + if len(args) > 0: + original_nic = args[0].nic + nics,ainics = self._get_nics_from_args(args[0]) + if len(nics) == 0: + args[0].nic = None + else: + args[0].nic = nics + result = subcommand(self, *args, **kwargs) + if len(args) > 0: + args[0].nic = original_nic + return result + return wrapper + + @_filter_nics_from_args + def _static_brcm_nic(self, args, multiple_devices=False, nic=None): + """Get Static information for target nic + + Args: + args (Namespace): Namespace containing the parsed CLI args + multiple_devices (bool, optional): True if checking for multiple devices. Defaults to False. + nic (device_handle, optional): device_handle for target device. Defaults to None. + + Returns: + None: Print output via AMDSMILogger to destination + """ + + if nic: + args.nic = nic + + # Handle multiple NICs + handled_multiple_nics, device_handle = self.helpers.handle_brcm_nics(args, self.logger, self._static_brcm_nic) + if handled_multiple_nics: + return # This function is recursive + args.nic = device_handle + if not args.nic: + return + + # Get nic id for logging + nic_id = self.helpers.get_nic_id_from_device_handle(args.nic) + logging.debug(f"Static Arg information for NIC {nic_id} on {self.helpers.os_info()}") + + static_dict = {} + if self.logger.is_json_format(): + static_dict['ai_nic'] = int(nic_id) + + if args.nic: + try: + nic_info = amdsmi_interface.amdsmi_get_nic_info(args.nic) + if nic_info: + static_dict["nic"] = { + "bdf" : f"{nic_info['bdf']}", + "UUID" : f"{nic_info['UUID']}", + "Device Name" : f"{nic_info['Device Name']}", + "Part Number" : f"{nic_info['Part Number']}", + "Firmware_Version" : f"{nic_info['Firmware_Version']}" + } + except amdsmi_exception.AmdSmiLibraryException as e: + static_dict["nic"] = "N/A" + logging.debug("Failed to get NIC %s | %s", nic_id, e.get_error_info()) + + multiple_devices_csv_override = False + if not self.logger.is_json_format(): + self.logger.store_nic_output(args.nic, 'values', static_dict) + else: + self.logger.store_nic_json_output.append(static_dict) + if multiple_devices: + self.logger.store_multiple_device_output() + return # Skip printing when there are multiple devices + if not self.logger.is_json_format(): + self.logger.print_output(multiple_device_enabled=multiple_devices_csv_override) + + def _filter_ainics_from_args(subcommand): + @functools.wraps(subcommand) + def wrapper(self, *args, **kwargs): + original_nic = None + if len(args) > 0: + original_nic = args[0].nic + nics,ainics = self._get_nics_from_args(args[0]) + if len(ainics) == 0: + args[0].nic = None + else: + args[0].nic = ainics + result = subcommand(self, *args, **kwargs) + if len(args) > 0: + args[0].nic = original_nic + return result + return wrapper + + @_filter_ainics_from_args + def _static_ainic(self, args, multiple_devices=False, nic=None): + """Get Static information for target ainic + + Args: + args (Namespace): Namespace containing the parsed CLI args + multiple_devices (bool, optional): True if checking for multiple devices. Defaults to False. + nic (device_handle, optional): device_handle for target device. Defaults to None. + + Returns: + None: Print output via AMDSMILogger to destination + """ + + if nic: + args.nic = nic + + # Handle multiple NICs + handled_multiple_nics, device_handle = self.helpers.handle_ainics(args, self.logger, self._static_ainic) + if handled_multiple_nics: + return # This function is recursive + args.nic = device_handle + if not args.nic: + return + + # Get nic id for logging + nic_id = self.helpers.get_ainic_id_from_device_handle(args.nic) + logging.debug(f"Static Arg information for NIC {nic_id} on {self.helpers.os_info()}") + + static_dict = {} + if self.logger.is_json_format(): + static_dict['ai_nic'] = int(nic_id) + + if args.nic: + try: + nic_info = amdsmi_interface.amdsmi_get_ainic_info(args.nic, True) + filter = [] + if hasattr(args, "asic") and getattr(args, "asic"): + filter.append("asic") + if hasattr(args, "bus") and getattr(args, "bus"): + filter.append("bus") + if hasattr(args, "driver") and getattr(args, "driver"): + filter.append("driver") + if hasattr(args, "numa") and getattr(args, "numa"): + filter.append("numa") + if len(filter) == 0 or len(filter) == 4: + static_dict["nic"] = nic_info + else: + nic_info_filtered = {} + for attr in filter: #remove all attributes except the one in filter: + nic_info_filtered = nic_info_filtered | {key: value for key, value in nic_info.items() if key.lower() == attr} + static_dict["nic"] = nic_info_filtered + except amdsmi_exception.AmdSmiLibraryException as e: + static_dict["nic"] = "N/A" + logging.debug("Failed to get NIC %s | %s", nic_id, e.get_error_info()) + + multiple_devices_csv_override = False + if not self.logger.is_json_format(): + self.logger.store_ainic_output(args.nic, 'values', static_dict) + else: + self.logger.store_nic_json_output.append(static_dict) + if multiple_devices: + self.logger.store_multiple_device_output() + return # Skip printing when there are multiple devices + if not self.logger.is_json_format(): + self.logger.print_output(multiple_device_enabled=multiple_devices_csv_override) + + def _static_nics(self, args, multiple_devices, nic): + if hasattr(args, "nic") and args.nic == None: + nic = None + if self.helpers.is_ainic_initialized(): + nic = self.device_handles_ainics + if self.helpers.is_brcm_nic_initialized(): + nic = self.device_handles_brcm_nics + args.nic = nic + return False + else: + if not self.helpers.is_ainic_initialized() and not self.helpers.is_brcm_nic_initialized(): + return False + self.logger.output = {} + self.logger.clear_multiple_devices_output() + if self.helpers.is_ainic_initialized(): + self._static_ainic(args, multiple_devices, nic) + if self.helpers.is_brcm_nic_initialized(): + self._static_brcm_nic(args, multiple_devices, nic) + return True + def static(self, args, multiple_devices=False, gpu=None, asic=None, bus=None, vbios=None, limit=None, driver=None, ras=None, board=None, numa=None, vram=None, cache=None, partition=None, - dfc_ucode=None, fb_info=None, num_vf=None, cpu=None, + dfc_ucode=None, fb_info=None, num_vf=None, cpu=None, nic=None, interface_ver=None, soc_pstate=None, xgmi_plpd = None, process_isolation=None, clock=None, profile=None): """Get Static information for target gpu and cpu @@ -1314,6 +1816,7 @@ def static(self, args, multiple_devices=False, gpu=None, asic=None, fb_info (bool, optional): Value override for args.fb_info. Defaults to None. num_vf (bool, optional): Value override for args.num_vf. Defaults to None. cpu (cpu_handle, optional): cpu_handle for target device. Defaults to None. + nic (nic_handle, optional): nic_handle for target device. Defaults to None. interface_ver (bool, optional): Value override for args.interface_ver. Defaults to None soc_pstate (bool, optional): Value override for args.soc_pstate. Defaults to None. xgmi_plpd (bool, optional): Value override for args.xgmi_plpd. Defaults to None. @@ -1329,6 +1832,14 @@ def static(self, args, multiple_devices=False, gpu=None, asic=None, args.cpu = cpu if gpu: args.gpu = gpu + if nic: + args.nic = nic + + if self._static_nics(args, multiple_devices, nic): + return True # we do not want to print cpu or gpu if user only wanted nic + + if (hasattr(args, 'cpu') and args.cpu) or (hasattr(args, 'gpu') and args.gpu): + args.nic = None # we do not want to output nic at the end if user wants only cpu or gpu # Check if a CPU argument has been set cpu_args_enabled = False @@ -1391,11 +1902,66 @@ def static(self, args, multiple_devices=False, gpu=None, asic=None, board, numa, vram, cache, partition, dfc_ucode, fb_info, num_vf, soc_pstate, xgmi_plpd, process_isolation, clock, profile) + + if hasattr(args, "nic") and args.nic: + self.logger.output = {} + self.logger.clear_multiple_devices_output() + self._static_ainic(args, multiple_devices, nic) + self._static_brcm_nic(args, multiple_devices, nic) + if self.logger.is_json_format(): self.logger.combine_arrays_to_json() - def firmware(self, args, multiple_devices=False, gpu=None, fw_list=True): + def firmware_nic(self, args, multiple_devices=False, nic=None, fw_list=True): + """ Get Firmware information for target nic + + Args: + args (Namespace): Namespace containing the parsed CLI args + multiple_devices (bool, optional): True if checking for multiple devices. Defaults to False. + nic (device_handle, optional): device_handle for target device. Defaults to None. + fw_list (bool, optional): True to get list of all firmware information + Raises: + IndexError: Index error if nic list is empty + + Returns: + None: Print output via AMDSMILogger to destination + """ + if fw_list: + args.fw_list = fw_list + if nic: + args.nic = nic + + # Handle No NIC passed + if args.nic==None: + args.nic = self.device_handles_brcm_nics + + # Handle multiple NICs + + if args.nic != None: + handled_multiple_nics, device_handle = self.helpers.handle_brcm_nics(args, self.logger, self.firmware_nic) + if handled_multiple_nics: + return # This function is recursive + + args.nic = device_handle + nic_id = self.helpers.get_nic_id_from_device_handle(args.nic) + if args.fw_list: + try: + fw_info = amdsmi_interface.amdsmi_get_nic_fw_info(args.nic) + except amdsmi_exception.AmdSmiLibraryException as e: + logging.debug("Failed to get firmware info for nic %s | %s", nic_id, e.get_error_info()) + + multiple_devices_csv_override = False + + self.logger.store_nic_output(args.nic, 'values', fw_info) + + if multiple_devices: + self.logger.store_multiple_device_output() + return # Skip printing when there are multiple devices + + self.logger.print_output() + + def firmware(self, args, multiple_devices=False, gpu=None, nic=None, fw_list=True, brcm_nic=None): """ Get Firmware information for target gpu Args: @@ -1403,6 +1969,7 @@ def firmware(self, args, multiple_devices=False, gpu=None, fw_list=True): multiple_devices (bool, optional): True if checking for multiple devices. Defaults to False. gpu (device_handle, optional): device_handle for target device. Defaults to None. fw_list (bool, optional): True to get list of all firmware information + brcm_nic (bool, optional): Value override for args.brcm_nic. Defaults to None. Raises: IndexError: Index error if gpu list is empty @@ -1418,6 +1985,11 @@ def firmware(self, args, multiple_devices=False, gpu=None, fw_list=True): if args.gpu == None: args.gpu = self.device_handles + if self.helpers.is_brcm_nic_initialized() and (args.brcm_nic or brcm_nic): + self.logger.output = {} + self.logger.clear_multiple_devices_output() + self.firmware_nic(args, multiple_devices, nic, fw_list) + return # Handle multiple GPUs handled_multiple_gpus, device_handle = self.helpers.handle_gpus(args, self.logger, self.firmware) if handled_multiple_gpus: @@ -3227,7 +3799,354 @@ def metric_core(self, args, multiple_devices=False, core=None, core_boost_limit= if not self.logger.is_json_format(): self.logger.print_output(multiple_device_enabled=multiple_devices_csv_override) + def metric_nic(self, args, multiple_devices=False, watching_output=False, watch=None, watch_time=None, + iterations=None, nic=None, nic_power=None, nic_temperature=None, nic_errors=None): + """Get Metric information for target nic + + Args: + args (Namespace): Namespace containing the parsed CLI args + multiple_devices (bool, optional): True if checking for multiple devices. Defaults to False. + watching_output (bool, optional): True if watch argument has been set. Defaults to False. + nic_power (bool, optional): Value override for args.nic_power. Defaults to None. + nic_temperature (bool, optional): Value override for args.nic_temperature. Defaults to None. + nic_errors (bool, optional): Value override for args.nic_errors. Defaults to None. + + Raises: + IndexError: Index error if nic list is empty + + Returns: + None: Print output via AMDSMILogger to destination + """ + # Set args.* to passed in arguments + if nic: + args.nic = nic + if watch: + args.watch = watch + if watch_time: + args.watch_time = watch_time + if iterations: + args.iterations = iterations + + #TODO: Need to add OS wise condition for the parameters + + if nic_power: + args.nic_power = nic_power + if nic_temperature: + args.nic_temperature = nic_temperature + if nic_errors: + args.nic_errors = nic_errors + + #Maintaining format as per other metric functions so above TODO can be resolved easily + current_platform_args = ["nic_power", "nic_temperature", "nic_errors"] + current_platform_values = [args.nic_power, args.nic_temperature, args.nic_errors] + + # Handle No NIC passed + if args.nic == None: + args.nic = self.device_handles_brcm_nics + + # Handle watch logic, will only enter this block once + if args.watch: + self.helpers.handle_watch(args=args, subcommand=self.metric_nic, logger=self.logger) + return + + # Handle multiple NICs + if isinstance(args.nic, list): + if len(args.nic) > 1: + # Deepcopy nics as recursion will destroy the nic list + stored_nics = [] + for nic in args.nic: + stored_nics.append(nic) + + # Store output from multiple devices + for device_handle in args.nic: + self.metric_nic(args, multiple_devices=True, watching_output=watching_output, nic=device_handle) + + # Reload original nics + args.nic = stored_nics + + # Print multiple device output + self.logger.print_output(multiple_device_enabled=True, watching_output=watching_output) + + # Add output to total watch output and clear multiple device output + if watching_output: + self.logger.store_watch_output(multiple_device_enabled=True) + + # Flush the watching output + self.logger.print_output(multiple_device_enabled=True, watching_output=watching_output) + + return + elif len(args.nic) == 1: + args.nic = args.nic[0] + else: + raise IndexError("args.nic should not be an empty list") + + # Get nic_id for logging + nic_id = self.helpers.get_nic_id_from_device_handle(args.nic) + + # Put the metrics table in the debug logs + nic_metric_info ={} + + try: + nic_metric_info = amdsmi_interface.amdsmi_get_nic_metrics_info(args.nic) + nic_metric_str = json.dumps(nic_metric_info, indent=4) + logging.debug("NIC Metrics table for %s | %s", nic_id, nic_metric_str) + except amdsmi_exception.AmdSmiLibraryException as e: + logging.debug("Unabled to load NIC Metrics table for %s | %s", nic_id, e.err_info) + + logging.debug(f"Metric Arg information for NIC {nic_id} on {self.helpers.os_info()}") + logging.debug(f"Args: {current_platform_args}") + logging.debug(f"Values: {current_platform_values}") + + # Set the platform applicable args to True if no args are set + if not any(current_platform_values): + for arg in current_platform_args: + setattr(args, arg, True) + + # Add timestamp and store values for specified arguments + values_dict = {} + + if "nic_power" in current_platform_args: + if args.nic_power: + power_dict = {} + sysfs_blocks = {"nic_power_async": "", "nic_power_control": "", "nic_power_runtime_active_time": "", + "nic_power_runtime_status": "", "nic_power_runtime_usage": "", "nic_power_runtime_active_kids": "", + "nic_power_runtime_enabled": "", "nic_power_runtime_suspended_time": ""} + + for key in nic_metric_info.keys(): + if key in sysfs_blocks.keys(): + if isinstance(nic_metric_info[key], int): + value = nic_metric_info[key] + else: + value = (nic_metric_info[key].split('\n')[0]).upper() + + if value == "" or value == 65535: + value = "N/A" + power_dict[key] = self.helpers.unit_format(self.logger, + value, + sysfs_blocks[key]) + + values_dict["nic_power"] = power_dict + + if "nic_temperature" in current_platform_args: + if args.nic_temperature: + temp_dict = {} + sysfs_blocks = {"nic_temp_crit_alarm": "", "nic_temp_emergency_alarm": "", "nic_temp_shutdown_alarm": "", + "nic_temp_max_alarm": "", "nic_temp_crit": "\N{DEGREE SIGN}C", "nic_temp_emergency": "\N{DEGREE SIGN}C", "nic_temp_input": "\N{DEGREE SIGN}C", + "nic_temp_max": "\N{DEGREE SIGN}C", "nic_temp_shutdown": "\N{DEGREE SIGN}C"} + + for key in nic_metric_info.keys(): + if key in sysfs_blocks.keys(): + if isinstance(nic_metric_info[key], int): + value = nic_metric_info[key] + else: + value = (nic_metric_info[key].split('\n')[0]).upper() + + if value == "" or value == 65535: + value = "N/A" + temp_dict[key] = self.helpers.unit_format(self.logger, + value, + sysfs_blocks[key]) + + values_dict["nic_temperature"] = temp_dict + + if "nic_errors" in current_platform_args: + if args.nic_errors: + + err_dict = {} + sysfs_blocks = ["nic_dev_correctable", "nic_dev_fatal", "nic_dev_nonfatal"] + + for key in nic_metric_info.keys(): + if key in sysfs_blocks: + err_dict[key] = {} + content_list = nic_metric_info[key].split('\n') + for content in content_list: + if content != "" and content.lower() != "n/a": + err_dict[key][content.split(' ')[0]] = content.split(' ')[1] + + values_dict["nic_errors"] = err_dict + + # Store timestamp first if watching_output is enabled + if watching_output: + self.logger.store_nic_output(args.nic, 'timestamp', int(time.time())) + self.logger.store_nic_output(args.nic, 'values', values_dict) + + if multiple_devices: + self.logger.store_multiple_device_output() + return # Skip printing when there are multiple devices + + self.logger.print_output(watching_output=watching_output) + + if watching_output: # End of single gpu add to watch_output + self.logger.store_watch_output(multiple_device_enabled=False) + + + def metric_switch(self, args, multiple_devices=False, watching_output=False, watch=None, watch_time=None, + iterations=None, switch=None, switch_power=None, switch_errors=None): + """Get Metric information for target switch + + Args: + args (Namespace): Namespace containing the parsed CLI args + multiple_devices (bool, optional): True if checking for multiple devices. Defaults to False. + watching_output (bool, optional): True if watch argument has been set. Defaults to False. + switch_power (bool, optional): Value override for args.switch_power. Defaults to None. + switch_errors (bool, optional): Value override for args.switch_errors. Defaults to None. + + Raises: + IndexError: Index error if switch list is empty + + Returns: + None: Print output via AMDSMILogger to destination + """ + # Set args.* to passed in arguments + if switch: + args.switch = switch + if watch: + args.watch = watch + if watch_time: + args.watch_time = watch_time + if iterations: + args.iterations = iterations + + #TODO: Need to add OS wise condition for the parameters + + if switch_power: + args.switch_power = switch_power + if switch_errors: + args.switch_errors = switch_errors + + #Maintaining format as per other metric functions so above TODO can be resolved easily + current_platform_args = ["switch_power", "switch_errors"] + current_platform_values = [args.switch_power, args.switch_errors] + + # Handle No SWITCH passed + if args.switch == None: + args.switch = self.device_handles_switchs + + # Handle watch logic, will only enter this block once + if args.watch: + self.helpers.handle_watch(args=args, subcommand=self.metric_switch, logger=self.logger) + return + + # Handle multiple Switches + if isinstance(args.switch, list): + if len(args.switch) > 1: + # Deepcopy switchs as recursion will destroy the switch list + stored_switches = [] + for switch in args.switch: + stored_switches.append(switch) + + # Store output from multiple devices + for device_handle in args.switch: + self.metric_switch(args, multiple_devices=True, watching_output=watching_output, switch=device_handle) + + # Reload original switchs + args.switch = stored_switches + + # Print multiple device output + self.logger.print_output(multiple_device_enabled=True, watching_output=watching_output) + + # Add output to total watch output and clear multiple device output + if watching_output: + self.logger.store_watch_output(multiple_device_enabled=True) + + # Flush the watching output + self.logger.print_output(multiple_device_enabled=True, watching_output=watching_output) + + return + elif len(args.switch) == 1: + args.switch = args.switch[0] + else: + return # intermittent issue with args.switch being an empty list. raise IndexError("args.switch should not be an empty list") + + # Get switch_id for logging + switch_id = self.helpers.get_switch_id_from_device_handle(args.switch) + + # Put the metrics table in the debug logs + switch_metric_info ={} + try: + switch_metric_info = amdsmi_interface.amdsmi_get_switch_metrics_info(args.switch) + switch_metric_str = json.dumps(switch_metric_info, indent=4) + logging.debug("SWITCH Metrics table for %s | %s", switch_id, switch_metric_str) + except amdsmi_exception.AmdSmiLibraryException as e: + logging.debug("Unabled to load SWITCH Metrics table for %s | %s", switch_id, e.err_info) + + logging.debug(f"Metric Arg information for SWITCH {switch_id} on {self.helpers.os_info()}") + logging.debug(f"Args: {current_platform_args}") + logging.debug(f"Values: {current_platform_values}") + + # Set the platform applicable args to True if no args are set + if not any(current_platform_values): + for arg in current_platform_args: + setattr(args, arg, True) + + # Add timestamp and store values for specified arguments + values_dict = {} + + if "switch_power" in current_platform_args: + if args.switch_power: + power_dict = {} + sysfs_blocks = {"brcm_power_async": "", "brcm_power_control": "", "brcm_power_runtime_active_kids": "", + "brcm_power_runtime_active_time": "", "brcm_power_runtime_enabled": "", "brcm_power_runtime_status": "", + "brcm_power_runtime_suspended_time": "", "brcm_power_runtime_usage": "", + "brcm_power_wakeup": "", "brcm_power_wakeup_abort_count": "", "brcm_power_wakeup_active": "", + "brcm_power_wakeup_active_count": "", "brcm_power_wakeup_count": "", "brcm_power_wakeup_last_time_ms": "", + "brcm_power_wakeup_max_time_ms": "", "brcm_power_wakeup_total_time_ms": ""} + + for key in switch_metric_info.keys(): + if key in sysfs_blocks.keys(): + if isinstance(switch_metric_info[key], int): + value = switch_metric_info[key] + else: + value = (switch_metric_info[key].split('\n')[0]).upper() + + if value == "": + value = "N/A" + power_dict[key] = self.helpers.unit_format(self.logger, + value, + sysfs_blocks[key]) + + values_dict["switch_power"] = power_dict + + if "switch_errors" in current_platform_args: + if args.switch_errors: + + err_dict = {} + sysfs_blocks = ["brcm_device_aer_dev_correctable", "brcm_device_aer_dev_fatal", "brcm_device_aer_dev_nonfatal"] + + for key in switch_metric_info.keys(): + if key in sysfs_blocks: + err_dict[key] = {} + + if switch_metric_info[key] == "N/A": + continue + + content_list = switch_metric_info[key].split('\n') + for content in content_list: + if content != "": + err_dict[key][content.split(' ')[0]] = content.split(' ')[1] + + values_dict["switch_errors"] = err_dict + + #TODO: ADD "NA" conditions in interface file + # Store timestamp first if watching_output is enabled + if watching_output: + self.logger.store_switch_output(args.switch, 'timestamp', int(time.time())) + + self.logger.store_switch_output(args.switch, 'values', values_dict) + + if multiple_devices: + self.logger.store_multiple_device_output() + return # Skip printing when there are multiple devices + + self.logger.print_output(watching_output=watching_output) + + if watching_output: # End of single gpu add to watch_output + self.logger.store_watch_output(multiple_device_enabled=False) + + def metric(self, args, multiple_devices=False, watching_output=False, gpu=None, + nic=None, nic_power=None, nic_temperature=None, nic_errors=None, brcm_nic=None, + switch=None, switch_power=None, switch_errors=None, brcm_switch=None, usage=None, watch=None, watch_time=None, iterations=None, power=None, clock=None, temperature=None, ecc=None, ecc_blocks=None, pcie=None, fan=None, voltage_curve=None, overdrive=None, perf_level=None, @@ -3297,6 +4216,16 @@ def metric(self, args, multiple_devices=False, watching_output=False, gpu=None, core_curr_active_freq_core_limit (bool, optional): Value override for args.core_curr_active_freq_core_limit. Defaults to None core_energy (bool, optional): Value override for args.core_energy. Defaults to None + nic (nic_handle, optional): device_handle for target device. Defaults to None. + nic_power (bool, optional): Value override for args.nic_power. Defaults to None. + nic_temperature (bool, optional): Value override for args.nic_temperature. Defaults to None. + nic_errors (bool, optional): Value override for args.nic_errors. Defaults to None. + brcm_nic (bool, optional): Value override for args.brcm_nic. Defaults to None. + switch (cpu_handle, optional): device_handle for target device. Defaults to None. + switch_power (bool, optional): Value override for args.switch_power. Defaults to None. + switch_errors (bool, optional): Value override for args.switch_errors. Defaults to None. + brcm_switch (bool, optional): Value override for args.brcm_switch. Defaults to None. + Raises: IndexError: Index error if gpu list is empty @@ -3311,6 +4240,24 @@ def metric(self, args, multiple_devices=False, watching_output=False, gpu=None, args.cpu = cpu if core: args.core = core + if self.helpers.is_brcm_nic_initialized() and (args.brcm_nic or brcm_nic): + args.nic_power = args.power + args.nic_temperature = args.temperature + args.nic_errors = args.ecc + self.logger.output = {} + self.logger.clear_multiple_devices_output() + self.metric_nic(args, multiple_devices, watching_output, watch, watch_time, iterations, + nic, nic_power, nic_temperature, nic_errors) + return + + if self.helpers.is_brcm_switch_initialized() and (args.brcm_switch or brcm_switch): + args.switch_power = args.power + args.switch_errors = args.ecc + self.logger.output = {} + self.logger.clear_multiple_devices_output() + self.metric_switch(args, multiple_devices, watching_output, watch, watch_time, iterations, + switch, switch_power, switch_errors) + return # Check if a GPU argument has been set gpu_args_enabled = False @@ -3735,10 +4682,297 @@ def _event_sigterm_handler(self, signum, frame): self.stop = True raise SystemExit(128 + signum) + def topology_nic(self, args, multiple_devices=False, gpu=None, nic=None, + nic_topo=None, nic_switch=None, multiple_device_enabled=None, switch=None): + + """ Get topology information for target gpus + params: + args - argparser args to pass to subcommand + multiple_devices (bool) - True if checking for multiple devices + gpu (device_handle) - device_handle for target device + nic (device_handle) - device_handle for target device + nic_topo (bool) - True if checking for connectivity between nic and gpu devices + nic_switch (bool) - True if checking for gpu, nic and switch device's affinity and parent switch + switch (device_handle) - device_handle for target device + + return: + Nothing + """ + # Set args.* to passed in arguments + if gpu: + args.gpu = gpu + if nic: + args.nic = nic + if nic_topo: + args.nic_topo=nic_topo + if nic_switch: + args.nic_switch=nic_switch + if switch: + args.switch = switch + + if not self.group_check_printed: + self.helpers.check_required_groups() + self.group_check_printed = True + + isSingleNICRequest = False #-N option + isSingleSwitchRequest= False #-bs option + isSingleGPURequest = False #-g option + + gpucount = 0 + niccount = 0 + switchcount = 0 + + if args.nic == None: + args.nic = self.device_handles_brcm_nics + if not isinstance(args.nic, list): + args.nic = [args.nic] + if len(args.nic) == 1: + isSingleNICRequest = True + niccount = len(args.nic) + + if args.switch == None: + args.switch = self.device_handles_switchs + if not isinstance(args.switch, list): + args.switch = [args.switch] + if len(args.switch) == 1: + isSingleSwitchRequest = True + switchcount = len(args.switch) + + if args.gpu == None: + args.gpu = self.device_handles + if not isinstance(args.gpu, list): + args.gpu = [args.gpu] + if len(args.gpu) == 1: + isSingleGPURequest = True + gpucount = len(args.switch) + + # Clear the table header + self.logger.table_header = ''.rjust(12) + + if args.nic_topo: + topo_dict = {} + + # Loop through each NIC to get its BDF and corresponding GPU statuses + for idx, dest_nic in enumerate(args.nic): + # Get NIC ID and BDF + nic_bdf = "" + nic_info = amdsmi_interface.amdsmi_get_nic_info(dest_nic) + if nic_info: + nic_bdf = nic_info['bdf'] + nic_id= self.helpers.get_nic_id_from_device_handle(dest_nic) + + # List to store the GPU statuses for this NIC + gpu_statuses_for_nic = [] + + # Loop through each GPU to determine its status + for gpu_dest in args.gpu: + gpu_bdf = amdsmi_interface.amdsmi_get_gpu_device_bdf(gpu_dest) + gpu_id = self.helpers.get_gpu_id_from_device_handle(gpu_dest) + status = amdsmi_interface.amdsmi_get_nic_gpu_topo_info(dest_nic,gpu_dest) + gpu_statuses_for_nic.append((gpu_bdf, status)) # Store BDF and status as tuple + + # Store NIC BDF and associated GPU statuses in the dictionary + topo_dict[nic_bdf] = gpu_statuses_for_nic + + # Prepare tabular output for logger + tabular_output = [] + + # Add header row for GPU BDFs + if self.logger.is_human_readable_format(): + header_row = {"brcm_nic": "", "bdf": "".rjust(19)} + else: + header_row = {} + + gpu_bdfs = [] # List to store GPU BDFs for the header + for gpu_dest in args.gpu: + gpu_id = self.helpers.get_gpu_id_from_device_handle(gpu_dest) + gpu_bdf = amdsmi_interface.amdsmi_get_gpu_device_bdf(gpu_dest) + if self.logger.is_human_readable_format(): + header_row[f"GPU BDF_{gpu_bdf}"] = f"{gpu_bdf}".rjust(20) + else: + header_row[f"GPU{gpu_id}"] = f"{gpu_bdf}" + gpu_bdfs.append(gpu_bdf) # Store GPU BDF for later reference + + # Add the header row + tabular_output.append(header_row) + + # Add NIC rows with their associated GPU statuses + for idx, (nic_bdf, gpu_info) in enumerate(topo_dict.items()): + if not isSingleNICRequest: + if self.logger.is_human_readable_format(): + nic_row = {'brcm_nic': f"BRCM_NIC{idx}".ljust(12), 'bdf': f"{nic_bdf}".ljust(18) } + else: + nic_row = {'brcm_nic': f"BRCM_NIC{idx}", 'bdf': f"{nic_bdf}"} + else: #nic_id get stored in the initial iteration + if self.logger.is_human_readable_format(): + nic_row = {'brcm_nic': f"BRCM_NIC{nic_id}".ljust(12), 'bdf': f"{nic_bdf}".ljust(18) } + else: + nic_row = {'brcm_nic': f"BRCM_NIC{nic_id}", 'bdf': f"{nic_bdf}"} + + # Add GPU BDFs and statuses in the row + for gpu_idx, (gpu_bdf, status) in enumerate(gpu_info): + if self.logger.is_human_readable_format(): + nic_row[f"GPU{gpu_idx} Status"] = status.ljust(20) + else: + nic_row[f"GPU{gpu_idx}_Topo"] = status + # Add the NIC row to the table + tabular_output.append(nic_row) + + # Use the logger to display the table + + # Construct the table header with GPU column names (adjusting for multiple GPUs) + gpu_columns = [f"GPU{idx} " for idx in range(len(gpu_bdfs))] + gpu_status_columns = [f"GPU{idx} Status" for idx in range(len(gpu_bdfs))] + if self.logger.is_human_readable_format(): + self.logger.table_header = f"{'Device'.ljust(30)}" + " ".join(gpu.ljust(18) for gpu in gpu_columns) + else: + self.logger.table_header = f"{'Device'}" + "".join(gpu for gpu in gpu_columns) + + # Output the table + self.logger.multiple_device_output = tabular_output + self.logger.table_title = "NIC-GPU ACCESS TABLE" + self.logger.print_output(multiple_device_enabled=True, tabular=True) + + if self.logger.is_human_readable_format(): + # Populate the legend output + legend_parts = [ + "\n\nLegend:", + " PCIe = gpu->nic are in same switch and numa", + " X-NUMA=gpu->nic are in different or same switch and across NUMA", + " NUMA= gpu->nic are in different or same switch and same NUMA" + ] + 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') + + return + + if args.nic_switch: + # Prepare the table's header and data + tabular_output = [] + + # Add header row for BDF, NUMA, and CPU Affinity + header_row = {"Device": "", "bdf": "", "NUMA": "", "SWITCH": "", "CPU Affinity": ""} + if self.logger.is_human_readable_format(): + tabular_output.append(header_row) + + if isSingleNICRequest: + gpucount = 0 + niccount = 1 + switchcount = 0 + if isSingleSwitchRequest: + gpucount = 0 + niccount = 0 + switchcount = 1 + if isSingleGPURequest: + gpucount = 1 + niccount = 0 + switchcount = 0 + + # First, add GPU information + if gpucount > 0: + for gpu_idx, gpu_dest in enumerate(args.gpu): + gpu_id= self.helpers.get_gpu_id_from_device_handle(gpu_dest) + gpu_bdf = amdsmi_interface.amdsmi_get_gpu_device_bdf(gpu_dest) + CPU_Affinity=amdsmi_interface.amdsmi_get_gpu_topo_cpu_affinity(gpu_dest) + numa_node=amdsmi_interface.amdsmi_get_gpu_topo_numa_affinity(gpu_dest) + switch_bdf = amdsmi_interface.amdsmi_get_root_switch(amdsmi_interface.amdsmi_get_gpu_device_bdf_bdf(gpu_dest)) + + # Add GPU row to the table + if self.logger.is_human_readable_format(): + device_row = { + "Device": f"GPU{gpu_id}".ljust(17), + "bdf": f"{gpu_bdf}".rjust(2), + "NUMA":f"{numa_node}".rjust(8).ljust(20), + "SWITCH":f"{switch_bdf}".rjust(8).ljust(20), + "CPU Affinity": f"{CPU_Affinity}".ljust(20) + } + else: + device_row = { + "Device": f"GPU{gpu_id}", + "bdf": gpu_bdf, + "NUMA":numa_node, + "SWITCH":switch_bdf, + "CPU Affinity": CPU_Affinity + } + tabular_output.append(device_row) + + ## Then, add NIC information + if niccount > 0: + for nic_idx, nic_dest in enumerate(args.nic): + nic_id= self.helpers.get_nic_id_from_device_handle(nic_dest) + nic_bdf = "" + nic_info = amdsmi_interface.amdsmi_get_nic_info(nic_dest) + if nic_info: + nic_bdf = nic_info['bdf'] + CPU_Affinity=amdsmi_interface.amdsmi_get_nic_topo_cpu_affinity(nic_dest) + numa_node=amdsmi_interface.amdsmi_get_nic_topo_numa_affinity(nic_dest) + switch_bdf = amdsmi_interface.amdsmi_get_root_switch(amdsmi_interface.amdsmi_get_nic_device_bdf_bdf(nic_dest)) + + # Add NIC row to the table + if self.logger.is_human_readable_format(): + device_row = { + "Device": f"BRCM_NIC{nic_id}".ljust(17), + "bdf": f"{nic_bdf}".rjust(2), + "NUMA": f"{numa_node}".rjust(8).ljust(20), + "SWITCH":f"{switch_bdf}".rjust(8).ljust(20), + "CPU Affinity": f"{CPU_Affinity}".ljust(20) + } + else: + device_row = { + "Device": f"BRCM_NIC{nic_id}", + "bdf": nic_bdf, + "NUMA": numa_node, + "SWITCH":switch_bdf, + "CPU Affinity": CPU_Affinity + } + tabular_output.append(device_row) + + ## Then, add SWITCH information + if switchcount > 0: + for switch_idx, switch_dest in enumerate(args.switch): + switch_id= self.helpers.get_switch_id_from_device_handle(switch_dest) + switch_bdf = amdsmi_interface.amdsmi_get_switch_device_bdf(switch_dest) + CPU_Affinity=amdsmi_interface.amdsmi_get_switch_topo_cpu_affinity(switch_dest) + numa_node=amdsmi_interface.amdsmi_get_switch_topo_numa_affinity(switch_dest) + pSwitch_bdf = "N/A" + + # Add NIC row to the table + if self.logger.is_human_readable_format(): + device_row = { + "Device": f"BRCM_SWITCH{switch_id}".ljust(17), + "bdf": f"{switch_bdf}".rjust(2), + "NUMA": f"{numa_node}".rjust(8).ljust(20), + "SWITCH":f"{pSwitch_bdf}".rjust(8).ljust(20), + "CPU Affinity": f"{CPU_Affinity}".ljust(20) + } + else: + device_row = { + "Device": f"BRCM_SWITCH{switch_id}", + "bdf": switch_bdf, + "NUMA": numa_node, + "SWITCH":pSwitch_bdf, + "CPU Affinity": CPU_Affinity + } + tabular_output.append(device_row) + + # Display the table using the logger + self.logger.table_title = "AFFINITY TABLE" + self.logger.table_header = "Device".ljust(17) + "bdf".ljust(17) + "NUMA".ljust(15) + "SWITCH".ljust(20) + "CPU Affinity".ljust(17) + self.logger.multiple_device_output = tabular_output + self.logger.print_output(multiple_device_enabled=True, tabular=True) + return + def topology(self, args, multiple_devices=False, gpu=None, access=None, - weight=None, hops=None, link_type=None, numa_bw=None, - coherent=None, atomics=None, dma=None, bi_dir=None): + weight=None, hops=None, link_type=None, numa_bw=None, coherent=None, + atomics=None, dma=None, bi_dir=None, nic=None, nic_topo=None, nic_switch=None, + multiple_device_enabled=None, switch=None): + """ Get topology information for target gpus params: args - argparser args to pass to subcommand @@ -3753,6 +4987,10 @@ def topology(self, args, multiple_devices=False, gpu=None, access=None, atomics (bool) - Value override for args.atomics dma (bool) - Value override for args.dma bi_dir (bool) - Value override for args.bi_dir + nic (device_handle) - device_handle for target device + nic_topo (bool) - True if checking for connectivity between nic and gpu devices + nic_switch (bool) - True if checking for gpu, nic and switch device's affinity and parent switch + switch (device_handle) - device_handle for target device return: Nothing """ @@ -3777,6 +5015,17 @@ def topology(self, args, multiple_devices=False, gpu=None, access=None, args.dma = dma if bi_dir: args.bi_dir = bi_dir + if nic: + args.nic = nic + if switch: + args.switch = switch + if ((self.helpers.is_brcm_nic_initialized() and + (nic_topo or args.nic_topo)) or + (self.helpers.is_brcm_switch_initialized() and + (nic_switch or args.nic_switch))): + self.topology_nic(args, multiple_devices, args.gpu, args.nic, args.nic_topo, args.nic_switch, + multiple_device_enabled, args.switch) + return # Handle No GPU passed if args.gpu == None: @@ -5820,13 +7069,15 @@ def monitor(self, args, multiple_devices=False, watching_output=False, gpu=None, temperature=None, base_board_temps=None, gpu_board_temps=None, gfx_util=None, mem_util=None, encoder=None, decoder=None, ecc=None, vram_usage=None, pcie=None, process=None, - violation=None): + violation=None, nic=None, switch=None, brcm_nic=None, brcm_switch=None): """ Populate a table with each GPU as an index to rows of targeted data Args: args (Namespace): Namespace containing the parsed CLI args multiple_devices (bool, optional): True if checking for multiple devices. Defaults to False. gpu (device_handle, optional): device_handle for target device. Defaults to None. + nic (device_handle, optional): device_handle for target nic device. Defaults to None. + switch (device_handle, optional): device_handle for target switch device. Defaults to None. watch (bool, optional): Value override for args.watch. Defaults to None. watch_time (int, optional): Value override for args.watch_time. Defaults to None. iterations (int, optional): Value override for args.iterations. Defaults to None. @@ -5843,6 +7094,8 @@ def monitor(self, args, multiple_devices=False, watching_output=False, gpu=None, pcie (bool, optional): Value override for args.pcie. Defaults to None. process (bool, optional): Value override for args.process. Defaults to None. violation (bool, optional): Value override for args.violation. Defaults to None. + brcm_nic (bool, optional): Value override for args.brcm_nic. Defaults to None. + brcm_switch (bool, optional): Value override for args.brcm_switch. Defaults to None. Raises: ValueError: Value error if no gpu value is provided @@ -5860,6 +7113,10 @@ def monitor(self, args, multiple_devices=False, watching_output=False, gpu=None, args.watch_time = watch_time if iterations: args.iterations = iterations + if nic: + args.nic = nic + if switch: + args.switch = switch # monitor args if power_usage: @@ -5886,6 +7143,14 @@ def monitor(self, args, multiple_devices=False, watching_output=False, gpu=None, args.pcie = pcie if process: args.process = process + if brcm_nic or args.brcm_nic: + self.monitor_nic(args, multiple_devices, watching_output, args.nic, watch, watch_time, iterations, + args.temperature, args.brcm_nic) + return + if brcm_switch or args.brcm_switch: + self.monitor_switch(args, multiple_devices, watching_output, args.switch, watch, watch_time, iterations, + args.pcie, args.brcm_switch) + return if not self.helpers.is_virtual_os(): if violation: args.violation = violation diff --git a/projects/amdsmi/amdsmi_cli/amdsmi_helpers.py b/projects/amdsmi/amdsmi_cli/amdsmi_helpers.py index bc01f4f0777..daed61d5d56 100755 --- a/projects/amdsmi/amdsmi_cli/amdsmi_helpers.py +++ b/projects/amdsmi/amdsmi_cli/amdsmi_helpers.py @@ -210,6 +210,15 @@ def is_amd_hsmp_initialized(self): return AMDSMI_INIT_FLAG & amdsmi_interface.amdsmi_wrapper.AMDSMI_INIT_AMD_CPUS + def is_ainic_initialized(self): + return AMDSMI_INIT_FLAG & amdsmi_interface.amdsmi_wrapper.AMDSMI_INIT_AMD_NICS + + def is_brcm_nic_initialized(self): + return False + + def is_brcm_switch_initialized(self): + return False + def get_rocm_version(self): try: rocm_lib_status, rocm_version = amdsmi_interface.amdsmi_get_rocm_version() @@ -360,7 +369,7 @@ def get_gpu_choices(self): bdf = amdsmi_interface.amdsmi_get_gpu_device_bdf(device_handle) uuid = amdsmi_interface.amdsmi_get_gpu_device_uuid(device_handle) gpu_choices[str(gpu_id)] = { - "BDF": bdf, + "bdf": bdf, "UUID": uuid, "Device Handle": device_handle, } @@ -377,6 +386,114 @@ def get_gpu_choices(self): return (gpu_choices, gpu_choices_str) + def nic_choices_from_nic_info(self, nic_info, nic_id, device_handle, max_padding, nic_choices, nic_choices_str): + bdf = nic_info['bdf'] + + #uuid="abc" + uuid = nic_info['UUID'] + + nic_choices[str(nic_id)] = { + "bdf": bdf, + "UUID": uuid, + "Device Handle": device_handle, + } + + if nic_id == 0: + id_padding = max_padding + else: + id_padding = max_padding - int(math.log10(nic_id)) + nic_choices_str += f"ID: {nic_id}{' ' * id_padding}| BDF: {bdf} | UUID: {uuid}\n" + return nic_choices, nic_choices_str + + def get_nic_choices(self): + nic_choices = {} + nic_choices_str = "" + nic_device_handles = [] + ainic_device_handles = [] + + try: + # get_nic_handles returns the device_handles storted for nic_id + nic_device_handles = amdsmi_interface.get_nic_handles() + ainic_device_handles = amdsmi_interface.get_ainic_handles() + + except amdsmi_interface.AmdSmiLibraryException as e: + if e.err_code in (amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_NOT_INIT, + amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_DRIVER_NOT_LOADED): + logging.info('Unable to get device choices, driver not initialized (BRCM_NIC, IONIC_NIC, RDMA_NIC not found in modules)') + else: + raise e + + if len(nic_device_handles) == 0 and len(ainic_device_handles) == 0: + logging.info('Unable to find any devices, check if driver is initialized (BRCM_NIC, IONIC_NIC, RDMA_NIC not found in modules)') + else: + # Handle spacing for the gpu_choices_str + max_padding = int(math.log10(len(nic_device_handles) + len(ainic_device_handles))) + 1 + + for nic_id, device_handle in enumerate(nic_device_handles): + nic_info = amdsmi_interface.amdsmi_get_nic_info(device_handle) + if nic_info: + nic_choices, nic_choices_str = self.nic_choices_from_nic_info(nic_info, nic_id, device_handle, max_padding, nic_choices, nic_choices_str) + + for nic_id, device_handle in enumerate(ainic_device_handles): + nic_info = amdsmi_interface.amdsmi_get_ainic_info(device_handle) + nic_id = nic_id + len(nic_device_handles) + nic_choices, nic_choices_str = self.nic_choices_from_nic_info(nic_info, nic_id, device_handle, max_padding, nic_choices, nic_choices_str) + + # Add the all option to the gpu_choices + nic_choices["all"] = "all" + nic_choices_str += f" all{' ' * max_padding}| Selects all devices\n" + + return (nic_choices, nic_choices_str) + + #BRCM POC to get switch choices + def get_switch_choices(self): + switch_choices = {} + switch_choices_str = "" + device_handles = [] + + try: + # get_switch_handles returns the device_handles storted for switch_id + device_handles = amdsmi_interface.get_switch_handles() + + except amdsmi_interface.AmdSmiLibraryException as e: + + if e.err_code in (amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_NOT_INIT, + amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_DRIVER_NOT_LOADED): + logging.info('Unable to get device choices, driver not initialized (BRCM_switch not found in modules)') + + else: + raise e + + if len(device_handles) == 0: + logging.info('Unable to find any devices, check if driver is initialized (BRCM_switch not found in modules)') + else: + # Handle spacing for the gpu_choices_str + max_padding = int(math.log10(len(device_handles))) + 1 + + for switch_id, device_handle in enumerate(device_handles): + bdf = amdsmi_interface.amdsmi_get_switch_device_bdf(device_handle) + + #uuid="abc" + uuid = amdsmi_interface.amdsmi_get_switch_device_uuid(device_handle) + + switch_choices[str(switch_id)] = { + "bdf": bdf, + "UUID": uuid, + "Device Handle": device_handle, + } + + if switch_id == 0: + id_padding = max_padding + else: + id_padding = max_padding - int(math.log10(switch_id)) + switch_choices_str += f"ID: {switch_id}{' ' * id_padding}| BDF: {bdf} | UUID: {uuid}\n" + + + # Add the all option to the gpu_choices + switch_choices["all"] = "all" + switch_choices_str += f" all{' ' * max_padding}| Selects all devices\n" + + return (switch_choices, switch_choices_str) @staticmethod def is_UUID(uuid_question: str) -> bool: @@ -419,7 +536,7 @@ def get_device_handles_from_gpu_selections(self, gpu_selections: List[str], gpu_ valid_gpu_choice = False for gpu_id, gpu_info in gpu_choices.items(): - bdf = gpu_info['BDF'] + bdf = gpu_info['bdf'] is_bdf = True uuid = gpu_info['UUID'] device_handle = gpu_info['Device Handle'] @@ -448,6 +565,120 @@ def get_device_handles_from_gpu_selections(self, gpu_selections: List[str], gpu_ return True, True, selected_device_handles + def get_device_handles_from_nic_selections(self, nic_selections: List[str], nic_choices=None): + + """Convert provided nic_selections to device_handles + + Args: + nic_selections (list[str]): Selected NIC ID(s), BDF(s), or UUID(s): + ex: ID:0 | BDF:0000:23:00.0 | UUID:ffffffff-0000-1000-0000-000000000000 + nic_choices (dict{nic_choices}): This is a dictionary of the possible gpu_choices + Returns: + (True, list[device_handles]): Returns a list of all the nic_selections converted to + amdsmi device_handles + (False, str): Return False, and the first input that failed to be converted + """ + if 'all' in nic_selections: + return (True, amdsmi_interface.get_nic_handles() + amdsmi_interface.get_ainic_handles()) + + if isinstance(nic_selections, str): + nic_selections = [nic_selections] + + if nic_choices is None: + nic_choices = self.get_nic_choices()[0] + + selected_device_handles = [] + for nic_selection in nic_selections: + valid_nic_choice = False + + for nic_id, nic_info in nic_choices.items(): + bdf = nic_info['bdf'] + uuid = nic_info['UUID'] + device_handle = nic_info['Device Handle'] + + + # Check if passed nic is a nic ID or UUID + if nic_selection == nic_id or nic_selection.lower() == uuid: + + device_type=amdsmi_interface.amdsmi_get_processor_type(device_handle) + + selected_device_handles.append(device_handle) + valid_nic_choice = True + break + else: # Check if nic passed is a BDF object + if BDF(nic_selection) == BDF(bdf): + selected_device_handles.append(device_handle) + valid_nic_choice = True + break + + if not valid_nic_choice: + logging.debug(f"AMDSMIHelpers.get_device_handles_from_gpu_selections - Unable to convert {nic_selection}") + + return False, nic_selection + + + return True, selected_device_handles + + #BRCM POC to get device handles from switch selections + def get_device_handles_from_switch_selections(self, switch_selections: List[str], switch_choices=None): + + """Convert provided switch_selections to device_handles + + Args: + switch_selections (list[str]): Selected switch ID(s), BDF(s), or UUID(s): + ex: ID:0 | BDF:0000:23:00.0 | UUID:ffffffff-0000-1000-0000-000000000000 + switch_choices (dict{switch_choices}): This is a dictionary of the possible gpu_choices + Returns: + (True, list[device_handles]): Returns a list of all the switch_selections converted to + amdsmi device_handles + (False, str): Return False, and the first input that failed to be converted + """ + if 'all' in switch_selections: + return (True, amdsmi_interface.get_switch_handles()) + + if isinstance(switch_selections, str): + switch_selections = [switch_selections] + + if switch_choices is None: + switch_choices = self.get_switch_choices()[0] + + selected_device_handles = [] + for switch_selection in switch_selections: + valid_switch_choice = False + + for switch_id, switch_info in switch_choices.items(): + bdf = switch_info['bdf'] + uuid = switch_info['UUID'] + device_handle = switch_info['Device Handle'] + + + # Check if passed switch is a switch ID or UUID + if switch_selection == switch_id or switch_selection.lower() == uuid: + + device_type=amdsmi_interface.amdsmi_get_processor_type(device_handle) + + selected_device_handles.append(device_handle) + valid_switch_choice = True + break + else: # Check if switch passed is a BDF object + try: + if BDF(switch_selection) == BDF(bdf): + selected_device_handles.append(device_handle) + valid_switch_choice = True + break + except Exception: + # Ignore exception when checking if the gpu_choice is a BDF + pass + + if not valid_switch_choice: + logging.debug(f"AMDSMIHelpers.get_device_handles_from_gpu_selections - Unable to convert {switch_selection}") + + return False, switch_selection + + + return True, selected_device_handles + + def get_device_handles_from_cpu_selections(self, cpu_selections: List[str], cpu_choices=None): """Convert provided cpu_selections to device_handles @@ -590,6 +821,121 @@ def handle_gpus(self, args, logger, subcommand): return False, args.gpu + def handle_switchs(self, args, logger, subcommand): + + """This function will run execute the subcommands based on the number + of gpus passed in via args. + params: + args - argparser args to pass to subcommand + current_platform_args (list) - GPU supported platform arguments + current_platform_values (list) - GPU supported values for the arguments + logger (AMDSMILogger) - Logger to print out output + subcommand (AMDSMICommands) - Function that can handle multiple gpus + + return: + tuple(bool, device_handle) : + bool - True if executed subcommand for multiple devices + device_handle - Return the device_handle if the list of devices is a length of 1 + (handled_multiple_gpus, device_handle) + + """ + + if isinstance(args.switch, list): + + if len(args.switch) > 1: + for device_handle in args.switch: + device_type=amdsmi_interface.amdsmi_get_processor_type(device_handle) + if device_type["processor_type"]==amdsmi_interface.AmdSmiProcessorType(amdsmi_interface.amdsmi_wrapper.AMDSMI_PROCESSOR_TYPE_BRCM_SWITCH).name: + subcommand(args, multiple_devices=True, switch=device_handle) + + logger.print_output(multiple_device_enabled=True) + return True, args.switch + elif len(args.switch) == 1: + args.switch = args.switch[0] + return False, args.switch + else: + logging.debug("args.switch has an empty list") + else: + return False, args.switch + + def handle_brcm_nics(self, args, logger, subcommand): + + """This function will run execute the subcommands based on the number + of nics passed in via args. + params: + args - argparser args to pass to subcommand + current_platform_args (list) - nic supported platform arguments + current_platform_values (list) - nic supported values for the arguments + logger (AMDSMILogger) - Logger to print out output + subcommand (AMDSMICommands) - Function that can handle multiple nics + + return: + tuple(bool, device_handle) : + bool - True if executed subcommand for multiple devices + device_handle - Return the device_handle if the list of devices is a length of 1 + (handled_multiple_gpus, device_handle) + + """ + + if isinstance(args.nic, list): + + if len(args.nic) > 1: + + for device_handle in args.nic: + + device_type=amdsmi_interface.amdsmi_get_processor_type(device_handle) + if device_type["processor_type"]==amdsmi_interface.AmdSmiProcessorType(amdsmi_interface.amdsmi_wrapper.AMDSMI_PROCESSOR_TYPE_BRCM_NIC).name: + subcommand(args, multiple_devices=True, nic=device_handle) + + logger.print_output(multiple_device_enabled=True) + return True, args.nic + elif len(args.nic) == 1: + args.nic = args.nic[0] + return False, args.nic + else: + logging.debug("args.nic has an empty list") + else: + return False, args.nic + + def handle_ainics(self, args, logger, subcommand): + + """This function will run execute the subcommands based on the number + of nics passed in via args. + params: + args - argparser args to pass to subcommand + current_platform_args (list) - nic supported platform arguments + current_platform_values (list) - nic supported values for the arguments + logger (AMDSMILogger) - Logger to print out output + subcommand (AMDSMICommands) - Function that can handle multiple nics + + return: + tuple(bool, device_handle) : + bool - True if executed subcommand for multiple devices + device_handle - Return the device_handle if the list of devices is a length of 1 + (handled_multiple_gpus, device_handle) + + """ + + if isinstance(args.nic, list): + + if len(args.nic) > 1: + + for device_handle in args.nic: + + device_type=amdsmi_interface.amdsmi_get_processor_type(device_handle) + if device_type["processor_type"]==amdsmi_interface.AmdSmiProcessorType(amdsmi_interface.amdsmi_wrapper.AMDSMI_PROCESSOR_TYPE_AMD_NIC).name: + subcommand(args, multiple_devices=True, nic=device_handle) + + logger.print_output(multiple_device_enabled=True) + return True, args.nic + elif len(args.nic) == 1: + args.nic = args.nic[0] + return False, args.nic + else: + logging.debug("args.nic has an empty list") + else: + return False, args.nic + def handle_cpus(self, args, logger, subcommand): """This function will run execute the subcommands based on the number of cpus passed in via args. @@ -746,7 +1092,45 @@ def get_gpu_id_from_device_handle(self, input_device_handle): raise amdsmi_exception.AmdSmiParameterException(input_device_handle, amdsmi_interface.amdsmi_wrapper.amdsmi_processor_handle, "Unable to find gpu ID from device_handle") + def get_nic_id_from_device_handle(self, input_device_handle): + """Get the nic index from the device_handle. + get_nic_handles() returns the list of device_handles in order of nic_index + """ + device_handles = amdsmi_interface.get_nic_handles() + if len(device_handles) == 0: + return -1 + for nic_index, device_handle in enumerate(device_handles): + if input_device_handle.value == device_handle.value: + return nic_index + raise amdsmi_exception.AmdSmiParameterException(input_device_handle, + amdsmi_interface.amdsmi_wrapper.amdsmi_processor_handle, + "Unable to find nic ID from device_handle") + + def get_ainic_id_from_device_handle(self, input_device_handle): + """Get the ainic index from the device_handle. + get_ainic_handles() returns the list of device_handles in order of ainic_index + """ + device_handles = amdsmi_interface.get_ainic_handles() + if len(device_handles) == 0: + return -1 + for nic_index, device_handle in enumerate(device_handles): + if input_device_handle.value == device_handle.value: + return nic_index + raise amdsmi_exception.AmdSmiParameterException(input_device_handle, + amdsmi_interface.amdsmi_wrapper.amdsmi_processor_handle, + "Unable to find nic ID from device_handle") + def get_switch_id_from_device_handle(self, input_device_handle): + """Get the nic index from the device_handle. + get_switch_handles() returns the list of device_handles in order of nic_index + """ + device_handles = amdsmi_interface.get_switch_handles() + for switch_index, device_handle in enumerate(device_handles): + if input_device_handle.value == device_handle.value: + return switch_index + raise amdsmi_exception.AmdSmiParameterException(input_device_handle, + amdsmi_interface.amdsmi_wrapper.amdsmi_processor_handle, + "Unable to find switch ID from device_handle") def get_cpu_id_from_device_handle(self, input_device_handle): """Get the cpu index from the device_handle. diff --git a/projects/amdsmi/amdsmi_cli/amdsmi_init.py b/projects/amdsmi/amdsmi_cli/amdsmi_init.py index e5eef0f94d9..9c6d5a3284a 100644 --- a/projects/amdsmi/amdsmi_cli/amdsmi_init.py +++ b/projects/amdsmi/amdsmi_cli/amdsmi_init.py @@ -81,6 +81,13 @@ def check_amd_hsmp_driver(): return True return False +def check_amd_ionic_driver(): + """ Returns true if ionic is found in the list of initialized modules """ + status_file = Path("/sys/module/ionic/initstate") + if status_file.exists(): + if status_file.read_text(encoding="ascii").strip() == "live": + return True + return False def amdsmi_cli_init(): """ Initializes AMDSMI Library for the CLI @@ -94,45 +101,26 @@ def amdsmi_cli_init(): Raises: err: AmdSmiLibraryException if not successful in initializing any drivers """ - init_flag = amdsmi_interface.AmdSmiInitFlags.INIT_ALL_PROCESSORS - if check_amdgpu_driver() and check_amd_hsmp_driver(): - init_flag = amdsmi_interface.AmdSmiInitFlags.INIT_AMD_APUS - logging.debug("Both amdgpu , amd_hsmp or hsmp_acpi driver's initstate is live") - try: - amdsmi_interface.amdsmi_init(init_flag) - except (amdsmi_interface.AmdSmiLibraryException, amdsmi_interface.AmdSmiParameterException) as e: - if e.err_code in (amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_NOT_INIT, - amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_DRIVER_NOT_LOADED): - logging.error("Drivers not loaded (amdgpu, amd_hsmp or hsmp_acpi drivers not found in modules)") - sys.exit(-1) - else: - raise e - elif check_amdgpu_driver(): - init_flag = amdsmi_interface.AmdSmiInitFlags.INIT_AMD_GPUS - logging.debug("amdgpu driver initstate is live") - try: - amdsmi_interface.amdsmi_init(init_flag) - except (amdsmi_interface.AmdSmiLibraryException, amdsmi_interface.AmdSmiParameterException) as e: - if e.err_code in (amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_NOT_INIT, - amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_DRIVER_NOT_LOADED): - logging.error("Driver not loaded (amdgpu not found in modules)") - sys.exit(-1) - else: - raise e - logging.debug("amdgpu driver initialized successfully, but amd_hsmp or hsmp_acpi initstate was not live") - elif check_amd_hsmp_driver(): - init_flag = amdsmi_interface.AmdSmiInitFlags.INIT_AMD_CPUS - logging.debug("amd_hsmp or hsmp_acpi driver initstate is live") - try: - amdsmi_interface.amdsmi_init(init_flag) - except (amdsmi_interface.AmdSmiLibraryException, amdsmi_interface.AmdSmiParameterException) as e: - if e.err_code in (amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_NOT_INIT, - amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_DRIVER_NOT_LOADED): - logging.error("Driver not loaded (amd_hsmp or hsmp_acpi not found in modules)") - sys.exit(-1) - else: - raise e - logging.debug("amd_hsmp or hsmp_acpi driver initialized successfully, but amdgpu initstate was not live") + init_flag = 0 + if check_amdgpu_driver(): + init_flag |= amdsmi_interface.AmdSmiInitFlags.INIT_AMD_GPUS + logging.debug("amdgpu driver's initstate is live") + if check_amd_hsmp_driver(): + init_flag |= amdsmi_interface.AmdSmiInitFlags.INIT_AMD_CPUS + logging.debug("hsmp driver's initstate is live") + if check_amd_ionic_driver(): + logging.debug("ionic driver's initstate is live") + init_flag |= amdsmi_interface.AmdSmiInitFlags.INIT_AMD_NICS + + try: + amdsmi_interface.amdsmi_init(init_flag) + except (amdsmi_interface.AmdSmiLibraryException, amdsmi_interface.AmdSmiParameterException) as e: + if e.err_code in (amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_NOT_INIT, + amdsmi_interface.amdsmi_wrapper.AMDSMI_STATUS_DRIVER_NOT_LOADED): + logging.error("Drivers not loaded (amdgpu, amd_hsmp, ionic, rdma drivers not found in modules)") + sys.exit(-1) + else: + raise e logging.debug(f"AMDSMI initialized with atleast one driver successfully | init flag: {init_flag}") diff --git a/projects/amdsmi/amdsmi_cli/amdsmi_logger.py b/projects/amdsmi/amdsmi_cli/amdsmi_logger.py index e28fbcd54a0..a9f7ff31961 100644 --- a/projects/amdsmi/amdsmi_cli/amdsmi_logger.py +++ b/projects/amdsmi/amdsmi_cli/amdsmi_logger.py @@ -46,6 +46,7 @@ def __init__(self, format='human_readable', destination='stdout', helpers=None) self.helpers = helpers self._cper_exit_message = True self.store_cpu_json_output = [] + self.store_nic_json_output = [] self.store_core_json_output = [] self.store_gpu_json_output = [] self.store_xgmi_metric_json_output = [] @@ -156,6 +157,10 @@ def _convert_json_to_tabular(self, json_object: Dict[str, any], dynamic=False): elif key == 'gpu': stored_gpu = string_value table_values += string_value.rjust(3) + elif key == 'brcm_nic': + table_values += string_value.rjust(3) + elif key == 'brcm_switch': + table_values += string_value.rjust(3) elif key == 'xcp': stored_gpu = string_value table_values += string_value.rjust(5) @@ -188,6 +193,27 @@ def _convert_json_to_tabular(self, json_object: Dict[str, any], dynamic=False): table_values += string_value.rjust(12) elif key in ('pcie_replay'): table_values += string_value.rjust(13) + #BRCM Device Metrics + #NIC + elif key == "NIC_TEMP_CURRENT": + table_values += string_value.rjust(21) + elif key == "NIC_TEMP_CRIT_ALARM": + table_values += string_value.rjust(22) + elif key == "NIC_TEMP_EMERGENCY_ALARM": + table_values += string_value.rjust(26) + elif key == "NIC_TEMP_SHUTDOWN_ALARM": + table_values += string_value.rjust(25) + elif key == "NIC_TEMP_MAX_ALARM": + table_values += string_value.rjust(20) + #SWITCH + elif key == "CURRENT_LINK_SPEED": + table_values += string_value.rjust(25) + elif key == "MAX_LINK_SPEED": + table_values += string_value.rjust(20) + elif key == "CURRENT_LINK_WIDTH": + table_values += string_value.rjust(20) + elif key == "MAX_LINK_WIDTH": + table_values += string_value.rjust(20) # Only for handling topology tables elif 'gpu_' in key: table_values += string_value.ljust(13) @@ -281,7 +307,7 @@ def _convert_json_to_human_readable(self, json_object: Dict[str, any]): # Increase tabbing for device arguments by pulling them out of the main dictionary and assiging them to an empty string tabbed_dictionary = {} for key, value in capitalized_json.items(): - if key not in ["GPU", "CPU", "CORE"]: + if key not in ["GPU", "CPU", "CORE","BRCM_NIC","BRCM_SWITCH","AI_NIC"]: tabbed_dictionary[key] = value # Filter out N/A values under clock if key == "CLOCK": @@ -419,6 +445,42 @@ def store_output(self, device_handle, argument, data): """ gpu_id = self.helpers.get_gpu_id_from_device_handle(device_handle) self._store_output_amdsmi(gpu_id=gpu_id, argument=argument, data=data) + + def store_nic_output(self, device_handle, argument, data): + """ Convert device handle to nic id and store output + params: + device_handle - device handle object to the target device output + argument (str) - key to store data + data (dict | list) - Data store against argument + return: + Nothing + """ + nic_id = self.helpers.get_nic_id_from_device_handle(device_handle) + self._store_nic_output_amdsmi(nic_id=nic_id, argument=argument, data=data) + + def store_ainic_output(self, device_handle, argument, data): + """ Convert device handle to ainic id and store output + params: + device_handle - device handle object to the target device output + argument (str) - key to store data + data (dict | list) - Data store against argument + return: + Nothing + """ + nic_id = self.helpers.get_ainic_id_from_device_handle(device_handle) + self._store_ainic_output_amdsmi(nic_id=nic_id, argument=argument, data=data) + + def store_switch_output(self, device_handle, argument, data): + """ Convert device handle to nic id and store output + params: + device_handle - device handle object to the target device output + argument (str) - key to store data + data (dict | list) - Data store against argument + return: + Nothing + """ + switch_id = self.helpers.get_switch_id_from_device_handle(device_handle) + self._store_switch_output_amdsmi(switch_id=switch_id, argument=argument, data=data) def store_cpu_output(self, device_handle, argument, data): @@ -511,7 +573,76 @@ def _store_output_amdsmi(self, gpu_id, argument, data): self.output[argument] = data else: raise ValueError("Invalid output format: expected json, csv, or human_readable") + + def _store_nic_output_amdsmi(self, nic_id, argument, data): + if argument == 'timestamp': # Make sure timestamp is the first element in the output + self.output['timestamp'] = int(time.time()) + + if self.is_json_format() or self.is_human_readable_format(): + self.output['brcm_nic'] = int(nic_id) + if argument == 'values' and isinstance(data, dict): + + self.output.update(data) + else: + + self.output[argument] = data + elif self.is_csv_format(): + self.output['brcm_nic'] = int(nic_id) + + if argument == 'values' or isinstance(data, dict): + flat_dict = self.flatten_dict(data) + self.output.update(flat_dict) + else: + self.output[argument] = data + else: + raise ValueError("Invalid output format: expected json, csv, or human_readable") + + def _store_ainic_output_amdsmi(self, nic_id, argument, data): + if argument == 'timestamp': # Make sure timestamp is the first element in the output + self.output['timestamp'] = int(time.time()) + + if self.is_json_format() or self.is_human_readable_format(): + self.output['ai_nic'] = int(nic_id) + if argument == 'values' and isinstance(data, dict): + + self.output.update(data) + else: + + self.output[argument] = data + elif self.is_csv_format(): + self.output['ai_nic'] = int(nic_id) + if argument == 'values' or isinstance(data, dict): + flat_dict = self.flatten_dict(data) + self.output.update(flat_dict) + else: + self.output[argument] = data + else: + raise ValueError("Invalid output format: expected json, csv, or human_readable") + + + def _store_switch_output_amdsmi(self, switch_id, argument, data): + if argument == 'timestamp': # Make sure timestamp is the first element in the output + self.output['timestamp'] = int(time.time()) + + if self.is_json_format() or self.is_human_readable_format(): + self.output['brcm_switch'] = int(switch_id) + if argument == 'values' and isinstance(data, dict): + + self.output.update(data) + else: + + self.output[argument] = data + elif self.is_csv_format(): + self.output['brcm_switch'] = int(switch_id) + + if argument == 'values' or isinstance(data, dict): + flat_dict = self.flatten_dict(data) + self.output.update(flat_dict) + else: + self.output[argument] = data + else: + raise ValueError("Invalid output format: expected json, csv, or human_readable") def store_multiple_device_output(self): """ Store the current output into the multiple_device_output @@ -606,6 +737,8 @@ def combine_arrays_to_json(self): combined_json = {} if self.store_cpu_json_output: combined_json["cpu_data"] = self.store_cpu_json_output + if self.store_nic_json_output: + combined_json["nic_data"] = self.store_nic_json_output if self.store_core_json_output: combined_json["core_data"] = self.store_core_json_output if self.store_gpu_json_output: diff --git a/projects/amdsmi/amdsmi_cli/amdsmi_parser.py b/projects/amdsmi/amdsmi_cli/amdsmi_parser.py index ff58601adb0..a34ebe5637d 100644 --- a/projects/amdsmi/amdsmi_cli/amdsmi_parser.py +++ b/projects/amdsmi/amdsmi_cli/amdsmi_parser.py @@ -83,9 +83,18 @@ def __init__(self, version, list, static, firmware, bad_pages, metric, # Get choices based on driver initialized if self.helpers.is_amdgpu_initialized(): self.gpu_choices, self.gpu_choices_str = self.helpers.get_gpu_choices() + self.switch_choices, self.switch_choices_str = self.helpers.get_switch_choices() else: self.gpu_choices = {} self.gpu_choices_str = "" + self.switch_choices = {} + self.switch_choices_str = "" + + if self.helpers.is_ainic_initialized() or self.helpers.is_brcm_nic_initialized(): + self.nic_choices, self.nic_choices_str = self.helpers.get_nic_choices() + else: + self.nic_choices = {} + self.nic_choices_str = "" if self.helpers.is_amd_hsmp_initialized(): self.cpu_choices, self.cpu_choices_str = self.helpers.get_cpu_choices() @@ -490,6 +499,62 @@ def __call__(self, parser, args, values, option_string=None): True, False, False) return _GPUSelectAction + + + def _nic_select(self, nic_choices): + + """ Custom argparse action to return the device handle(s) for the nics(s) selected + This will set the destination (args.nic) to a list of 1 or more device handles + If 1 or more device handles are not found then raise an ArgumentError for the first invalid nic seen + """ + + amdsmi_helpers = self.helpers + class _NICSelectAction(argparse.Action): + ouputformat=self.helpers.get_output_format() + # Checks the values + def __call__(self, parser, args, values, option_string=None): + if "all" in nic_choices: + del nic_choices["all"] + status, selected_device_handles = amdsmi_helpers.get_device_handles_from_nic_selections(nic_selections=values, + nic_choices=nic_choices) + if status: + setattr(args, self.dest, selected_device_handles) + else: + if selected_device_handles == '': + raise amdsmi_cli_exceptions.AmdSmiMissingParameterValueException("--nic", _NICSelectAction.ouputformat) + else: + raise amdsmi_cli_exceptions.AmdSmiDeviceNotFoundException(selected_device_handles, _NICSelectAction.ouputformat) + + + return _NICSelectAction + + + def _switch_select(self, switch_choices): + + """ Custom argparse action to return the device handle(s) for the switchs(s) selected + This will set the destination (args.switch) to a list of 1 or more device handles + If 1 or more device handles are not found then raise an ArgumentError for the first invalid switch seen + """ + + amdsmi_helpers = self.helpers + class _switchSelectAction(argparse.Action): + ouputformat=self.helpers.get_output_format() + # Checks the values + def __call__(self, parser, args, values, option_string=None): + if "all" in switch_choices: + del switch_choices["all"] + status, selected_device_handles = amdsmi_helpers.get_device_handles_from_switch_selections(switch_selections=values, + switch_choices=switch_choices) + if status: + setattr(args, self.dest, selected_device_handles) + else: + if selected_device_handles == '': + raise amdsmi_cli_exceptions.AmdSmiMissingParameterValueException("--switch", _switchSelectAction.ouputformat) + else: + raise amdsmi_cli_exceptions.AmdSmiDeviceNotFoundException(selected_device_handles, _switchSelectAction.ouputformat) + + + return _switchSelectAction def _cpu_select(self, cpu_choices): @@ -754,6 +819,7 @@ def _add_device_arguments(self, subcommand_parser: argparse.ArgumentParser, requ vf_help = "Gets general information about the specified VF (timeslice, fb info, …).\ \nAvailable only on virtualization OSs" cpu_help = f"Select a CPU ID from the possible choices:\n{self.cpu_choices_str}" + nic_help = f"Select a NIC ID from the possible choices:\n{self.nic_choices_str}" core_help = f"Select a Core ID from the possible choices:\n{self.core_choices_str}" # Create argument group for all the devices @@ -779,7 +845,38 @@ def _add_device_arguments(self, subcommand_parser: argparse.ArgumentParser, requ device_args.add_argument('-v', '--vf', action='store', nargs='+', help=vf_help, choices=self.vf_choices) + if self.helpers.is_ainic_initialized() or self.helpers.is_brcm_nic_initialized(): + nic_help = f"Select a NIC ID, BDF, or UUID from the possible choices:\n{self.nic_choices_str}" + device_args.add_argument('-N', '--nic', action=self._nic_select(self.nic_choices), + nargs='+', help=nic_help) + if self.helpers.is_brcm_switch_initialized(): + switch_help = f"Select a SWITCH ID, BDF, or UUID from the possible choices:\n{self.switch_choices_str}" + device_args.add_argument('-bs', '--switch', action=self._switch_select(self.switch_choices), + nargs='+', help=switch_help) + + def _add_brcm_nic_device_arguments(self, subcommand_parser: argparse.ArgumentParser, nicMandatory=False, required=False): + # Device arguments help text + nic_help = f"Select a NIC ID, BDF, or UUID from the possible choices:\n{self.nic_choices_str}" + if nicMandatory: + nic_help = f"Select a NIC ID, BDF, or UUID from the possible choices:\n {self.nic_choices_str} Note: -nic, --brcm_nic is mandatory argument for this option.\n" + # Mutually Exclusive Args within the subparser + device_args = subcommand_parser.add_mutually_exclusive_group(required=required) + + device_args.add_argument('-N', '--nic', action=self._nic_select(self.nic_choices), + nargs='+', help=nic_help) + + def _add_brcm_switch_device_arguments(self, subcommand_parser: argparse.ArgumentParser, switchMandatory=False, required=False): + # Device arguments help text + switch_help = f"Select a SWITCH ID, BDF, or UUID from the possible choices:\n{self.switch_choices_str}" + if switchMandatory: + switch_help = f"Select a SWITCH ID, BDF, or UUID from the possible choices:\n{self.switch_choices_str} Note: -switch, --brcm_switch is mandatory argument for this option.\n" + + # Mutually Exclusive Args within the subparser + device_args = subcommand_parser.add_mutually_exclusive_group(required=required) + device_args.add_argument('-bs', '--switch', action=self._switch_select(self.switch_choices), + nargs='+', help=switch_help) + def _add_command_modifiers(self, subcommand_parser: argparse.ArgumentParser): json_help = "Displays output in JSON format" csv_help = "Displays output in CSV format" @@ -854,10 +951,12 @@ def _add_version_parser(self, subparsers: argparse._SubParsersAction, func): # help info gpu_version_help = "Display the current amdgpu driver version" cpu_version_help = "Display the current amd_hsmp or hsmp_acpi driver version" + nic_version_help = "Display the current nic driver version" # Add GPU and CPU version Arguments version_parser.add_argument('-g', '--gpu_version', action='store_true', required=False, help=gpu_version_help, default=None) version_parser.add_argument('-c', '--cpu_version', action='store_true', required=False, help=cpu_version_help, default=None) + version_parser.add_argument('-n', '--nic_version', action='store_true', required=False, help=nic_version_help, default=None) def _add_list_parser(self, subparsers: argparse._SubParsersAction, func): @@ -993,6 +1092,7 @@ def _add_firmware_parser(self, subparsers: argparse._SubParsersAction, func): # Optional arguments help text fw_list_help = "All FW list information" + nic_firmware_help = "BRCM NIC devices's Firmware attributes" err_records_help = "All error records information" # Create firmware subparser @@ -1003,6 +1103,8 @@ def _add_firmware_parser(self, subparsers: argparse._SubParsersAction, func): # Optional Args firmware_parser.add_argument('-f', '--ucode-list', '--fw-list', dest='fw_list', action='store_true', required=False, help=fw_list_help, default=True) + if self.helpers.is_brcm_nic_initialized(): + firmware_parser.add_argument('-nic', '--brcm_nic', action='store_true', required=False, help=nic_firmware_help) # Options to only display on a Hypervisor if self.helpers.is_hypervisor(): @@ -1010,6 +1112,8 @@ def _add_firmware_parser(self, subparsers: argparse._SubParsersAction, func): # Add Universal Arguments self._add_device_arguments(firmware_parser, required=False) + if self.helpers.is_brcm_nic_initialized(): + self._add_brcm_nic_device_arguments(firmware_parser, nicMandatory=True, required=False) self._add_command_modifiers(firmware_parser) @@ -1062,6 +1166,8 @@ def _add_metric_parser(self, subparsers: argparse._SubParsersAction, func): # Help text for Arguments only Available on Linux Virtual OS and Baremetal platforms mem_usage_help = "Memory usage per block" + nic_metric_help = "Broadcom NIC's metrics attributes" + switch_metric_help = "Broadcom SWITCH's metrics attributes" # Help text for Arguments only on Hypervisor and Baremetal platforms power_help = "Current power usage" @@ -1316,6 +1422,8 @@ def _add_topology_parser(self, subparsers: argparse._SubParsersAction, func): atomics_help = "Display 32 and 64-bit atomic io link capability between nodes" dma_help = "Display P2P direct memory access (DMA) link capability between nodes" bi_dir_help = "Display P2P bi-directional link capability between nodes" + nic_topo_help = "Display nic and gpu connectivity" + nic_shownuma_help = "Display nic,gpu's numa and cpu affinity" # Create topology subparser topology_parser = subparsers.add_parser('topology', help=topology_help, description=topology_subcommand_help) @@ -1337,6 +1445,10 @@ def _add_topology_parser(self, subparsers: argparse._SubParsersAction, func): topology_parser.add_argument('-n', '--atomics', action='store_true', required=False, help=atomics_help) topology_parser.add_argument('-d', '--dma', action='store_true', required=False, help=dma_help) topology_parser.add_argument('-z', '--bi-dir', action='store_true', required=False, help=bi_dir_help) + if self.helpers.is_brcm_nic_initialized(): + topology_parser.add_argument('-nic', '--nic_topo', action='store_true', required=False, help=nic_topo_help) + if self.helpers.is_brcm_switch_initialized(): + topology_parser.add_argument('-nic_switch', '--nic_switch', action='store_true', required=False, help=nic_shownuma_help) def _add_set_value_parser(self, subparsers: argparse._SubParsersAction, func): @@ -1537,6 +1649,8 @@ def _add_monitor_parser(self, subparsers: argparse._SubParsersAction, func): ecc_help = "Monitor ECC single bit, ECC double bit, and PCIe replay error counts" mem_usage_help = "Monitor memory usage in MB" pcie_bandwidth_help = "Monitor PCIe bandwidth in Mb/s" + nic_monitor_help = "BRCM NIC devices's Monitor attributes" + switch_monitor_help = "BRCM Switch devices's Monitor attributes" process_help = "Enable Process information table below monitor output;\n Process Name may require elevated permissions" violation_help = "Monitor power and thermal violation status (%%);\n Only available for MI300 or newer ASICs" @@ -1559,12 +1673,18 @@ def _add_monitor_parser(self, subparsers: argparse._SubParsersAction, func): monitor_parser.add_argument('-v', '--vram-usage', action='store_true', required=False, help=mem_usage_help) monitor_parser.add_argument('-r', '--pcie', action='store_true', required=False, help=pcie_bandwidth_help) monitor_parser.add_argument('-q', '--process', action='store_true', required=False, help=process_help) + monitor_parser.add_argument('-nic', '--brcm_nic', action='store_true', required=False, help=nic_monitor_help) + monitor_parser.add_argument('-switch', '--brcm_switch', action='store_true', required=False, help=switch_monitor_help) if not self.helpers.is_virtual_os(): monitor_parser.add_argument('-V', '--violation', action='store_true', required=False, help=violation_help) # Add Universal Arguments & Watch Args self._add_watch_arguments(monitor_parser) self._add_device_arguments(monitor_parser, required=False) + if self.helpers.is_brcm_nic_initialized(): + self._add_brcm_nic_device_arguments(monitor_parser, nicMandatory=True, required=False) + if self.helpers.is_brcm_switch_initialized(): + self._add_brcm_switch_device_arguments(monitor_parser, switchMandatory=True, required=False) self._add_command_modifiers(monitor_parser) @@ -1689,8 +1809,11 @@ def _add_ras_parser(self, subparsers: argparse._SubParsersAction, func): self._add_device_arguments(ras_parser, required=False) self._add_command_modifiers(ras_parser) - def _add_node_parser(self, subparsers: argparse._SubParsersAction, func): + if self.helpers.is_virtual_os(): + # This subparser is not available to Guest and Hypervisor systems + return + # Subparser help text node_help = "Gets power and baseboard information for the node" node_subcommand_help = f"{self.description}\n\nReturns information for node 0 on the system.\ diff --git a/projects/amdsmi/example/CMakeLists.txt b/projects/amdsmi/example/CMakeLists.txt index 876e276f531..9dcb003e226 100644 --- a/projects/amdsmi/example/CMakeLists.txt +++ b/projects/amdsmi/example/CMakeLists.txt @@ -50,10 +50,12 @@ message("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&") set(SMI_DRM_EXAMPLE_EXE "amd_smi_drm_ex") add_executable(${SMI_DRM_EXAMPLE_EXE} "amd_smi_drm_example.cc") target_link_libraries(${SMI_DRM_EXAMPLE_EXE} amd_smi) +target_compile_definitions(${SMI_DRM_EXAMPLE_EXE} PUBLIC ENABLE_ESMI_LIB) set(SMI_NODRM_EXAMPLE_EXE "amd_smi_nodrm_ex") add_executable(${SMI_NODRM_EXAMPLE_EXE} "amd_smi_nodrm_example.cc") target_link_libraries(${SMI_NODRM_EXAMPLE_EXE} amd_smi) +target_compile_definitions(${SMI_NODRM_EXAMPLE_EXE} PUBLIC ENABLE_ESMI_LIB) if(ENABLE_ESMI_LIB) set(ESMI_SAMPLE_EXE "amd_smi_esmi_ex") @@ -61,3 +63,11 @@ if(ENABLE_ESMI_LIB) target_link_libraries(${ESMI_SAMPLE_EXE} amd_smi) target_compile_definitions(${ESMI_SAMPLE_EXE} PUBLIC ENABLE_ESMI_LIB) endif() + + +add_executable(ainic amd_smi_nic.cc) +target_compile_definitions(ainic PRIVATE ENABLE_ESMI_LIB) +target_include_directories(ainic PRIVATE /opt/rocm/include /opt/rocm/include/amd_smi/impl/nic) +target_link_directories(ainic PRIVATE /opt/rocm/lib) +target_link_libraries(ainic PRIVATE amd_smi) + diff --git a/projects/amdsmi/example/amd_smi_drm_example.cc b/projects/amdsmi/example/amd_smi_drm_example.cc index 1672cd38381..3019b4e597a 100644 --- a/projects/amdsmi/example/amd_smi_drm_example.cc +++ b/projects/amdsmi/example/amd_smi_drm_example.cc @@ -285,7 +285,9 @@ static const std::map {AMDSMI_PROCESSOR_TYPE_NON_AMD_GPU, "NON_AMD_GPU"}, {AMDSMI_PROCESSOR_TYPE_NON_AMD_CPU, "NON_AMD_CPU"}, {AMDSMI_PROCESSOR_TYPE_AMD_CPU_CORE, "AMD_CPU_CORE"}, - {AMDSMI_PROCESSOR_TYPE_AMD_APU, "AMD_APU"} + {AMDSMI_PROCESSOR_TYPE_AMD_NIC, "AMD_AINIC"}, + {AMDSMI_PROCESSOR_TYPE_BRCM_NIC, "BRCM_NIC,"}, + {AMDSMI_PROCESSOR_TYPE_BRCM_SWITCH, "BRCM_SWITCH"} }; static const std::map diff --git a/projects/amdsmi/example/amd_smi_nic.cc b/projects/amdsmi/example/amd_smi_nic.cc new file mode 100644 index 00000000000..6fde6ca2dbd --- /dev/null +++ b/projects/amdsmi/example/amd_smi_nic.cc @@ -0,0 +1,353 @@ +/* + * Copyright (c) Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace { +struct RAII { + RAII(std::function init, std::function finish) + : _finish(finish) { + init(); + } + ~RAII() { + _finish(); + } + std::function _finish; +}; + + +void dump_ainic_info(int idx, const amd::smi::AMDSmiAINICDevice::AINICInfo &ainic_info) { +#if 0//Expected Output: +NIC: 0 + ASIC: + VENDOR_ID: 0x1dd8 + SUBVENDOR_ID: 0x1dd8 + DEVICE_ID: 0x8 + SUBSYSTEM_ID: 0x5201 + REVISION: 0x0 + PERMANENT_ADDRESS: 04:90:81:01:09:38 + PRODUCT_NAME: ,Pensando DSC2-200 50/100/200G 2p QSFP56 Card + PART_NUMBER: DSC2-2Q200-32R32F64P-R4 + SERIAL_NUMBER: FPF2316002EEC0V2 + VENDOR_NAME: AMD Pensando Systems, Inc. + BUS: + BDF: 0000:e2:00.0 + MAX_PCIE_WIDTH: 16 + MAX_PCIE_SPEED: 32 + PCIE_INTERFACE_VERSION: N/A + SLOT_TYPE: N/A + DRIVER: + NAME: ionic + VERSION: 25.08.2.001 + FW_VERSION: 1.97.0-A-5 + BUS_INFO: 0000:83:00.0 + NUMA: + NODE: 1 + AFFINITY: 16-31,48-63 + VERSIONS: + RUNNING: + fw: ?? + fw.heartbeat: ?? + fw.status: ?? + PORTS: + PORT_0: + BDF: 0 + TYPE: Ethernet + FLAVOUR: N/A + NETDEV: enp131s0 + IFINDEX: 11 + MAC_ADDRESS: 04:90:81:01:09:38 + CARRIER: 0 + MTU: 1500B + LINK_STATE: down + LINK_SPEED: 0 Mb/s + ACTIVE_FEC: Off + AUTONEG: off + PAUSE_AUTONEG: off + PAUSE_RX: on + PAUSE_TX: on + PORT_1: + BDF: 0 + TYPE: Ethernet + FLAVOUR: N/A + NETDEV: enp132s0 + IFINDEX: 12 + MAC_ADDRESS: 04:90:81:01:09:39 + CARRIER: 0 + MTU: 1500B + LINK_STATE: down + LINK_SPEED: 0 Mb/s + ACTIVE_FEC: Off + AUTONEG: off + PAUSE_AUTONEG: off + PAUSE_RX: on + PAUSE_TX: on + PORT_2: + BDF: 0 + TYPE: Ethernet + FLAVOUR: N/A + NETDEV: enp229s0 + IFINDEX: 14 + MAC_ADDRESS: 04:90:81:2c:77:b0 + CARRIER: 0 + MTU: 1500B + LINK_STATE: down + LINK_SPEED: 0 Mb/s + ACTIVE_FEC: Off + AUTONEG: off + PAUSE_AUTONEG: off + PAUSE_RX: off + PAUSE_TX: off + RDMA_DEVICES: + RDMA_DEVICES: + RDMA_DEVICE_0: + NAME: rocep131s0 + NODE_GUID: 0690:81ff:fe01:0938 + NODE_TYPE: 1: CA + SYS_IMAGE_GUID: 0690:81ff:fe01:0938 + FW_VER: 1.97.0-A-5 + PORTS: + PORT_0: + NETDEV: + PORT_NUM: 1 + STATE: DOWN + MAX_MTU: 65535 + ACTIVE_MTU: 65535 + RDMA_DEVICES: + RDMA_DEVICE_0: + NAME: rocep132s0 + NODE_GUID: 0690:81ff:fe01:0939 + NODE_TYPE: 1: CA + SYS_IMAGE_GUID: 0690:81ff:fe01:0939 + FW_VER: 1.97.0-A-5 + PORTS: + PORT_0: + NETDEV: + PORT_NUM: 1 + STATE: DOWN + MAX_MTU: 65535 + ACTIVE_MTU: 65535 + RDMA_DEVICES: + RDMA_DEVICE_0: + NAME: rocep229s0 + NODE_GUID: 0690:81ff:fe2c:77b0 + NODE_TYPE: 1: CA + SYS_IMAGE_GUID: 0690:81ff:fe2c:77b0 + FW_VER: 1.110.1-a-1 + PORTS: + PORT_0: + NETDEV: + PORT_NUM: 1 + STATE: DOWN + MAX_MTU: 65535 + ACTIVE_MTU: 65535 +#endif//0 + std::ostringstream oss; + oss << std::hex << + std::setw(4) << std::setfill('0') << ainic_info.bus.bdf.domain_number << ":" << + std::setw(2) << std::setfill('0') << ainic_info.bus.bdf.bus_number << ":" << + std::setw(2) << std::setfill('0') << ainic_info.bus.bdf.device_number << "." << + std::setw(2) << std::setfill('0') << ainic_info.bus.bdf.function_number; + std::string bdf_str = oss.str(); + oss.str(""); + oss << "===============================================\n" << + "NIC: " << idx << "\n" << + " ASIC:" << "\n" << + " VENDOR_ID: 0x" << std::hex << ainic_info.asic.vendor_id << std::dec << "\n" << + " SUBVENDOR_ID: 0x" << std::hex << ainic_info.asic.subvendor_id << std::dec << "\n" << + " DEVICE_ID: 0x" << std::hex << ainic_info.asic.device_id << std::dec << "\n" << + " SUBSYSTEM_ID: 0x" << std::hex << ainic_info.asic.subsystem_id << std::dec << "\n" << + " REVISION: 0x" << std::hex << static_cast(ainic_info.asic.revision) << std::dec << "\n" << + " PERMANENT_ADDRESS: " << ainic_info.asic.permanent_address << "\n" << + " PRODUCT_NAME: " << ainic_info.asic.product_name << "\n" << + " PART_NUMBER: " << ainic_info.asic.part_number << "\n" << + " SERIAL_NUMBER: " << ainic_info.asic.serial_number << "\n" << + " VENDOR_NAME: " << ainic_info.asic.vendor_name << "\n" << + " BUS:" << "\n" << + " BDF: " << bdf_str << "\n" << + " MAX_PCIE_WIDTH: " << static_cast(ainic_info.bus.max_pcie_width) << "\n" << + " MAX_PCIE_SPEED: " << ainic_info.bus.max_pcie_speed << "\n" << + " PCIE_INTERFACE_VERSION: " << ainic_info.bus.pcie_interface_version << "\n" << + " SLOT_TYPE: " << ainic_info.bus.slot_type << "\n" << + " DRIVER:" << "\n" << + " NAME: " << ainic_info.driver.name << "\n" << + " VERSION: " << ainic_info.driver.version << "\n" << + // " FW_VERSION: " << ainic_info.driver.version << "\n" << + // " BUS_INFO: " << ainic_info.driver.bus_info << "\n" << + " NUMA:" << "\n" << + " NODE: " << static_cast(ainic_info.numa.node) << "\n" << + " AFFINITY: " << ainic_info.numa.affinity << "\n" << + // " LIMIT:" << "\n" << //can be fetched through https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface or https://github.com/lm-sensors/lm-sensors + // " MAX_POWER: ?? W" << "\n" << + // " MAX_TEMPERATURE: ?? C" << "\n" << + // " VERSIONS:" << "\n" << //these can be obtained only through netlink interface which we currently don't have. It requires 3rd party library dependency or manually netlink hdr/msg construction which is hard to write and maintain/test. So, netlink interface implementation is postponed. + // " RUNNING:" << "\n" << + // " fw: ??" << ainic_info.versions.running.fw << "\n" << + // " fw.heartbeat: ??" << ainic_info.versions.running.fw_heartbeat << "\n" << + // " fw.status: ??" << ainic_info.versions.running.fw_status << "\n" + " PORTS:" << "\n"; + for(int port_idx = 0; port_idx < ainic_info.port.num_ports; ++port_idx) { + oss << + " PORT_" << static_cast(port_idx) << ":\n" << + " BDF: " << 0 << "\n" << + " TYPE: " << ainic_info.port.ports[port_idx].type << "\n" << + " FLAVOUR: " << ainic_info.port.ports[port_idx].flavour << "\n" << + " NETDEV: " << ainic_info.port.ports[port_idx].netdev << "\n" << + " IFINDEX: " << static_cast(ainic_info.port.ports[port_idx].ifindex) << "\n" << + " MAC_ADDRESS: " << ainic_info.port.ports[port_idx].mac_address << "\n" << + " CARRIER: " << static_cast(ainic_info.port.ports[port_idx].carrier) << "\n" << + " MTU: " << ainic_info.port.ports[port_idx].mtu << "B\n" << + " LINK_STATE: " << ainic_info.port.ports[port_idx].link_state << "\n" << + " LINK_SPEED: " << ainic_info.port.ports[port_idx].link_speed << " Mb/s\n" << + " ACTIVE_FEC: " << ainic_info.port.ports[port_idx].active_fec << "\n" << + " AUTONEG: " << ainic_info.port.ports[port_idx].autoneg << "\n" << + " PAUSE_AUTONEG: " << ainic_info.port.ports[port_idx].pause_autoneg << "\n" << + " PAUSE_RX: " << ainic_info.port.ports[port_idx].pause_rx << "\n" << + " PAUSE_TX: " << ainic_info.port.ports[port_idx].pause_tx << "\n"; + }//port + oss << + " RDMA_DEVICES: " << "\n"; + int rdma_dev_idx = 0; + for(int port_idx = 0; port_idx < ainic_info.port.num_ports; ++port_idx) { + oss << + " RDMA_DEVICES: " << "\n"; + for(uint8_t rdma_dev_idx = 0; rdma_dev_idx < ainic_info.rdma_dev.num_rdma_dev; ++rdma_dev_idx) { + oss << + " RDMA_DEVICE_" << static_cast(rdma_dev_idx) << ":\n" << + " NAME: " <(rdma_port_idx) << ":\n" << + " NETDEV: " <(ainic_info.port.ports[port_idx].rdma_dev[rdma_dev_idx].rdma_port_info[rdma_port_idx].port_num) << "\n" << + " STATE: " <(ainic_info.port.ports[port_idx].rdma_dev[rdma_dev_idx].rdma_port_info[rdma_port_idx].max_mtu) << "\n" << + // " ACTIVE_MTU: " << static_cast(ainic_info.port.ports[port_idx].rdma_dev[rdma_dev_idx].rdma_port_info[rdma_port_idx].active_mtu) << + "\n"; + } + }//num_infiniband + + } + std::cout << oss.str(); +} + +std::optional> get_nics() { + + auto &amdsmi = amd::smi::AMDSmiSystem::getInstance(); + uint32_t soc_count = 10; + std::vector sockets(soc_count); + // Get the sockets of the system + amdsmi_status_t status = amdsmi_get_socket_handles(&soc_count, &sockets[0]); + if (status != AMDSMI_STATUS_SUCCESS){ + return std::nullopt; + } + std::cout << "Got " << soc_count << " socket(s)\n"; + + std::vector nics; + for (uint32_t index = 0 ; index < soc_count; index++){ + uint32_t processor_count = 0; + status = amdsmi_get_processor_handles_by_type( + sockets[index], + AMDSMI_PROCESSOR_TYPE_AMD_NIC, + nullptr, &processor_count); + if (status != AMDSMI_STATUS_SUCCESS){ + return std::nullopt; + } + std::cout << "Got " << processor_count << " processors for socket " << index << ":\n"; + std::vector processor_handles(processor_count); + status = amdsmi_get_processor_handles_by_type( + sockets[index], + AMDSMI_PROCESSOR_TYPE_AMD_NIC, + processor_handles.data(), &processor_count); + if (status != AMDSMI_STATUS_SUCCESS){ + return std::nullopt; + } + + for(uint32_t idx = 0; idx < processor_count; ++idx){ + amd::smi::AMDSmiAINICDevice::AINICInfo ainic_info = {0}; + amdsmi_status_t status = amdsmi_get_ainic_info(processor_handles[idx], &ainic_info); + if(status == AMDSMI_STATUS_SUCCESS){ + dump_ainic_info(idx, ainic_info); + nics.emplace_back(ainic_info); + } + } + } + return nics; +} + +std::map port_stats() { + amdsmi_processor_handle processor_handle = nullptr; + amdsmi_status_t status = smi_amdgpu_get_ainic_processor_handle_by_index(0, &processor_handle); + + uint32_t rdma_port_index = 0; + uint32_t num_stats = 0; + std::unique_ptr stats; + status = amdsmi_get_nic_rdma_port_statistics( + processor_handle, + rdma_port_index, + &num_stats, + nullptr); + if(status != AMDSMI_STATUS_SUCCESS) { + return {}; + } + + std::cout << "[" << __FILE__ << ":" << __LINE__ << "]\nnum_stats: " << num_stats << "\n"; + stats = std::make_unique(num_stats); + status = amdsmi_get_nic_rdma_port_statistics( + processor_handle, + rdma_port_index, + &num_stats, + stats.get()); + if(status != AMDSMI_STATUS_SUCCESS) { + return {}; + } + + std::map values; + for(uint32_t idx = 0; idx < num_stats; ++idx) { + std::cout << "Stat " << idx << ": " << stats[idx].name << " = " << stats[idx].value << std::endl; + values[stats[idx].name] = stats[idx].value; + } + return values; +} +} + +int main(int argc, char *argv[]) { + RAII _([]() {amdsmi_init(AMDSMI_INIT_AMD_NICS);}, []() { amdsmi_shut_down(); }); + auto nics = get_nics(); + for(const auto& [key, value]: port_stats()) { + std::cout << key << ":" << value << "\n"; + } + + return 0; +} \ No newline at end of file diff --git a/projects/amdsmi/goamdsmi_shim/smiwrapper/amdsmi_go_shim.c b/projects/amdsmi/goamdsmi_shim/smiwrapper/amdsmi_go_shim.c index 543a0dd4326..386b67f505d 100644 --- a/projects/amdsmi/goamdsmi_shim/smiwrapper/amdsmi_go_shim.c +++ b/projects/amdsmi/goamdsmi_shim/smiwrapper/amdsmi_go_shim.c @@ -121,7 +121,7 @@ goamdsmi_status_t go_shim_amdsmiapu_init(goamdsmi_Init_t goamdsmi_Init) { if (enable_debug_level(GOAMDSMI_DEBUG_LEVEL_2)) {printf("AMDSMI, Status, Identified APU machine and going to enumurate APU\n");} - if( (AMDSMI_STATUS_SUCCESS == amdsmi_init(AMDSMI_INIT_AMD_APUS)) && + if( (AMDSMI_STATUS_SUCCESS == amdsmi_init(AMDSMI_INIT_AMD_GPUS|AMDSMI_INIT_AMD_CPUS)) && (AMDSMI_STATUS_SUCCESS == amdsmi_get_socket_handles(&num_apuSockets, nullptr)) && (AMDSMI_STATUS_SUCCESS == amdsmi_get_socket_handles(&num_apuSockets, &amdsmi_apusocket_handle_all_socket[0])) && (GOAMDSMI_VALUE_0 != num_apuSockets)) diff --git a/projects/amdsmi/include/amd_smi/amdsmi.h b/projects/amdsmi/include/amd_smi/amdsmi.h index 2953734253a..6fc9c8f594b 100644 --- a/projects/amdsmi/include/amd_smi/amdsmi.h +++ b/projects/amdsmi/include/amd_smi/amdsmi.h @@ -51,8 +51,9 @@ typedef enum { AMDSMI_INIT_AMD_GPUS = (1 << 1), //!< Initialize AMD GPUS AMDSMI_INIT_NON_AMD_CPUS = (1 << 2), //!< Initialize Non-AMD CPUS AMDSMI_INIT_NON_AMD_GPUS = (1 << 3), //!< Initialize Non-AMD GPUS - AMDSMI_INIT_AMD_APUS = (AMDSMI_INIT_AMD_CPUS | AMDSMI_INIT_AMD_GPUS) /**< Initialize AMD CPUS and GPUS + AMDSMI_INIT_AMD_APUS = (AMDSMI_INIT_AMD_CPUS | AMDSMI_INIT_AMD_GPUS), /**< Initialize AMD CPUS and GPUS (Default option) */ + AMDSMI_INIT_AMD_NICS = (1 << 4) //!< Initialize NIC's } amdsmi_init_flags_t; /** @@ -309,7 +310,9 @@ typedef enum { AMDSMI_PROCESSOR_TYPE_NON_AMD_CPU, //!< Non-AMD CPU processor type AMDSMI_PROCESSOR_TYPE_AMD_CPU_CORE, //!< AMD CPU-Core processor type, individual processing units within the CPU AMDSMI_PROCESSOR_TYPE_AMD_APU, //!< AMD Accelerated processor type, GPU and CPU on a single die - AMDSMI_PROCESSOR_TYPE_AMD_NIC //!< AMD Network Interface Card processor type + AMDSMI_PROCESSOR_TYPE_AMD_NIC, //!< AMD Network Interface Card processor type + AMDSMI_PROCESSOR_TYPE_BRCM_NIC, //!< Broadcom Network Interface Card type + AMDSMI_PROCESSOR_TYPE_BRCM_SWITCH //!< Broadcomm Switch type } processor_type_t; /** @@ -2479,6 +2482,200 @@ typedef struct { uint32_t cores_per_socket; } amdsmi_sock_info_t; +/** + * @brief Maximum size definitions AMDSMI NIC + * + * @cond @tag{gpu_bm_linux} @tag{host} @endcond + */ +#define AMDSMI_MAX_NIC_PORTS 32 //!< Maximum number of NIC ports +#define AMDSMI_MAX_NIC_RDMA_DEV 32 //!< Maximum number of NIC RDMA devices +#define AMDSMI_MAX_NIC_FW 16 //!< Maximum number of NIC firmwares + +/** + * @brief NIC Link Types. This enum is used to identify the link type between + * NIC and GPU processors based on their PCIe and NUMA connectivity. + * + * @cond @tag{gpu_bm_linux} @tag{host} @endcond + */ +typedef enum { + AMDSMI_NIC_LINK_TYPE_UNKNOWN, //!< unknown type. + AMDSMI_NIC_LINK_TYPE_PCIE, //!< two processors connect via same PCIe + AMDSMI_NIC_LINK_TYPE_NUMA, //!< two processors connect via different PCIe switches but on the same CPU + AMDSMI_NIC_LINK_TYPE_X_NUMA, //!< two processors connect via different PCIe switches but on different CPUs + } amdsmi_nic_link_type_t; + +/** + * @brief Structure for NIC statistic name-value pairs + * + * @cond @tag{gpu_bm_linux} @tag{host} @endcond + * + * This structure represents a single NIC statistic with its name and value. + */ +typedef struct { + char name[AMDSMI_MAX_STRING_LENGTH]; + uint64_t value; +} amdsmi_nic_stat_t; + +/** + * @brief NIC asic information + * + * @cond @tag{gpu_bm_linux} @tag{host} @endcond + */ +typedef struct { + uint16_t vendor_id; + uint16_t subvendor_id; + uint16_t device_id; + uint16_t subsystem_id; + uint8_t revision; + char permanent_address[AMDSMI_MAX_STRING_LENGTH]; + char product_name[AMDSMI_MAX_STRING_LENGTH]; + char part_number[AMDSMI_MAX_STRING_LENGTH]; + char serial_number[AMDSMI_MAX_STRING_LENGTH]; + char vendor_name[AMDSMI_MAX_STRING_LENGTH]; +} amdsmi_nic_asic_info_t; + +/** + * @brief NIC bus information + * + * @cond @tag{gpu_bm_linux} @tag{host} @endcond + */ +typedef struct { + amdsmi_bdf_t bdf; + uint8_t max_pcie_width; + uint32_t max_pcie_speed; //!< maximum PCIe speed in GT/s + char pcie_interface_version[AMDSMI_MAX_STRING_LENGTH]; + char slot_type[AMDSMI_MAX_STRING_LENGTH]; +} amdsmi_nic_bus_info_t; + +/** + * @brief NIC NUMA information + * + * @cond @tag{gpu_bm_linux} @tag{host} @endcond + */ +typedef struct { + uint8_t node; + char affinity[AMDSMI_MAX_STRING_LENGTH]; +} amdsmi_nic_numa_info_t; + +/** + * @brief NIC firmware information + * + * @cond @tag{gpu_bm_linux} @tag{host} @endcond + */ +typedef struct { + char name[AMDSMI_MAX_STRING_LENGTH]; + char version[AMDSMI_MAX_STRING_LENGTH]; +} amdsmi_nic_fw_t; + +/** + * @brief NIC firmware information collection + * + * @cond @tag{gpu_bm_linux} @tag{host} @endcond + */ +typedef struct { + uint32_t num_fw; + amdsmi_nic_fw_t fw[AMDSMI_MAX_NIC_FW]; +} amdsmi_nic_fw_info_t; + +/** + * @brief NIC port information + * + * Active FEC Modes: + * The active_fec field provides a bitmask representation of Active FEC (Active Forward Error Correction) modes. + * The bitmask values are derived from the `ethtool_fecparam` structure, specifically + * the `active_fec` field. Below are examples of the defined FEC modes: + * + * Examples: + * - ETHTOOL_FEC_NONE (0x01) + * - ETHTOOL_FEC_AUTO (0x02) + * - ETHTOOL_FEC_RS (0x04) + * - ETHTOOL_FEC_BASER (0x08) + * - ETHTOOL_FEC_LLRS (0x10) + * - ETHTOOL_FEC_OFF (0x20) + * + * Note: These definitions are based on the latest available ethtool information. Users should + * verify if there are any updates or changes to these definitions in the relevant ethtool + * structure or field before implementing them in their code. + * + * @cond @tag{gpu_bm_linux} @tag{host} @endcond + */ +typedef struct { + amdsmi_bdf_t bdf; + uint32_t port_num; + char type[AMDSMI_MAX_STRING_LENGTH]; + char flavour[AMDSMI_MAX_STRING_LENGTH]; + char netdev[AMDSMI_MAX_STRING_LENGTH]; + uint8_t ifindex; + char mac_address[AMDSMI_MAX_STRING_LENGTH]; + uint8_t carrier; + uint16_t mtu; + char link_state[AMDSMI_MAX_STRING_LENGTH]; + uint32_t link_speed; + uint32_t active_fec; //!< Active FEC modes bitmask (see about FEC modes in the description) + char autoneg[AMDSMI_MAX_STRING_LENGTH]; + char pause_autoneg[AMDSMI_MAX_STRING_LENGTH]; + char pause_rx[AMDSMI_MAX_STRING_LENGTH]; + char pause_tx[AMDSMI_MAX_STRING_LENGTH]; +} amdsmi_nic_port_t; + +/** + * @brief NIC port information collection + * + * @cond @tag{gpu_bm_linux} @tag{host} @endcond + */ +typedef struct { + uint32_t num_ports; + amdsmi_nic_port_t ports[AMDSMI_MAX_NIC_PORTS]; +} amdsmi_nic_port_info_t; + +/** + * @brief NIC driver information + * + * @cond @tag{gpu_bm_linux} @tag{host} @endcond + */ +typedef struct { + char name[AMDSMI_MAX_STRING_LENGTH]; + char version[AMDSMI_MAX_STRING_LENGTH]; +} amdsmi_nic_driver_info_t; + +/** + * @brief NIC RDMA port information + * + * @cond @tag{gpu_bm_linux} @tag{host} @endcond + */ +typedef struct { + char netdev[AMDSMI_MAX_STRING_LENGTH]; + char state[AMDSMI_MAX_STRING_LENGTH]; + uint8_t rdma_port; + uint16_t max_mtu; + uint16_t active_mtu; +} amdsmi_nic_rdma_port_info_t; + +/** + * @brief NIC RDMA device information + * + * @cond @tag{gpu_bm_linux} @tag{host} @endcond + */ +typedef struct { + char rdma_dev[AMDSMI_MAX_STRING_LENGTH]; + char node_guid[AMDSMI_MAX_STRING_LENGTH]; + char node_type[AMDSMI_MAX_STRING_LENGTH]; + char sys_image_guid[AMDSMI_MAX_STRING_LENGTH]; + char fw_ver[AMDSMI_MAX_STRING_LENGTH]; + uint8_t num_rdma_ports; + amdsmi_nic_rdma_port_info_t rdma_port_info[AMDSMI_MAX_NIC_PORTS]; +} amdsmi_nic_rdma_dev_info_t; + +/** + * @brief NIC RDMA devices information collection + * + * @cond @tag{gpu_bm_linux} @tag{host} @endcond + */ +typedef struct { + uint8_t num_rdma_dev; + amdsmi_nic_rdma_dev_info_t rdma_dev_info[AMDSMI_MAX_NIC_RDMA_DEV]; +} amdsmi_nic_rdma_devices_info_t; + /*****************************************************************************/ /** @defgroup tagInitShutdown Initialization and Shutdown * @{ @@ -2567,23 +2764,6 @@ amdsmi_status_t amdsmi_shut_down(void); */ amdsmi_status_t amdsmi_get_socket_handles(uint32_t *socket_count, amdsmi_socket_handle* socket_handles); -/** - * @brief Returns the index of the given processor handle - * - * @ingroup tagProcDiscovery - * - * @platform{gpu_bm_linux} @platform{host} @platform{cpu_bm} @platform{guest_1vf} - * @platform{guest_mvf} @platform{guest_windows} - * - * @param[in] processor_handle Processor handle for which to query - * - * @param[out] processor_index Pointer to integer to store the processor index. Must be - * allocated by user. - * - * @return ::amdsmi_status_t | ::AMDSMI_STATUS_SUCCESS on success, non-zero on fail - */ -amdsmi_status_t amdsmi_get_index_from_processor_handle(amdsmi_processor_handle processor_handle, uint32_t *processor_index); - #ifdef ENABLE_ESMI_LIB /** @@ -2660,37 +2840,6 @@ amdsmi_status_t amdsmi_get_socket_info(amdsmi_socket_handle socket_handle, size_ */ amdsmi_status_t amdsmi_get_processor_info(amdsmi_processor_handle processor_handle, size_t len, char *name); -/** - * @brief Get the list of cpu socket handles in the system. - * - * @ingroup tagProcDiscovery - * - * @platform{cpu_bm} - * - * @details Depends on AMDSMI_INIT_AMD_CPUS flag passed to ::amdsmi_init. - * The socket handles can be used to query the processor handles in that socket, which - * will be used in other APIs to get processor detail information. - * - * @param[in,out] socket_count As input, the value passed - * through this parameter is the number of ::amdsmi_cpusocket_handle that - * may be safely written to the memory pointed to by @p socket_handles. This is the - * limit on how many socket handles will be written to @p socket_handles. On return, @p - * socket_count will contain the number of socket handles written to @p socket_handles, - * or the number of socket handles that could have been written if enough memory had been - * provided. - * If @p socket_handles is NULL, as output, @p socket_count will contain - * how many sockets are available to read in the system. - * - * @param[in,out] socket_handles A pointer to a block of memory to which the - * ::amdsmi_cpusocket_handle values will be written. This value may be NULL. - * In this case, this function can be used to query how many sockets are - * available to read in the system. - * - * @return ::amdsmi_status_t | ::AMDSMI_STATUS_SUCCESS on success, non-zero on fail - */ -amdsmi_status_t amdsmi_get_cpusocket_handles(uint32_t *socket_count, - amdsmi_cpusocket_handle* socket_handles); - /** * @brief Get respective processor counts from the processor handles * @@ -2873,25 +3022,6 @@ amdsmi_status_t amdsmi_get_cpucore_handles(uint32_t *cores_count, */ amdsmi_status_t amdsmi_get_processor_type(amdsmi_processor_handle processor_handle, processor_type_t* processor_type); -/** - * @brief Returns the processor handle from the given processor index - * - * @ingroup tagProcDiscovery - * - * @platform{gpu_bm_linux} @platform{host} @platform{cpu_bm} @platform{guest_1vf} - * @platform{guest_mvf} @platform{guest_windows} - * - * @param[in] processor_index Function processor_index to query - * - * @note On the @platform{host} this function currently supports only AMD GPU indexes. - * - * @param[out] processor_handle Reference to the processor handle. - * Must be allocated by user. - * - * @return ::amdsmi_status_t | ::AMDSMI_STATUS_SUCCESS on success, non-zero on fail - */ -amdsmi_status_t amdsmi_get_processor_handle_from_index(uint32_t processor_index, amdsmi_processor_handle *processor_handle); - /** * @brief Get processor handle with the matching bdf. * @@ -2928,38 +3058,6 @@ amdsmi_status_t amdsmi_get_processor_handle_from_bdf(amdsmi_bdf_t bdf, amdsmi_pr amdsmi_status_t amdsmi_get_gpu_device_bdf(amdsmi_processor_handle processor_handle, amdsmi_bdf_t *bdf); -/** - * @brief Returns BDF of the given device - * - * @ingroup tagProcDiscovery - * - * @platform{gpu_bm_linux} @platform{host} @platform{guest_1vf} @platform{guest_mvf} - * @platform{guest_windows} - * - * @param[in] processor_handle Device which to query - * - * @param[out] bdf Reference to BDF. Must be allocated by user. - * - * @return ::amdsmi_status_t | ::AMDSMI_STATUS_SUCCESS on success, non-zero on fail - */ -amdsmi_status_t amdsmi_get_processor_bdf(amdsmi_processor_handle processor_handle, amdsmi_bdf_t *bdf); - -/** - * @brief Returns the processor handle from the given UUID - * - * @ingroup tagProcDiscovery - * - * @platform{gpu_bm_linux} @platform{host} @platform{guest_windows} - * - * @param[in] uuid Function UUID to query. - * - * @param[out] processor_handle Reference to the processor handle. - * Must be allocated by user. - * - * @return ::amdsmi_status_t | ::AMDSMI_STATUS_SUCCESS on success, non-zero on fail - */ -amdsmi_status_t amdsmi_get_processor_handle_from_uuid(const char *uuid, amdsmi_processor_handle *processor_handle); - /** * @brief Returns the UUID of the device * @@ -5136,52 +5234,6 @@ amdsmi_status_t amdsmi_get_afids_from_cper(char* cper_buffer, uint32_t buf_size, */ amdsmi_status_t amdsmi_get_gpu_ras_feature_info(amdsmi_processor_handle processor_handle, amdsmi_ras_feature_t *ras_feature); -/** - * @brief Get the RAS policy info for a device - * - * @ingroup tagRasInfo - * - * @platform{gpu_bm_linux} @platform{host} - * - * @details Given a processor handle @p processor_handle, this function will retrieve - * the RAS policy information for the device. - * - * @param[in] processor_handle PF of a processor for which to query - * - * @param[out] info RAS policy info for the device. Must be allocated by user. - * - * @return ::amdsmi_status_t | ::AMDSMI_STATUS_SUCCESS on success, non-zero on fail - */ -amdsmi_status_t amdsmi_get_gpu_ras_policy_info(amdsmi_processor_handle processor_handle, - amdsmi_gpu_ras_policy_info_t *info); - -/** - * @brief Get the bad page threshold for a device - * - * @ingroup tagRasInfo - * - * @platform{gpu_bm_linux} @platform{host} - * - * @details Given a processor handle @p processor_handle and a pointer to a uint32_t @p threshold, - * this function will retrieve the bad page threshold value associated - * with device @p processor_handle and store the value at location pointed to by - * @p threshold. - * - * @note This function requires the admin/sudo privileges on @platform{gpu_bm_linux} - * - * @param[in] processor_handle a processor handle - * - * @param[in,out] threshold pointer to location where bad page threshold value will - * be written. - * If this parameter is nullptr, this function will return - * ::AMDSMI_STATUS_INVAL if the function is supported with the provided, - * arguments and ::AMDSMI_STATUS_NOT_SUPPORTED if it is not supported with the - * provided arguments. - * - * @return ::amdsmi_status_t | ::AMDSMI_STATUS_SUCCESS on success, non-zero on fail - */ -amdsmi_status_t amdsmi_get_bad_page_threshold(amdsmi_processor_handle processor_handle, uint32_t *threshold); - /** * @brief Retrieve CPER entries cached in the driver. * @@ -6028,29 +6080,6 @@ amdsmi_status_t amdsmi_set_gpu_compute_partition(amdsmi_processor_handle processor_handle, amdsmi_compute_partition_type_t compute_partition); -/** - * @brief Reverts a selected device's compute partition setting back to its - * boot state. - * - * @ingroup tagComputePartition - * - * @platform{gpu_bm_linux} - * - * @details Given a processor handle @p processor_handle, this function will attempt to - * revert its compute partition setting back to its boot state. - * - * @param[in] processor_handle Device which to query - * - * @retval ::AMDSMI_STATUS_SUCCESS call was successful - * @retval ::AMDSMI_STATUS_PERMISSION function requires admin/sudo privileges - * @retval ::AMDSMI_STATUS_NOT_SUPPORTED installed software or hardware does not - * support this function - * @return ::amdsmi_status_t - */ -amdsmi_status_t amdsmi_reset_gpu_compute_partition(amdsmi_processor_handle processor_handle); - -/** @} End tagComputePartition */ - /*****************************************************************************/ /** @defgroup tagMemoryPartition Memory Partition Functions * These functions are used to query and set the device's current memory @@ -6126,29 +6155,6 @@ amdsmi_status_t amdsmi_set_gpu_memory_partition(amdsmi_processor_handle processor_handle, amdsmi_memory_partition_type_t memory_partition); -/** - * @brief Reverts a selected device's memory partition setting back to its - * boot state. - * - * @ingroup tagMemoryPartition - * - * @platform{gpu_bm_linux} - * - * @details Given a processor handle @p processor_handle, this function will attempt to - * revert its current memory partition setting back to its boot state. - * - * @param[in] processor_handle Device which to query - * - * @retval ::AMDSMI_STATUS_SUCCESS call was successful - * @retval ::AMDSMI_STATUS_PERMISSION function requires admin/sudo privileges - * @retval ::AMDSMI_STATUS_NOT_SUPPORTED installed software or hardware does not - * support this function - * @retval ::AMDSMI_STATUS_AMDGPU_RESTART_ERR could not successfully restart - * the amdgpu driver - * @return ::amdsmi_status_t - */ -amdsmi_status_t amdsmi_reset_gpu_memory_partition(amdsmi_processor_handle processor_handle); - /** * @brief Returns current gpu memory partition capabilities * @@ -7873,6 +7879,134 @@ amdsmi_status_t amdsmi_get_dfc_ctrl(amdsmi_processor_handle processor_handle, ui #endif +/*****************************************************************************/ +/** @defgroup tagNicInfo NIC Information + * @{ + */ + +/** + * @brief Retrieves information about the NIC driver + * + * @ingroup tagNicInfo + * + * @platform{host} @platform{gpu_bm_linux} + * + * @param[in] processor_handle NIC for which to query + * + * @param[out] info reference to the nic driver info struct. + * Must be allocated by user. + * + * @return ::amdsmi_status_t | ::AMDSMI_STATUS_SUCCESS on success, non-zero on fail + */ +amdsmi_status_t amdsmi_get_nic_driver_info(amdsmi_processor_handle processor_handle, amdsmi_nic_driver_info_t *info); + +/** + * @brief Retrieves ASIC information for the NIC + * + * @ingroup tagNicInfo + * + * @platform{host} @platform{gpu_bm_linux} + * + * @param[in] processor_handle NIC for which to query + * + * @param[out] info reference to the nic asic info struct. + * Must be allocated by user. + * + * @return ::amdsmi_status_t | ::AMDSMI_STATUS_SUCCESS on success, non-zero on fail + */ +amdsmi_status_t amdsmi_get_nic_asic_info(amdsmi_processor_handle processor_handle, amdsmi_nic_asic_info_t *info); + +/** + * @brief Retrieves BUS information for the NIC + * + * @ingroup tagNicInfo + * + * @platform{host} @platform{gpu_bm_linux} + * + * @param[in] processor_handle NIC for which to query + * + * @param[out] info reference to the nic bus info struct. + * Must be allocated by user. + * + * @return ::amdsmi_status_t | ::AMDSMI_STATUS_SUCCESS on success, non-zero on fail + */ +amdsmi_status_t amdsmi_get_nic_bus_info(amdsmi_processor_handle processor_handle, amdsmi_nic_bus_info_t *info); + +/** + * @brief Retrieves NUMA information for the NIC + * + * @ingroup tagNicInfo + * + * @platform{host} @platform{gpu_bm_linux} + * + * @param[in] processor_handle NIC for which to query + * + * @param[out] info reference to the nic numa info struct. + * Must be allocated by user. + * + * @return ::amdsmi_status_t | ::AMDSMI_STATUS_SUCCESS on success, non-zero on fail + */ +amdsmi_status_t amdsmi_get_nic_numa_info(amdsmi_processor_handle processor_handle, amdsmi_nic_numa_info_t *info); + +/** + * @brief Retrieves PORT information for the NIC + * + * @ingroup tagNicInfo + * + * @platform{host} @platform{gpu_bm_linux} + * + * @param[in] processor_handle NIC for which to query + * + * @param[out] info reference to the nic port info struct. + * Must be allocated by user. + * + * @return ::amdsmi_status_t | ::AMDSMI_STATUS_SUCCESS on success, non-zero on fail + */ +amdsmi_status_t amdsmi_get_nic_port_info(amdsmi_processor_handle processor_handle, amdsmi_nic_port_info_t *info); + +/** + * @brief Retrieves RDMA devices information for the NIC + * + * @ingroup tagNicInfo + * + * @platform{host} @platform{gpu_bm_linux} + * + * @param[in] processor_handle NIC for which to query + * + * @param[out] info reference to the nic rdma devices info struct. + * Must be allocated by user. + * + * @return ::amdsmi_status_t | ::AMDSMI_STATUS_SUCCESS on success, non-zero on fail + */ +amdsmi_status_t amdsmi_get_nic_rdma_dev_info(amdsmi_processor_handle processor_handle, amdsmi_nic_rdma_devices_info_t *info); + +/** + * @brief Retrieve RDMA port statistics for the NIC + * + * @ingroup tagNicInfo + * + * @platform{host} @platform{gpu_bm_linux} + * + * This function follows a two-call pattern: + * 1. First call with stats=NULL to get the count of available statistics + * 2. Second call with allocated array to retrieve all statistics + * + * @param[in] processor_handle NIC for which to query + * @param[in] rdma_port_index index of the NIC RDMA port to query + * @param[in,out] num_stats pointer to the number of statistics + * - Input: maximum number of statistics that stats array can hold + * - Output: actual number of statistics available/returned + * @param[out] stats pointer to array of amdsmi_nic_stat_t structures to be filled + * - If NULL, only num_stats is filled with the count of available statistics + * - If not NULL, must be allocated by user with at least num_stats elements + * + * @return ::amdsmi_status_t | ::AMDSMI_STATUS_SUCCESS on success, non-zero on fail + */ +amdsmi_status_t amdsmi_get_nic_rdma_port_statistics(amdsmi_processor_handle processor_handle, uint32_t rdma_port_index, + uint32_t *num_stats, amdsmi_nic_stat_t *stats); + +/** @} End tagNicInfo */ + #ifdef __cplusplus } #endif // __cplusplus diff --git a/projects/amdsmi/include/amd_smi/impl/amd_smi_common.h b/projects/amdsmi/include/amd_smi/impl/amd_smi_common.h index 66b7082a8b3..e22f77c043e 100644 --- a/projects/amdsmi/include/amd_smi/impl/amd_smi_common.h +++ b/projects/amdsmi/include/amd_smi/impl/amd_smi_common.h @@ -34,6 +34,9 @@ extern "C" { } #endif +extern "C" { +#include +} namespace amd::smi { // Define a map of rsmi status codes to amdsmi status codes @@ -113,6 +116,21 @@ const std::map esmi_status_map = { amdsmi_status_t esmi_to_amdsmi_status(esmi_status_t status); #endif + +// Define a map of smi nic status codes to amdsmi status codes +const std::map ainic_status_map = { + {SMI_NIC_STATUS_SUCCESS, AMDSMI_STATUS_SUCCESS}, + {SMI_NIC_STATUS_ERROR, AMDSMI_STATUS_API_FAILED}, + {SMI_NIC_STATUS_WRONG_PARAM, AMDSMI_STATUS_INVAL}, + {SMI_NIC_STATUS_NOT_FOUND, AMDSMI_STATUS_NOT_FOUND}, + {SMI_NIC_STATUS_NO_RESOURCE, AMDSMI_STATUS_OUT_OF_RESOURCES}, + {SMI_NIC_STATUS_NOT_SUPPORTED, AMDSMI_STATUS_NOT_YET_IMPLEMENTED}, + {SMI_NIC_STATUS_NOT_INIT, AMDSMI_STATUS_NOT_INIT}, + {SMI_NIC_STATUS_NO_DATA, AMDSMI_STATUS_NO_DATA}, + {SMI_NIC_STATUS_DRIVER_NOT_LOADED, AMDSMI_STATUS_DRIVER_NOT_LOADED} +}; +amdsmi_status_t ainic_to_amdsmi_status(smi_nic_status_t status); + } // namespace amd::smi #endif // AMD_SMI_INCLUDE_AMD_SMI_COMMON_H_ diff --git a/projects/amdsmi/include/amd_smi/impl/amd_smi_drm.h b/projects/amdsmi/include/amd_smi/impl/amd_smi_drm.h index 83cf8606515..ea6198c1187 100644 --- a/projects/amdsmi/include/amd_smi/impl/amd_smi_drm.h +++ b/projects/amdsmi/include/amd_smi/impl/amd_smi_drm.h @@ -47,6 +47,9 @@ class AMDSmiDrm { std::vector get_bdfs(); std::vector& get_drm_paths(); bool check_if_drm_is_supported(); + + amdsmi_status_t amdgpu_query_cpu_affinity(const std::string &device_path, std::string& cpu_affinity); + uint32_t get_vendor_id(); private: diff --git a/projects/amdsmi/include/amd_smi/impl/amd_smi_gpu_device.h b/projects/amdsmi/include/amd_smi/impl/amd_smi_gpu_device.h index 370155f3cca..ca0d570e460 100644 --- a/projects/amdsmi/include/amd_smi/impl/amd_smi_gpu_device.h +++ b/projects/amdsmi/include/amd_smi/impl/amd_smi_gpu_device.h @@ -69,6 +69,8 @@ class AMDSmiGPUDevice: public AMDSmiProcessor { const GPUComputeProcessList_t& amdgpu_get_compute_process_list(ComputeProcessListType_t list_type = ComputeProcessListType_t::kAllProcessesOnDevice); + amdsmi_status_t amdgpu_query_cpu_affinity(std::string& cpu_affinity) const; + // New methods for -e feature std::string bdf_to_string() const; // -e feature std::vector get_bitmask_from_numa_node(int32_t node_id, uint32_t size) const; diff --git a/projects/amdsmi/include/amd_smi/impl/amd_smi_socket.h b/projects/amdsmi/include/amd_smi/impl/amd_smi_socket.h index f1f33e4bbc4..59a1689c86b 100644 --- a/projects/amdsmi/include/amd_smi/impl/amd_smi_socket.h +++ b/projects/amdsmi/include/amd_smi/impl/amd_smi_socket.h @@ -48,6 +48,15 @@ class AMDSmiSocket { case AMDSMI_PROCESSOR_TYPE_AMD_CPU_CORE: cpu_core_processors_.push_back(processor); break; + case AMDSMI_PROCESSOR_TYPE_BRCM_NIC: + nic_processors_.push_back(processor); + break; + case AMDSMI_PROCESSOR_TYPE_BRCM_SWITCH: + switch_processors_.push_back(processor); + break; + case AMDSMI_PROCESSOR_TYPE_AMD_NIC: + ainic_processors_.push_back(processor); + break; default: break; } @@ -61,6 +70,12 @@ class AMDSmiSocket { return cpu_processors_; case AMDSMI_PROCESSOR_TYPE_AMD_CPU_CORE: return cpu_core_processors_; + case AMDSMI_PROCESSOR_TYPE_AMD_NIC: + return ainic_processors_; + case AMDSMI_PROCESSOR_TYPE_BRCM_NIC: + return nic_processors_; + case AMDSMI_PROCESSOR_TYPE_BRCM_SWITCH: + return switch_processors_; default: return processors_; } @@ -73,6 +88,9 @@ class AMDSmiSocket { std::vector processors_; std::vector cpu_processors_; std::vector cpu_core_processors_; + std::vector ainic_processors_; + std::vector nic_processors_; + std::vector switch_processors_; }; } // namespace amd::smi diff --git a/projects/amdsmi/include/amd_smi/impl/amd_smi_system.h b/projects/amdsmi/include/amd_smi/impl/amd_smi_system.h index f78dab8dc94..2d4c1d30d07 100644 --- a/projects/amdsmi/include/amd_smi/impl/amd_smi_system.h +++ b/projects/amdsmi/include/amd_smi/impl/amd_smi_system.h @@ -25,20 +25,24 @@ #include #include -#include "amd_smi/amdsmi.h" -#include "amd_smi/impl/amd_smi_socket.h" -#include "amd_smi/impl/amd_smi_processor.h" -#include "amd_smi/impl/amd_smi_drm.h" +#include +#include +#include +#include +#include +#include + +#ifdef BRCM_NIC +#include "amd_smi/impl/nic/amd_smi_no_drm_nic.h" +#include "amd_smi/impl/nic/amd_smi_no_drm_switch.h" +#endif//BRCM_NIC namespace amd::smi { // Singleton: Only one system in an application class AMDSmiSystem { public: - static AMDSmiSystem& getInstance() { - static AMDSmiSystem instance; - return instance; - } + static AMDSmiSystem& getInstance(); amdsmi_status_t init(uint64_t flags); amdsmi_status_t cleanup(); @@ -64,6 +68,8 @@ class AMDSmiSystem { amdsmi_status_t get_sys_num_of_cpu_sockets(uint32_t *sock_num); std::vector get_cpu_sockets_from_numa_node(int32_t numa_node); + + const auto &get_ai_nic_info() const; private: AMDSmiSystem() : init_flag_(AMDSMI_INIT_AMD_GPUS) {} @@ -74,10 +80,22 @@ class AMDSmiSystem { amdsmi_status_t get_gpu_socket_id(uint32_t index, std::string& socketid); amdsmi_status_t populate_amd_gpu_devices(); amdsmi_status_t populate_amd_cpus(); + amdsmi_status_t populate_amd_ainic_devices(); + amdsmi_status_t populate_brcm_nic_devices(); + amdsmi_status_t populate_brcm_switch_devices(); uint64_t init_flag_; AMDSmiDrm drm_; + smi_nic_ctx_t ainic_ctx_; + std::vector ai_nic_info_; +#ifdef BRCM_NIC + AMDSmiNoDrmNIC no_drm_nic; + AMDSmiNoDrmSwitch no_drm_switch; +#endif//BRCM_NIC std::vector sockets_; std::set processors_; // Track valid processors + std::set nic_processors_; // Track valid nic processors + std::set switch_processors_; // Track valid switch processors + std::set ainic_processors_; }; } // namespace amd::smi diff --git a/projects/amdsmi/include/amd_smi/impl/amd_smi_utils.h b/projects/amdsmi/include/amd_smi/impl/amd_smi_utils.h index 794a727d625..e76b3688f44 100644 --- a/projects/amdsmi/include/amd_smi/impl/amd_smi_utils.h +++ b/projects/amdsmi/include/amd_smi/impl/amd_smi_utils.h @@ -59,6 +59,11 @@ amdsmi_status_t smi_amdgpu_is_gpu_power_management_enabled(amd::smi::AMDSmiGPUDe std::string smi_split_string(std::string str, char delim); std::vector split_string(const std::string& line, char delim); std::string smi_amdgpu_get_status_string(amdsmi_status_t ret, bool fullStatus); + +uint32_t smi_brcm_get_value_u32(const std::string &folder, const std::string &file_name); +std::string smi_brcm_get_value_string(const std::string &folder, const std::string &file_name); +amdsmi_status_t smi_brcm_execute_cmd_get_data(const std::string &command, std::string *data); + amdsmi_status_t smi_clear_char_and_reinitialize(char buffer[], uint32_t len, std::string newString); @@ -96,6 +101,29 @@ amdsmi_status_t smi_amdgpu_get_device_index(amdsmi_processor_handle processor_ha */ amdsmi_status_t smi_amdgpu_get_device_count(uint32_t *total_num_devices); +/** + * @brief Get the ainic processor handle given the device index. + * + * @details Given a uint32_t @p device_index and a pointer to + * a ainic processor handle @p processor_handle, the device index will be used to + * find the processor handle of the device and store it in the provided pointer + * + * @param[in] device_index a uint32_t to value to help find the corresponding + * ainic processor handle + * + * @param[inout] processor_handle a pointer to amdsmi_processor_handle + * which the corresponding processor_handle will be stored + * + * @retval ::AMDSMI_STATUS_SUCCESS is returned upon successful call. + * ::AMDSMI_STATUS_INVAL is returned if user provides a null pointer + * for processor_handle. + * ::AMDSMI_STATUS_API_FAILED is returned if the device_index is cannot + * be found. + */ +amdsmi_status_t smi_amdgpu_get_ainic_processor_handle_by_index( + uint32_t device_index, + amdsmi_processor_handle *processor_handle); + /** * @brief Get the processor handle given the device index. * @@ -199,4 +227,12 @@ void fill_2d_array(A& arr, T value) { */ uint64_t get_product_serial_number(amdsmi_processor_handle processor_handle); +/** + * @brief Tokenize bdfid into components. + * + * @param[in] bdfid a uint64_t containing the bdfid + * + * @retval ::Tuple of domain, bus, device, function + */ +std::tuple parse_bdfid(uint64_t bdfid); #endif // AMD_SMI_INCLUDE_AMD_SMI_UTILS_H_ diff --git a/projects/amdsmi/include/amd_smi/impl/nic/amd_smi_ainic_device.h b/projects/amdsmi/include/amd_smi/impl/nic/amd_smi_ainic_device.h new file mode 100644 index 00000000000..c2435d76b25 --- /dev/null +++ b/projects/amdsmi/include/amd_smi/impl/nic/amd_smi_ainic_device.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include "amd_smi/amdsmi.h" +#include "amd_smi/impl/amd_smi_processor.h" +#include + +// User could to get the AI_NIC processor using below existing function +// ret = amdsmi_get_processor_handles_by_type(sockets[i], AMDSMI_PROCESSOR_TYPE_AMD_NIC, nullptr, &tmp_device_count); +// Get the ai nic information using the nic handle + +namespace amd::smi { + +class AMDSmiAINICDevice: public AMDSmiProcessor { + public: + +/** + * @brief Main NIC Information + * + * @cond @tag{gpu_bm_linux} @endcond + */ + struct AINICInfo{ + amdsmi_nic_asic_info_t asic; + amdsmi_nic_bus_info_t bus; + amdsmi_nic_driver_info_t driver; + amdsmi_nic_numa_info_t numa; + amdsmi_nic_fw_t versions; + amdsmi_nic_port_info_t port; + amdsmi_nic_rdma_devices_info_t rdma_dev; + }; + + AMDSmiAINICDevice(uint32_t nic_idx, const amdsmi_bdf_t &bdf, const AINICInfo &ai_nic_info) + : AMDSmiProcessor(AMDSMI_PROCESSOR_TYPE_AMD_NIC) + , nic_idx_(nic_idx) + , bdf_(bdf) + , ai_nic_info_(ai_nic_info) { + } + ~AMDSmiAINICDevice() = default; + amdsmi_status_t amd_query_nic_info(AINICInfo& info) const; + private: + uint32_t nic_idx_; + amdsmi_bdf_t bdf_; + AINICInfo ai_nic_info_; +}; + +} // namespace amd::smi + +amdsmi_status_t +amdsmi_get_ainic_info(amdsmi_processor_handle processor_handle, amd::smi::AMDSmiAINICDevice::AINICInfo *info); diff --git a/projects/amdsmi/include/amd_smi/impl/nic/amd_smi_lspci_commands.h b/projects/amdsmi/include/amd_smi/impl/nic/amd_smi_lspci_commands.h new file mode 100644 index 00000000000..3f071f92372 --- /dev/null +++ b/projects/amdsmi/include/amd_smi/impl/nic/amd_smi_lspci_commands.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) Broadcom Inc All Rights Reserved. + * + * Developed by: + * Broadcom Inc + * + * www.broadcom.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef AMD_SMI_LSPCI_COMMANDS_H_ +#define AMD_SMI_LSPCI_COMMANDS_H_ + +#include "amd_smi/amdsmi.h" +#include "rocm_smi/rocm_smi_logger.h" + +amdsmi_status_t get_lspci_device_data(std::string bdfStr, std::string search_key, std::string &version); +amdsmi_status_t get_lspci_root_switch(amdsmi_bdf_t devicehBdf, amdsmi_bdf_t *switchBdf); + +#endif //AMD_SMI_LSPCI_COMMANDS_H_ \ No newline at end of file diff --git a/projects/amdsmi/include/amd_smi/impl/nic/amd_smi_nic_device.h b/projects/amdsmi/include/amd_smi/impl/nic/amd_smi_nic_device.h new file mode 100644 index 00000000000..5bdf96e528d --- /dev/null +++ b/projects/amdsmi/include/amd_smi/impl/nic/amd_smi_nic_device.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) Broadcom Inc All Rights Reserved. + * + * Developed by: + * Broadcom Inc + * + * www.broadcom.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef AMD_SMI_INCLUDE_IMPL_AMD_SMI_NIC_DEVICE_H_ +#define AMD_SMI_INCLUDE_IMPL_AMD_SMI_NIC_DEVICE_H_ + +#include "amd_smi/amdsmi.h" +#include "amd_smi/impl/amd_smi_processor.h" +#include "amd_smi/impl/nic/amd_smi_no_drm_nic.h" +#include "shared_mutex.h" // NOLINT +#include "rocm_smi/rocm_smi_logger.h" + +namespace amd::smi { + +class AMDSmiNICDevice: public AMDSmiProcessor { + public: + + AMDSmiNICDevice(uint32_t nic_id, amdsmi_bdf_t bdf, AMDSmiNoDrmNIC& no_drm_nic) + : AMDSmiProcessor(AMDSMI_PROCESSOR_TYPE_BRCM_NIC), nic_id_(nic_id), bdf_(bdf), nodrm_(no_drm_nic) { + if (check_if_no_drm_is_supported()) this->get_no_drm_data(); + } + + ~AMDSmiNICDevice() = default; + + amdsmi_status_t get_no_drm_data(); + pthread_mutex_t* get_mutex(); + uint32_t get_nic_id() const; + std::string& get_nic_path(); + amdsmi_bdf_t get_bdf(); + bool check_if_no_drm_is_supported() { return nodrm_.check_if_no_drm_is_supported(); } + uint32_t get_vendor_id(); + + amdsmi_status_t amd_query_nic_info(amdsmi_brcm_nic_info_t& info) const; + amdsmi_status_t amd_query_nic_temp_info(amdsmi_brcm_nic_temperature_metric_t& info) const; + amdsmi_status_t amd_query_nic_device_info(amdsmi_brcm_nic_hwmon_device_t& info) const; + amdsmi_status_t amd_query_nic_power_info(amdsmi_brcm_nic_hwmon_power_t& info) const; + amdsmi_status_t amd_query_nic_uuid(std::string& version) const; + amdsmi_status_t amd_query_nic_numa_affinity(int32_t *numa_node) const; + amdsmi_status_t amd_query_nic_cpu_affinity(std::string& cpu_affinity) const; + + amdsmi_status_t amd_query_nic_firmware_info(amdsmi_brcm_nic_firmware_t& info) const; + + private: + uint32_t nic_id_; + std::string path_; + amdsmi_bdf_t bdf_; + AMDSmiNoDrmNIC& nodrm_; +}; + +} // namespace amd::smi + +#endif // AMD_SMI_INCLUDE_IMPL_AMD_SMI_NIC_DEVICE_H_ diff --git a/projects/amdsmi/include/amd_smi/impl/nic/amd_smi_no_drm_nic.h b/projects/amdsmi/include/amd_smi/impl/nic/amd_smi_no_drm_nic.h new file mode 100644 index 00000000000..9d3da2f4216 --- /dev/null +++ b/projects/amdsmi/include/amd_smi/impl/nic/amd_smi_no_drm_nic.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) Broadcom Inc All Rights Reserved. + * + * Developed by: + * Broadcom Inc + * + * www.broadcom.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef AMD_SMI_INCLUDE_IMPL_AMD_SMI_NO_DRM_NIC_H_ +#define AMD_SMI_INCLUDE_IMPL_AMD_SMI_NO_DRM_NIC_H_ + +#include +#include +#include +#include // NOLINT +#include "amd_smi/amdsmi.h" +#include "rocm_smi/rocm_smi_logger.h" + +namespace amd::smi { + +class AMDSmiNoDrmNIC { + public: + amdsmi_status_t init(); + amdsmi_status_t cleanup(); + amdsmi_status_t get_bdf_by_index(uint32_t nic_index, amdsmi_bdf_t *bdf_info) const; + amdsmi_status_t get_interface_name_by_index(uint32_t nic_index, std::string* interface_name) const; + amdsmi_status_t get_device_path_by_index(uint32_t nic_index, std::string* device_path) const; + amdsmi_status_t get_hwmon_path_by_index(uint32_t nic_index, std::string* hwm_path) const; + std::vector get_bdfs(); + std::vector& get_device_paths(); + std::vector& get_hwmon_paths(); + bool check_if_no_drm_is_supported(); + + uint32_t get_vendor_id(); + amdsmi_status_t amd_query_nic_info(uint32_t nic_index, amdsmi_brcm_nic_info_t& info); + amdsmi_status_t amd_query_nic_uuid(std::string devicePath, std::string& version); + amdsmi_status_t amd_query_nic_temp(std::string hwmonPath, amdsmi_brcm_nic_temperature_metric_t& info); + amdsmi_status_t amd_query_nic_device(std::string hwmonPath, amdsmi_brcm_nic_hwmon_device_t& info); + amdsmi_status_t amd_query_nic_power(std::string hwmonPath, amdsmi_brcm_nic_hwmon_power_t& info); + amdsmi_status_t amd_query_nic_numa_affinity(std::string devicePath, int32_t *numa_node); + amdsmi_status_t amd_query_nic_cpu_affinity(std::string devicePath, std::string& cpu_affinity); + + amdsmi_status_t amd_query_nic_fw_info(std::string devicePath, amdsmi_brcm_nic_firmware_t& info); + private: + // when file is not found, the empty string will be returned + std::vector device_paths_; + std::vector hwmon_paths_; + std::vector interfaces_; + std::vector no_drm_bdfs_; +}; + +} // namespace amd::smi + +#endif // AMD_SMI_INCLUDE_IMPL_AMD_SMI_NO_DRM_NIC_H_ diff --git a/projects/amdsmi/include/amd_smi/impl/nic/amd_smi_no_drm_switch.h b/projects/amdsmi/include/amd_smi/impl/nic/amd_smi_no_drm_switch.h new file mode 100644 index 00000000000..0f59db840a1 --- /dev/null +++ b/projects/amdsmi/include/amd_smi/impl/nic/amd_smi_no_drm_switch.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) Broadcom Inc All Rights Reserved. + * + * Developed by: + * Broadcom Inc + * + * www.broadcom.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef AMD_SMI_INCLUDE_IMPL_AMD_SMI_NO_DRM_SWITCH_H_ +#define AMD_SMI_INCLUDE_IMPL_AMD_SMI_NO_DRM_SWITCH_H_ + +#include +#include +#include +#include // NOLINT +#include "amd_smi/amdsmi.h" + +namespace amd::smi { + +class AMDSmiNoDrmSwitch { + public: + + amdsmi_status_t init(); + amdsmi_status_t cleanup(); + amdsmi_status_t get_bdf_by_index(uint32_t switch_index, amdsmi_bdf_t *bdf_info) const; + amdsmi_status_t get_device_path_by_index(uint32_t switch_index, std::string* device_path) const; + amdsmi_status_t get_hwmon_path_by_index(uint32_t switch_index, std::string* hwm_path) const; + + std::vector get_bdfs(); + std::vector& get_device_paths(); + std::vector& get_hwmon_paths(); + bool check_if_no_drm_is_supported(); + + uint32_t get_vendor_id(); + amdsmi_status_t amd_query_switch_link(std::string devicePath, amdsmi_brcm_switch_link_metric_t& info); + amdsmi_status_t amd_query_switch_uuid(std::string bdfStr, std::string& serial); + amdsmi_status_t amd_query_switch_numa_affinity(std::string devicePath, int32_t *numa_node); + amdsmi_status_t amd_query_switch_cpu_affinity(std::string devicePath, std::string& cpu_affinity); + amdsmi_status_t amd_query_switch_device( std::string devicePath,amdsmi_brcm_switch_device_metric_t &info); + amdsmi_status_t amd_query_switch_power( std::string devicePath,amdsmi_brcm_switch_power_metric_t &info); + + private: + // when file is not found, the empty string will be returned + std::vector device_paths_; + std::vector host_paths_; + std::vector no_drm_bdfs_; +}; + +} // namespace amd::smi + +#endif // AMD_SMI_INCLUDE_IMPL_AMD_SMI_NO_DRM_SWITCH_H_ diff --git a/projects/amdsmi/include/amd_smi/impl/nic/amd_smi_switch_device.h b/projects/amdsmi/include/amd_smi/impl/nic/amd_smi_switch_device.h new file mode 100644 index 00000000000..e3e76e6549e --- /dev/null +++ b/projects/amdsmi/include/amd_smi/impl/nic/amd_smi_switch_device.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) Broadcom Inc All Rights Reserved. + * + * Developed by: + * Broadcom Inc + * + * www.broadcom.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef AMD_SMI_INCLUDE_IMPL_AMD_SMI_SWITCH_DEVICE_H_ +#define AMD_SMI_INCLUDE_IMPL_AMD_SMI_SWITCH_DEVICE_H_ + +#include "amd_smi/amdsmi.h" +#include "amd_smi/impl/amd_smi_processor.h" +#include "amd_smi/impl/nic/amd_smi_no_drm_switch.h" +#include "shared_mutex.h" // NOLINT +#include "rocm_smi/rocm_smi_logger.h" + +namespace amd::smi { + +class AMDSmiSWITCHDevice: public AMDSmiProcessor { + public: + + AMDSmiSWITCHDevice(uint32_t switch_id, amdsmi_bdf_t bdf, AMDSmiNoDrmSwitch& no_drm_switch) + : AMDSmiProcessor(AMDSMI_PROCESSOR_TYPE_BRCM_SWITCH), switch_id_(switch_id), bdf_(bdf), nodrm_(no_drm_switch) { + if (check_if_no_drm_is_supported()) this->get_no_drm_data(); + } + + ~AMDSmiSWITCHDevice() = default; + + amdsmi_status_t get_no_drm_data(); + pthread_mutex_t* get_mutex(); + uint32_t get_switch_id() const; + std::string& get_switch_path(); + amdsmi_bdf_t get_bdf(); + bool check_if_no_drm_is_supported() { return nodrm_.check_if_no_drm_is_supported(); } + + amdsmi_status_t amd_query_switch_link_info(amdsmi_brcm_switch_link_metric_t& info) const; + amdsmi_status_t amd_query_switch_uuid(std::string& serial) const; + amdsmi_status_t amd_query_switch_numa_affinity(int32_t *numa_node) const; + amdsmi_status_t amd_query_switch_cpu_affinity(std::string& cpu_affinity) const; + amdsmi_status_t amd_query_switch_device_info(amdsmi_brcm_switch_device_metric_t& info) const; + amdsmi_status_t amd_query_switch_power_info(amdsmi_brcm_switch_power_metric_t& info) const; + + private: + uint32_t switch_id_; + std::string path_; + amdsmi_bdf_t bdf_; + AMDSmiNoDrmSwitch& nodrm_; +}; + +} // namespace amd::smi + +#endif // AMD_SMI_INCLUDE_IMPL_AMD_SMI_SWITCH_DEVICE_H_ diff --git a/projects/amdsmi/py-interface/amdsmi_interface.py b/projects/amdsmi/py-interface/amdsmi_interface.py index 61c79efc3bb..63e461d3742 100644 --- a/projects/amdsmi/py-interface/amdsmi_interface.py +++ b/projects/amdsmi/py-interface/amdsmi_interface.py @@ -85,7 +85,7 @@ class MaxUIntegerTypes(IntEnum): AMDSMI_MAX_NUM_XGMI_PHYSICAL_LINK = 64 AMDSMI_GPU_UUID_SIZE = 38 _AMDSMI_STRING_LENGTH = 80 - +_AMDSMI_MAX_STRING_LENGTH = 256 class AmdSmiStatus(IntEnum): SUCCESS = amdsmi_wrapper.AMDSMI_STATUS_SUCCESS INVAL = amdsmi_wrapper.AMDSMI_STATUS_INVAL @@ -139,9 +139,10 @@ class AmdSmiInitFlags(IntEnum): INIT_ALL_PROCESSORS = amdsmi_wrapper.AMDSMI_INIT_ALL_PROCESSORS INIT_AMD_CPUS = amdsmi_wrapper.AMDSMI_INIT_AMD_CPUS INIT_AMD_GPUS = amdsmi_wrapper.AMDSMI_INIT_AMD_GPUS - INIT_AMD_APUS = amdsmi_wrapper.AMDSMI_INIT_AMD_APUS INIT_NON_AMD_CPUS = amdsmi_wrapper.AMDSMI_INIT_NON_AMD_CPUS INIT_NON_AMD_GPUS = amdsmi_wrapper.AMDSMI_INIT_NON_AMD_GPUS + INIT_AMD_NICS = amdsmi_wrapper.AMDSMI_INIT_AMD_NICS + INIT_AMD_APUS = amdsmi_wrapper.AMDSMI_INIT_AMD_APUS class AmdSmiContainerTypes(IntEnum): @@ -155,7 +156,10 @@ class AmdSmiDeviceType(IntEnum): AMD_CPU_DEVICE = amdsmi_wrapper.AMDSMI_PROCESSOR_TYPE_AMD_CPU NON_AMD_GPU_DEVICE = amdsmi_wrapper.AMDSMI_PROCESSOR_TYPE_NON_AMD_GPU NON_AMD_CPU_DEVICE = amdsmi_wrapper.AMDSMI_PROCESSOR_TYPE_NON_AMD_CPU - + AMD_CPU_CORE_DEVICE = amdsmi_wrapper.AMDSMI_PROCESSOR_TYPE_AMD_CPU_CORE + AINIC_DEVICE = amdsmi_wrapper.AMDSMI_PROCESSOR_TYPE_AMD_NIC + BRCM_NIC_DEVICE = amdsmi_wrapper.AMDSMI_PROCESSOR_TYPE_BRCM_NIC + BRCM_SWITCH_DEVICE = amdsmi_wrapper.AMDSMI_PROCESSOR_TYPE_BRCM_SWITCH class AmdSmiMmIp(IntEnum): UVD = amdsmi_wrapper.AMDSMI_MM_UVD @@ -563,6 +567,9 @@ class AmdSmiProcessorType(IntEnum): NON_AMD_CPU = amdsmi_wrapper.AMDSMI_PROCESSOR_TYPE_NON_AMD_CPU AMD_CPU_CORE = amdsmi_wrapper.AMDSMI_PROCESSOR_TYPE_AMD_CPU_CORE AMD_APU = amdsmi_wrapper.AMDSMI_PROCESSOR_TYPE_AMD_APU + AMD_AINIC = amdsmi_wrapper.AMDSMI_PROCESSOR_TYPE_AMD_NIC + AMD_BRCM_NIC = amdsmi_wrapper.AMDSMI_PROCESSOR_TYPE_BRCM_NIC + AMD_BRCM_SWITCH = amdsmi_wrapper.AMDSMI_PROCESSOR_TYPE_BRCM_SWITCH class AmdSmiRegType(IntEnum): @@ -723,7 +730,7 @@ def _format_bad_page_info(bad_page_info, bad_page_count: ctypes.c_uint32) -> Lis return table_records -def _format_bdf(amdsmi_bdf: amdsmi_wrapper.amdsmi_bdf_t) -> str: +def _format_bdf(amdsmi_bdf: Union[amdsmi_wrapper.amdsmi_bdf_t, amdsmi_wrapper.struct_amdsmi_bdf_t]) -> str: """ Format BDF struct to readable data. @@ -734,11 +741,15 @@ def _format_bdf(amdsmi_bdf: amdsmi_wrapper.amdsmi_bdf_t) -> str: Returns: `str`: String containing BDF data in a readable format. """ - domain = hex(amdsmi_bdf.struct_amdsmi_bdf_t.domain_number)[2:].zfill(4) - bus = hex(amdsmi_bdf.struct_amdsmi_bdf_t.bus_number)[2:].zfill(2) - device = hex(amdsmi_bdf.struct_amdsmi_bdf_t.device_number)[2:].zfill(2) - function = hex(amdsmi_bdf.struct_amdsmi_bdf_t.function_number)[2:] - + try: + struct = amdsmi_bdf.struct_amdsmi_bdf_t + except AttributeError: + struct = amdsmi_bdf + + domain = hex(struct.domain_number)[2:].zfill(4) + bus = hex(struct.bus_number)[2:].zfill(2) + device = hex(struct.device_number)[2:].zfill(2) + function = hex(struct.function_number)[2:] return domain + ":" + bus + ":" + device + "." + function @@ -1059,6 +1070,181 @@ def amdsmi_get_processor_handles() -> List[c_void_p]: return devices +def get_switch_handles() -> List[amdsmi_wrapper.amdsmi_processor_handle]: + + switch_handles = [] + switch_type = amdsmi_wrapper.AMDSMI_PROCESSOR_TYPE_BRCM_SWITCH + socket_handles = amdsmi_get_socket_handles() + + for socket in socket_handles: + switch_count = ctypes.c_uint32() + null_ptr = ctypes.POINTER(amdsmi_wrapper.amdsmi_processor_handle)() + + # First call to get the count of Switch processors + _check_res( + amdsmi_wrapper.amdsmi_get_processor_handles_by_type( + socket, + switch_type, + null_ptr, + ctypes.byref(switch_count), + ) + ) + + if switch_count.value > 0: + c_handles = (amdsmi_wrapper.amdsmi_processor_handle * switch_count.value)() + _check_res( + amdsmi_wrapper.amdsmi_get_processor_handles_by_type( + socket, + switch_type, + c_handles, + ctypes.byref(switch_count) + ) + ) + + switch_handles.extend([ + amdsmi_wrapper.amdsmi_processor_handle(c_handles[dev_idx]) + for dev_idx in range(switch_count.value) + ]) + + return switch_handles + +def get_nic_handles() -> List[amdsmi_wrapper.amdsmi_processor_handle]: + + nic_handles = [] + nic_type = amdsmi_wrapper.AMDSMI_PROCESSOR_TYPE_BRCM_NIC + socket_handles = amdsmi_get_socket_handles() + + for socket in socket_handles: + nic_count = ctypes.c_uint32() + null_ptr = ctypes.POINTER(amdsmi_wrapper.amdsmi_processor_handle)() + + # First call to get the count of NIC processors + _check_res( + amdsmi_wrapper.amdsmi_get_processor_handles_by_type( + socket, + nic_type, + null_ptr, + ctypes.byref(nic_count), + ) + ) + + if nic_count.value > 0: + c_handles = (amdsmi_wrapper.amdsmi_processor_handle * nic_count.value)() + _check_res( + amdsmi_wrapper.amdsmi_get_processor_handles_by_type( + socket, + nic_type, + c_handles, + ctypes.byref(nic_count) + ) + ) + + nic_handles.extend([ + amdsmi_wrapper.amdsmi_processor_handle(c_handles[dev_idx]) + for dev_idx in range(nic_count.value) + ]) + + return nic_handles + +def get_gpu_handles() -> List[amdsmi_wrapper.amdsmi_processor_handle]: + + gpu_handles = [] + gpu_type = amdsmi_wrapper.AMDSMI_PROCESSOR_TYPE_AMD_GPU + socket_handles = amdsmi_get_socket_handles() + + for socket in socket_handles: + gpu_count = ctypes.c_uint32() + null_ptr = ctypes.POINTER(amdsmi_wrapper.amdsmi_processor_handle)() + + # First call to get the count of GPU processors + _check_res( + amdsmi_wrapper.amdsmi_get_processor_handles_by_type( + socket, + gpu_type, + null_ptr, + ctypes.byref(gpu_count), + ) + ) + + if gpu_count.value > 0: + c_handles = (amdsmi_wrapper.amdsmi_processor_handle * gpu_count.value)() + _check_res( + amdsmi_wrapper.amdsmi_get_processor_handles_by_type( + socket, + gpu_type, + c_handles, + ctypes.byref(gpu_count) + ) + ) + + gpu_handles.extend([ + amdsmi_wrapper.amdsmi_processor_handle(c_handles[dev_idx]) + for dev_idx in range(gpu_count.value) + ]) + + return gpu_handles + +def get_ainic_handles() -> List[amdsmi_wrapper.amdsmi_processor_handle]: + + nic_handles = [] + nic_type = amdsmi_wrapper.AMDSMI_PROCESSOR_TYPE_AMD_NIC + socket_handles = amdsmi_get_socket_handles() + + for socket in socket_handles: + nic_count = ctypes.c_uint32() + null_ptr = ctypes.POINTER(amdsmi_wrapper.amdsmi_processor_handle)() + + # First call to get the count of NIC processors + _check_res( + amdsmi_wrapper.amdsmi_get_processor_handles_by_type( + socket, + nic_type, + null_ptr, + ctypes.byref(nic_count), + ) + ) + + if nic_count.value > 0: + c_handles = (amdsmi_wrapper.amdsmi_processor_handle * nic_count.value)() + _check_res( + amdsmi_wrapper.amdsmi_get_processor_handles_by_type( + socket, + nic_type, + c_handles, + ctypes.byref(nic_count) + ) + ) + + nic_handles.extend([ + amdsmi_wrapper.amdsmi_processor_handle(c_handles[dev_idx]) + for dev_idx in range(nic_count.value) + ]) + return nic_handles + +def amdsmi_get_processor_handles_devices() -> List[amdsmi_wrapper.amdsmi_processor_handle]: + + socket_handles = amdsmi_get_socket_handles() # Assuming this retrieves socket handles + gpu_handles = [] + + # Retrieve GPU handles + gpu_handles.extend(get_gpu_handles()) + + # Retrieve NIC handles + nic_handles = get_nic_handles() + gpu_handles.extend(nic_handles) + + # Retrieve Switch handles + switch_handles = get_switch_handles() + gpu_handles.extend(switch_handles) + + ainic_handles = get_ainic_handles() + gpu_handles.extend(ainic_handles) + + gpu_handles_count = len(gpu_handles) + #print(f"Total GPU and NIC handles: {gpu_handles_count}") + + return gpu_handles + def amdsmi_get_cpucore_handles() -> List[c_void_p]: cores_count = ctypes.c_uint32(0) null_ptr = POINTER(amdsmi_wrapper.amdsmi_processor_handle)() @@ -2017,8 +2203,21 @@ def amdsmi_get_cpu_socket_count(): ) return sock_count.value +def _amdsmi_init_enum_flag_is_valid(flag): + """Validate that flag contains only valid initialization bits.""" + if flag == amdsmi_wrapper.AMDSMI_INIT_ALL_PROCESSORS: + return True + + # Build mask of all valid flags (excluding ALL_PROCESSORS) + valid_mask = 0 + for enum_flag in AmdSmiInitFlags: + if enum_flag != AmdSmiInitFlags.INIT_ALL_PROCESSORS: + valid_mask |= enum_flag.value + # Check if flag contains only valid bits and is not zero + return flag != 0 and (flag & ~valid_mask) == 0 + def amdsmi_init(flag=AmdSmiInitFlags.INIT_AMD_GPUS): - if not isinstance(flag, AmdSmiInitFlags): + if not _amdsmi_init_enum_flag_is_valid(flag): raise AmdSmiParameterException(flag, AmdSmiInitFlags) _check_res(amdsmi_wrapper.amdsmi_init(flag)) @@ -2057,9 +2256,298 @@ def amdsmi_get_gpu_device_bdf(processor_handle: processor_handle_t) -> str: amdsmi_wrapper.amdsmi_get_gpu_device_bdf( processor_handle, ctypes.byref(bdf_info)) ) + + return _format_bdf(bdf_info.struct_amdsmi_bdf_t) + +def amdsmi_get_gpu_device_bdf_bdf(processor_handle: amdsmi_wrapper.amdsmi_processor_handle) -> amdsmi_wrapper.amdsmi_bdf_t: + if not isinstance(processor_handle, amdsmi_wrapper.amdsmi_processor_handle): + raise AmdSmiParameterException( + processor_handle, amdsmi_wrapper.amdsmi_processor_handle + ) + + bdf_info = amdsmi_wrapper.amdsmi_bdf_t() + _check_res( + amdsmi_wrapper.amdsmi_get_gpu_device_bdf( + processor_handle, ctypes.byref(bdf_info)) + ) + + return bdf_info + +def amdsmi_get_nic_info( + processor_handle: amdsmi_wrapper.amdsmi_processor_handle, +) -> Dict[str, Any]: + if not isinstance(processor_handle, amdsmi_wrapper.amdsmi_processor_handle): + raise AmdSmiParameterException( + processor_handle, amdsmi_wrapper.amdsmi_processor_handle + ) + return None # disabled feature + +def amdsmi_get_ainic_info_summary(nic_info): + return { + "bdf": _format_bdf(nic_info.bus.bdf.struct_amdsmi_bdf_t), + "UUID": nic_info.asic.permanent_address.decode('utf-8'), + "Permanent Address": nic_info.asic.permanent_address.decode('utf-8'), + # "Device Name": nic_info.asic.product_name.decode('utf-8'), + "Product Name": nic_info.asic.product_name.decode('utf-8'), + "Part Number": nic_info.asic.part_number.decode('utf-8'), + "Serial Number": nic_info.asic.serial_number.decode('utf-8'), + "Vendor Name": nic_info.asic.vendor_name.decode('utf-8') + # "Firmware_Version": nic_info.versions.running.fw.decode('utf-8'), + } + +def amdsmi_get_ainic_info_detail(ainic_info_struct): + ainic_info = { + "ASIC":{ + "VENDOR_ID": hex(ainic_info_struct.asic.vendor_id), + "SUBVENDOR_ID": hex(ainic_info_struct.asic.subvendor_id), + "DEVICE_ID": hex(ainic_info_struct.asic.device_id), + "SUBSYSTEM_ID": hex(ainic_info_struct.asic.subsystem_id), + "REVISION": hex(ainic_info_struct.asic.revision), + "PERMANENT_ADDRESS": ainic_info_struct.asic.permanent_address.decode('utf-8'), + "PRODUCT_NAME": ainic_info_struct.asic.product_name.decode('utf-8'), + "PART_NUMBER": ainic_info_struct.asic.part_number.decode('utf-8'), + "SERIAL_NUMBER": ainic_info_struct.asic.serial_number.decode('utf-8'), + "VENDOR_NAME": ainic_info_struct.asic.vendor_name.decode('utf-8') + }, + "BUS":{ + "bdf": _format_bdf(ainic_info_struct.bus.bdf.struct_amdsmi_bdf_t), + "MAX_PCIE_WIDTH": int(ainic_info_struct.bus.max_pcie_width), + "MAX_PCIE_SPEED": ainic_info_struct.bus.max_pcie_speed, + "PCIE_INTERFACE_VERSION": ainic_info_struct.bus.pcie_interface_version.decode('utf-8'), + "SLOT_TYPE": ainic_info_struct.bus.slot_type.decode('utf-8'), + }, + "DRIVER":{ + "NAME": ainic_info_struct.driver.name.decode('utf-8'), + "VERSION": ainic_info_struct.driver.version.decode('utf-8'), + }, + "NUMA":{ + "NODE": ainic_info_struct.numa.node, + "AFFINITY": ainic_info_struct.numa.affinity.decode('utf-8') + }, + "FW":{ + "FW_0": ainic_info_struct.driver.name.decode('utf-8'), + "FW_HEARTBEAT": ainic_info_struct.driver.version.decode('utf-8') + }, + "PORTS":{}, + "RDMA_DEVICES":{} + } + port_num = 0 + total_rdma_dev_num = 0 + for port_info in ainic_info_struct.port.ports: + if port_num == ainic_info_struct.port.num_ports: + break + ainic_info["PORTS"][f"PORT_{port_num}"] = { + "TYPE" : port_info.type.decode('utf-8'), + "FLAVOUR" : port_info.flavour.decode('utf-8'), + "NETDEV" : port_info.netdev.decode('utf-8'), + "IFINDEX" : port_info.ifindex, + "MAC_ADDRESS" : port_info.mac_address.decode('utf-8'), + "CARRIER" : port_info.carrier, + "MTU" : f"{port_info.mtu}B", + "LINK_STATE" : port_info.link_state.decode('utf-8'), + "LINK_SPEED" : f"{port_info.link_speed} Mb/s", + "ACTIVE_FEC" : f"{port_info.active_fec}", + "AUTONEG" : port_info.autoneg.decode('utf-8'), + "PAUSE_AUTONEG" : port_info.pause_autoneg.decode('utf-8'), + "PAUSE_RX" : port_info.pause_rx.decode('utf-8'), + "PAUSE_TX" : port_info.pause_tx.decode('utf-8'), + } + rdma_dev_num = 0 + for dev in ainic_info_struct.rdma_dev.rdma_dev_info: + if rdma_dev_num == ainic_info_struct.rdma_dev.num_rdma_dev: + break + ainic_info["RDMA_DEVICES"][f"RDMA_DEVICE_{total_rdma_dev_num}"] = { + "NAME": dev.rdma_dev.decode('utf-8'), + "NODE_GUID": dev.node_guid.decode('utf-8'), + "NODE_TYPE": dev.node_type.decode('utf-8'), + "SYS_IMAGE_GUID": dev.sys_image_guid.decode('utf-8'), + "FW_VER": dev.fw_ver.decode('utf-8') + } + rdma_port_num = 0 + for rdma_port in dev.rdma_port_info: + if rdma_port_num == ainic_info_struct.rdma_dev.rdma_dev_info[rdma_dev_num].num_rdma_ports: + break + max_mtu = rdma_port.max_mtu + max_uint16_t = (1 << 16) - 1 + if max_mtu == max_uint16_t: + max_mtu = "N/A" + active_mtu = rdma_port.active_mtu + if active_mtu == max_uint16_t: + active_mtu = "N/A" + ainic_info["RDMA_DEVICES"][f"RDMA_DEVICE_{total_rdma_dev_num}"][f"PORT_{rdma_port_num}"] = { + "NETDEV": rdma_port.netdev.decode('utf-8'), + "PORT_NUM": rdma_port.rdma_port, + "STATE": rdma_port.state.decode('utf-8'), + "MAX_MTU": max_mtu, + "ACTIVE_MTU": active_mtu + } + rdma_port_num = rdma_port_num + 1 + rdma_dev_num = rdma_dev_num + 1 + total_rdma_dev_num = total_rdma_dev_num + 1 + port_num = port_num + 1 + return ainic_info + +class ainic_info_t: + def __init__(self, processor_handle): + self.asic = amdsmi_wrapper.amdsmi_nic_asic_info_t() + self.bus = amdsmi_wrapper.amdsmi_nic_bus_info_t() + self.driver = amdsmi_wrapper.amdsmi_nic_driver_info_t() + self.numa = amdsmi_wrapper.amdsmi_nic_numa_info_t() + self.port = amdsmi_wrapper.amdsmi_nic_port_info_t() + self.rdma_dev = amdsmi_wrapper.amdsmi_nic_rdma_devices_info_t() + _check_res( + amdsmi_wrapper.amdsmi_get_nic_asic_info( + processor_handle, ctypes.byref(self.asic)) + ) + _check_res( + amdsmi_wrapper.amdsmi_get_nic_bus_info( + processor_handle, ctypes.byref(self.bus)) + ) + _check_res( + amdsmi_wrapper.amdsmi_get_nic_driver_info( + processor_handle, ctypes.byref(self.driver)) + ) + _check_res( + amdsmi_wrapper.amdsmi_get_nic_numa_info( + processor_handle, ctypes.byref(self.numa)) + ) + _check_res( + amdsmi_wrapper.amdsmi_get_nic_port_info( + processor_handle, ctypes.byref(self.port)) + ) + _check_res( + amdsmi_wrapper.amdsmi_get_nic_rdma_dev_info( + processor_handle, ctypes.byref(self.rdma_dev)) + ) + +def amdsmi_get_ainic_info( + processor_handle: amdsmi_wrapper.amdsmi_processor_handle, + detail = False, +) -> Dict[str, Any]: + if not isinstance(processor_handle, amdsmi_wrapper.amdsmi_processor_handle): + raise AmdSmiParameterException( + processor_handle, amdsmi_wrapper.amdsmi_processor_handle + ) + nic_info = ainic_info_t(processor_handle) + if detail: + return amdsmi_get_ainic_info_detail(nic_info) + else: + return amdsmi_get_ainic_info_summary(nic_info) + +def amdsmi_get_switch_device_bdf(processor_handle: amdsmi_wrapper.amdsmi_processor_handle) -> str: + + if not isinstance(processor_handle, amdsmi_wrapper.amdsmi_processor_handle): + raise AmdSmiParameterException( + processor_handle, amdsmi_wrapper.amdsmi_processor_handle + + ) + + bdf_info = amdsmi_wrapper.amdsmi_bdf_t() + _check_res( + amdsmi_wrapper.amdsmi_get_switch_device_bdf( + processor_handle, ctypes.byref(bdf_info)) + ) - return _format_bdf(bdf_info) + return _format_bdf( bdf_info.struct_amdsmi_bdf_t) +def amdsmi_get_nic_temp_info( + processor_handle: amdsmi_wrapper.amdsmi_processor_handle, +) -> Dict[str, ctypes.c_uint32]: + if not isinstance(processor_handle, amdsmi_wrapper.amdsmi_processor_handle): + raise AmdSmiParameterException( + processor_handle, amdsmi_wrapper.amdsmi_processor_handle + ) + + power_measure = amdsmi_wrapper.amdsmi_brcm_nic_temperature_metric_t() + _check_res( + amdsmi_wrapper.amdsmi_get_nic_temp_info( + processor_handle, ctypes.byref(power_measure) + ) + ) + + temp_info_dict = { + "NIC_TEMP_CURRENT": math.trunc(power_measure.nic_temp_input / 1000), + "NIC_TEMP_CRIT_ALARM": power_measure.nic_temp_crit_alarm, + "NIC_TEMP_EMERGENCY_ALARM": power_measure.nic_temp_emergency_alarm, + "NIC_TEMP_SHUTDOWN_ALARM": power_measure.nic_temp_shutdown_alarm, + "NIC_TEMP_MAX_ALARM": power_measure.nic_temp_max_alarm, + } + for key, value in temp_info_dict.items(): + if value == 0xFFFF: + temp_info_dict[key] = "N/A" + + return temp_info_dict + +def amdsmi_get_nic_fw_info( + processor_handle: amdsmi_wrapper.amdsmi_processor_handle, +) -> Dict[str, ctypes.c_uint32]: + if not isinstance(processor_handle, amdsmi_wrapper.amdsmi_processor_handle): + raise AmdSmiParameterException( + processor_handle, amdsmi_wrapper.amdsmi_processor_handle + ) + + fw_info = amdsmi_wrapper.struct_amdsmi_brcm_nic_firmware_t() + _check_res( + amdsmi_wrapper.amdsmi_get_nic_fw_info( + processor_handle, ctypes.byref(fw_info) + ) + ) + + fw_info_dict = { + "Package Version": fw_info.nic_fw_pkg_version.decode("utf-8"), + "EFI Version": fw_info.nic_fw_efi_version.decode("utf-8"), + "Firmware Version": fw_info.nic_fw_version.decode("utf-8"), + "NCSI Version": fw_info.nic_fw_ncsi_version.decode("utf-8"), + "RoCE Version": fw_info.nic_fw_roce_version.decode("utf-8"), + } + for key, value in fw_info_dict.items(): + if value == "": + fw_info_dict[key] = "N/A" + + return fw_info_dict + +def amdsmi_get_switch_link_info( + processor_handle: amdsmi_wrapper.amdsmi_processor_handle, +) -> Dict[str, ctypes.c_uint32]: + if not isinstance(processor_handle, amdsmi_wrapper.amdsmi_processor_handle): + raise AmdSmiParameterException( + processor_handle, amdsmi_wrapper.amdsmi_processor_handle + ) + + power_measure = amdsmi_wrapper.struct_amdsmi_brcm_switch_link_metric_t() + _check_res( + amdsmi_wrapper.amdsmi_get_switch_link_info( + processor_handle, ctypes.byref(power_measure) + ) + ) + + link_info_dict = { + "CURRENT_LINK_SPEED": power_measure.current_link_speed, + "MAX_LINK_SPEED": power_measure.max_link_speed, + "CURRENT_LINK_WIDTH": power_measure.current_link_width, + "MAX_LINK_WIDTH": power_measure.max_link_width, + + } + for key, value in link_info_dict.items(): + if value == 0xFFFF: + link_info_dict[key] = "N/A" + + return link_info_dict + +def amdsmi_get_root_switch(amdsmi_bdf: amdsmi_wrapper.amdsmi_bdf_t)-> str: + if not isinstance(amdsmi_bdf, amdsmi_wrapper.amdsmi_bdf_t): + raise AmdSmiParameterException( + amdsmi_bdf, amdsmi_wrapper.amdsmi_bdf_t + ) + + switch_bdf_info = amdsmi_wrapper.amdsmi_bdf_t() + + _check_res( + amdsmi_wrapper.amdsmi_get_root_switch( + amdsmi_bdf, ctypes.byref(switch_bdf_info)) + ) + + return _format_bdf(switch_bdf_info.struct_amdsmi_bdf_t) def amdsmi_get_gpu_device_uuid(processor_handle: processor_handle_t) -> str: if not isinstance(processor_handle, amdsmi_wrapper.amdsmi_processor_handle): @@ -3081,6 +3569,63 @@ def amdsmi_get_gpu_process_list( return result +def amdsmi_get_nic_fw_version(processor_handle: amdsmi_wrapper.amdsmi_processor_handle) -> str: + if not isinstance(processor_handle, amdsmi_wrapper.amdsmi_processor_handle): + raise AmdSmiParameterException( + processor_handle, amdsmi_wrapper.amdsmi_processor_handle + ) + + uuid = ctypes.create_string_buffer(_AMDSMI_MAX_STRING_LENGTH) + + uuid_length = ctypes.c_uint32() + uuid_length.value = _AMDSMI_MAX_STRING_LENGTH + + _check_res( + amdsmi_wrapper.amdsmi_get_nic_fw_version( + processor_handle, ctypes.byref(uuid_length), uuid + ) + ) + return uuid.value.decode("utf-8") + +def amdsmi_get_nic_device_uuid(processor_handle: amdsmi_wrapper.amdsmi_processor_handle) -> str: + + if not isinstance(processor_handle, amdsmi_wrapper.amdsmi_processor_handle): + raise AmdSmiParameterException( + processor_handle, amdsmi_wrapper.amdsmi_processor_handle + ) + + uuid = ctypes.create_string_buffer(AMDSMI_GPU_UUID_SIZE) + + uuid_length = ctypes.c_uint32() + uuid_length.value = AMDSMI_GPU_UUID_SIZE + + _check_res( + amdsmi_wrapper.amdsmi_get_nic_device_uuid( + processor_handle, ctypes.byref(uuid_length), uuid + ) + ) + + return uuid.value.decode("utf-8") + +def amdsmi_get_switch_device_uuid(processor_handle: amdsmi_wrapper.amdsmi_processor_handle) -> str: + + if not isinstance(processor_handle, amdsmi_wrapper.amdsmi_processor_handle): + raise AmdSmiParameterException( + processor_handle, amdsmi_wrapper.amdsmi_processor_handle + ) + + uuid = ctypes.create_string_buffer(AMDSMI_GPU_UUID_SIZE) + + uuid_length = ctypes.c_uint32() + uuid_length.value = AMDSMI_GPU_UUID_SIZE + + _check_res( + amdsmi_wrapper.amdsmi_get_switch_device_uuid( + processor_handle, ctypes.byref(uuid_length), uuid + ) + ) + + return uuid.value.decode("utf-8") def amdsmi_get_gpu_driver_info( processor_handle: processor_handle_t, @@ -4304,6 +4849,117 @@ def amdsmi_get_gpu_topo_numa_affinity(processor_handle: processor_handle_t): return numa_node.value +def amdsmi_get_nic_topo_numa_affinity(processor_handle: amdsmi_wrapper.amdsmi_processor_handle): + if not isinstance(processor_handle, amdsmi_wrapper.amdsmi_processor_handle): + raise AmdSmiParameterException( + processor_handle, amdsmi_wrapper.amdsmi_processor_handle + ) + + numa_node = ctypes.c_int32() + + _check_res( + amdsmi_wrapper.amdsmi_get_nic_topo_numa_affinity( + processor_handle, ctypes.byref(numa_node)) + ) + + return numa_node.value + +def amdsmi_get_switch_topo_numa_affinity(processor_handle: amdsmi_wrapper.amdsmi_processor_handle): + if not isinstance(processor_handle, amdsmi_wrapper.amdsmi_processor_handle): + raise AmdSmiParameterException( + processor_handle, amdsmi_wrapper.amdsmi_processor_handle + ) + + numa_node = ctypes.c_int32() + + _check_res( + amdsmi_wrapper.amdsmi_get_switch_topo_numa_affinity( + processor_handle, ctypes.byref(numa_node)) + ) + + return numa_node.value + +def amdsmi_get_gpu_topo_cpu_affinity(processor_handle: amdsmi_wrapper.amdsmi_processor_handle): + if not isinstance(processor_handle, amdsmi_wrapper.amdsmi_processor_handle): + raise AmdSmiParameterException( + processor_handle, amdsmi_wrapper.amdsmi_processor_handle + ) + + gpucpuaffid = ctypes.create_string_buffer(_AMDSMI_MAX_STRING_LENGTH) + + gpucpuaffid_length = ctypes.c_uint32() + gpucpuaffid_length.value = _AMDSMI_MAX_STRING_LENGTH + + _check_res( + amdsmi_wrapper.amdsmi_get_gpu_topo_cpu_affinity( + processor_handle, ctypes.byref(gpucpuaffid_length), gpucpuaffid + ) + ) + return gpucpuaffid.value.decode("utf-8") + + +def amdsmi_get_nic_topo_cpu_affinity(processor_handle: amdsmi_wrapper.amdsmi_processor_handle): + if not isinstance(processor_handle, amdsmi_wrapper.amdsmi_processor_handle): + raise AmdSmiParameterException( + processor_handle, amdsmi_wrapper.amdsmi_processor_handle + ) + + niccpuaffid = ctypes.create_string_buffer(_AMDSMI_MAX_STRING_LENGTH) + + niccpuaffid_length = ctypes.c_uint32() + niccpuaffid_length.value = _AMDSMI_MAX_STRING_LENGTH + + _check_res( + amdsmi_wrapper.amdsmi_get_nic_topo_cpu_affinity( + processor_handle, ctypes.byref(niccpuaffid_length), niccpuaffid + ) + ) + return niccpuaffid.value.decode("utf-8") + +def amdsmi_get_switch_topo_cpu_affinity(processor_handle: amdsmi_wrapper.amdsmi_processor_handle): + if not isinstance(processor_handle, amdsmi_wrapper.amdsmi_processor_handle): + raise AmdSmiParameterException( + processor_handle, amdsmi_wrapper.amdsmi_processor_handle + ) + + switchcpuaffid = ctypes.create_string_buffer(_AMDSMI_MAX_STRING_LENGTH) + + switchcpuaffid_length = ctypes.c_uint32() + switchcpuaffid_length.value = _AMDSMI_MAX_STRING_LENGTH + + _check_res( + amdsmi_wrapper.amdsmi_get_switch_topo_cpu_affinity( + processor_handle, ctypes.byref(switchcpuaffid_length), switchcpuaffid + ) + ) + return switchcpuaffid.value.decode("utf-8") + + +def amdsmi_get_nic_gpu_topo_info( processor_handle_src: amdsmi_wrapper.amdsmi_processor_handle, + processor_handle_dst: amdsmi_wrapper.amdsmi_processor_handle): + + if not isinstance(processor_handle_src, amdsmi_wrapper.amdsmi_processor_handle): + raise AmdSmiParameterException( + processor_handle_src, amdsmi_wrapper.amdsmi_processor_handle + ) + if not isinstance(processor_handle_dst, amdsmi_wrapper.amdsmi_processor_handle): + raise AmdSmiParameterException( + processor_handle_dst, amdsmi_wrapper.amdsmi_processor_handle + ) + + niccgpuinfo = ctypes.create_string_buffer(_AMDSMI_MAX_STRING_LENGTH) + + niccgpuinfo_length = ctypes.c_uint32() + niccgpuinfo_length.value = _AMDSMI_MAX_STRING_LENGTH + + _check_res( + amdsmi_wrapper.amdsmi_get_nic_gpu_topo_info( + processor_handle_src,processor_handle_dst, ctypes.byref(niccgpuinfo_length), niccgpuinfo + ) + ) + return niccgpuinfo.value.decode("utf-8") + + def amdsmi_set_power_cap( processor_handle: processor_handle_t, sensor_ind: int, cap: int @@ -5297,6 +5953,100 @@ def amdsmi_get_gpu_partition_metrics_info( gpu_metrics_output['xcp_stats.gfx_below_host_limit_total_acc'][xcp_index] = xcp_detail return gpu_metrics_output +def amdsmi_get_nic_metrics_info( + processor_handle: amdsmi_wrapper.amdsmi_processor_handle, +) -> Dict[str, Any]: + if not isinstance(processor_handle, amdsmi_wrapper.amdsmi_processor_handle): + raise AmdSmiParameterException( + processor_handle, amdsmi_wrapper.amdsmi_processor_handle + ) + + #Create data type + nic_metrics = amdsmi_wrapper.amdsmi_brcm_nic_hwmon_metrics_t() + nic_power_metrics = amdsmi_wrapper.amdsmi_brcm_nic_hwmon_power_t() + nic_temperature_metrics = amdsmi_wrapper.amdsmi_brcm_nic_temperature_metric_t() + + _check_res( + amdsmi_wrapper.amdsmi_get_nic_metrics_info( + processor_handle, ctypes.byref(nic_metrics) + ) + ) + + nic_power_metrics = nic_metrics.nic_power + nic_temperature_metrics = nic_metrics.nic_temperature + + nic_metrics_output = { + # Power attributes + "nic_power_async": nic_power_metrics.nic_power_async.decode("utf-8"), + "nic_power_control": nic_power_metrics.nic_power_control.decode("utf-8"), + "nic_power_runtime_active_time": nic_power_metrics.nic_power_runtime_active_time, + "nic_power_runtime_status": nic_power_metrics.nic_power_runtime_status.decode("utf-8"), + "nic_power_runtime_usage": nic_power_metrics.nic_power_runtime_usage, + "nic_power_runtime_active_kids": nic_power_metrics.nic_power_runtime_active_kids, + "nic_power_runtime_enabled": nic_power_metrics.nic_power_runtime_enabled.decode("utf-8"), + "nic_power_runtime_suspended_time": nic_power_metrics.nic_power_runtime_suspended_time, + # Temperature attributes + "nic_temp_crit_alarm": nic_temperature_metrics.nic_temp_crit_alarm, + "nic_temp_emergency_alarm": nic_temperature_metrics.nic_temp_emergency_alarm, + "nic_temp_shutdown_alarm": nic_temperature_metrics.nic_temp_shutdown_alarm, + "nic_temp_max_alarm": nic_temperature_metrics.nic_temp_max_alarm, + "nic_temp_crit": math.trunc(nic_temperature_metrics.nic_temp_crit / 1000), + "nic_temp_emergency": math.trunc(nic_temperature_metrics.nic_temp_emergency / 1000), + "nic_temp_input": math.trunc(nic_temperature_metrics.nic_temp_input / 1000), + "nic_temp_max": math.trunc(nic_temperature_metrics.nic_temp_max / 1000), + "nic_temp_shutdown": math.trunc(nic_temperature_metrics.nic_temp_shutdown / 1000), + # Error attributes + "nic_dev_correctable": nic_metrics.nic_device_aer_dev_correctable.decode("utf-8"), + "nic_dev_fatal": nic_metrics.nic_device_aer_dev_fatal.decode("utf-8"), + "nic_dev_nonfatal": nic_metrics.nic_device_aer_dev_nonfatal.decode("utf-8"), + } + + return nic_metrics_output + +def amdsmi_get_switch_metrics_info( + processor_handle: amdsmi_wrapper.amdsmi_processor_handle, +) -> Dict[str, Any]: + if not isinstance(processor_handle, amdsmi_wrapper.amdsmi_processor_handle): + raise AmdSmiParameterException( + processor_handle, amdsmi_wrapper.amdsmi_processor_handle + ) + + #Create data type + switch_metrics = amdsmi_wrapper.struct_amdsmi_brcm_switch_metric_t() + switch_power_metrics = amdsmi_wrapper.amdsmi_brcm_switch_power_metric_t() + + _check_res( + amdsmi_wrapper.amdsmi_get_switch_metrics_info( + processor_handle, ctypes.byref(switch_metrics) + ) + ) + + switch_power_metrics = switch_metrics.brcm_power + + switch_metrics_output = { + "brcm_power_async": switch_power_metrics.brcm_power_async.decode("utf-8"), + "brcm_power_control": switch_power_metrics.brcm_power_control.decode("utf-8"), + "brcm_power_runtime_active_kids": switch_power_metrics.brcm_power_runtime_active_kids.decode("utf-8"), + "brcm_power_runtime_active_time": switch_power_metrics.brcm_power_runtime_active_time.decode("utf-8"), + "brcm_power_runtime_enabled": switch_power_metrics.brcm_power_runtime_enabled.decode("utf-8"), + "brcm_power_runtime_status": switch_power_metrics.brcm_power_runtime_status.decode("utf-8"), + "brcm_power_runtime_suspended_time": switch_power_metrics.brcm_power_runtime_suspended_time.decode("utf-8"), + "brcm_power_runtime_usage": switch_power_metrics.brcm_power_runtime_usage.decode("utf-8"), + "brcm_power_wakeup": switch_power_metrics.brcm_power_wakeup.decode("utf-8"), + "brcm_power_wakeup_abort_count": switch_power_metrics.brcm_power_wakeup_abort_count.decode("utf-8"), + "brcm_power_wakeup_active": switch_power_metrics.brcm_power_wakeup_active.decode("utf-8"), + "brcm_power_wakeup_active_count": switch_power_metrics.brcm_power_wakeup_active_count.decode("utf-8"), + "brcm_power_wakeup_count": switch_power_metrics.brcm_power_wakeup_count.decode("utf-8"), + "brcm_power_wakeup_last_time_ms": switch_power_metrics.brcm_power_wakeup_last_time_ms.decode("utf-8"), + "brcm_power_wakeup_max_time_ms": switch_power_metrics.brcm_power_wakeup_max_time_ms.decode("utf-8"), + "brcm_power_wakeup_total_time_ms": switch_power_metrics.brcm_power_wakeup_total_time_ms.decode("utf-8"), + # Error attributes + "brcm_device_aer_dev_correctable": switch_metrics.brcm_device_aer_dev_correctable.decode("utf-8"), + "brcm_device_aer_dev_fatal": switch_metrics.brcm_device_aer_dev_fatal.decode("utf-8"), + "brcm_device_aer_dev_nonfatal": switch_metrics.brcm_device_aer_dev_nonfatal.decode("utf-8"), + } + + return switch_metrics_output def amdsmi_get_gpu_od_volt_curve_regions( processor_handle: processor_handle_t, num_regions: int diff --git a/projects/amdsmi/py-interface/amdsmi_wrapper.py b/projects/amdsmi/py-interface/amdsmi_wrapper.py index 1baca3c5ced..b9c21fed187 100644 --- a/projects/amdsmi/py-interface/amdsmi_wrapper.py +++ b/projects/amdsmi/py-interface/amdsmi_wrapper.py @@ -218,16 +218,6 @@ def find_smi_library(): amdsmi_free_name_value_pairs = _libraries['libamd_smi.so'].amdsmi_free_name_value_pairs amdsmi_free_name_value_pairs.restype = None amdsmi_free_name_value_pairs.argtypes = [ctypes.POINTER(None)] -class FunctionFactoryStub: - def __getattr__(self, _): - return ctypes.CFUNCTYPE(lambda y:y) - -# libraries['FIXME_STUB'] explanation -# As you did not list (-l libraryname.so) a library that exports this function -# This is a non-working stub instead. -# You can either re-run clan2py with -l /path/to/library.so -# Or manually fix this by comment the ctypes.CDLL loading -_libraries['FIXME_STUB'] = FunctionFactoryStub() # ctypes.CDLL('FIXME_STUB') @@ -239,6 +229,7 @@ def __getattr__(self, _): 4: 'AMDSMI_INIT_NON_AMD_CPUS', 8: 'AMDSMI_INIT_NON_AMD_GPUS', 3: 'AMDSMI_INIT_AMD_APUS', + 16: 'AMDSMI_INIT_AMD_NICS', } AMDSMI_INIT_ALL_PROCESSORS = 4294967295 AMDSMI_INIT_AMD_CPUS = 1 @@ -246,6 +237,7 @@ def __getattr__(self, _): AMDSMI_INIT_NON_AMD_CPUS = 4 AMDSMI_INIT_NON_AMD_GPUS = 8 AMDSMI_INIT_AMD_APUS = 3 +AMDSMI_INIT_AMD_NICS = 16 amdsmi_init_flags_t = ctypes.c_uint32 # enum # values for enumeration 'amdsmi_mm_ip_t' @@ -294,6 +286,8 @@ class struct_amdsmi_hsmp_driver_version_t(Structure): 5: 'AMDSMI_PROCESSOR_TYPE_AMD_CPU_CORE', 6: 'AMDSMI_PROCESSOR_TYPE_AMD_APU', 7: 'AMDSMI_PROCESSOR_TYPE_AMD_NIC', + 8: 'AMDSMI_PROCESSOR_TYPE_BRCM_NIC', + 9: 'AMDSMI_PROCESSOR_TYPE_BRCM_SWITCH', } AMDSMI_PROCESSOR_TYPE_UNKNOWN = 0 AMDSMI_PROCESSOR_TYPE_AMD_GPU = 1 @@ -303,6 +297,8 @@ class struct_amdsmi_hsmp_driver_version_t(Structure): AMDSMI_PROCESSOR_TYPE_AMD_CPU_CORE = 5 AMDSMI_PROCESSOR_TYPE_AMD_APU = 6 AMDSMI_PROCESSOR_TYPE_AMD_NIC = 7 +AMDSMI_PROCESSOR_TYPE_BRCM_NIC = 8 +AMDSMI_PROCESSOR_TYPE_BRCM_SWITCH = 9 processor_type_t = ctypes.c_uint32 # enum # values for enumeration 'amdsmi_status_t' @@ -2579,6 +2575,178 @@ class struct_amdsmi_sock_info_t(Structure): ] amdsmi_sock_info_t = struct_amdsmi_sock_info_t + +# values for enumeration 'amdsmi_nic_link_type_t' +amdsmi_nic_link_type_t__enumvalues = { + 0: 'AMDSMI_NIC_LINK_TYPE_UNKNOWN', + 1: 'AMDSMI_NIC_LINK_TYPE_PCIE', + 2: 'AMDSMI_NIC_LINK_TYPE_NUMA', + 3: 'AMDSMI_NIC_LINK_TYPE_X_NUMA', +} +AMDSMI_NIC_LINK_TYPE_UNKNOWN = 0 +AMDSMI_NIC_LINK_TYPE_PCIE = 1 +AMDSMI_NIC_LINK_TYPE_NUMA = 2 +AMDSMI_NIC_LINK_TYPE_X_NUMA = 3 +amdsmi_nic_link_type_t = ctypes.c_uint32 # enum +class struct_amdsmi_nic_stat_t(Structure): + pass + +struct_amdsmi_nic_stat_t._pack_ = 1 # source:False +struct_amdsmi_nic_stat_t._fields_ = [ + ('name', ctypes.c_char * 256), + ('value', ctypes.c_uint64), +] + +amdsmi_nic_stat_t = struct_amdsmi_nic_stat_t +class struct_amdsmi_nic_asic_info_t(Structure): + pass + +struct_amdsmi_nic_asic_info_t._pack_ = 1 # source:False +struct_amdsmi_nic_asic_info_t._fields_ = [ + ('vendor_id', ctypes.c_uint16), + ('subvendor_id', ctypes.c_uint16), + ('device_id', ctypes.c_uint16), + ('subsystem_id', ctypes.c_uint16), + ('revision', ctypes.c_ubyte), + ('permanent_address', ctypes.c_char * 256), + ('product_name', ctypes.c_char * 256), + ('part_number', ctypes.c_char * 256), + ('serial_number', ctypes.c_char * 256), + ('vendor_name', ctypes.c_char * 256), + ('PADDING_0', ctypes.c_ubyte), +] + +amdsmi_nic_asic_info_t = struct_amdsmi_nic_asic_info_t +class struct_amdsmi_nic_bus_info_t(Structure): + pass + +struct_amdsmi_nic_bus_info_t._pack_ = 1 # source:False +struct_amdsmi_nic_bus_info_t._fields_ = [ + ('bdf', amdsmi_bdf_t), + ('max_pcie_width', ctypes.c_ubyte), + ('PADDING_0', ctypes.c_ubyte * 3), + ('max_pcie_speed', ctypes.c_uint32), + ('pcie_interface_version', ctypes.c_char * 256), + ('slot_type', ctypes.c_char * 256), +] + +amdsmi_nic_bus_info_t = struct_amdsmi_nic_bus_info_t +class struct_amdsmi_nic_numa_info_t(Structure): + pass + +struct_amdsmi_nic_numa_info_t._pack_ = 1 # source:False +struct_amdsmi_nic_numa_info_t._fields_ = [ + ('node', ctypes.c_ubyte), + ('affinity', ctypes.c_char * 256), +] + +amdsmi_nic_numa_info_t = struct_amdsmi_nic_numa_info_t +class struct_amdsmi_nic_fw_t(Structure): + pass + +struct_amdsmi_nic_fw_t._pack_ = 1 # source:False +struct_amdsmi_nic_fw_t._fields_ = [ + ('name', ctypes.c_char * 256), + ('version', ctypes.c_char * 256), +] + +amdsmi_nic_fw_t = struct_amdsmi_nic_fw_t +class struct_amdsmi_nic_fw_info_t(Structure): + pass + +struct_amdsmi_nic_fw_info_t._pack_ = 1 # source:False +struct_amdsmi_nic_fw_info_t._fields_ = [ + ('num_fw', ctypes.c_uint32), + ('fw', struct_amdsmi_nic_fw_t * 16), +] + +amdsmi_nic_fw_info_t = struct_amdsmi_nic_fw_info_t +class struct_amdsmi_nic_port_t(Structure): + pass + +struct_amdsmi_nic_port_t._pack_ = 1 # source:False +struct_amdsmi_nic_port_t._fields_ = [ + ('bdf', amdsmi_bdf_t), + ('port_num', ctypes.c_uint32), + ('type', ctypes.c_char * 256), + ('flavour', ctypes.c_char * 256), + ('netdev', ctypes.c_char * 256), + ('ifindex', ctypes.c_ubyte), + ('mac_address', ctypes.c_char * 256), + ('carrier', ctypes.c_ubyte), + ('mtu', ctypes.c_uint16), + ('link_state', ctypes.c_char * 256), + ('link_speed', ctypes.c_uint32), + ('active_fec', ctypes.c_uint32), + ('autoneg', ctypes.c_char * 256), + ('pause_autoneg', ctypes.c_char * 256), + ('pause_rx', ctypes.c_char * 256), + ('pause_tx', ctypes.c_char * 256), +] + +amdsmi_nic_port_t = struct_amdsmi_nic_port_t +class struct_amdsmi_nic_port_info_t(Structure): + pass + +struct_amdsmi_nic_port_info_t._pack_ = 1 # source:False +struct_amdsmi_nic_port_info_t._fields_ = [ + ('num_ports', ctypes.c_uint32), + ('PADDING_0', ctypes.c_ubyte * 4), + ('ports', struct_amdsmi_nic_port_t * 32), +] + +amdsmi_nic_port_info_t = struct_amdsmi_nic_port_info_t +class struct_amdsmi_nic_driver_info_t(Structure): + pass + +struct_amdsmi_nic_driver_info_t._pack_ = 1 # source:False +struct_amdsmi_nic_driver_info_t._fields_ = [ + ('name', ctypes.c_char * 256), + ('version', ctypes.c_char * 256), +] + +amdsmi_nic_driver_info_t = struct_amdsmi_nic_driver_info_t +class struct_amdsmi_nic_rdma_port_info_t(Structure): + pass + +struct_amdsmi_nic_rdma_port_info_t._pack_ = 1 # source:False +struct_amdsmi_nic_rdma_port_info_t._fields_ = [ + ('netdev', ctypes.c_char * 256), + ('state', ctypes.c_char * 256), + ('rdma_port', ctypes.c_ubyte), + ('PADDING_0', ctypes.c_ubyte), + ('max_mtu', ctypes.c_uint16), + ('active_mtu', ctypes.c_uint16), +] + +amdsmi_nic_rdma_port_info_t = struct_amdsmi_nic_rdma_port_info_t +class struct_amdsmi_nic_rdma_dev_info_t(Structure): + pass + +struct_amdsmi_nic_rdma_dev_info_t._pack_ = 1 # source:False +struct_amdsmi_nic_rdma_dev_info_t._fields_ = [ + ('rdma_dev', ctypes.c_char * 256), + ('node_guid', ctypes.c_char * 256), + ('node_type', ctypes.c_char * 256), + ('sys_image_guid', ctypes.c_char * 256), + ('fw_ver', ctypes.c_char * 256), + ('num_rdma_ports', ctypes.c_ubyte), + ('PADDING_0', ctypes.c_ubyte), + ('rdma_port_info', struct_amdsmi_nic_rdma_port_info_t * 32), +] + +amdsmi_nic_rdma_dev_info_t = struct_amdsmi_nic_rdma_dev_info_t +class struct_amdsmi_nic_rdma_devices_info_t(Structure): + pass + +struct_amdsmi_nic_rdma_devices_info_t._pack_ = 1 # source:False +struct_amdsmi_nic_rdma_devices_info_t._fields_ = [ + ('num_rdma_dev', ctypes.c_ubyte), + ('PADDING_0', ctypes.c_ubyte), + ('rdma_dev_info', struct_amdsmi_nic_rdma_dev_info_t * 32), +] + +amdsmi_nic_rdma_devices_info_t = struct_amdsmi_nic_rdma_devices_info_t uint64_t = ctypes.c_uint64 amdsmi_init = _libraries['libamd_smi.so'].amdsmi_init amdsmi_init.restype = amdsmi_status_t @@ -2589,9 +2757,6 @@ class struct_amdsmi_sock_info_t(Structure): amdsmi_get_socket_handles = _libraries['libamd_smi.so'].amdsmi_get_socket_handles amdsmi_get_socket_handles.restype = amdsmi_status_t amdsmi_get_socket_handles.argtypes = [ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.POINTER(None))] -amdsmi_get_index_from_processor_handle = _libraries['FIXME_STUB'].amdsmi_get_index_from_processor_handle -amdsmi_get_index_from_processor_handle.restype = amdsmi_status_t -amdsmi_get_index_from_processor_handle.argtypes = [amdsmi_processor_handle, ctypes.POINTER(ctypes.c_uint32)] amdsmi_get_cpu_handles = _libraries['libamd_smi.so'].amdsmi_get_cpu_handles amdsmi_get_cpu_handles.restype = amdsmi_status_t amdsmi_get_cpu_handles.argtypes = [ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.POINTER(None))] @@ -2602,9 +2767,6 @@ class struct_amdsmi_sock_info_t(Structure): amdsmi_get_processor_info = _libraries['libamd_smi.so'].amdsmi_get_processor_info amdsmi_get_processor_info.restype = amdsmi_status_t amdsmi_get_processor_info.argtypes = [amdsmi_processor_handle, size_t, ctypes.POINTER(ctypes.c_char)] -amdsmi_get_cpusocket_handles = _libraries['FIXME_STUB'].amdsmi_get_cpusocket_handles -amdsmi_get_cpusocket_handles.restype = amdsmi_status_t -amdsmi_get_cpusocket_handles.argtypes = [ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.POINTER(None))] amdsmi_get_processor_count_from_handles = _libraries['libamd_smi.so'].amdsmi_get_processor_count_from_handles amdsmi_get_processor_count_from_handles.restype = amdsmi_status_t amdsmi_get_processor_count_from_handles.argtypes = [ctypes.POINTER(ctypes.POINTER(None)), ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.c_uint32)] @@ -2623,28 +2785,19 @@ class struct_amdsmi_sock_info_t(Structure): amdsmi_get_processor_type = _libraries['libamd_smi.so'].amdsmi_get_processor_type amdsmi_get_processor_type.restype = amdsmi_status_t amdsmi_get_processor_type.argtypes = [amdsmi_processor_handle, ctypes.POINTER(processor_type_t)] -uint32_t = ctypes.c_uint32 -amdsmi_get_processor_handle_from_index = _libraries['FIXME_STUB'].amdsmi_get_processor_handle_from_index -amdsmi_get_processor_handle_from_index.restype = amdsmi_status_t -amdsmi_get_processor_handle_from_index.argtypes = [uint32_t, ctypes.POINTER(ctypes.POINTER(None))] amdsmi_get_processor_handle_from_bdf = _libraries['libamd_smi.so'].amdsmi_get_processor_handle_from_bdf amdsmi_get_processor_handle_from_bdf.restype = amdsmi_status_t amdsmi_get_processor_handle_from_bdf.argtypes = [amdsmi_bdf_t, ctypes.POINTER(ctypes.POINTER(None))] amdsmi_get_gpu_device_bdf = _libraries['libamd_smi.so'].amdsmi_get_gpu_device_bdf amdsmi_get_gpu_device_bdf.restype = amdsmi_status_t amdsmi_get_gpu_device_bdf.argtypes = [amdsmi_processor_handle, ctypes.POINTER(union_amdsmi_bdf_t)] -amdsmi_get_processor_bdf = _libraries['FIXME_STUB'].amdsmi_get_processor_bdf -amdsmi_get_processor_bdf.restype = amdsmi_status_t -amdsmi_get_processor_bdf.argtypes = [amdsmi_processor_handle, ctypes.POINTER(union_amdsmi_bdf_t)] -amdsmi_get_processor_handle_from_uuid = _libraries['FIXME_STUB'].amdsmi_get_processor_handle_from_uuid -amdsmi_get_processor_handle_from_uuid.restype = amdsmi_status_t -amdsmi_get_processor_handle_from_uuid.argtypes = [ctypes.POINTER(ctypes.c_char), ctypes.POINTER(ctypes.POINTER(None))] amdsmi_get_gpu_device_uuid = _libraries['libamd_smi.so'].amdsmi_get_gpu_device_uuid amdsmi_get_gpu_device_uuid.restype = amdsmi_status_t amdsmi_get_gpu_device_uuid.argtypes = [amdsmi_processor_handle, ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.c_char)] amdsmi_get_gpu_enumeration_info = _libraries['libamd_smi.so'].amdsmi_get_gpu_enumeration_info amdsmi_get_gpu_enumeration_info.restype = amdsmi_status_t amdsmi_get_gpu_enumeration_info.argtypes = [amdsmi_processor_handle, ctypes.POINTER(struct_amdsmi_enumeration_info_t)] +uint32_t = ctypes.c_uint32 amdsmi_get_cpu_affinity_with_scope = _libraries['libamd_smi.so'].amdsmi_get_cpu_affinity_with_scope amdsmi_get_cpu_affinity_with_scope.restype = amdsmi_status_t amdsmi_get_cpu_affinity_with_scope.argtypes = [amdsmi_processor_handle, uint32_t, ctypes.POINTER(ctypes.c_uint64), amdsmi_affinity_scope_t] @@ -2938,12 +3091,6 @@ class struct_amdsmi_cper_hdr_t(Structure): amdsmi_get_gpu_ras_feature_info = _libraries['libamd_smi.so'].amdsmi_get_gpu_ras_feature_info amdsmi_get_gpu_ras_feature_info.restype = amdsmi_status_t amdsmi_get_gpu_ras_feature_info.argtypes = [amdsmi_processor_handle, ctypes.POINTER(struct_amdsmi_ras_feature_t)] -amdsmi_get_gpu_ras_policy_info = _libraries['FIXME_STUB'].amdsmi_get_gpu_ras_policy_info -amdsmi_get_gpu_ras_policy_info.restype = amdsmi_status_t -amdsmi_get_gpu_ras_policy_info.argtypes = [amdsmi_processor_handle, ctypes.POINTER(struct_amdsmi_gpu_ras_policy_info_t)] -amdsmi_get_bad_page_threshold = _libraries['FIXME_STUB'].amdsmi_get_bad_page_threshold -amdsmi_get_bad_page_threshold.restype = amdsmi_status_t -amdsmi_get_bad_page_threshold.argtypes = [amdsmi_processor_handle, ctypes.POINTER(ctypes.c_uint32)] amdsmi_get_gpu_cper_entries = _libraries['libamd_smi.so'].amdsmi_get_gpu_cper_entries amdsmi_get_gpu_cper_entries.restype = amdsmi_status_t amdsmi_get_gpu_cper_entries.argtypes = [amdsmi_processor_handle, uint32_t, ctypes.POINTER(ctypes.c_char), ctypes.POINTER(ctypes.c_uint64), ctypes.POINTER(ctypes.POINTER(struct_amdsmi_cper_hdr_t)), ctypes.POINTER(ctypes.c_uint64), ctypes.POINTER(ctypes.c_uint64)] @@ -3022,18 +3169,12 @@ class struct_amdsmi_cper_hdr_t(Structure): amdsmi_set_gpu_compute_partition = _libraries['libamd_smi.so'].amdsmi_set_gpu_compute_partition amdsmi_set_gpu_compute_partition.restype = amdsmi_status_t amdsmi_set_gpu_compute_partition.argtypes = [amdsmi_processor_handle, amdsmi_compute_partition_type_t] -amdsmi_reset_gpu_compute_partition = _libraries['FIXME_STUB'].amdsmi_reset_gpu_compute_partition -amdsmi_reset_gpu_compute_partition.restype = amdsmi_status_t -amdsmi_reset_gpu_compute_partition.argtypes = [amdsmi_processor_handle] amdsmi_get_gpu_memory_partition = _libraries['libamd_smi.so'].amdsmi_get_gpu_memory_partition amdsmi_get_gpu_memory_partition.restype = amdsmi_status_t amdsmi_get_gpu_memory_partition.argtypes = [amdsmi_processor_handle, ctypes.POINTER(ctypes.c_char), uint32_t] amdsmi_set_gpu_memory_partition = _libraries['libamd_smi.so'].amdsmi_set_gpu_memory_partition amdsmi_set_gpu_memory_partition.restype = amdsmi_status_t amdsmi_set_gpu_memory_partition.argtypes = [amdsmi_processor_handle, amdsmi_memory_partition_type_t] -amdsmi_reset_gpu_memory_partition = _libraries['FIXME_STUB'].amdsmi_reset_gpu_memory_partition -amdsmi_reset_gpu_memory_partition.restype = amdsmi_status_t -amdsmi_reset_gpu_memory_partition.argtypes = [amdsmi_processor_handle] amdsmi_get_gpu_memory_partition_config = _libraries['libamd_smi.so'].amdsmi_get_gpu_memory_partition_config amdsmi_get_gpu_memory_partition_config.restype = amdsmi_status_t amdsmi_get_gpu_memory_partition_config.argtypes = [amdsmi_processor_handle, ctypes.POINTER(struct_amdsmi_memory_partition_config_t)] @@ -3265,6 +3406,27 @@ class struct_amdsmi_cper_hdr_t(Structure): amdsmi_get_dfc_ctrl = _libraries['libamd_smi.so'].amdsmi_get_dfc_ctrl amdsmi_get_dfc_ctrl.restype = amdsmi_status_t amdsmi_get_dfc_ctrl.argtypes = [amdsmi_processor_handle, ctypes.POINTER(ctypes.c_ubyte)] +amdsmi_get_nic_driver_info = _libraries['libamd_smi.so'].amdsmi_get_nic_driver_info +amdsmi_get_nic_driver_info.restype = amdsmi_status_t +amdsmi_get_nic_driver_info.argtypes = [amdsmi_processor_handle, ctypes.POINTER(struct_amdsmi_nic_driver_info_t)] +amdsmi_get_nic_asic_info = _libraries['libamd_smi.so'].amdsmi_get_nic_asic_info +amdsmi_get_nic_asic_info.restype = amdsmi_status_t +amdsmi_get_nic_asic_info.argtypes = [amdsmi_processor_handle, ctypes.POINTER(struct_amdsmi_nic_asic_info_t)] +amdsmi_get_nic_bus_info = _libraries['libamd_smi.so'].amdsmi_get_nic_bus_info +amdsmi_get_nic_bus_info.restype = amdsmi_status_t +amdsmi_get_nic_bus_info.argtypes = [amdsmi_processor_handle, ctypes.POINTER(struct_amdsmi_nic_bus_info_t)] +amdsmi_get_nic_numa_info = _libraries['libamd_smi.so'].amdsmi_get_nic_numa_info +amdsmi_get_nic_numa_info.restype = amdsmi_status_t +amdsmi_get_nic_numa_info.argtypes = [amdsmi_processor_handle, ctypes.POINTER(struct_amdsmi_nic_numa_info_t)] +amdsmi_get_nic_port_info = _libraries['libamd_smi.so'].amdsmi_get_nic_port_info +amdsmi_get_nic_port_info.restype = amdsmi_status_t +amdsmi_get_nic_port_info.argtypes = [amdsmi_processor_handle, ctypes.POINTER(struct_amdsmi_nic_port_info_t)] +amdsmi_get_nic_rdma_dev_info = _libraries['libamd_smi.so'].amdsmi_get_nic_rdma_dev_info +amdsmi_get_nic_rdma_dev_info.restype = amdsmi_status_t +amdsmi_get_nic_rdma_dev_info.argtypes = [amdsmi_processor_handle, ctypes.POINTER(struct_amdsmi_nic_rdma_devices_info_t)] +amdsmi_get_nic_rdma_port_statistics = _libraries['libamd_smi.so'].amdsmi_get_nic_rdma_port_statistics +amdsmi_get_nic_rdma_port_statistics.restype = amdsmi_status_t +amdsmi_get_nic_rdma_port_statistics.argtypes = [amdsmi_processor_handle, uint32_t, ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(struct_amdsmi_nic_stat_t)] __all__ = \ ['AGG_BW0', 'AMDSMI_ACCELERATOR_DECODER', 'AMDSMI_ACCELERATOR_DMA', 'AMDSMI_ACCELERATOR_ENCODER', @@ -3394,27 +3556,31 @@ class struct_amdsmi_cper_hdr_t(Structure): 'AMDSMI_GPU_BLOCK_UMC', 'AMDSMI_GPU_BLOCK_VCN', 'AMDSMI_GPU_BLOCK_XGMI_WAFL', 'AMDSMI_INIT_ALL_PROCESSORS', 'AMDSMI_INIT_AMD_APUS', 'AMDSMI_INIT_AMD_CPUS', - 'AMDSMI_INIT_AMD_GPUS', 'AMDSMI_INIT_NON_AMD_CPUS', - 'AMDSMI_INIT_NON_AMD_GPUS', 'AMDSMI_LINK_STATUS_DISABLED', - 'AMDSMI_LINK_STATUS_ENABLED', 'AMDSMI_LINK_STATUS_ERROR', - 'AMDSMI_LINK_STATUS_INACTIVE', 'AMDSMI_LINK_TYPE_INTERNAL', - 'AMDSMI_LINK_TYPE_NOT_APPLICABLE', 'AMDSMI_LINK_TYPE_PCIE', - 'AMDSMI_LINK_TYPE_UNKNOWN', 'AMDSMI_LINK_TYPE_XGMI', - 'AMDSMI_MEMORY_PARTITION_NPS1', 'AMDSMI_MEMORY_PARTITION_NPS2', - 'AMDSMI_MEMORY_PARTITION_NPS4', 'AMDSMI_MEMORY_PARTITION_NPS8', - 'AMDSMI_MEMORY_PARTITION_UNKNOWN', + 'AMDSMI_INIT_AMD_GPUS', 'AMDSMI_INIT_AMD_NICS', + 'AMDSMI_INIT_NON_AMD_CPUS', 'AMDSMI_INIT_NON_AMD_GPUS', + 'AMDSMI_LINK_STATUS_DISABLED', 'AMDSMI_LINK_STATUS_ENABLED', + 'AMDSMI_LINK_STATUS_ERROR', 'AMDSMI_LINK_STATUS_INACTIVE', + 'AMDSMI_LINK_TYPE_INTERNAL', 'AMDSMI_LINK_TYPE_NOT_APPLICABLE', + 'AMDSMI_LINK_TYPE_PCIE', 'AMDSMI_LINK_TYPE_UNKNOWN', + 'AMDSMI_LINK_TYPE_XGMI', 'AMDSMI_MEMORY_PARTITION_NPS1', + 'AMDSMI_MEMORY_PARTITION_NPS2', 'AMDSMI_MEMORY_PARTITION_NPS4', + 'AMDSMI_MEMORY_PARTITION_NPS8', 'AMDSMI_MEMORY_PARTITION_UNKNOWN', 'AMDSMI_MEM_PAGE_STATUS_PENDING', 'AMDSMI_MEM_PAGE_STATUS_RESERVED', 'AMDSMI_MEM_PAGE_STATUS_UNRESERVABLE', 'AMDSMI_MEM_TYPE_FIRST', 'AMDSMI_MEM_TYPE_GTT', 'AMDSMI_MEM_TYPE_LAST', 'AMDSMI_MEM_TYPE_VIS_VRAM', 'AMDSMI_MEM_TYPE_VRAM', 'AMDSMI_MM_UVD', 'AMDSMI_MM_VCE', 'AMDSMI_MM_VCN', - 'AMDSMI_MM__MAX', 'AMDSMI_NPM_STATUS_DISABLED', + 'AMDSMI_MM__MAX', 'AMDSMI_NIC_LINK_TYPE_NUMA', + 'AMDSMI_NIC_LINK_TYPE_PCIE', 'AMDSMI_NIC_LINK_TYPE_UNKNOWN', + 'AMDSMI_NIC_LINK_TYPE_X_NUMA', 'AMDSMI_NPM_STATUS_DISABLED', 'AMDSMI_NPM_STATUS_ENABLED', 'AMDSMI_POWER_CAP_TYPE_PPT0', 'AMDSMI_POWER_CAP_TYPE_PPT1', 'AMDSMI_PROCESSOR_TYPE_AMD_APU', 'AMDSMI_PROCESSOR_TYPE_AMD_CPU', 'AMDSMI_PROCESSOR_TYPE_AMD_CPU_CORE', 'AMDSMI_PROCESSOR_TYPE_AMD_GPU', 'AMDSMI_PROCESSOR_TYPE_AMD_NIC', + 'AMDSMI_PROCESSOR_TYPE_BRCM_NIC', + 'AMDSMI_PROCESSOR_TYPE_BRCM_SWITCH', 'AMDSMI_PROCESSOR_TYPE_NON_AMD_CPU', 'AMDSMI_PROCESSOR_TYPE_NON_AMD_GPU', 'AMDSMI_PROCESSOR_TYPE_UNKNOWN', 'AMDSMI_PTL_DATA_FORMAT_BF16', @@ -3578,9 +3744,9 @@ class struct_amdsmi_cper_hdr_t(Structure): 'amdsmi_freq_volt_region_t', 'amdsmi_frequencies_t', 'amdsmi_frequency_range_t', 'amdsmi_fw_block_t', 'amdsmi_fw_info_t', 'amdsmi_get_afids_from_cper', - 'amdsmi_get_bad_page_threshold', 'amdsmi_get_clk_freq', - 'amdsmi_get_clock_info', 'amdsmi_get_cpu_affinity_with_scope', - 'amdsmi_get_cpu_cclk_limit', 'amdsmi_get_cpu_core_boostlimit', + 'amdsmi_get_clk_freq', 'amdsmi_get_clock_info', + 'amdsmi_get_cpu_affinity_with_scope', 'amdsmi_get_cpu_cclk_limit', + 'amdsmi_get_cpu_core_boostlimit', 'amdsmi_get_cpu_core_current_freq_limit', 'amdsmi_get_cpu_core_energy', 'amdsmi_get_cpu_cores_per_socket', 'amdsmi_get_cpu_current_io_bandwidth', @@ -3644,9 +3810,8 @@ class struct_amdsmi_cper_hdr_t(Structure): 'amdsmi_get_gpu_ptl_formats', 'amdsmi_get_gpu_ptl_state', 'amdsmi_get_gpu_ras_block_features_enabled', 'amdsmi_get_gpu_ras_feature_info', - 'amdsmi_get_gpu_ras_policy_info', 'amdsmi_get_gpu_reg_table_info', - 'amdsmi_get_gpu_revision', 'amdsmi_get_gpu_subsystem_id', - 'amdsmi_get_gpu_subsystem_name', + 'amdsmi_get_gpu_reg_table_info', 'amdsmi_get_gpu_revision', + 'amdsmi_get_gpu_subsystem_id', 'amdsmi_get_gpu_subsystem_name', 'amdsmi_get_gpu_topo_numa_affinity', 'amdsmi_get_gpu_total_ecc_count', 'amdsmi_get_gpu_vbios_info', 'amdsmi_get_gpu_vendor_name', @@ -3655,18 +3820,17 @@ class struct_amdsmi_cper_hdr_t(Structure): 'amdsmi_get_gpu_vram_usage', 'amdsmi_get_gpu_vram_vendor', 'amdsmi_get_gpu_xcd_counter', 'amdsmi_get_gpu_xgmi_link_status', 'amdsmi_get_hsmp_metrics_table', - 'amdsmi_get_hsmp_metrics_table_version', - 'amdsmi_get_index_from_processor_handle', - 'amdsmi_get_lib_version', 'amdsmi_get_link_metrics', - 'amdsmi_get_link_topology_nearest', + 'amdsmi_get_hsmp_metrics_table_version', 'amdsmi_get_lib_version', + 'amdsmi_get_link_metrics', 'amdsmi_get_link_topology_nearest', 'amdsmi_get_minmax_bandwidth_between_processors', - 'amdsmi_get_node_handle', 'amdsmi_get_npm_info', - 'amdsmi_get_pcie_info', 'amdsmi_get_power_cap_info', - 'amdsmi_get_power_info', 'amdsmi_get_processor_bdf', + 'amdsmi_get_nic_asic_info', 'amdsmi_get_nic_bus_info', + 'amdsmi_get_nic_driver_info', 'amdsmi_get_nic_numa_info', + 'amdsmi_get_nic_port_info', 'amdsmi_get_nic_rdma_dev_info', + 'amdsmi_get_nic_rdma_port_statistics', 'amdsmi_get_node_handle', + 'amdsmi_get_npm_info', 'amdsmi_get_pcie_info', + 'amdsmi_get_power_cap_info', 'amdsmi_get_power_info', 'amdsmi_get_processor_count_from_handles', 'amdsmi_get_processor_handle_from_bdf', - 'amdsmi_get_processor_handle_from_index', - 'amdsmi_get_processor_handle_from_uuid', 'amdsmi_get_processor_handles', 'amdsmi_get_processor_handles_by_type', 'amdsmi_get_processor_info', 'amdsmi_get_processor_type', @@ -3692,22 +3856,28 @@ class struct_amdsmi_cper_hdr_t(Structure): 'amdsmi_link_status_t', 'amdsmi_link_type_t', 'amdsmi_memory_page_status_t', 'amdsmi_memory_partition_config_t', 'amdsmi_memory_partition_type_t', 'amdsmi_memory_type_t', - 'amdsmi_mm_ip_t', 'amdsmi_name_value_t', 'amdsmi_node_handle', - 'amdsmi_npm_info_t', 'amdsmi_npm_status_t', 'amdsmi_nps_caps_t', - 'amdsmi_od_vddc_point_t', 'amdsmi_od_volt_curve_t', - 'amdsmi_od_volt_freq_data_t', 'amdsmi_p2p_capability_t', - 'amdsmi_pcie_bandwidth_t', 'amdsmi_pcie_info_t', - 'amdsmi_power_cap_info_t', 'amdsmi_power_cap_type_t', - 'amdsmi_power_info_t', 'amdsmi_power_profile_preset_masks_t', + 'amdsmi_mm_ip_t', 'amdsmi_name_value_t', 'amdsmi_nic_asic_info_t', + 'amdsmi_nic_bus_info_t', 'amdsmi_nic_driver_info_t', + 'amdsmi_nic_fw_info_t', 'amdsmi_nic_fw_t', + 'amdsmi_nic_link_type_t', 'amdsmi_nic_numa_info_t', + 'amdsmi_nic_port_info_t', 'amdsmi_nic_port_t', + 'amdsmi_nic_rdma_dev_info_t', 'amdsmi_nic_rdma_devices_info_t', + 'amdsmi_nic_rdma_port_info_t', 'amdsmi_nic_stat_t', + 'amdsmi_node_handle', 'amdsmi_npm_info_t', 'amdsmi_npm_status_t', + 'amdsmi_nps_caps_t', 'amdsmi_od_vddc_point_t', + 'amdsmi_od_volt_curve_t', 'amdsmi_od_volt_freq_data_t', + 'amdsmi_p2p_capability_t', 'amdsmi_pcie_bandwidth_t', + 'amdsmi_pcie_info_t', 'amdsmi_power_cap_info_t', + 'amdsmi_power_cap_type_t', 'amdsmi_power_info_t', + 'amdsmi_power_profile_preset_masks_t', 'amdsmi_power_profile_status_t', 'amdsmi_proc_info_t', 'amdsmi_process_handle_t', 'amdsmi_process_info_t', 'amdsmi_processor_handle', 'amdsmi_ptl_data_format_t', 'amdsmi_range_t', 'amdsmi_ras_err_state_t', 'amdsmi_ras_feature_t', 'amdsmi_reg_type_t', 'amdsmi_reset_gpu', - 'amdsmi_reset_gpu_compute_partition', 'amdsmi_reset_gpu_fan', - 'amdsmi_reset_gpu_memory_partition', - 'amdsmi_reset_gpu_xgmi_error', 'amdsmi_retired_page_record_t', - 'amdsmi_set_clk_freq', 'amdsmi_set_cpu_core_boostlimit', + 'amdsmi_reset_gpu_fan', 'amdsmi_reset_gpu_xgmi_error', + 'amdsmi_retired_page_record_t', 'amdsmi_set_clk_freq', + 'amdsmi_set_cpu_core_boostlimit', 'amdsmi_set_cpu_df_pstate_range', 'amdsmi_set_cpu_gmi3_link_width_range', 'amdsmi_set_cpu_pcie_link_rate', @@ -3771,8 +3941,15 @@ class struct_amdsmi_cper_hdr_t(Structure): 'struct_amdsmi_hsmp_metrics_table_t', 'struct_amdsmi_kfd_info_t', 'struct_amdsmi_link_id_bw_type_t', 'struct_amdsmi_link_metrics_t', 'struct_amdsmi_memory_partition_config_t', - 'struct_amdsmi_name_value_t', 'struct_amdsmi_npm_info_t', - 'struct_amdsmi_od_vddc_point_t', 'struct_amdsmi_od_volt_curve_t', + 'struct_amdsmi_name_value_t', 'struct_amdsmi_nic_asic_info_t', + 'struct_amdsmi_nic_bus_info_t', 'struct_amdsmi_nic_driver_info_t', + 'struct_amdsmi_nic_fw_info_t', 'struct_amdsmi_nic_fw_t', + 'struct_amdsmi_nic_numa_info_t', 'struct_amdsmi_nic_port_info_t', + 'struct_amdsmi_nic_port_t', 'struct_amdsmi_nic_rdma_dev_info_t', + 'struct_amdsmi_nic_rdma_devices_info_t', + 'struct_amdsmi_nic_rdma_port_info_t', 'struct_amdsmi_nic_stat_t', + 'struct_amdsmi_npm_info_t', 'struct_amdsmi_od_vddc_point_t', + 'struct_amdsmi_od_volt_curve_t', 'struct_amdsmi_od_volt_freq_data_t', 'struct_amdsmi_p2p_capability_t', 'struct_amdsmi_pcie_bandwidth_t', 'struct_amdsmi_pcie_info_t', diff --git a/projects/amdsmi/rocm_smi/include/rocm_smi/rocm_smi.h b/projects/amdsmi/rocm_smi/include/rocm_smi/rocm_smi.h index 0eeef15ad40..79105072d43 100644 --- a/projects/amdsmi/rocm_smi/include/rocm_smi/rocm_smi.h +++ b/projects/amdsmi/rocm_smi/include/rocm_smi/rocm_smi.h @@ -1646,6 +1646,34 @@ rsmi_status_t rsmi_driver_status(rsmi_driver_state_t* state); */ rsmi_status_t rsmi_num_monitor_devices(uint32_t *num_devices); +/** + * @brief Get the number of brcm nic devices that have monitor information. + * + * @details The number of devices brcm nic which have monitors is returned. Monitors + * are referenced by the index which can be between 0 and @p num_devices - 1. + * + * @param[inout] num_devices Caller provided pointer to uint32_t. Upon + * successful call, the value num_devices will contain the number of brcm nic monitor + * devices. + * + * @retval ::RSMI_STATUS_SUCCESS is returned upon successful call. + */ +rsmi_status_t rsmi_num_nic_monitor_devices(uint32_t *num_devices); + +/** + * @brief Get the number of brcm switch devices that have monitor information. + * + * @details The number of devices brcm switch which have monitors is returned. Monitors + * are referenced by the index which can be between 0 and @p num_devices - 1. + * + * @param[inout] num_devices Caller provided pointer to uint32_t. Upon + * successful call, the value num_devices will contain the number of brcm switch monitor + * devices. + * + * @retval ::RSMI_STATUS_SUCCESS is returned upon successful call. + */ +rsmi_status_t rsmi_num_switch_monitor_devices(uint32_t *num_devices); + /** * @brief Get the device id associated with the device with provided device * index. @@ -2272,6 +2300,8 @@ rsmi_dev_pci_bandwidth_get(uint32_t dv_ind, rsmi_pcie_bandwidth_t *bandwidth); * @retval ::RSMI_STATUS_INVALID_ARGS the provided arguments are not valid */ rsmi_status_t rsmi_dev_pci_id_get(uint32_t dv_ind, uint64_t *bdfid); +rsmi_status_t rsmi_nic_dev_pci_id_get(uint32_t dv_ind, uint64_t *bdfid); +rsmi_status_t rsmi_switch_dev_pci_id_get(uint32_t dv_ind, uint64_t *bdfid); /** * @brief Get the NUMA node associated with a device diff --git a/projects/amdsmi/rocm_smi/include/rocm_smi/rocm_smi_common.h b/projects/amdsmi/rocm_smi/include/rocm_smi/rocm_smi_common.h index 04065f285ed..8e4e3e23177 100644 --- a/projects/amdsmi/rocm_smi/include/rocm_smi/rocm_smi_common.h +++ b/projects/amdsmi/rocm_smi/include/rocm_smi/rocm_smi_common.h @@ -41,6 +41,28 @@ std::shared_ptr dev = smi.devices()[dv_ind]; \ assert(dev != nullptr); +#define CHECK_NIC_DV_IND_RANGE \ + amd::smi::RocmSMI& smi = amd::smi::RocmSMI::getInstance(); \ + if (dv_ind >= smi.nic_devices().size()) { \ + return RSMI_STATUS_INVALID_ARGS; \ + } + +#define GET_NIC_DEV_FROM_INDX \ + CHECK_NIC_DV_IND_RANGE \ + std::shared_ptr dev = smi.nic_devices()[dv_ind]; \ + assert(dev != nullptr); + +#define CHECK_SWITCH_DV_IND_RANGE \ + amd::smi::RocmSMI& smi = amd::smi::RocmSMI::getInstance(); \ + if (dv_ind >= smi.switch_devices().size()) { \ + return RSMI_STATUS_INVALID_ARGS; \ + } + +#define GET_SWITCH_DEV_FROM_INDX \ + CHECK_SWITCH_DV_IND_RANGE \ + std::shared_ptr dev = smi.switch_devices()[dv_ind]; \ + assert(dev != nullptr); + #define GET_DEV_AND_KFDNODE_FROM_INDX \ GET_DEV_FROM_INDX \ diff --git a/projects/amdsmi/rocm_smi/include/rocm_smi/rocm_smi_main.h b/projects/amdsmi/rocm_smi/include/rocm_smi/rocm_smi_main.h index 82c1171fdb5..6d4c10814c5 100644 --- a/projects/amdsmi/rocm_smi/include/rocm_smi/rocm_smi_main.h +++ b/projects/amdsmi/rocm_smi/include/rocm_smi/rocm_smi_main.h @@ -52,10 +52,13 @@ class RocmSMI { void Initialize(uint64_t flags); void Cleanup(void); - std::vector>& - devices() {return devices_;} + std::vector>& devices() {return devices_;} + std::vector> &nic_devices() { return nic_devices_; } + std::vector> &switch_devices() { return switch_devices_; } uint32_t DiscoverAmdgpuDevices(void); + uint32_t DiscoverBRCMnicDevices(void); + uint32_t DiscoverBRCMswitchDevices(void); int DiscoverAMDPowerMonitors(bool force_update = false); // Will execute "func" for every Device object known about, or until func @@ -100,14 +103,21 @@ class RocmSMI { private: std::vector> devices_; + std::vector> nic_devices_; + std::vector> switch_devices_; std::map> kfd_node_map_; std::vector> monitors_; + std::vector> nic_monitors_; + std::vector> switch_monitors_; std::vector> power_mons_; std::set amd_monitor_types_; std::map, std::shared_ptr> io_link_map_; std::map dev_ind_to_node_ind_map_; - void AddToDeviceList(std::string dev_name, uint64_t bdfid = 0); + void AddToDeviceList(const std::string &dev_name, uint64_t bdfid = 0); + void AddToNICDeviceList(const std::string &dev_name, uint64_t bdfid = 0, uint32_t card_indx=0); + void AddToSWITCHDeviceList(const std::string &dev_name, uint64_t bdfid = 0); + typedef struct rsmi_device_enumeration_t { uint32_t card_index = std::numeric_limits::max(); std::string dev_name = ""; diff --git a/projects/amdsmi/rocm_smi/include/rocm_smi/rocm_smi_utils.h b/projects/amdsmi/rocm_smi/include/rocm_smi/rocm_smi_utils.h index dc52aede89e..9a1bd3fea4b 100644 --- a/projects/amdsmi/rocm_smi/include/rocm_smi/rocm_smi_utils.h +++ b/projects/amdsmi/rocm_smi/include/rocm_smi/rocm_smi_utils.h @@ -658,7 +658,7 @@ inline ostream_joiner, CharType, TraitsType> }; } - +uint64_t bdfid_from_domain(uint64_t bdfid, uint64_t domain); } // namespace amd::smi #endif // INCLUDE_ROCM_SMI_ROCM_SMI_UTILS_H_ diff --git a/projects/amdsmi/rocm_smi/src/rocm_smi.cc b/projects/amdsmi/rocm_smi/src/rocm_smi.cc index 77c137e7d5f..6d246e6f4b4 100644 --- a/projects/amdsmi/rocm_smi/src/rocm_smi.cc +++ b/projects/amdsmi/rocm_smi/src/rocm_smi.cc @@ -606,6 +606,32 @@ rsmi_num_monitor_devices(uint32_t *num_devices) { CATCH } +rsmi_status_t rsmi_num_nic_monitor_devices(uint32_t *num_devices) { + TRY assert(num_devices != nullptr); + if (num_devices == nullptr) { + return RSMI_STATUS_INVALID_ARGS; + } + + amd::smi::RocmSMI &smi = amd::smi::RocmSMI::getInstance(); + + *num_devices = static_cast(smi.nic_devices().size()); + return RSMI_STATUS_SUCCESS; + CATCH +} + +rsmi_status_t rsmi_num_switch_monitor_devices(uint32_t *num_devices) { + TRY assert(num_devices != nullptr); + if (num_devices == nullptr) { + return RSMI_STATUS_INVALID_ARGS; + } + + amd::smi::RocmSMI &smi = amd::smi::RocmSMI::getInstance(); + + *num_devices = static_cast(smi.switch_devices().size()); + return RSMI_STATUS_SUCCESS; + CATCH +} + rsmi_status_t rsmi_dev_ecc_enabled_get(uint32_t dv_ind, uint64_t *enabled_blks) { TRY @@ -830,8 +856,6 @@ rsmi_dev_pci_id_get(uint32_t dv_ind, uint64_t *bdfid) { CHK_API_SUPPORT_ONLY(bdfid, RSMI_DEFAULT_VARIANT, RSMI_DEFAULT_VARIANT) DEVICE_MUTEX - *bdfid = dev->bdfid(); - uint64_t domain = 0; kfd_node->get_property_value("domain", &domain); @@ -848,9 +872,8 @@ rsmi_dev_pci_id_get(uint32_t dv_ind, uint64_t *bdfid) { * bits [7:3] = Device * bits [2:0] = Function (partition id maybe in bits [2:0]) <-- Fallback for non SPX modes */ - assert((domain & 0xFFFFFFFF00000000) == 0); - (*bdfid) &= 0xFFFFFFFF; // keep bottom 32 bits of pci_id - *bdfid |= (domain & 0xFFFFFFFF) << 32; // Add domain to top of pci_id + *bdfid = amd::smi::bdfid_from_domain(dev->bdfid(), domain); + uint64_t pci_id = *bdfid; uint32_t node = UINT32_MAX; rsmi_dev_node_id_get(dv_ind, &node); @@ -868,6 +891,40 @@ rsmi_dev_pci_id_get(uint32_t dv_ind, uint64_t *bdfid) { CATCH } +rsmi_status_t rsmi_nic_dev_pci_id_get(uint32_t dv_ind, uint64_t *bdfid) { + TRY std::ostringstream ss; + ss << __PRETTY_FUNCTION__ << "| ======= start ======="; + LOG_TRACE(ss); + + GET_NIC_DEV_FROM_INDX + + uint64_t domain = 0; + *bdfid = amd::smi::bdfid_from_domain(dev->bdfid(), domain); + + ss << __PRETTY_FUNCTION__ << " | ======= end =======" + << ", reporting RSMI_STATUS_SUCCESS"; + LOG_TRACE(ss); + return RSMI_STATUS_SUCCESS; + CATCH +} + +rsmi_status_t rsmi_switch_dev_pci_id_get(uint32_t dv_ind, uint64_t *bdfid) { + TRY std::ostringstream ss; + ss << __PRETTY_FUNCTION__ << "| ======= start ======="; + LOG_TRACE(ss); + + GET_SWITCH_DEV_FROM_INDX + + uint64_t domain = 0; + *bdfid = amd::smi::bdfid_from_domain(dev->bdfid(), domain); + + ss << __PRETTY_FUNCTION__ << " | ======= end =======" + << ", reporting RSMI_STATUS_SUCCESS"; + LOG_TRACE(ss); + return RSMI_STATUS_SUCCESS; + CATCH +} + rsmi_status_t rsmi_topo_numa_affinity_get(uint32_t dv_ind, int32_t *numa_node) { TRY diff --git a/projects/amdsmi/rocm_smi/src/rocm_smi_gpu_metrics.cc b/projects/amdsmi/rocm_smi/src/rocm_smi_gpu_metrics.cc index 5eae12beb8e..930dc4c9dd7 100644 --- a/projects/amdsmi/rocm_smi/src/rocm_smi_gpu_metrics.cc +++ b/projects/amdsmi/rocm_smi/src/rocm_smi_gpu_metrics.cc @@ -376,7 +376,7 @@ uint16_t translate_flag_to_metric_version(AMDGpuMetricVersionFlags_t version_fla rsmi_status_t is_gpu_metrics_version_supported( const AMDGpuMetricsHeader_v1_t& metrics_header, bool is_partition_metrics) { - rsmi_status_t status_code(RSMI_STATUS_NOT_SUPPORTED); + (void)is_partition_metrics;//unused const auto flag_version = join_metrics_version(metrics_header); if (flag_version == static_cast( AMDGpuMetricVersionFlags_t::kGpuMetricNone)) { @@ -388,6 +388,8 @@ rsmi_status_t is_gpu_metrics_version_supported( GpuMetricsBasePtr amdgpu_metrics_factory(AMDGpuMetricVersionFlags_t v, bool is_partition_metrics, const std::string& file_path) { + + (void)(file_path);//unused if (!is_partition_metrics) { switch (v) { case AMDGpuMetricVersionFlags_t::kGpuMetricV10: return std::make_shared(); diff --git a/projects/amdsmi/rocm_smi/src/rocm_smi_main.cc b/projects/amdsmi/rocm_smi/src/rocm_smi_main.cc index 19e6f3a338f..eb175ecb546 100644 --- a/projects/amdsmi/rocm_smi/src/rocm_smi_main.cc +++ b/projects/amdsmi/rocm_smi/src/rocm_smi_main.cc @@ -47,9 +47,23 @@ static const char *kPathDRMRoot = "/sys/class/drm"; static const char *kPathHWMonRoot = "/sys/class/hwmon"; static const char *kPathPowerRoot = "/sys/kernel/debug/dri"; +static const char *kPathNICRoot = "/sys/class/net"; +static const char *kPathSwitchRoot = "/sys/class/scsi_host"; +static const char *kPathDeviceMon = "/device/hwmon"; +static const char *kPathDeviceVendor = "/device/vendor"; +static const char *kPathVendor = "/vendor"; +static const char *kPathDevice = "/device"; +static const char *kPathPciDevices = "/sys/bus/pci/devices/"; + + +static const char *kNICPrefix = "e"; //changed to 'e' since SLES syspath is 'eth' and RHEL/Ubuntu is 'en' +static const char *kSwitchPrefix = "host"; static const char *kAMDMonitorTypes[] = {"radeon", "amdgpu", ""}; +using BDFDevicePair_t = std::pair>; +using BdfDeviceVector_t = std::vector; + namespace amd::smi { static uint32_t GetDeviceIndex(const std::string s) { @@ -303,7 +317,7 @@ RocmSMI::Initialize(uint64_t flags) { std::shared_ptr dev; // Sort index based on the BDF, collect BDF id firstly. - std::vector>> dv_to_id; + BdfDeviceVector_t dv_to_id; dv_to_id.reserve(devices_.size()); for (uint32_t dv_ind = 0; dv_ind < devices_.size(); ++dv_ind) { dev = devices_[dv_ind]; @@ -318,8 +332,8 @@ RocmSMI::Initialize(uint64_t flags) { // Stable sort to keep the order if bdf is equal. std::stable_sort(dv_to_id.begin(), dv_to_id.end(), [] - (const std::pair>& p1, - const std::pair>& p2) { + (const BDFDevicePair_t& p1, + const BDFDevicePair_t& p2) { return p1.first < p2.first; }); devices_.clear(); @@ -396,6 +410,126 @@ RocmSMI::Initialize(uint64_t flags) { ss << __PRETTY_FUNCTION__ << " | current device paths = " << amdGPUDeviceList; // std::cout << ss.str() << std::endl; LOG_DEBUG(ss); + { + /* + * Discover the BRCM NIC devices from the sysfs entry + * Construct the BDF for the discovered NIC devices + * Push NIC SMI Device object to nic_devices vector + */ + ret = DiscoverBRCMnicDevices(); + if (ret != 0) { + throw amd::smi::rsmi_exception(RSMI_INITIALIZATION_ERROR, "DiscoverBRCMnicDevices() failed."); + } + uint64_t bdfid; + for (auto &device : nic_devices_) { + if (ConstructBDFID(device->path(), &bdfid) != 0) { + std::cerr << "Failed to construct BDFID." << std::endl; + } else if (device->bdfid() != UINT64_MAX && device->bdfid() != bdfid) { + // handles secondary partitions - compute partition feature nodes + ss << __PRETTY_FUNCTION__ << " | [before] device->path() = " << device->path() + << "\n | bdfid = " << bdfid << "\n | device->bdfid() = " << device->bdfid() + << "\n | (xgmi node) setting to setting " + << "device->set_bdfid(device->bdfid())"; + LOG_TRACE(ss); + device->set_bdfid(device->bdfid()); + } else { + // legacy & pcie card updates + ss << __PRETTY_FUNCTION__ << " | [before] device->path() = " << device->path() + << "\n | bdfid = " << bdfid << "\n | device->bdfid() = " << device->bdfid() + << "\n | (legacy/pcie card) setting device->set_bdfid(bdfid)"; + LOG_TRACE(ss); + device->set_bdfid(bdfid); + } + ss << __PRETTY_FUNCTION__ << " | [after] device->path() = " << device->path() + << "\n | bdfid = " << bdfid << "\n | device->bdfid() = " << device->bdfid() + << "\n | final update: device->bdfid() holds correct device bdf"; + LOG_TRACE(ss); + } + + std::shared_ptr dev; + // Sort index based on the BDF, collect BDF id firstly. + BdfDeviceVector_t dv_to_id; + dv_to_id.reserve(nic_devices_.size()); + for (uint32_t dv_ind = 0; dv_ind < nic_devices_.size(); ++dv_ind) { + dev = nic_devices_[dv_ind]; + uint64_t bdfid = dev->bdfid(); + dv_to_id.push_back({bdfid, dev}); + } + ss << __PRETTY_FUNCTION__ << " Sort index based on BDF."; + LOG_DEBUG(ss); + + // Stable sort to keep the order if bdf is equal. + std::stable_sort(dv_to_id.begin(), dv_to_id.end(), + [](const BDFDevicePair_t& p1, + const BDFDevicePair_t& p2) { + return p1.first < p2.first; + }); + nic_devices_.clear(); + for (uint32_t dv_ind = 0; dv_ind < dv_to_id.size(); ++dv_ind) { + nic_devices_.push_back(dv_to_id[dv_ind].second); + } + } + + { + /* + * Discover the BRCM switches from the sysfs entry + * Construct the BDF for the discovered switch devices + * Push SWITCH SMI Device object to switch_devices vector + */ + + ret = DiscoverBRCMswitchDevices(); + if (ret != 0) { + throw amd::smi::rsmi_exception(RSMI_INITIALIZATION_ERROR, "DiscoverBRCMswitchDevices() failed."); + } + uint64_t bdfid; + for (auto &device : switch_devices_) { + if (ConstructBDFID(device->path(), &bdfid) != 0) { + std::cerr << "Failed to construct BDFID." << std::endl; + } else if (device->bdfid() != UINT64_MAX && device->bdfid() != bdfid) { + // handles secondary partitions - compute partition feature nodes + ss << __PRETTY_FUNCTION__ << " | [before] device->path() = " << device->path() + << "\n | bdfid = " << bdfid << "\n | device->bdfid() = " << device->bdfid() + << "\n | (xgmi node) setting to setting " + << "device->set_bdfid(device->bdfid())"; + LOG_TRACE(ss); + device->set_bdfid(device->bdfid()); + } else { + // legacy & pcie card updates + ss << __PRETTY_FUNCTION__ << " | [before] device->path() = " << device->path() + << "\n | bdfid = " << bdfid << "\n | device->bdfid() = " << device->bdfid() + << "\n | (legacy/pcie card) setting device->set_bdfid(bdfid)"; + LOG_TRACE(ss); + device->set_bdfid(bdfid); + } + ss << __PRETTY_FUNCTION__ << " | [after] device->path() = " << device->path() + << "\n | bdfid = " << bdfid << "\n | device->bdfid() = " << device->bdfid() + << "\n | final update: device->bdfid() holds correct device bdf"; + LOG_TRACE(ss); + } + + std::shared_ptr dev; + // Sort index based on the BDF, collect BDF id firstly. + BdfDeviceVector_t dv_to_id; + dv_to_id.reserve(switch_devices_.size()); + for (uint32_t dv_ind = 0; dv_ind < switch_devices_.size(); ++dv_ind) { + dev = switch_devices_[dv_ind]; + uint64_t bdfid = dev->bdfid(); + dv_to_id.push_back({bdfid, dev}); + } + ss << __PRETTY_FUNCTION__ << " Sort index based on BDF."; + LOG_DEBUG(ss); + + // Stable sort to keep the order if bdf is equal. + std::stable_sort(dv_to_id.begin(), dv_to_id.end(), + [](const BDFDevicePair_t& p1, + const BDFDevicePair_t& p2) { + return p1.first < p2.first; + }); + switch_devices_.clear(); + for (uint32_t dv_ind = 0; dv_ind < dv_to_id.size(); ++dv_ind) { + switch_devices_.push_back(dv_to_id[dv_ind].second); + } + } } void @@ -635,7 +769,7 @@ RocmSMI::FindMonitor(std::string monitor_path) { return m; } -void RocmSMI::AddToDeviceList(std::string dev_name, uint64_t bdfid) { +void RocmSMI::AddToDeviceList(const std::string &dev_name, uint64_t bdfid) { static const int BYTE = 8; std::ostringstream ss; ss << __PRETTY_FUNCTION__ << " | ======= start ======="; @@ -647,7 +781,7 @@ void RocmSMI::AddToDeviceList(std::string dev_name, uint64_t bdfid) { auto dev = std::make_shared(dev_path, &env_vars_); - std::shared_ptr m = FindMonitor(dev_path + "/device/hwmon"); + std::shared_ptr m = FindMonitor(dev_path + kPathDeviceMon); dev->set_monitor(m); const std::string& d_name = dev_name; @@ -727,7 +861,7 @@ rsmi_status_t RocmSMI::AddToDeviceList2(RocmSMI::rsmi_device_enumeration_t devic auto dev = std::make_shared(dev_path, &env_vars_); - std::shared_ptr m = FindMonitor(dev_path + "/device/hwmon"); + std::shared_ptr m = FindMonitor(dev_path + kPathDeviceMon); dev->set_monitor(m); const std::string& d_name = device.dev_name; @@ -773,12 +907,68 @@ rsmi_status_t RocmSMI::AddToDeviceList2(RocmSMI::rsmi_device_enumeration_t devic return RSMI_STATUS_SUCCESS; } +void RocmSMI::AddToNICDeviceList(const std::string &dev_name, uint64_t bdfid, uint32_t card_indx) { + std::ostringstream ss; + ss << __PRETTY_FUNCTION__ << " | ======= start ======="; + LOG_TRACE(ss); + auto dev_path = std::string(kPathNICRoot); + dev_path += "/"; + dev_path += dev_name; + + auto dev = std::make_shared(dev_path, &env_vars_); + + std::shared_ptr m = FindMonitor(dev_path + kPathDeviceMon); + dev->set_monitor(m); + + dev->set_card_index(card_indx); + GetSupportedEventGroups(card_indx, dev->supported_event_groups()); + if (bdfid != 0) { + dev->set_bdfid(bdfid); + } + + nic_devices_.push_back(dev); + ss << __PRETTY_FUNCTION__ << " | Adding to nic device list dev_name = " << dev_name + << " | path = " << dev_path << " | bdfid = " << bdfid + << " | card index = " << std::to_string(card_indx) << " | "; + LOG_DEBUG(ss); +} + +void RocmSMI::AddToSWITCHDeviceList(const std::string &dev_name, uint64_t bdfid) { + std::ostringstream ss; + ss << __PRETTY_FUNCTION__ << " | ======= start ======="; + LOG_TRACE(ss); + auto dev_path = std::string(kPathSwitchRoot); + dev_path += "/"; + dev_path += dev_name; + + auto dev = std::make_shared(dev_path, &env_vars_); + + std::shared_ptr m = FindMonitor(dev_path + kPathDeviceMon); + dev->set_monitor(m); + + const std::string &d_name = dev_name; + uint32_t card_indx = GetDeviceIndex(d_name); + dev->set_card_index(card_indx); + GetSupportedEventGroups(card_indx, dev->supported_event_groups()); + if (bdfid != 0) { + dev->set_bdfid(bdfid); + } + + switch_devices_.push_back(dev); + ss << __PRETTY_FUNCTION__ << " | Adding to nic device list dev_name = " << dev_name + << " | path = " << dev_path << " | bdfid = " << bdfid + << " | card index = " << std::to_string(card_indx) << " | "; + LOG_DEBUG(ss); +} static const uint32_t kAmdGpuId = 0x1002; +static const uint32_t kBRCMnicId = 0x14e4; +static const uint32_t kBRCMswitchId = 0x1000; +static const uint32_t kBRCMswitchDId = 0x00b2; [[maybe_unused]] static bool isAMDGPU(std::string dev_path) { bool isAmdGpu = false; std::ostringstream ss; - std::string vend_path = dev_path + "/device/vendor"; + std::string vend_path = dev_path + kPathDeviceVendor; if (!FileExists(vend_path.c_str())) { ss << __PRETTY_FUNCTION__ << " | device_path = " << dev_path << " is an amdgpu device - " << (isAmdGpu ? "TRUE": " FALSE"); @@ -811,6 +1001,84 @@ static const uint32_t kAmdGpuId = 0x1002; return isAmdGpu; } +static bool isBRCMnic(std::string dev_path) { + bool isBRCMnic = false; + std::ostringstream ss; + std::string vend_path = dev_path + kPathDeviceVendor; + if (!FileExists(vend_path.c_str())) { + ss << __PRETTY_FUNCTION__ << " | device_path = " << dev_path << " is an BRCMnic device - " + << (isBRCMnic ? "TRUE" : " FALSE"); + LOG_DEBUG(ss); + return isBRCMnic; + } + + std::ifstream fs; + fs.open(vend_path); + + if (!fs.is_open()) { + ss << __PRETTY_FUNCTION__ << " | device_path = " << dev_path << " is an BRCMnic device - " + << (isBRCMnic ? "TRUE" : " FALSE"); + LOG_DEBUG(ss); + return isBRCMnic; + } + + uint32_t vendor_id; + + fs >> std::hex >> vendor_id; + + fs.close(); + + if (vendor_id == kBRCMnicId) { + isBRCMnic = true; + } + ss << __PRETTY_FUNCTION__ << " | device_path = " << dev_path << " is an BRCMnic device - " + << (isBRCMnic ? "TRUE" : " FALSE"); + LOG_DEBUG(ss); + return isBRCMnic; +} + +static bool isBRCMswitch(std::string dev_path) { + bool isBRCMswitch = false; + std::ostringstream ss; + std::string vend_path = dev_path + kPathVendor; + std::string ldev_path = dev_path + kPathDevice; + + if (!FileExists(vend_path.c_str()) || !FileExists(ldev_path.c_str())) { + ss << __PRETTY_FUNCTION__ << " | device_path = " << dev_path << " is an BRCMswitch device - " + << (isBRCMswitch ? "TRUE" : " FALSE"); + LOG_DEBUG(ss); + return isBRCMswitch; + } + + std::ifstream vfs, dfs; + vfs.open(vend_path); + dfs.open(ldev_path); + + if (!vfs.is_open() || !dfs.is_open()) { + ss << __PRETTY_FUNCTION__ << " | device_path = " << dev_path << " is an BRCMswitch device - " + << (isBRCMswitch ? "TRUE" : " FALSE"); + LOG_DEBUG(ss); + return isBRCMswitch; + } + + uint32_t vendor_id; + uint32_t dev_id; + + vfs >> std::hex >> vendor_id; + dfs >> std::hex >> dev_id; + + vfs.close(); + dfs.close(); + + if (vendor_id == kBRCMswitchId && dev_id == kBRCMswitchDId) { + isBRCMswitch = true; + } + ss << __PRETTY_FUNCTION__ << " | device_path = " << dev_path << " is an BRCMswitch device - " + << (isBRCMswitch ? "TRUE" : " FALSE"); + LOG_DEBUG(ss); + return isBRCMswitch; +} + uint32_t GetLargestNodeNumber(const std::string& path = "/sys/class/kfd/kfd/topology/nodes/") { std::ostringstream ss; uint32_t largest_node_number = 0; @@ -1000,6 +1268,134 @@ uint32_t RocmSMI::DiscoverAmdgpuDevices(void) { return 0; } +uint32_t RocmSMI::DiscoverBRCMnicDevices(void) { + std::string err_msg; + uint32_t count = 0; + std::ostringstream ss; + + // If this gets called more than once, clear previous findings. + nic_devices_.clear(); + nic_monitors_.clear(); + + if(!std::filesystem::exists(kPathNICRoot)) { + err_msg = "Failed to open hwmon root directory, while DiscoverBRCMnicDevices and graceful exit."; + err_msg += kPathNICRoot; + err_msg += "."; + perror(err_msg.c_str()); + return 0; + } + + for (const auto& entry : std::filesystem::directory_iterator(kPathNICRoot)) { + if (memcmp( entry.path().filename().string().c_str(), kNICPrefix, strlen(kNICPrefix)) == 0) { + if ((entry.path().filename().string() == ".") || (entry.path().filename().string() == "..")) { + continue; + } + std::string path = kPathNICRoot; + path += "/" + entry.path().filename().string(); + if (isBRCMnic(path)) { + AddToNICDeviceList(entry.path().filename().string(), UINT64_MAX, count); + count++; + } + } + } + ss << __PRETTY_FUNCTION__ << " | Discovered a potential of " << std::to_string(count) << " nic" + << " | "; + LOG_DEBUG(ss); + + return 0; +} + +uint32_t RocmSMI::DiscoverBRCMswitchDevices(void) { + std::string err_msg; + uint32_t count = 0; + std::ostringstream ss; + + // If this gets called more than once, clear previous findings. + switch_devices_.clear(); + switch_monitors_.clear(); + + auto scsi_host_dir = opendir(kPathSwitchRoot); + if (scsi_host_dir == nullptr) { + err_msg = "Failed to open scsi_host root directory, while DiscoverBRCMswitchDevices and graceful exit."; + err_msg += kPathSwitchRoot; + err_msg += "."; + perror(err_msg.c_str()); + return 0; + } + + auto dentry = readdir(scsi_host_dir); + + while (dentry != nullptr) { + if (memcmp(dentry->d_name, kSwitchPrefix, strlen(kSwitchPrefix)) == 0) { + if ((strcmp(dentry->d_name, ".") == 0) || (strcmp(dentry->d_name, "..") == 0)) continue; + count++; + } + dentry = readdir(scsi_host_dir); + } + ss << __PRETTY_FUNCTION__ << " | Discovered a potential of " << std::to_string(count) << " host" + << " | "; + LOG_DEBUG(ss); + + + int numofretry = 0; + // Discover all root cards + for (uint32_t cardId = 0; cardId < count; cardId++) { + std::string path = kPathSwitchRoot; + path += "/" + std::string(kSwitchPrefix); + path += std::to_string(cardId); + + //sometime cardId is not in correct increment order in sysfs + auto path_dir = opendir(path.c_str()); + if (path_dir == nullptr) { + //move to the next index + static constexpr int kMaxRetries = 3; + if (numofretry == kMaxRetries) continue; + count++; + numofretry++; + } else { + closedir(path_dir); + } + + // each identified switch node is a primary node for + // potential matching unique ids + std::vector buf(512); + ssize_t len; + + do { + buf.resize(buf.size() + 100); + len = ::readlink(path.c_str(), &(buf[0]), buf.size()); + } while (static_cast(buf.size()) == len); + + if (len > 0) { + buf[len] = '\0'; + path = std::string(&(buf[0])); + std::string suffixDel = "host" + std::to_string(cardId) + "/scsi_host/" + "host" + std::to_string(cardId) + "/"; + path.erase(path.length() - suffixDel.length()); + + auto first = path.begin(); + constexpr auto MAX_BDF_LENGTH = std::size_t(12); + auto end = path.begin() + path.length() - MAX_BDF_LENGTH; + path.erase(first, end); + + std::string prefixAdd = kPathPciDevices; + path = prefixAdd.append(path); + } + + if (isBRCMswitch(path)) { + std::string d_name = kSwitchPrefix; + d_name += std::to_string(cardId); + AddToSWITCHDeviceList(d_name, UINT64_MAX); + } + } + + if (closedir(scsi_host_dir)) { + err_msg = "Failed to close switch root directory, while DiscoverBRCMswitchDevices and graceful exit."; + err_msg += kPathSwitchRoot; + err_msg += "."; + perror(err_msg.c_str()); + } + return 0; +} // Since these sysfs files require sudo access, we won't discover them // with rsmi_init() (and thus always require the user to use "sudo". diff --git a/projects/amdsmi/rocm_smi/src/rocm_smi_utils.cc b/projects/amdsmi/rocm_smi/src/rocm_smi_utils.cc index e6c152fa7e4..e5e02c027fb 100644 --- a/projects/amdsmi/rocm_smi/src/rocm_smi_utils.cc +++ b/projects/amdsmi/rocm_smi/src/rocm_smi_utils.cc @@ -1336,4 +1336,10 @@ uint64_t get_multiplier_from_char(char units_char) { return multiplier; } +uint64_t bdfid_from_domain(uint64_t bdfid, uint64_t domain) { + assert((domain & 0xFFFFFFFF00000000) == 0); + (bdfid) &= 0xFFFFFFFF; // keep bottom 32 bits of pci_id + bdfid |= (domain & 0xFFFFFFFF) << 32; // Add domain to top of pci_id + return bdfid; +} } // namespace amd::smi diff --git a/projects/amdsmi/src/CMakeLists.txt b/projects/amdsmi/src/CMakeLists.txt index c5d6c56d866..de8ead5ffa3 100644 --- a/projects/amdsmi/src/CMakeLists.txt +++ b/projects/amdsmi/src/CMakeLists.txt @@ -14,6 +14,35 @@ set(SO_VERSION_GIT_TAG_PREFIX "amdsmi_so_ver") set(SRC_DIR "amd_smi") set(INC_DIR "${PROJECT_SOURCE_DIR}/include/amd_smi") +option(BRCM_NIC "Build brcm-nic" OFF) +if(BRCM_NIC) + add_compile_definitions(BRCM_NIC) + set(NIC_SOURCES + "${PROJECT_SOURCE_DIR}/src/nic/brcm-nic/amd_smi_lspci_commands.cc" + "${PROJECT_SOURCE_DIR}/src/nic/brcm-nic/amd_smi_nic_device.cc" + "${PROJECT_SOURCE_DIR}/src/nic/brcm-nic/amd_smi_no_drm_nic.cc" + "${PROJECT_SOURCE_DIR}/src/nic/brcm-nic/amd_smi_no_drm_switch.cc" + "${PROJECT_SOURCE_DIR}/src/nic/brcm-nic/amd_smi_switch_device.cc" + ) + set(NIC_INCLUDES + ${INC_DIR}/impl/nic/amd_smi_lspci_commands.h + ${INC_DIR}/impl/nic/amd_smi_nic_device.h + ${INC_DIR}/impl/nic/amd_smi_no_drm_nic.h + ${INC_DIR}/impl/nic/amd_smi_no_drm_switch.h + ${INC_DIR}/impl/nic/amd_smi_switch_device.h + ) +endif() + +set(NIC_SOURCES ${NIC_SOURCES} + "${PROJECT_SOURCE_DIR}/src/nic/ai-nic/amd_smi_ainic_device.cc" +) +set(NIC_INCLUDES ${NIC_INCLUDES} + ${INC_DIR}/impl/nic/amd_smi_ainic_device.h +) +set(NIC_INCLUDE_DIRS + "${PROJECT_SOURCE_DIR}/src/nic/ai-nic/" +) + set(SRC_LIST "${SRC_DIR}/amd_smi.cc" "${SRC_DIR}/amd_smi_cper.cc" @@ -27,22 +56,19 @@ set(SRC_LIST "${SRC_DIR}/amd_smi_uuid.cc" "${SRC_DIR}/scoped_fd.cc" "${SRC_DIR}/fdinfo.cc" - "${CMN_SRC_LIST}") + "${CMN_SRC_LIST}" + "${NIC_SOURCES}" +) + +file(GLOB IMPL_HEADERS "${INC_DIR}/impl/*.h") set(INC_LIST + "${IMPL_HEADERS}" "${INC_DIR}/amdsmi.h" - "${INC_DIR}/impl/amd_smi_common.h" - "${INC_DIR}/impl/amd_smi_cper.h" - "${INC_DIR}/impl/amd_smi_processor.h" - "${INC_DIR}/impl/amd_smi_drm.h" - "${INC_DIR}/impl/amd_smi_gpu_device.h" - "${INC_DIR}/impl/amd_smi_lib_loader.h" - "${INC_DIR}/impl/amd_smi_socket.h" - "${INC_DIR}/impl/amd_smi_system.h" - "${INC_DIR}/impl/amd_smi_utils.h" - "${INC_DIR}/impl/amd_smi_uuid.h" - "${INC_DIR}/impl/scoped_fd.h" + "${PROJECT_SOURCE_DIR}" "${PROJECT_SOURCE_DIR}/rocm_smi/include/rocm_smi/rocm_smi.h" - "${PROJECT_SOURCE_DIR}/rocm_smi/include/rocm_smi/rocm_smi_utils.h") + "${PROJECT_SOURCE_DIR}/rocm_smi/include/rocm_smi/rocm_smi_utils.h" + "${NIC_INCLUDES}" +) set(RAS_DECODE "ras-decode") set(RAS_DECODE_REPO "git@github.amd.com:dctools/ras-decode-instinct-staging.git") @@ -83,24 +109,33 @@ message("Package version: ${PKG_VERSION_STR}") set(SO_VERSION_STRING "${MAJOR}.${MINOR}.${RELEASE}") message("SOVERSION: ${SO_VERSION_STRING}") +add_subdirectory(nic/ai-nic/amdsmi_unified) add_library(${AMD_SMI} ${SRC_LIST} ${INC_LIST}) target_link_libraries(${AMD_SMI} PRIVATE rt Threads::Threads ${CMAKE_DL_LIBS} + amdsminic ${FILESYSTEM_LIB} ) +target_link_directories(${AMD_SMI} PRIVATE + ${CMAKE_CURRENT_BINARY_DIR}/nic/ai-nic/amdsmi_unified/build/ +) target_include_directories(${AMD_SMI} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/../ ${PROJECT_SOURCE_DIR}/rocm_smi/include ${PROJECT_SOURCE_DIR}/common/shared_mutex ${RAS_DECODE_INC_DIR} ${DRM_INCLUDE_DIRS} ${DRM_AMDGPU_INCLUDE_DIRS} + ${NIC_INCLUDE_DIRS} ) # use the target_include_directories() command to specify the include directories for the target -target_include_directories(${AMD_SMI} PUBLIC "$" +target_include_directories(${AMD_SMI} PUBLIC + "$" + "$" "$") ## Set the VERSION and SOVERSION values @@ -128,3 +163,53 @@ install( FILES ${PROJECT_SOURCE_DIR}/include/amd_smi/amdsmi.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/amd_smi COMPONENT dev) + +install( + FILES ${IMPL_HEADERS} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/amd_smi/impl/ + COMPONENT dev) + +install( + FILES ${NIC_INCLUDES} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/amd_smi/impl/nic/ + COMPONENT dev) + +set(UNIFIED_NIC_INCLUDES + ${PROJECT_SOURCE_DIR}/src/nic/ai-nic/amdsmi_unified/interface/smi_nic_interface.h +) +install( + FILES ${UNIFIED_NIC_INCLUDES} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/amd_smi/impl/nic/ + COMPONENT dev) +install( + FILES ${UNIFIED_NIC_INCLUDES} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/amd_smi/impl/nic/amdsmi_unified/interface + COMPONENT dev) +set(UNIFIED_NIC_INCLUDES + "${PROJECT_SOURCE_DIR}/src/nic/ai-nic/amdsmi_unified/inc/smi_devlink_netlink.h" + "${PROJECT_SOURCE_DIR}/src/nic/ai-nic/amdsmi_unified/inc/smi_ethtool_ioctl.h" + "${PROJECT_SOURCE_DIR}/src/nic/ai-nic/amdsmi_unified/inc/smi_nic.h" + "${PROJECT_SOURCE_DIR}/src/nic/ai-nic/amdsmi_unified/inc/smi_nic_subsystem.h" + "${PROJECT_SOURCE_DIR}/src/nic/ai-nic/amdsmi_unified/inc/smi_nic_system.h" + "${PROJECT_SOURCE_DIR}/src/nic/ai-nic/amdsmi_unified/inc/smi_sysfs.h" +) +install( + FILES ${UNIFIED_NIC_INCLUDES} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/amd_smi/impl/nic/amdsmi_unified/inc/ + COMPONENT dev) + +file(GLOB ESMI_HEADERS "${PROJECT_SOURCE_DIR}/esmi_ib_library/include/e_smi/*.h") +install( + FILES ${ESMI_HEADERS} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/e_smi/ + COMPONENT dev) + +install( + FILES "${PROJECT_SOURCE_DIR}/rocm_smi/include/rocm_smi/rocm_smi_logger.h" + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/rocm_smi/ + COMPONENT dev) + +install( + FILES "${PROJECT_SOURCE_DIR}/rocm_smi/include/rocm_smi/rocm_smi_logger.h" + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/rocm_smi/ + COMPONENT dev) \ No newline at end of file diff --git a/projects/amdsmi/src/amd_smi/amd_smi.cc b/projects/amdsmi/src/amd_smi/amd_smi.cc index a8e3c47edc9..9363e9c1864 100644 --- a/projects/amdsmi/src/amd_smi/amd_smi.cc +++ b/projects/amdsmi/src/amd_smi/amd_smi.cc @@ -28,7 +28,6 @@ #include #include - #include #include #include @@ -54,6 +53,14 @@ #include "amd_smi/impl/amd_smi_system.h" #include "amd_smi/impl/amd_smi_socket.h" #include "amd_smi/impl/amd_smi_gpu_device.h" +#include "amd_smi/impl/nic/amd_smi_ainic_device.h" +#include "amdsmi_unified/interface/smi_nic_interface.h" + +#ifdef BRCM_NIC +#include "amd_smi/impl/nic/amd_smi_nic_device.h" +#include "amd_smi/impl/nic/amd_smi_switch_device.h" +#include "amd_smi/impl/nic/amd_smi_lspci_commands.h" +#endif//BRCM_NIC #include "amd_smi/impl/amd_smi_uuid.h" #include "amd_smi/impl/xf86drm.h" #include "amd_smi/impl/amd_smi_utils.h" @@ -142,8 +149,6 @@ static amdsmi_status_t get_gpu_device_from_handle(amdsmi_processor_handle proces LOG_ERROR(ss); return AMDSMI_STATUS_NOT_SUPPORTED; } - - template amdsmi_status_t rsmi_wrapper(F && f, amdsmi_processor_handle processor_handle, uint32_t increment_gpu_id, Args &&... args) { @@ -179,6 +184,121 @@ amdsmi_status_t rsmi_wrapper(F && f, LOG_INFO(ss); return r; } +static amdsmi_status_t get_ainic_device_from_handle(amdsmi_processor_handle processor_handle, + amd::smi::AMDSmiAINICDevice **nicdevice) { + AMDSMI_CHECK_INIT(); + if (processor_handle == nullptr || nicdevice == nullptr) return AMDSMI_STATUS_INVAL; + + amd::smi::AMDSmiProcessor *device = nullptr; + amdsmi_status_t r = + amd::smi::AMDSmiSystem::getInstance().handle_to_processor(processor_handle, &device); + if (r != AMDSMI_STATUS_SUCCESS) return r; + + if (device->get_processor_type() == AMDSMI_PROCESSOR_TYPE_AMD_NIC) { + *nicdevice = static_cast(device); + return AMDSMI_STATUS_SUCCESS; + } + + return AMDSMI_STATUS_NOT_SUPPORTED; +} +#ifdef BRCM_NIC +static amdsmi_status_t get_nic_device_from_handle(amdsmi_processor_handle processor_handle, + amd::smi::AMDSmiNICDevice **nicdevice) { + AMDSMI_CHECK_INIT(); + + if (processor_handle == nullptr || nicdevice == nullptr) return AMDSMI_STATUS_INVAL; + + amd::smi::AMDSmiProcessor *device = nullptr; + amdsmi_status_t r = + amd::smi::AMDSmiSystem::getInstance().handle_to_processor(processor_handle, &device); + if (r != AMDSMI_STATUS_SUCCESS) return r; + + if (device->get_processor_type() == AMDSMI_PROCESSOR_TYPE_BRCM_NIC) { + *nicdevice = static_cast(device); + return AMDSMI_STATUS_SUCCESS; + } + + return AMDSMI_STATUS_NOT_SUPPORTED; +} + +static amdsmi_status_t get_switch_device_from_handle(amdsmi_processor_handle processor_handle, + amd::smi::AMDSmiSWITCHDevice **switchdevice) { + AMDSMI_CHECK_INIT(); + + if (processor_handle == nullptr || switchdevice == nullptr) return AMDSMI_STATUS_INVAL; + + amd::smi::AMDSmiProcessor *device = nullptr; + amdsmi_status_t r = + amd::smi::AMDSmiSystem::getInstance().handle_to_processor(processor_handle, &device); + if (r != AMDSMI_STATUS_SUCCESS) return r; + + if (device->get_processor_type() == AMDSMI_PROCESSOR_TYPE_BRCM_SWITCH) { + *switchdevice = static_cast(device); + return AMDSMI_STATUS_SUCCESS; + } + + return AMDSMI_STATUS_NOT_SUPPORTED; +} + +template +static amdsmi_status_t +rsmi_nic_wrapper(F &&f, amdsmi_processor_handle processor_handle, Args &&... args) { + + std::ostringstream ss; + const char *status_string = nullptr; + + amd::smi::AMDSmiNICDevice *nic_device = nullptr; + amdsmi_status_t r = get_nic_device_from_handle(processor_handle, &nic_device); + if (r != AMDSMI_STATUS_SUCCESS) { + amdsmi_status_code_to_string(r, &status_string); + ss << __PRETTY_FUNCTION__ << " | " << status_string; + LOG_INFO(ss); + return r; + } + + uint32_t nic_index = nic_device->get_nic_id(); + auto rstatus = std::forward(f)(nic_index, std::forward(args)...); + r = amd::smi::rsmi_to_amdsmi_status(rstatus); + amdsmi_status_code_to_string(r, &status_string); + ss << __PRETTY_FUNCTION__ << " | returning status = " << status_string; + if (r != AMDSMI_STATUS_SUCCESS) { + LOG_ERROR(ss); + } + else { + LOG_INFO(ss); + } + return r; +} + +template +amdsmi_status_t rsmi_switch_wrapper(F &&f, amdsmi_processor_handle processor_handle, Args &&... args) { + + std::ostringstream ss; + const char *status_string = nullptr; + + amd::smi::AMDSmiSWITCHDevice *switch_device = nullptr; + amdsmi_status_t r = get_switch_device_from_handle(processor_handle, &switch_device); + if (r != AMDSMI_STATUS_SUCCESS) { + amdsmi_status_code_to_string(r, &status_string); + ss << __PRETTY_FUNCTION__ << " | " << status_string; + LOG_INFO(ss); + return r; + } + + uint32_t switch_index = switch_device->get_switch_id(); + auto rstatus = std::forward(f)(switch_index, std::forward(args)...); + r = amd::smi::rsmi_to_amdsmi_status(rstatus); + amdsmi_status_code_to_string(r, &status_string); + ss << __PRETTY_FUNCTION__ << " | returning status = " << status_string; + if (r != AMDSMI_STATUS_SUCCESS) { + LOG_ERROR(ss); + } + else { + LOG_INFO(ss); + } + return r; +} +#endif//BRCM_NIC amdsmi_status_t amdsmi_init(uint64_t flags) { @@ -415,7 +535,7 @@ amdsmi_status_t amdsmi_get_socket_info( .handle_to_socket(socket_handle, &socket); if (r != AMDSMI_STATUS_SUCCESS) return r; - strncpy(name, socket->get_socket_id().c_str(), len); + snprintf(name, len, "%s", socket->get_socket_id().c_str()); return AMDSMI_STATUS_SUCCESS; } @@ -437,7 +557,7 @@ amdsmi_status_t amdsmi_get_processor_info( if (r != AMDSMI_STATUS_SUCCESS) return r; snprintf(proc_id, sizeof(proc_id), "%d", processor->get_processor_index()); - strncpy(name, proc_id, len); + snprintf(name, len, "%s", proc_id); return AMDSMI_STATUS_SUCCESS; } @@ -478,6 +598,75 @@ amdsmi_status_t amdsmi_get_processor_handles(amdsmi_socket_handle socket_handle, return AMDSMI_STATUS_SUCCESS; } +amdsmi_status_t amdsmi_get_nic_processor_handles(amdsmi_socket_handle socket_handle, + uint32_t* processor_count, + amdsmi_processor_handle* processor_handles) { + AMDSMI_CHECK_INIT(); + + if (processor_count == nullptr) { + return AMDSMI_STATUS_INVAL; + } + + // Get the socket object via socket handle. + amd::smi::AMDSmiSocket* socket = nullptr; + amdsmi_status_t r = amd::smi::AMDSmiSystem::getInstance() + .handle_to_socket(socket_handle, &socket); + if (r != AMDSMI_STATUS_SUCCESS) return r; + + std::vector& processors = socket->get_processors(AMDSMI_PROCESSOR_TYPE_BRCM_NIC); + uint32_t processor_size = static_cast(processors.size()); + // Get the processor count only + if (processor_handles == nullptr) { + *processor_count = processor_size; + return AMDSMI_STATUS_SUCCESS; + } + + // If the processor_handles can hold all processors, return all of them. + *processor_count = *processor_count >= processor_size ? processor_size : *processor_count; + + // Copy the processor handles + for (uint32_t i = 0; i < *processor_count; i++) { + processor_handles[i] = reinterpret_cast(processors[i]); + } + + return AMDSMI_STATUS_SUCCESS; +} + +amdsmi_status_t amdsmi_get_switch_processor_handles(amdsmi_socket_handle socket_handle, + uint32_t* processor_count, + amdsmi_processor_handle* processor_handles) { + AMDSMI_CHECK_INIT(); + + if (processor_count == nullptr) { + return AMDSMI_STATUS_INVAL; + } + + // Get the socket object via socket handle. + amd::smi::AMDSmiSocket* socket = nullptr; + amdsmi_status_t r = amd::smi::AMDSmiSystem::getInstance() + .handle_to_socket(socket_handle, &socket); + if (r != AMDSMI_STATUS_SUCCESS) return r; + + processor_type_t processor_type = static_cast(AMDSMI_PROCESSOR_TYPE_BRCM_SWITCH); + std::vector& processors = socket->get_processors(processor_type); + uint32_t processor_size = static_cast(processors.size()); + // Get the processor count only + if (processor_handles == nullptr) { + *processor_count = processor_size; + return AMDSMI_STATUS_SUCCESS; + } + + // If the processor_handles can hold all processors, return all of them. + *processor_count = *processor_count >= processor_size ? processor_size : *processor_count; + + // Copy the processor handles + for (uint32_t i = 0; i < *processor_count; i++) { + processor_handles[i] = reinterpret_cast(processors[i]); + } + + return AMDSMI_STATUS_SUCCESS; +} + amdsmi_status_t amdsmi_get_node_handle(amdsmi_processor_handle processor_handle, amdsmi_node_handle *node_handle) { @@ -655,6 +844,420 @@ amdsmi_get_gpu_device_bdf(amdsmi_processor_handle processor_handle, amdsmi_bdf_t return AMDSMI_STATUS_SUCCESS; } +amdsmi_status_t +amdsmi_get_ainic_info(amdsmi_processor_handle processor_handle, amd::smi::AMDSmiAINICDevice::AINICInfo *info) { + AMDSMI_CHECK_INIT(); + + if (!info) { + return AMDSMI_STATUS_INVAL; + } + + amd::smi::AMDSmiAINICDevice *nic_device = nullptr; + amdsmi_status_t r = get_ainic_device_from_handle(processor_handle, &nic_device); + if (r != AMDSMI_STATUS_SUCCESS || !nic_device) return r; + + nic_device->amd_query_nic_info(*info); + return AMDSMI_STATUS_SUCCESS; +} + +amdsmi_status_t amdsmi_get_nic_asic_info(amdsmi_processor_handle processor_handle, amdsmi_nic_asic_info_t *info) { + AMDSMI_CHECK_INIT(); + + if (info == NULL) { + return AMDSMI_STATUS_INVAL; + } + amd::smi::AMDSmiAINICDevice::AINICInfo ainic_info = {}; + amdsmi_status_t status = amdsmi_get_ainic_info(processor_handle, &ainic_info); + if(status != AMDSMI_STATUS_SUCCESS){ + return status; + } + *info = ainic_info.asic; + return AMDSMI_STATUS_SUCCESS; +} +amdsmi_status_t amdsmi_get_nic_bus_info(amdsmi_processor_handle processor_handle, amdsmi_nic_bus_info_t *info) { + AMDSMI_CHECK_INIT(); + + if (info == NULL) { + return AMDSMI_STATUS_INVAL; + } + amd::smi::AMDSmiAINICDevice::AINICInfo ainic_info = {}; + amdsmi_status_t status = amdsmi_get_ainic_info(processor_handle, &ainic_info); + if(status != AMDSMI_STATUS_SUCCESS){ + return status; + } + *info = ainic_info.bus; + return AMDSMI_STATUS_SUCCESS; +} +amdsmi_status_t amdsmi_get_nic_driver_info(amdsmi_processor_handle processor_handle, amdsmi_nic_driver_info_t *info) { + AMDSMI_CHECK_INIT(); + + if (info == NULL) { + return AMDSMI_STATUS_INVAL; + } + amd::smi::AMDSmiAINICDevice::AINICInfo ainic_info = {}; + amdsmi_status_t status = amdsmi_get_ainic_info(processor_handle, &ainic_info); + if(status != AMDSMI_STATUS_SUCCESS){ + return status; + } + *info = ainic_info.driver; + return AMDSMI_STATUS_SUCCESS; +} +amdsmi_status_t amdsmi_get_nic_numa_info(amdsmi_processor_handle processor_handle, amdsmi_nic_numa_info_t *info) { + AMDSMI_CHECK_INIT(); + + if (info == NULL) { + return AMDSMI_STATUS_INVAL; + } + amd::smi::AMDSmiAINICDevice::AINICInfo ainic_info = {}; + amdsmi_status_t status = amdsmi_get_ainic_info(processor_handle, &ainic_info); + if(status != AMDSMI_STATUS_SUCCESS){ + return status; + } + *info = ainic_info.numa; + return AMDSMI_STATUS_SUCCESS; +} +amdsmi_status_t amdsmi_get_nic_port_info(amdsmi_processor_handle processor_handle, amdsmi_nic_port_info_t *info) { + AMDSMI_CHECK_INIT(); + + if (info == NULL) { + return AMDSMI_STATUS_INVAL; + } + amd::smi::AMDSmiAINICDevice::AINICInfo ainic_info = {}; + amdsmi_status_t status = amdsmi_get_ainic_info(processor_handle, &ainic_info); + if(status != AMDSMI_STATUS_SUCCESS){ + return status; + } + *info = ainic_info.port; + return AMDSMI_STATUS_SUCCESS; +} +amdsmi_status_t amdsmi_get_nic_rdma_dev_info(amdsmi_processor_handle processor_handle, amdsmi_nic_rdma_devices_info_t *info) { + AMDSMI_CHECK_INIT(); + + if (info == NULL) { + return AMDSMI_STATUS_INVAL; + } + amd::smi::AMDSmiAINICDevice::AINICInfo ainic_info = {}; + amdsmi_status_t status = amdsmi_get_ainic_info(processor_handle, &ainic_info); + if(status != AMDSMI_STATUS_SUCCESS){ + return status; + } + *info = ainic_info.rdma_dev; + return AMDSMI_STATUS_SUCCESS; +} + +#ifdef BRCM_NIC +amdsmi_status_t amdsmi_get_nic_info(amdsmi_processor_handle processor_handle, amdsmi_brcm_nic_info_t *info) { + AMDSMI_CHECK_INIT(); + + if (info == NULL) { + return AMDSMI_STATUS_INVAL; + } + + amd::smi::AMDSmiNICDevice *nic_device = nullptr; + amdsmi_status_t r = get_nic_device_from_handle(processor_handle, &nic_device); + if (r != AMDSMI_STATUS_SUCCESS) return r; + + nic_device->amd_query_nic_info(*info); + return AMDSMI_STATUS_SUCCESS; +} + +amdsmi_status_t amdsmi_get_nic_temp_info(amdsmi_processor_handle processor_handle, + amdsmi_brcm_nic_temperature_metric_t *info) { + AMDSMI_CHECK_INIT(); + + if (info == NULL) { + return AMDSMI_STATUS_INVAL; + } + + amd::smi::AMDSmiNICDevice *nic_device = nullptr; + amdsmi_status_t r = get_nic_device_from_handle(processor_handle, &nic_device); + if (r != AMDSMI_STATUS_SUCCESS) return r; + + nic_device->amd_query_nic_temp_info(*info); + return AMDSMI_STATUS_SUCCESS; +} + +amdsmi_status_t amdsmi_get_nic_power_info(amdsmi_processor_handle processor_handle, + amdsmi_brcm_nic_hwmon_power_t *info) { + AMDSMI_CHECK_INIT(); + if (info == NULL) { + return AMDSMI_STATUS_INVAL; + } + amd::smi::AMDSmiNICDevice *nic_device = nullptr; + amdsmi_status_t r = get_nic_device_from_handle(processor_handle, &nic_device); + if (r != AMDSMI_STATUS_SUCCESS) return r; + + nic_device->amd_query_nic_power_info(*info); + return AMDSMI_STATUS_SUCCESS; +} + +amdsmi_status_t amdsmi_get_nic_device_info(amdsmi_processor_handle processor_handle, + amdsmi_brcm_nic_hwmon_device_t *info) { + AMDSMI_CHECK_INIT(); + if (info == NULL) { + return AMDSMI_STATUS_INVAL; + } + amd::smi::AMDSmiNICDevice *nic_device = nullptr; + amdsmi_status_t r = get_nic_device_from_handle(processor_handle, &nic_device); + if (r != AMDSMI_STATUS_SUCCESS) return r; + + nic_device->amd_query_nic_device_info(*info); + return AMDSMI_STATUS_SUCCESS; +} + +amdsmi_status_t amdsmi_get_nic_metrics_info(amdsmi_processor_handle processor_handle, + amdsmi_brcm_nic_hwmon_metrics_t *metrics) { + AMDSMI_CHECK_INIT(); + if (metrics == NULL) { + return AMDSMI_STATUS_INVAL; + } + + amdsmi_status_t ret; + + amd::smi::AMDSmiNICDevice *nic_device = nullptr; + amdsmi_status_t r = get_nic_device_from_handle(processor_handle, &nic_device); + if (r != AMDSMI_STATUS_SUCCESS) return r; + + // Fetch power metrics + ret = nic_device->amd_query_nic_power_info(metrics->nic_power); + if (ret != AMDSMI_STATUS_SUCCESS) { + std::ostringstream ss; + ss << __PRETTY_FUNCTION__ + << " | Failed to fetch NIC power metrics: " << ret; + LOG_INFO(ss); + return ret; + } + + // Fetch temperature metrics + ret = nic_device->amd_query_nic_temp_info(metrics->nic_temperature); + if (ret != AMDSMI_STATUS_SUCCESS) { + std::ostringstream ss; + ss << __PRETTY_FUNCTION__ + << " | Failed to fetch NIC temperature metrics: " << ret; + LOG_INFO(ss); + return ret; + } + + // Fetch the full device struct + amdsmi_brcm_nic_hwmon_device_t full_device; + ret = nic_device->amd_query_nic_device_info(full_device); + if (ret != AMDSMI_STATUS_SUCCESS) { + std::ostringstream ss; + ss << __PRETTY_FUNCTION__ + << " | Failed to fetch NIC device metrics: " << ret; + LOG_INFO(ss); + return ret; + } + + // Copy only the 3 required fields into metrics + snprintf(metrics->nic_device_aer_dev_correctable, AMDSMI_MAX_STRING_LENGTH - 1, "%s", full_device.nic_device_aer_dev_correctable); + snprintf(metrics->nic_device_aer_dev_fatal, AMDSMI_MAX_STRING_LENGTH - 1, "%s", full_device.nic_device_aer_dev_fatal); + snprintf(metrics->nic_device_aer_dev_nonfatal, AMDSMI_MAX_STRING_LENGTH - 1, "%s", full_device.nic_device_aer_dev_nonfatal); + return AMDSMI_STATUS_SUCCESS; +} + +amdsmi_status_t amdsmi_get_switch_device_bdf(amdsmi_processor_handle processor_handle, + amdsmi_bdf_t* bdf) { + AMDSMI_CHECK_INIT(); + + if (bdf == NULL) { + return AMDSMI_STATUS_INVAL; + } + + amd::smi::AMDSmiSWITCHDevice* switch_device = nullptr; + amdsmi_status_t r = get_switch_device_from_handle(processor_handle, &switch_device); + if (r != AMDSMI_STATUS_SUCCESS) return r; + + // get bdf from sysfs file + *bdf = switch_device->get_bdf(); + return AMDSMI_STATUS_SUCCESS; +} + +amdsmi_status_t amdsmi_get_switch_link_info(amdsmi_processor_handle processor_handle, + amdsmi_brcm_switch_link_metric_t *info) { + AMDSMI_CHECK_INIT(); + + if (info == NULL) { + return AMDSMI_STATUS_INVAL; + } + + amd::smi::AMDSmiSWITCHDevice *switch_device = nullptr; + amdsmi_status_t r = get_switch_device_from_handle(processor_handle, &switch_device); + if (r != AMDSMI_STATUS_SUCCESS) return r; + + switch_device->amd_query_switch_link_info(*info); + return AMDSMI_STATUS_SUCCESS; +} + +amdsmi_status_t amdsmi_get_switch_power_info(amdsmi_processor_handle processor_handle, + amdsmi_brcm_switch_power_metric_t *info) { + AMDSMI_CHECK_INIT(); + + if (info == NULL) { + return AMDSMI_STATUS_INVAL; + } + + amd::smi::AMDSmiSWITCHDevice *switch_device = nullptr; + amdsmi_status_t r = get_switch_device_from_handle(processor_handle, &switch_device); + if (r != AMDSMI_STATUS_SUCCESS) return r; + + switch_device->amd_query_switch_power_info(*info); + return AMDSMI_STATUS_SUCCESS; +} + +amdsmi_status_t amdsmi_get_switch_device_info(amdsmi_processor_handle processor_handle, + amdsmi_brcm_switch_device_metric_t *info) { + AMDSMI_CHECK_INIT(); + + if (info == NULL) { + return AMDSMI_STATUS_INVAL; + } + amdsmi_status_t ret; + ret = amdsmi_get_switch_power_info(processor_handle, &(info->brcm_device_power)); + if (ret != AMDSMI_STATUS_SUCCESS) { + std::ostringstream ss; + ss << __PRETTY_FUNCTION__ << " amdsmi_get_switch_device_info - Failed to fetch power metrics"; + LOG_ERROR(ss); + return ret; + } + + amd::smi::AMDSmiSWITCHDevice *switch_device = nullptr; + amdsmi_status_t r = get_switch_device_from_handle(processor_handle, &switch_device); + if (r != AMDSMI_STATUS_SUCCESS) return r; + + switch_device->amd_query_switch_device_info(*info); + return AMDSMI_STATUS_SUCCESS; +} + +amdsmi_status_t amdsmi_get_switch_metrics_info(amdsmi_processor_handle processor_handle, amdsmi_brcm_switch_metric_t *info){ + AMDSMI_CHECK_INIT(); + + if (info == NULL) { + return AMDSMI_STATUS_INVAL; + } + amdsmi_status_t ret; + ret = amdsmi_get_switch_power_info(processor_handle, &(info->brcm_power)); + if (ret != AMDSMI_STATUS_SUCCESS) { + std::ostringstream ss; + ss << __PRETTY_FUNCTION__ << " amdsmi_get_switch_metrics_info - Failed to fetch power metrics"; + LOG_ERROR(ss); + return ret; + } + + // Fetch the full device struct + amdsmi_brcm_switch_device_metric_t full_device; + ret = amdsmi_get_switch_device_info(processor_handle, &full_device); + if (ret != AMDSMI_STATUS_SUCCESS) { + std::ostringstream ss; + ss << __PRETTY_FUNCTION__ << " amdsmi_get_switch_metrics_info - Failed to fetch switch device."; + LOG_ERROR(ss); + return ret; + } + + // Copy only the 3 required fields into metrics + snprintf(info->brcm_device_aer_dev_correctable, AMDSMI_MAX_STRING_LENGTH - 1, "%s", full_device.brcm_device_aer_dev_correctable); + snprintf(info->brcm_device_aer_dev_nonfatal, AMDSMI_MAX_STRING_LENGTH - 1, "%s", full_device.brcm_device_aer_dev_nonfatal); + snprintf(info->brcm_device_aer_dev_fatal, AMDSMI_MAX_STRING_LENGTH - 1, "%s", full_device.brcm_device_aer_dev_fatal); + + return AMDSMI_STATUS_SUCCESS; +} +amdsmi_status_t amdsmi_get_nic_fw_info(amdsmi_processor_handle processor_handle, + amdsmi_brcm_nic_firmware_t *info) { + AMDSMI_CHECK_INIT(); + if (info == NULL) { + return AMDSMI_STATUS_INVAL; + } + amd::smi::AMDSmiNICDevice *nic_device = nullptr; + amdsmi_status_t r = get_nic_device_from_handle(processor_handle, &nic_device); + if (r != AMDSMI_STATUS_SUCCESS) return r; + nic_device->amd_query_nic_firmware_info(*info); + return AMDSMI_STATUS_SUCCESS; +} +#endif//BRCM_NIC + +amdsmi_status_t amdsmi_get_nic_rdma_port_statistics( + amdsmi_processor_handle processor_handle, + uint32_t rdma_port_index, + uint32_t *num_stats, + amdsmi_nic_stat_t *stats) { + + amdsmi_status_t status = AMDSMI_STATUS_SUCCESS; + std::ostringstream ss; + AMDSMI_CHECK_INIT(); + + amd::smi::AMDSmiAINICDevice *nic_device = nullptr; + status = get_ainic_device_from_handle(processor_handle, &nic_device); + if (status != AMDSMI_STATUS_SUCCESS) { + ss << __PRETTY_FUNCTION__ << smi_amdgpu_get_status_string(status, false); + LOG_ERROR(ss); + return status; + } + amd::smi::AMDSmiAINICDevice::AINICInfo nic_info = {}; + status = nic_device->amd_query_nic_info(nic_info); + if (status != AMDSMI_STATUS_SUCCESS) { + ss << __PRETTY_FUNCTION__ << " | Failed to query NIC info"; + LOG_ERROR(ss); + return status; + } + if(nic_info.rdma_dev.num_rdma_dev < 1) { + ss << __PRETTY_FUNCTION__ << " | No RDMA devices found"; + LOG_ERROR(ss); + return AMDSMI_STATUS_NOT_SUPPORTED; + } + else if(rdma_port_index >= nic_info.rdma_dev.num_rdma_dev) { + ss << __PRETTY_FUNCTION__ << " | NIC ports (" << rdma_port_index << ") is out of range (max ports:" << nic_info.rdma_dev.num_rdma_dev << ")"; + LOG_ERROR(ss); + return AMDSMI_STATUS_NOT_SUPPORTED; + } + else if(nic_info.rdma_dev.rdma_dev_info[0].num_rdma_ports < 1) { + ss << __PRETTY_FUNCTION__ << " | No RDMA ports found"; + LOG_ERROR(ss); + return AMDSMI_STATUS_NOT_SUPPORTED; + } + else if(!num_stats) { + ss << __PRETTY_FUNCTION__ << " | Invalid num_stats pointer"; + LOG_ERROR(ss); + return AMDSMI_STATUS_INVAL; + } + else if(!stats && *num_stats > 0) { + ss << __PRETTY_FUNCTION__ << " | Invalid stats and num_stats pointers"; + LOG_ERROR(ss); + return AMDSMI_STATUS_INVAL; + } + + std::string netdev(amd::smi::trim(nic_info.rdma_dev.rdma_dev_info[0].rdma_port_info[rdma_port_index].netdev)); + std::string rdmadev(nic_info.rdma_dev.rdma_dev_info[0].rdma_dev); + int port_num = nic_info.rdma_dev.rdma_dev_info[0].rdma_port_info[rdma_port_index].rdma_port; + + std::string directory_path = "/sys/class/net/" + netdev + "/device/infiniband/" + rdmadev + "/subsystem/" + rdmadev + "/subsystem/" + rdmadev + "/ports/" + std::to_string(port_num) + "/hw_counters/"; + if(!std::filesystem::exists(directory_path)) { + ss << __PRETTY_FUNCTION__ << " | Directory does not exist: " << directory_path; + LOG_ERROR(ss); + return AMDSMI_STATUS_FILE_ERROR; + } + + uint32_t idx = 0; + for (const auto& entry : std::filesystem::directory_iterator(directory_path)) { + if (std::filesystem::is_regular_file(entry.path())) { + if(stats && num_stats && idx < *num_stats) { + snprintf(stats[idx].name, sizeof(stats[idx].name), "%s", entry.path().filename().string().c_str()); + std::ifstream in(entry.path()); + if (!in.is_open()) { + ss << __PRETTY_FUNCTION__ << smi_amdgpu_get_status_string(status, false); + LOG_ERROR(ss); + return AMDSMI_STATUS_FILE_ERROR; + } + in >> stats[idx].value; + } + ++idx; + } + } + if(num_stats) { + *num_stats = idx; + } + return AMDSMI_STATUS_SUCCESS; +} + amdsmi_status_t amdsmi_get_gpu_device_uuid(amdsmi_processor_handle processor_handle, unsigned int *uuid_length, @@ -2111,7 +2714,7 @@ amdsmi_status_t amdsmi_get_gpu_vram_info( // init the info structure with default value info->vram_type = AMDSMI_VRAM_TYPE_UNKNOWN; info->vram_size = 0; - strncpy(info->vram_vendor, "UNKNOWN", AMDSMI_MAX_STRING_LENGTH); + snprintf(info->vram_vendor, AMDSMI_MAX_STRING_LENGTH, "UNKNOWN"); info->vram_bit_width = std::numeric_limitsvram_bit_width)>::max(); info->vram_max_bandwidth = std::numeric_limitsvram_max_bandwidth)>::max(); @@ -2204,7 +2807,7 @@ amdsmi_status_t amdsmi_get_gpu_vram_info( if (r == AMDSMI_STATUS_SUCCESS) { for (auto &x : brand) x = static_cast(toupper(x)); - strncpy(info->vram_vendor, brand, AMDSMI_MAX_STRING_LENGTH); + snprintf(info->vram_vendor, AMDSMI_MAX_STRING_LENGTH, "%s", brand); } uint64_t total = 0; r = rsmi_wrapper(rsmi_dev_memory_total_get, processor_handle, 0, @@ -2409,13 +3012,14 @@ amdsmi_status_t amdsmi_get_link_metrics(amdsmi_processor_handle processor_handle size_t last_slash = target.find_last_of('/'); std::string bdf_str = (last_slash != std::string::npos) ? target.substr(last_slash + 1) : target; // Parse BDF string: "dddd:bb:dd.f" - unsigned domain = 0, bus = 0, device = 0, function = 0; - if (sscanf(bdf_str.c_str(), "%4x:%2x:%2x.%1x", &domain, &bus, &device, &function) == 4) { + uint64_t domain = 0; + uint32_t bus = 0, device = 0, function = 0; + if (sscanf(bdf_str.c_str(), "%4lx:%2x:%2x.%1x", &domain, &bus, &device, &function) == 4) { amdsmi_bdf_t dst_bdf = {}; - dst_bdf.domain_number = static_cast(domain); - dst_bdf.bus_number = static_cast(bus); - dst_bdf.device_number = static_cast(device); - dst_bdf.function_number = static_cast(function); + dst_bdf.domain_number = domain & 0xffffffffffff; + dst_bdf.bus_number = static_cast(bus) & 0xff; + dst_bdf.device_number = static_cast(device) & 0x1f; + dst_bdf.function_number = static_cast(function) & 0x07; link_metrics->links[i].bdf = dst_bdf; } break; // Found, stop searching @@ -3545,7 +4149,6 @@ amdsmi_get_power_cap_info(amdsmi_processor_handle processor_handle, if (info == nullptr) return AMDSMI_STATUS_INVAL; - bool set_ret_success = false; amd::smi::AMDSmiGPUDevice* gpudevice = nullptr; amdsmi_status_t r = get_gpu_device_from_handle(processor_handle, &gpudevice); if (r != AMDSMI_STATUS_SUCCESS) @@ -3560,7 +4163,6 @@ amdsmi_get_power_cap_info(amdsmi_processor_handle processor_handle, // Ignore errors to get as much as possible info. memset(info, 0, sizeof(amdsmi_power_cap_info_t)); - int power_cap = 0; int dpm = 0; auto smi_power_cap_status = rsmi_wrapper(rsmi_dev_power_cap_get, processor_handle, 0, sensor_ind, &(info->power_cap)); @@ -3865,9 +4467,8 @@ amdsmi_status_t amdsmi_get_soc_pstate(amdsmi_processor_handle processor_handle, for (uint32_t i = 0; i < rsmi_policy.num_supported && i < AMDSMI_MAX_NUM_PM_POLICIES; i++) { policy->policies[i].policy_id = rsmi_policy.policies[i].policy_id; - strncpy(policy->policies[i].policy_description, - rsmi_policy.policies[i].policy_description, - AMDSMI_MAX_STRING_LENGTH - 1); + snprintf(policy->policies[i].policy_description, AMDSMI_MAX_STRING_LENGTH - 1, "%s", + rsmi_policy.policies[i].policy_description); policy->policies[i].policy_description[AMDSMI_MAX_STRING_LENGTH - 1] = '\0'; } @@ -3909,9 +4510,8 @@ amdsmi_status_t amdsmi_get_xgmi_plpd(amdsmi_processor_handle processor_handle, for (uint32_t i = 0; i < rsmi_policy.num_supported && i < AMDSMI_MAX_NUM_PM_POLICIES; i++) { policy->policies[i].policy_id = rsmi_policy.policies[i].policy_id; - strncpy(policy->policies[i].policy_description, - rsmi_policy.policies[i].policy_description, - AMDSMI_MAX_STRING_LENGTH - 1); + snprintf(policy->policies[i].policy_description, AMDSMI_MAX_STRING_LENGTH - 1, "%s", + rsmi_policy.policies[i].policy_description); policy->policies[i].policy_description[AMDSMI_MAX_STRING_LENGTH - 1] = '\0'; } @@ -4133,6 +4733,188 @@ amdsmi_status_t amdsmi_get_gpu_topo_numa_affinity( numa_node); } +amdsmi_status_t amdsmi_get_gpu_topo_cpu_affinity(amdsmi_processor_handle processor_handle, + unsigned int *cpu_aff_length, char *cpu_aff_data) { + AMDSMI_CHECK_INIT(); + + if (cpu_aff_length == nullptr || cpu_aff_data == nullptr || cpu_aff_length == nullptr || + *cpu_aff_length < AMDSMI_MAX_STRING_LENGTH) { + return AMDSMI_STATUS_INVAL; + } + + amdsmi_status_t status = AMDSMI_STATUS_SUCCESS; + amd::smi::AMDSmiGPUDevice* gpu_device = nullptr; + status = get_gpu_device_from_handle(processor_handle, &gpu_device); + if (status != AMDSMI_STATUS_SUCCESS) + return status; + + std::string cpu_affinity; + status = gpu_device->amdgpu_query_cpu_affinity(cpu_affinity); + if (status != AMDSMI_STATUS_SUCCESS) { + std::ostringstream ss; + ss << __PRETTY_FUNCTION__ + << " | Getting cpu_affinity info failed. Return code:: " << status; + LOG_INFO(ss); + return status; + } + snprintf(cpu_aff_data, *cpu_aff_length - 1,"%s", cpu_affinity.c_str()); + return status; +} + +#ifdef BRCM_NIC +amdsmi_status_t amdsmi_get_nic_gpu_topo_info(amdsmi_processor_handle nic_processor_handle, + amdsmi_processor_handle gpu_processor_handle, size_t *topo_info_length, char *topo_info) { + std::ostringstream ss; + AMDSMI_CHECK_INIT(); + if (topo_info_length == nullptr || topo_info == nullptr || topo_info_length == nullptr || + *topo_info_length < AMDSMI_MAX_STRING_LENGTH) { + return AMDSMI_STATUS_INVAL; + } + amdsmi_status_t status = AMDSMI_STATUS_SUCCESS; + amd::smi::AMDSmiNICDevice *nic_device = nullptr; + amdsmi_status_t r = get_nic_device_from_handle(nic_processor_handle, &nic_device); + if (status != AMDSMI_STATUS_SUCCESS) { + ss << __PRETTY_FUNCTION__ + << " | Received invalid NIC handler. Return code: " << status; + LOG_INFO(ss); + return status; + } + amd::smi::AMDSmiGPUDevice* gpu_device = nullptr; + status = get_gpu_device_from_handle(gpu_processor_handle, &gpu_device); + if (status != AMDSMI_STATUS_SUCCESS) { + ss << __PRETTY_FUNCTION__ + << " | Received invalid GPU handler. Return code: " << status; + LOG_INFO(ss); + return status; + } + amdsmi_bdf_t nic_switchBdf = {}; + status = amdsmi_get_root_switch(nic_device->get_bdf(), &nic_switchBdf); + if (status != AMDSMI_STATUS_SUCCESS) { + ss << __PRETTY_FUNCTION__ + << " | Not able to get nic's switch bdf. Return code: " << status; + LOG_INFO(ss); + return status; + } + amdsmi_bdf_t gpu_switchBdf = {}; + status = amdsmi_get_root_switch(gpu_device->get_bdf(), &gpu_switchBdf); + if (status != AMDSMI_STATUS_SUCCESS) { + ss << __PRETTY_FUNCTION__ + << " | Not able to get gpu's switch bdf. Return code: " << status; + LOG_INFO(ss); + return status; + } + int32_t gpu_numa_node; + status = rsmi_wrapper(rsmi_topo_numa_affinity_get, gpu_processor_handle, 0, &gpu_numa_node); + if (status != AMDSMI_STATUS_SUCCESS) { + ss << __PRETTY_FUNCTION__ + << " | Not able to get gpu's NUMA. Return code: " << status; + LOG_INFO(ss); + return status; + } + int32_t nic_numa_node; + status = nic_device->amd_query_nic_numa_affinity(&nic_numa_node); + if (nic_numa_node == 65535) { + ss << __PRETTY_FUNCTION__ + << " | Not able to get nic's NUMA. Return code: " << status; + LOG_INFO(ss); + return status; + } + if(gpu_numa_node != nic_numa_node) { + snprintf(topo_info, *topo_info_length - 1, "%s", "X-NUMA"); + return AMDSMI_STATUS_SUCCESS; + } + if(gpu_numa_node == nic_numa_node) { + snprintf(topo_info, *topo_info_length - 1, "%s", "NUMA"); + if ((gpu_switchBdf.bus_number == nic_switchBdf.bus_number) && + (gpu_switchBdf.device_number == nic_switchBdf.device_number) && + (gpu_switchBdf.domain_number == nic_switchBdf.domain_number) && + (gpu_switchBdf.function_number == nic_switchBdf.function_number)) { + snprintf(topo_info, *topo_info_length - 1, "%s", "PCIe"); + } + } + return AMDSMI_STATUS_SUCCESS; +} + +amdsmi_status_t amdsmi_get_root_switch(amdsmi_bdf_t devicehBdf, amdsmi_bdf_t *switchBdf) { + AMDSMI_CHECK_INIT(); + amdsmi_status_t status = get_lspci_root_switch(devicehBdf, switchBdf); + return status; +} + +amdsmi_status_t amdsmi_get_nic_topo_numa_affinity( + amdsmi_processor_handle processor_handle, int32_t *numa_node) { + AMDSMI_CHECK_INIT(); + + amd::smi::AMDSmiNICDevice *nic_device = nullptr; + amdsmi_status_t r = get_nic_device_from_handle(processor_handle, &nic_device); + if (r != AMDSMI_STATUS_SUCCESS) return r; + + return nic_device->amd_query_nic_numa_affinity(numa_node); +} + +amdsmi_status_t amdsmi_get_nic_topo_cpu_affinity(amdsmi_processor_handle processor_handle, + unsigned int *cpu_aff_length, char *cpu_aff_data) { + amdsmi_status_t status = AMDSMI_STATUS_SUCCESS; + AMDSMI_CHECK_INIT(); + if (cpu_aff_length == nullptr || cpu_aff_data == nullptr || cpu_aff_length == nullptr || + *cpu_aff_length < AMDSMI_MAX_STRING_LENGTH) { + return AMDSMI_STATUS_INVAL; + } + + amd::smi::AMDSmiNICDevice *nic_device = nullptr; + amdsmi_status_t r = get_nic_device_from_handle(processor_handle, &nic_device); + if (r != AMDSMI_STATUS_SUCCESS) return r; + + std::string cpu_affinity; + status = nic_device->amd_query_nic_cpu_affinity(cpu_affinity); + if (status != AMDSMI_STATUS_SUCCESS) { + std::ostringstream ss; + ss << __PRETTY_FUNCTION__ + << " | Getting cpu_affinity info failed. Return code: " << status; + LOG_INFO(ss); + return status; + } + snprintf(cpu_aff_data, *cpu_aff_length - 1, "%s", cpu_affinity.c_str()); + return status; +} + +amdsmi_status_t amdsmi_get_switch_topo_numa_affinity( + amdsmi_processor_handle processor_handle, int32_t *numa_node) { + AMDSMI_CHECK_INIT(); + + amd::smi::AMDSmiSWITCHDevice *switch_device = nullptr; + amdsmi_status_t r = get_switch_device_from_handle(processor_handle, &switch_device); + if (r != AMDSMI_STATUS_SUCCESS) return r; + + return switch_device->amd_query_switch_numa_affinity(numa_node); +} + +amdsmi_status_t amdsmi_get_switch_topo_cpu_affinity(amdsmi_processor_handle processor_handle, + size_t *cpu_aff_length, char *cpu_aff_data) { + amdsmi_status_t status = AMDSMI_STATUS_SUCCESS; + AMDSMI_CHECK_INIT(); + if (cpu_aff_length == nullptr || cpu_aff_data == nullptr || cpu_aff_length == nullptr || + *cpu_aff_length < AMDSMI_MAX_STRING_LENGTH) { + return AMDSMI_STATUS_INVAL; + } + + amd::smi::AMDSmiSWITCHDevice *switch_device = nullptr; + amdsmi_status_t r = get_switch_device_from_handle(processor_handle, &switch_device); + if (r != AMDSMI_STATUS_SUCCESS) return r; + + std::string cpu_affinity; + status = switch_device->amd_query_switch_cpu_affinity(cpu_affinity); + if (status != AMDSMI_STATUS_SUCCESS) { + std::ostringstream ss; + ss << __PRETTY_FUNCTION__ + << " | Getting cpu_affinity info failed. Return code: " << status; + LOG_INFO(ss); + return status; + } + snprintf(cpu_aff_data, *cpu_aff_length - 1, "%s", cpu_affinity.c_str()); + return status; +} +#endif//BRCM_NIC amdsmi_status_t amdsmi_get_lib_version(amdsmi_version_t *version) { if (version == nullptr) return AMDSMI_STATUS_INVAL; @@ -4225,14 +5007,12 @@ amdsmi_get_gpu_vbios_info(amdsmi_processor_handle processor_handle, amdsmi_vbios sizeof(struct drm_amdgpu_info)); if (drm_write == 0) { - strncpy(info->name, reinterpret_cast(vbios.name), AMDSMI_MAX_STRING_LENGTH); - strncpy(info->build_date, reinterpret_cast(vbios.date), AMDSMI_MAX_STRING_LENGTH - 1); + snprintf(info->name, AMDSMI_MAX_STRING_LENGTH, "%s", reinterpret_cast(vbios.name)); + snprintf(info->build_date, AMDSMI_MAX_STRING_LENGTH - 1, "%s", reinterpret_cast(vbios.date) ); info->build_date[AMDSMI_MAX_STRING_LENGTH - 1] = '\0'; - strncpy(info->part_number, reinterpret_cast(vbios.vbios_pn), - AMDSMI_MAX_STRING_LENGTH); + snprintf(info->part_number, AMDSMI_MAX_STRING_LENGTH, "%s", reinterpret_cast(vbios.vbios_pn)); // Navi devices still interpret vbios version from drm vbios_ver_str - strncpy(info->version, reinterpret_cast(vbios.vbios_ver_str), - AMDSMI_MAX_STRING_LENGTH); + snprintf(info->version, AMDSMI_MAX_STRING_LENGTH, "%s", reinterpret_cast(vbios.vbios_ver_str)); } else { // get sysfs vbios_version string which is known as the part number char vbios_version[AMDSMI_MAX_STRING_LENGTH]; @@ -4241,7 +5021,7 @@ amdsmi_get_gpu_vbios_info(amdsmi_processor_handle processor_handle, amdsmi_vbios // fail if cannot get vbios version from sysfs if (status == AMDSMI_STATUS_SUCCESS) { - strncpy(info->part_number, vbios_version, AMDSMI_MAX_STRING_LENGTH); + snprintf(info->part_number, AMDSMI_MAX_STRING_LENGTH, "%s", vbios_version); } } libdrm.unload(); @@ -4255,8 +5035,8 @@ amdsmi_get_gpu_vbios_info(amdsmi_processor_handle processor_handle, amdsmi_vbios // Continue if sysfs doesn't exist if (build_status == AMDSMI_STATUS_SUCCESS) { // This device has an ifwi version so swap the version and boot_firmware - strncpy(info->boot_firmware, info->version, AMDSMI_MAX_STRING_LENGTH); - strncpy(info->version, vbios_build_number, AMDSMI_MAX_STRING_LENGTH); + snprintf(info->boot_firmware, AMDSMI_MAX_STRING_LENGTH, "%s", info->version); + snprintf(info->version, AMDSMI_MAX_STRING_LENGTH, "%s", vbios_build_number); } ss << __PRETTY_FUNCTION__ @@ -4832,11 +5612,11 @@ amdsmi_status_t amdsmi_get_gpu_driver_info(amdsmi_processor_handle processor_han driver_date = driver_date.substr(0, 4) + "/" + driver_date.substr(4, 2) + "/" + driver_date.substr(6, 2) + " 00:00"; } - strncpy(info->driver_date, driver_date.c_str(), AMDSMI_MAX_STRING_LENGTH-1); + snprintf(info->driver_date, AMDSMI_MAX_STRING_LENGTH, "%s", driver_date.c_str()); // Get the driver name std::string driver_name = version->name; - strncpy(info->driver_name, driver_name.c_str(), AMDSMI_MAX_STRING_LENGTH-1); + snprintf(info->driver_name, AMDSMI_MAX_STRING_LENGTH, "%s", driver_name.c_str()); drm_free_version(version); libdrm.unload(); ss << __PRETTY_FUNCTION__ @@ -4848,7 +5628,61 @@ amdsmi_status_t amdsmi_get_gpu_driver_info(amdsmi_processor_handle processor_han return status; } +#ifdef BRCM_NIC +amdsmi_status_t amdsmi_get_nic_device_uuid(amdsmi_processor_handle processor_handle, + unsigned int *uuid_length, char *uuid) { + amdsmi_status_t status = AMDSMI_STATUS_SUCCESS; + AMDSMI_CHECK_INIT(); + + if (uuid_length == nullptr || uuid == nullptr || uuid_length == nullptr || + *uuid_length < AMDSMI_GPU_UUID_SIZE) { + return AMDSMI_STATUS_INVAL; + } + amd::smi::AMDSmiNICDevice *nic_device = nullptr; + amdsmi_status_t r = get_nic_device_from_handle(processor_handle, &nic_device); + if (r != AMDSMI_STATUS_SUCCESS) return r; + + std::string uuidStr; + status = nic_device->amd_query_nic_uuid(uuidStr); + if (status != AMDSMI_STATUS_SUCCESS) { + std::ostringstream ss; + ss << __PRETTY_FUNCTION__ + << " | Getting NIC UUID failed. Return code: " << status; + LOG_INFO(ss); + return status; + } + snprintf(uuid, *uuid_length - 1, "%s", uuidStr.c_str()); + return status; +} + +amdsmi_status_t amdsmi_get_switch_device_uuid(amdsmi_processor_handle processor_handle, + unsigned int *uuid_length, char *uuid) { + amdsmi_status_t status = AMDSMI_STATUS_SUCCESS; + AMDSMI_CHECK_INIT(); + + if (uuid_length == nullptr || uuid == nullptr || uuid_length == nullptr || + *uuid_length < AMDSMI_GPU_UUID_SIZE) { + return AMDSMI_STATUS_INVAL; + } + + amd::smi::AMDSmiSWITCHDevice *switch_device = nullptr; + amdsmi_status_t r = get_switch_device_from_handle(processor_handle, &switch_device); + if (r != AMDSMI_STATUS_SUCCESS) return r; + + std::string uuidStr; + status = switch_device->amd_query_switch_uuid(uuidStr); + if (status != AMDSMI_STATUS_SUCCESS) { + std::ostringstream ss; + ss << __PRETTY_FUNCTION__ + << " | Getting switch UUID failed. Return code: " << status; + LOG_INFO(ss); + return status; + } + snprintf(uuid, *uuid_length - 1, "%s", uuidStr.c_str()); + return status; +} +#endif//BRCM_NIC amdsmi_status_t amdsmi_get_pcie_info(amdsmi_processor_handle processor_handle, amdsmi_pcie_info_t *info) { AMDSMI_CHECK_INIT(); std::ostringstream ss; @@ -4894,7 +5728,10 @@ amdsmi_status_t amdsmi_get_pcie_info(amdsmi_processor_handle processor_handle, a fscanf(fp, "%lf %s", &pcie_speed, buff); fclose(fp); } else { - printf("Failed to open file: %s \n", path_max_link_speed.c_str()); + std::ostringstream ss; + ss << __PRETTY_FUNCTION__ + << " | Failed to open file: " << path_max_link_speed; + LOG_ERROR(ss); return AMDSMI_STATUS_API_FAILED; } @@ -5444,7 +6281,7 @@ amdsmi_ptl_data_format_t token_to_amdsmi_fmt(std::string token) { // Ensure upper case for comparison for (auto &c : token) { - c = std::toupper(static_cast(c)); + c = static_cast(std::toupper(static_cast(c))); } for (size_t i = 0; i < kPtlFormatMapSize; ++i) { @@ -6766,13 +7603,14 @@ amdsmi_status_t amdsmi_get_cpu_model_name(amdsmi_processor_handle processor_hand if (status != AMDSMI_STATUS_SUCCESS) return amdsmi_errno_to_esmi_status(status); - strncpy(cpu_info->model_name, model_name.c_str(), AMDSMI_MAX_STRING_LENGTH -1); + snprintf(cpu_info->model_name, AMDSMI_MAX_STRING_LENGTH, "%s", model_name.c_str()); return AMDSMI_STATUS_SUCCESS; } amdsmi_status_t amdsmi_get_cpu_cores_per_socket(uint32_t sock_count, amdsmi_sock_info_t *sock_info) { + (void)(sock_count);//unused amdsmi_status_t status; uint32_t core_num; status = amd::smi::AMDSmiSystem::getInstance().get_sys_cpu_cores_per_socket(&core_num); diff --git a/projects/amdsmi/src/amd_smi/amd_smi_common.cc b/projects/amdsmi/src/amd_smi/amd_smi_common.cc index 54ac67b2c54..f21e3ebb5aa 100644 --- a/projects/amdsmi/src/amd_smi/amd_smi_common.cc +++ b/projects/amdsmi/src/amd_smi/amd_smi_common.cc @@ -69,5 +69,18 @@ amdsmi_status_t esmi_to_amdsmi_status(esmi_status_t status) { } #endif +amdsmi_status_t ainic_to_amdsmi_status(smi_nic_status_t status) { + amdsmi_status_t amdsmi_status = AMDSMI_STATUS_MAP_ERROR; + + // Look for it in the map + // If found: use the mapped value + // If not found: return the map error established above + if (auto search_itr = ainic_status_map.find(status); search_itr != ainic_status_map.end()) { + amdsmi_status = search_itr->second; + } + + return amdsmi_status; +} + } // namespace amd::smi diff --git a/projects/amdsmi/src/amd_smi/amd_smi_cper.cc b/projects/amdsmi/src/amd_smi/amd_smi_cper.cc index 27f4d9252db..b28091f2e29 100644 --- a/projects/amdsmi/src/amd_smi/amd_smi_cper.cc +++ b/projects/amdsmi/src/amd_smi/amd_smi_cper.cc @@ -156,8 +156,6 @@ static auto amdsmi_read_cper_file(const std::string &filepath) -> CperFileCtx { GUID_INIT(0xDC3EA0B0, 0xA144, 0x4797, 0xB9, 0x5B, 0x53, 0xFA, \ 0x24, 0x2B, 0x6E, 0x1D) -static amdsmi_cper_guid_t mce = CPER_NOTIFY_MCE; -static amdsmi_cper_guid_t cmc = CPER_NOTIFY_CMC; static amdsmi_cper_guid_t bt = BOOT_TYPE; static amdsmi_cper_guid_t cr = AMD_OOB_CRASHDUMP; static amdsmi_cper_guid_t nonstd = AMD_GPU_NONSTANDARD_ERROR; @@ -300,7 +298,7 @@ static int cper_dump_nonstd_err(const struct cper_sec_nonstd_err *nonstd_err, co LOG_DEBUG(ss); - return aca_decode_corrected_error(body->err_ctx.reg_dump, sizeof(body->err_ctx.reg_dump)/sizeof(uint64_t), + return aca_decode_corrected_error(body->err_ctx.reg_dump, sizeof(body->err_ctx.reg_dump)/sizeof(body->err_ctx.reg_dump[0]), section->flags_mask, section->revision_major, body->err_ctx.reg_ctx_type); } @@ -341,7 +339,7 @@ static int cper_dump_cr_boot(const struct cper_sec_crashdump *crashdump, const c } static void inject_product_serial_number(amdsmi_cper_hdr_t *cper, uint64_t product_serial) { - for (size_t i = 0; i < cper_num_sec(cper); i++) { + for (int i = 0; i < cper_num_sec(cper); i++) { void *sec_desc_offset = cper_get_sec_desc_offset(cper, i); struct cper_sec_desc *sec_desc = static_cast(sec_desc_offset); strncpy(sec_desc->fru_id, std::to_string(product_serial).c_str(), sizeof(sec_desc->fru_id) - 1); diff --git a/projects/amdsmi/src/amd_smi/amd_smi_drm.cc b/projects/amdsmi/src/amd_smi/amd_smi_drm.cc index 180f100411c..de0cc7178fa 100644 --- a/projects/amdsmi/src/amd_smi/amd_smi_drm.cc +++ b/projects/amdsmi/src/amd_smi/amd_smi_drm.cc @@ -29,6 +29,7 @@ #include #include "config/amd_smi_config.h" #include "amd_smi/impl/amd_smi_drm.h" +#include "amd_smi/impl/amd_smi_utils.h" #include "impl/scoped_fd.h" #include "rocm_smi/rocm_smi.h" #include "rocm_smi/rocm_smi_main.h" @@ -140,16 +141,17 @@ amdsmi_status_t AMDSmiDrm::init() { drm_free_version(version); } - uint64_t bdf_rocm = 0; - rsmi_status_t rsmi_ret = rsmi_dev_pci_id_get(i, &bdf_rocm); + uint64_t bdfid = 0; + rsmi_status_t rsmi_ret = rsmi_dev_pci_id_get(i, &bdfid); + auto [domain, bus, device_id, function] = parse_bdfid(bdfid); if (rsmi_ret != RSMI_STATUS_SUCCESS) { // Set empty values on error bdf = {}; // zero-initialize } else { - bdf.domain_number = static_cast(((bdf_rocm >> 32) & 0xFFFFFFFF)); - bdf.bus_number = static_cast(((bdf_rocm >> 8) & 0xFF)); - bdf.device_number = static_cast(((bdf_rocm >> 3) & 0x1F)); - bdf.function_number = static_cast((bdf_rocm & 0x7)); + bdf.function_number = function & 0x7; + bdf.device_number = device_id & 0x1f; + bdf.bus_number = bus & 0xff; + bdf.domain_number = domain & 0xffffffffffff; } drm_bdfs_.push_back(bdf); } @@ -172,6 +174,13 @@ amdsmi_status_t AMDSmiDrm::get_bdf_by_index(uint32_t gpu_index, amdsmi_bdf_t *bd return AMDSMI_STATUS_SUCCESS; } +amdsmi_status_t AMDSmiDrm::amdgpu_query_cpu_affinity(const std::string &device_path, std::string &cpu_affinity) { + std::string cpuAffFile = "cpulistaffinity"; + cpu_affinity = smi_brcm_get_value_string(device_path, cpuAffFile); + + return AMDSMI_STATUS_SUCCESS; +} + amdsmi_status_t AMDSmiDrm::get_drm_path_by_index(uint32_t gpu_index, std::string *drm_path) const { if (gpu_index + 1 > drm_paths_.size()) return AMDSMI_STATUS_NOT_SUPPORTED; *drm_path = drm_paths_[gpu_index]; diff --git a/projects/amdsmi/src/amd_smi/amd_smi_gpu_device.cc b/projects/amdsmi/src/amd_smi/amd_smi_gpu_device.cc index c19268316ce..dc7ced527f3 100644 --- a/projects/amdsmi/src/amd_smi/amd_smi_gpu_device.cc +++ b/projects/amdsmi/src/amd_smi/amd_smi_gpu_device.cc @@ -119,6 +119,15 @@ pthread_mutex_t* AMDSmiGPUDevice::get_mutex() { return amd::smi::GetMutex(gpu_id_); } +amdsmi_status_t AMDSmiGPUDevice::amdgpu_query_cpu_affinity(std::string& cpu_affinity) const { + char bdf_str[20]; + snprintf(bdf_str, sizeof(bdf_str)-1, "%04lx:%02x", bdf_.domain_number, bdf_.bus_number); + std::stringstream domain_bus_sstream; + domain_bus_sstream << "/sys/class/pci_bus/" << std::string(bdf_str); + + return drm_.amdgpu_query_cpu_affinity(domain_bus_sstream.str(), cpu_affinity); +} + // cache the compute process list for the device static std::atomic last_compute_process_list_update_time{std::chrono::steady_clock::time_point{}}; static const std::chrono::milliseconds compute_process_list_cache_duration = std::chrono::milliseconds(500); // 500 ms @@ -413,7 +422,7 @@ std::vector AMDSmiGPUDevice::get_bitmask_from_numa_node(int32_t node_i std::vector AMDSmiGPUDevice::get_bitmask_from_local_cpulist(uint32_t drm_card, uint32_t size) const { std::vector bitmask(size, 0); - if (drm_card < 0) { + if (drm_card == std::numeric_limits::max()) { bitmask[0] = std::numeric_limits::max(); return bitmask; } diff --git a/projects/amdsmi/src/amd_smi/amd_smi_socket.cc b/projects/amdsmi/src/amd_smi/amd_smi_socket.cc index 7e5566ea29c..9759038d310 100644 --- a/projects/amdsmi/src/amd_smi/amd_smi_socket.cc +++ b/projects/amdsmi/src/amd_smi/amd_smi_socket.cc @@ -38,6 +38,15 @@ AMDSmiSocket::~AMDSmiSocket() { delete cpu_core_processors_[i]; } cpu_core_processors_.clear(); + for (uint32_t i = 0; i < nic_processors_.size(); i++) { + delete nic_processors_[i]; + } + nic_processors_.clear(); + + for (uint32_t i = 0; i < switch_processors_.size(); i++) { + delete switch_processors_[i]; + } + switch_processors_.clear(); } amdsmi_status_t AMDSmiSocket::get_processor_count(uint32_t* processor_count) const { @@ -57,6 +66,15 @@ amdsmi_status_t AMDSmiSocket::get_processor_count(processor_type_t type, uint32_ case AMDSMI_PROCESSOR_TYPE_AMD_CPU_CORE: *processor_count = static_cast(cpu_core_processors_.size()); break; + case AMDSMI_PROCESSOR_TYPE_AMD_NIC: + *processor_count = static_cast(ainic_processors_.size()); + break; + case AMDSMI_PROCESSOR_TYPE_BRCM_NIC: + *processor_count = static_cast(nic_processors_.size()); + break; + case AMDSMI_PROCESSOR_TYPE_BRCM_SWITCH: + *processor_count = static_cast(switch_processors_.size()); + break; default: *processor_count = 0; ret = AMDSMI_STATUS_INVAL; diff --git a/projects/amdsmi/src/amd_smi/amd_smi_system.cc b/projects/amdsmi/src/amd_smi/amd_smi_system.cc index c648066005e..ec44a2dbacd 100644 --- a/projects/amdsmi/src/amd_smi/amd_smi_system.cc +++ b/projects/amdsmi/src/amd_smi/amd_smi_system.cc @@ -20,6 +20,7 @@ * THE SOFTWARE. */ +#include #include #include #include @@ -28,16 +29,51 @@ #include #include "amd_smi/impl/amd_smi_system.h" #include "amd_smi/impl/amd_smi_gpu_device.h" +#ifdef BRCM_NIC +#include "amd_smi/impl/nic/amd_smi_nic_device.h" +#include "amd_smi/impl/nic/amd_smi_switch_device.h" +#endif//BRCM_NIC +#include "amd_smi/impl/amd_smi_utils.h" #include "amd_smi/impl/amd_smi_common.h" #include "rocm_smi/rocm_smi.h" +#include "rocm_smi/rocm_smi_logger.h" #include #include +#include + namespace amd::smi { #define AMD_SMI_INIT_FLAG_RESRV_TEST1 0x800000000000000 //!< Reserved for test +AMDSmiSystem& AMDSmiSystem::getInstance() { + static AMDSmiSystem instance; + return instance; +} + +const std::map smi_nic_status_str = { + {SMI_NIC_STATUS_SUCCESS, "API completed successfully"}, + {SMI_NIC_STATUS_ERROR, "Generic error"}, + {SMI_NIC_STATUS_WRONG_PARAM, "Wrong parameter provided"}, + {SMI_NIC_STATUS_NOT_FOUND, "NIC not found"}, + {SMI_NIC_STATUS_NO_RESOURCE, "Memory allocation failed"}, + {SMI_NIC_STATUS_NOT_SUPPORTED, "API not supported"}, + {SMI_NIC_STATUS_NOT_INIT, "Not initialized"}, + {SMI_NIC_STATUS_NO_DATA, "Requested data not found"}, + {SMI_NIC_STATUS_DRIVER_NOT_LOADED, "Required driver not loaded"}, +}; + + +#define CHK_AMDNIC_RET(status) \ + if (status != SMI_NIC_STATUS_SUCCESS) { \ + std::ostringstream ss; \ + ss << __PRETTY_FUNCTION__ \ + << "[" << __FILE__ << ":" << __LINE__ << "] smi_nic_status_t: " << status << ":" << smi_nic_status_str.at(status) << std::endl; \ + LOG_INFO(ss); \ + return amd::smi::ainic_to_amdsmi_status(status); \ + } + #ifdef ENABLE_ESMI_LIB amdsmi_status_t AMDSmiSystem::get_cpu_family(uint32_t *cpu_family) { amdsmi_status_t ret; @@ -247,7 +283,17 @@ amdsmi_status_t AMDSmiSystem::init(uint64_t flags) { return amd_smi_status; } #endif - + if (flags & AMDSMI_INIT_AMD_NICS) { + amd_smi_status = populate_brcm_nic_devices(); + if (amd_smi_status != AMDSMI_STATUS_SUCCESS) + return amd_smi_status; + amd_smi_status = populate_brcm_switch_devices(); + if (amd_smi_status != AMDSMI_STATUS_SUCCESS) + return amd_smi_status; + amd_smi_status = populate_amd_ainic_devices(); + if (amd_smi_status != AMDSMI_STATUS_SUCCESS) + return amd_smi_status; + } return AMDSMI_STATUS_SUCCESS; } @@ -347,6 +393,225 @@ amdsmi_status_t AMDSmiSystem::populate_amd_gpu_devices() { return AMDSMI_STATUS_SUCCESS; } +static amdsmi_status_t populate_amd_ainic_device(const smi_nic_ctx_t &ctx, uint64_t bdf_int, AMDSmiAINICDevice::AINICInfo &ai_nic_info) { + static_assert(sizeof(smi_nic_bus_info_t) == sizeof(ai_nic_info.bus)); + smi_nic_status_t status = smi_get_nic_bus_info(ctx, bdf_int, reinterpret_cast(&ai_nic_info.bus)); + CHK_AMDNIC_RET(status) + + static_assert(sizeof(smi_nic_driver_info_t) == sizeof(ai_nic_info.driver)); + status = smi_get_nic_driver_info(ctx, bdf_int, reinterpret_cast(&ai_nic_info.driver)); + CHK_AMDNIC_RET(status) + + static_assert(sizeof(smi_nic_asic_info_t) == sizeof(ai_nic_info.asic)); + status = smi_get_nic_asic_info(ctx, bdf_int, reinterpret_cast(&ai_nic_info.asic)); + CHK_AMDNIC_RET(status) + + static_assert(sizeof(smi_nic_numa_info_t) == sizeof(ai_nic_info.numa)); + status = smi_get_nic_numa_info(ctx, bdf_int, reinterpret_cast(&ai_nic_info.numa)); + CHK_AMDNIC_RET(status) + + static_assert(sizeof(smi_nic_port_info_t) == sizeof(ai_nic_info.port)); + status = smi_get_nic_port_info(ctx, bdf_int, reinterpret_cast(&ai_nic_info.port)); + CHK_AMDNIC_RET(status); + + static_assert(sizeof(smi_nic_rdma_devices_info_t) == sizeof(ai_nic_info.rdma_dev)); + status = smi_get_nic_rdma_dev_info(ctx, bdf_int, reinterpret_cast(&ai_nic_info.rdma_dev)); + CHK_AMDNIC_RET(status) + + return AMDSMI_STATUS_SUCCESS; +} + +std::tuple bdf_to_int(const std::string &bdf) { + std::regex pattern("([0-9a-fA-F]{1,12}):([0-9a-fA-F]{1,2}):([0-9a-fA-F]{1,2})\\.([0-9a-fA-F]{1,2})"); + std::smatch matches; + amdsmi_bdf_t bdf_info = {}; + if (std::regex_search(bdf, matches, pattern)) { + bdf_info.domain_number = std::stoul(matches[1], nullptr, 16) & 0xffffffffffff; + bdf_info.bus_number = std::stoul(matches[2], nullptr, 16) & 0xff; + bdf_info.device_number = std::stoul(matches[3], nullptr, 16) & 0x1f; + bdf_info.function_number = std::stoul(matches[4], nullptr, 16) & 0x7; + return {(bdf_info.domain_number << 16) | (bdf_info.bus_number << 8) | (bdf_info.device_number << 3) | (bdf_info.function_number << 0), bdf_info}; + } + return {0, bdf_info}; +} + +amdsmi_status_t AMDSmiSystem::populate_amd_ainic_devices() { + smi_nic_status_t status = smi_nic_create_context(&ainic_ctx_); + CHK_AMDNIC_RET(status); + + smi_nic_discovery_t discovery = {}; + status = smi_discover_nics(ainic_ctx_, &discovery); + if (status == SMI_NIC_STATUS_NO_DATA) { + // No AMD NIC devices present (e.g. CI without NIC hardware) - not fatal + return AMDSMI_STATUS_SUCCESS; + } + CHK_AMDNIC_RET(status); + + for(uint32_t nic_idx = 0; nic_idx < discovery.count; ++nic_idx) { + const char *bdf_str = discovery.devices[nic_idx].bdf; + auto [bdfid, bdf_info] = bdf_to_int(bdf_str); + AMDSmiAINICDevice::AINICInfo ai_nic_info = {}; + amdsmi_status_t status = populate_amd_ainic_device(ainic_ctx_, bdfid, ai_nic_info); + if (status != AMDSMI_STATUS_SUCCESS) { + return status; + } + ai_nic_info_.emplace_back(ai_nic_info); + + auto [domain, bus, device_id, function] = parse_bdfid(bdfid); + + // The BD part of the BDF is used as the socket id as it + // represents a physical device. + std::stringstream ss; + ss << std::setfill('0') << std::uppercase << std::hex << std::setw(4) << domain << ":" + << std::setw(2) << bus << ":" << std::setw(2) << device_id; + std::string socket_id = ss.str(); + + // Multiple devices may share the same socket + AMDSmiSocket* socket = nullptr; + for (unsigned int j = 0; j < sockets_.size(); j++) { + if (sockets_[j]->get_socket_id() == socket_id) { + socket = sockets_[j]; + break; + } + } + if (socket == nullptr) { + socket = new AMDSmiSocket(socket_id); + sockets_.push_back(socket); + } + + auto device = std::make_unique(nic_idx, bdf_info, ai_nic_info); + socket->add_processor(device.get()); + ainic_processors_.insert(device.get()); + device.release(); + } + return AMDSMI_STATUS_SUCCESS; +} +const auto &AMDSmiSystem::get_ai_nic_info() const { + return ai_nic_info_; +} + +amdsmi_status_t AMDSmiSystem::populate_brcm_nic_devices() { +#ifdef BRCM_NIC + uint32_t device_count = 0; + amdsmi_status_t amd_smi_status = no_drm_nic.init(); + + rsmi_status_t ret = rsmi_num_nic_monitor_devices(&device_count); + if (ret != RSMI_STATUS_SUCCESS) { + return amd::smi::rsmi_to_amdsmi_status(ret); + } + + for (uint32_t i = 0; i < device_count; i++) { + // NIC device uses the bdf as the socket id + std::string socket_id; + uint64_t bdfid = 0; + rsmi_status_t ret = rsmi_nic_dev_pci_id_get(i, &bdfid); + if (ret != RSMI_STATUS_SUCCESS) { + continue; + } + + auto [domain, bus, device_id, function] = parse_bdfid(bdfid); + + // The BD part of the BDF is used as the socket id as it + // represents a physical device. + std::stringstream ss; + ss << std::setfill('0') << std::uppercase << std::hex << std::setw(4) << domain << ":" + << std::setw(2) << bus << ":" << std::setw(2) << device_id; + socket_id = ss.str(); + + // Multiple devices may share the same socket + AMDSmiSocket* socket = nullptr; + for (unsigned int j = 0; j < sockets_.size(); j++) { + if (sockets_[j]->get_socket_id() == socket_id) { + socket = sockets_[j]; + break; + } + } + if (socket == nullptr) { + socket = new AMDSmiSocket(socket_id); + sockets_.push_back(socket); + } + + auto [domain_number, bus_number, device_number, function_number] = parse_bdfid(bdfid); + amdsmi_bdf_t bdf = { + .function_number = function_number, + .device_number = device_number, + .bus_number = bus_number, + .domain_number = domain_number + }; + + auto device = std::make_unique(i, bdf, no_drm_nic); + + std::string nicPath; + if ( (no_drm_nic.get_device_path_by_index(i, &nicPath)) != AMDSMI_STATUS_SUCCESS) continue; + std::string driverPath = nicPath + "/driver"; + std::string command = "readlink " + driverPath; + std::string getData; + if (smi_brcm_execute_cmd_get_data(command, &getData) != AMDSMI_STATUS_SUCCESS) continue; + if (getData.find("bnxt_en") == std::string::npos) continue; + + socket->add_processor(device.get()); + nic_processors_.insert(deviceget()); + device.release(); + } +#endif//BRCM_NIC + return AMDSMI_STATUS_SUCCESS; +} + +amdsmi_status_t AMDSmiSystem::populate_brcm_switch_devices() { +#ifdef BRCM_NIC + uint32_t device_count = 0; + amdsmi_status_t amd_smi_status = no_drm_switch.init(); + rsmi_status_t ret = rsmi_num_switch_monitor_devices(&device_count); + if (ret != RSMI_STATUS_SUCCESS) { + return amd::smi::rsmi_to_amdsmi_status(ret); + } + + for (uint32_t i = 0; i < device_count; i++) { + // NIC device uses the bdf as the socket id + std::string socket_id; + uint64_t bdfid = 0; + rsmi_status_t ret = rsmi_switch_dev_pci_id_get(i, &bdfid); + if (ret != RSMI_STATUS_SUCCESS) { + // return amd::smi::rsmi_to_amdsmi_status(ret); + // device might be removed; continue with next device; + continue; + } + + auto [domain, bus, device_id, function] = parse_bdfid(bdfid); + + // The BD part of the BDF is used as the socket id as it + // represents a physical device. + std::stringstream ss; + ss << std::setfill('0') << std::uppercase << std::hex << std::setw(4) << domain << ":" + << std::setw(2) << bus << ":" << std::setw(2) << device_id; + socket_id = ss.str(); + + // Multiple devices may share the same socket + AMDSmiSocket* socket = nullptr; + for (unsigned int j = 0; j < sockets_.size(); j++) { + if (sockets_[j]->get_socket_id() == socket_id) { + socket = sockets_[j]; + break; + } + } + if (socket == nullptr) { + socket = new AMDSmiSocket(socket_id); + sockets_.push_back(socket); + } + + amdsmi_bdf_t bdf = {}; + bdf.function_number = bdfid & 0x7; + bdf.device_number = (bdfid >> 3) & 0x1f; + bdf.bus_number = (bdfid >> 8) & 0xff; + bdf.domain_number = (bdfid >> 32) & 0xffffffff; + + AMDSmiProcessor* device = new AMDSmiSWITCHDevice(i, bdf, no_drm_switch); + socket->add_processor(device); + switch_processors_.insert(device); + } +#endif + return AMDSMI_STATUS_SUCCESS; +} amdsmi_status_t AMDSmiSystem::get_gpu_socket_id(uint32_t index, std::string& socket_id) { uint64_t bdfid = 0; @@ -366,16 +631,7 @@ amdsmi_status_t AMDSmiSystem::get_gpu_socket_id(uint32_t index, * | Function | [ 2: 0] | "location id" | (LOCATION & 0x7) | */ - uint64_t domain = (bdfid >> 32) & 0xffffffff; - /* May need later - // may need to identify with partition_id in the future as well... TBD - uint64_t partition_id = (bdfid >> 28) & 0xf; - */ - uint64_t bus = (bdfid >> 8) & 0xff; - uint64_t device_id = (bdfid >> 3) & 0x1f; - /* May need later - uint64_t function = bdfid & 0x7; - */ + auto [domain, bus, device_id, function] = parse_bdfid(bdfid); // The BD part of the BDF is used as the socket id as it // represents a physical device. @@ -411,6 +667,9 @@ amdsmi_status_t AMDSmiSystem::cleanup() { return amd::smi::rsmi_to_amdsmi_status(ret); } } + if (init_flag_ & AMDSMI_INIT_AMD_NICS) { + smi_nic_destroy_context(ainic_ctx_); + } return AMDSMI_STATUS_SUCCESS; } @@ -443,6 +702,18 @@ amdsmi_status_t AMDSmiSystem::handle_to_processor( != processors_.end()) { return AMDSMI_STATUS_SUCCESS; } + if (std::find(nic_processors_.begin(), nic_processors_.end(), *processor) + != nic_processors_.end()) { + return AMDSMI_STATUS_SUCCESS; + } + if (std::find(switch_processors_.begin(), switch_processors_.end(), *processor) != + switch_processors_.end()) { + return AMDSMI_STATUS_SUCCESS; + } + if (std::find(ainic_processors_.begin(), ainic_processors_.end(), *processor) + != ainic_processors_.end()) { + return AMDSMI_STATUS_SUCCESS; + } return AMDSMI_STATUS_NOT_FOUND; } diff --git a/projects/amdsmi/src/amd_smi/amd_smi_utils.cc b/projects/amdsmi/src/amd_smi/amd_smi_utils.cc index c4059dc0ab1..aac3170b704 100644 --- a/projects/amdsmi/src/amd_smi/amd_smi_utils.cc +++ b/projects/amdsmi/src/amd_smi/amd_smi_utils.cc @@ -826,6 +826,62 @@ std::string smi_amdgpu_get_status_string(amdsmi_status_t ret, bool fullStatus = return std::string(err_str); } +uint32_t smi_brcm_get_value_u32(const std::string &folder, const std::string &file_name) { + + std::string file_path = folder + "/" + file_name; + std::ifstream file(file_path.c_str(), std::ifstream::in); + if (!file.is_open()) { + return 0xFFFF; + } + else { + std::string line; + getline(file, line); + return static_cast(stoi(line)); + } + + return 0; +} + +std::string smi_brcm_get_value_string(const std::string &folder, const std::string &file_name) { + + std::stringstream temp; + std::string file_path = folder + "/" + file_name; + std::ifstream file(file_path.c_str(), std::ifstream::in); + if (!file.is_open()) { + return "N/A"; + } + else { + std::string line; + while (std::getline(file, line)) { + if (line.empty()) { + break; + } + temp << line; + } + } + + return temp.str(); +} + +amdsmi_status_t smi_brcm_execute_cmd_get_data(const std::string &command, std::string *data) { + std::string result; + char buffer[128]; + + // Open a pipe to execute the command + std::shared_ptr pipe(popen(command.c_str(), "r"), pclose); + if (!pipe) { + return AMDSMI_STATUS_API_FAILED; + } + + // Read the output of the command into the buffer + while (fgets(buffer, sizeof(buffer), pipe.get()) != nullptr) { + result += buffer; + } + *data = result; + + return AMDSMI_STATUS_SUCCESS; +} + // TODO(amdsmi_team): Do we want to include these functions in header? amdsmi_status_t smi_amdgpu_get_device_index(amdsmi_processor_handle processor_handle, uint32_t *device_index) { @@ -940,6 +996,26 @@ amdsmi_status_t smi_amdgpu_get_device_count(uint32_t *total_num_devices) { return AMDSMI_STATUS_SUCCESS; } +amdsmi_status_t smi_amdgpu_get_ainic_processor_handle_by_index( + uint32_t device_index, + amdsmi_processor_handle *processor_handle) { + + if(!processor_handle) { + return AMDSMI_STATUS_INVAL; + } + for(const auto &socket: amd::smi::AMDSmiSystem::getInstance().get_sockets()) { + uint32_t idx = 0; + for(const auto &processor: socket->get_processors(AMDSMI_PROCESSOR_TYPE_AMD_NIC)) { + if (device_index == idx) { + *processor_handle = processor; + return AMDSMI_STATUS_SUCCESS; + } + idx++; + } + } + return AMDSMI_STATUS_API_FAILED; +} + // TODO(amdsmi_team): Do we want to include these functions in header? amdsmi_status_t smi_amdgpu_get_processor_handle_by_index( uint32_t device_index, @@ -1034,7 +1110,7 @@ uint64_t get_product_serial_number(amdsmi_processor_handle processor_handle) { LOG_DEBUG(ss); return serial_number; } - if (!board_info.product_serial || !*board_info.product_serial) { + if (!*board_info.product_serial) { std::ostringstream ss; ss << __PRETTY_FUNCTION__ << "\n:" << __LINE__ << " Product serial string is empty."; @@ -1058,3 +1134,11 @@ uint64_t get_product_serial_number(amdsmi_processor_handle processor_handle) { } return serial_number; } + +std::tuple parse_bdfid(uint64_t bdfid) { + uint64_t domain = (bdfid >> 32) & 0xffffffff; + uint64_t bus = (bdfid >> 8) & 0xff; + uint64_t device_id = (bdfid >> 3) & 0x1f; + uint64_t function = bdfid & 0x7; + return std::tuple(domain, bus, device_id, function); +} diff --git a/projects/amdsmi/src/nic/ai-nic/amd_smi_ainic_device.cc b/projects/amdsmi/src/nic/ai-nic/amd_smi_ainic_device.cc new file mode 100644 index 00000000000..5582febbae8 --- /dev/null +++ b/projects/amdsmi/src/nic/ai-nic/amd_smi_ainic_device.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include "amd_smi/impl/nic/amd_smi_ainic_device.h" + +namespace amd::smi { +amdsmi_status_t AMDSmiAINICDevice::amd_query_nic_info(AINICInfo& info) const { + info = ai_nic_info_; + return AMDSMI_STATUS_SUCCESS; +} +} // namespace amd::smi \ No newline at end of file diff --git a/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/CMakeLists.txt b/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/CMakeLists.txt new file mode 100644 index 00000000000..d078c91a7c3 --- /dev/null +++ b/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/CMakeLists.txt @@ -0,0 +1,42 @@ +# +# Copyright (c) 2025 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +cmake_minimum_required(VERSION 3.16) + +project(amdsminic LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(PROJECT_ROOT ${CMAKE_CURRENT_SOURCE_DIR}) +set(NIC_INCLUDE_DIR ${PROJECT_ROOT}/inc ${PROJECT_ROOT}/interface) +set(NIC_SOURCE_DIR ${PROJECT_ROOT}/src) +set(NIC_BUILD_DIR ${CMAKE_BINARY_DIR}/build) +set(NIC_INTERFACE_DIR ${CMAKE_BINARY_DIR}/interface) + +set(NIC_DEFAULT_CXX_FLAGS "-Wall -Wextra -Werror -Wno-missing-field-initializers -Wno-array-bounds -Wmissing-declarations -Werror=conversion -Wshift-negative-value -fPIC") +set(NIC_DEFAULT_CXX_FLAGS "${NIC_DEFAULT_CXX_FLAGS} -Wl,-z,relro,-z,noexecstack,-z,noexecheap -Wl,--strip-debug -Wl,--strip-all") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${NIC_DEFAULT_CXX_FLAGS}") + +file(GLOB CPP_SRCS "${NIC_SOURCE_DIR}/*.cpp") +add_library(amdsminic STATIC ${CPP_SRCS}) +set_target_properties(amdsminic PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${NIC_BUILD_DIR}) +target_include_directories(amdsminic PUBLIC ${NIC_INCLUDE_DIR} ${NIC_INTERFACE_DIR}) diff --git a/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/Makefile b/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/Makefile new file mode 100644 index 00000000000..699aad1d65d --- /dev/null +++ b/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/Makefile @@ -0,0 +1,31 @@ +# +# Copyright (c) 2025 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +BUILD_DIR := build + +all: $(BUILD_DIR) + cd $(BUILD_DIR) && cmake .. && $(MAKE) + +$(BUILD_DIR): + mkdir -p $(BUILD_DIR) + +clean: + rm -rf $(BUILD_DIR) diff --git a/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/VERSION b/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/VERSION new file mode 100644 index 00000000000..41916b9404d --- /dev/null +++ b/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/VERSION @@ -0,0 +1,3 @@ +major=1 +minor=0 +release=0 diff --git a/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/inc/smi_devlink_netlink.h b/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/inc/smi_devlink_netlink.h new file mode 100644 index 00000000000..7e9713f1a13 --- /dev/null +++ b/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/inc/smi_devlink_netlink.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2025 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef __SMI_DEVLINK_NETLINK_H__ +#define __SMI_DEVLINK_NETLINK_H__ + +#include +#include + +#endif // __SMI_DEVLINK_NETLINK_H__ diff --git a/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/inc/smi_ethtool_ioctl.h b/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/inc/smi_ethtool_ioctl.h new file mode 100644 index 00000000000..8bd0fb66f86 --- /dev/null +++ b/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/inc/smi_ethtool_ioctl.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2025 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef __SMI_ETHTOOL_IOCTL_H__ +#define __SMI_ETHTOOL_IOCTL_H__ + +#include +#include + +#include +#include +#include +#include + +#include +#include + +/** + * @brief Generic template function to perform ethtool ioctl on network devices. + * + * @tparam T The ethtool structure type (e.g., ethtool_stats, ethtool_drvinfo, etc.) + * @param device Network device name (e.g., "eth0") + * @param data Pointer to ethtool data structure to be populated or used for the ioctl + * + * @return 0 on success, -1 on failure + * + * @note The caller must properly initialize the data structure's cmd field before calling. + * @note Supported for various ethtool structures including ethtool_stats, ethtool_gstrings, + * ethtool_drvinfo, ethtool_pauseparam, ethtool_fecparam, ethtool_link_settings,... + */ +template +int smi_ethtool_ioctl(const std::string& device, T* data); + +#endif // __SMI_ETHTOOL_IOCTL_H__ diff --git a/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/inc/smi_nic.h b/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/inc/smi_nic.h new file mode 100644 index 00000000000..b2f92b92132 --- /dev/null +++ b/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/inc/smi_nic.h @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2025 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef __SMI_NIC__H__ +#define __SMI_NIC__H__ + +#include +#include +#include +#include +#include + +#include "smi_ethtool_ioctl.h" + +enum class NicType { + Unknown, + PCIBridge, + Ethernet, + InfiniBand, +}; + +enum class NicVendor { + Unknown, + AMD, + Broadcom +}; + +// TODO: broadcom - update enum with the right products +enum class NicProduct { + Unknown, + AINIC, // AMD Pensando AINIC + Thor, // Broadcom Thor +}; + +class SmiInfiniBandPort { +public: + SmiInfiniBandPort(std::string& netdev, std::string& name, const std::string& sysfs_path_); + + const std::string& netdev() const; + const std::string& name() const; + std::optional port_num() const; + std::optional state() const; + std::optional max_mtu() const; + std::optional active_mtu() const; + void collect_hw_counters(); + const std::map& get_hw_counters_map() const; + +private: + std::string netdev_; + std::string name_; + std::string sysfs_path_; + std::map hw_counters_map_; +}; + +class SmiInfiniBand { +public: + SmiInfiniBand(std::string& name, const std::string& sysfs_path); + + std::string rdma_dev() const; + std::optional node_guid() const; + std::optional node_type() const; + std::optional sys_image_guid() const; + std::optional fw_ver() const; + + void add_port(const SmiInfiniBandPort& port); + const std::vector& ports() const; + uint8_t ports_num() const; + +private: + std::string name_; + std::string sysfs_path_; + NicType type_ = NicType::InfiniBand; + std::vector ports_; +}; + +class SmiNicPort { +public: + SmiNicPort(const std::string& iface, const std::string& bdf, const std::string& sysfs_class_path, const std::string& sysfs_bus_path); + + const std::string& interface() const; + const std::string& bdf() const; + const std::string& sysfs_class_path() const; + const std::string& sysfs_bus_path() const; + + std::optional mac_address() const; + std::optional port_num() const; + std::optional ifindex() const; + std::optional carrier() const; + std::optional mtu() const; + std::optional link_state() const; + std::optional link_speed() const; + + const std::string port_type() const; + std::string flavour() const; + + std::optional active_fec() const; + std::optional autoneg() const; + std::optional pause_autoneg() const; + std::optional pause_rx() const; + std::optional pause_tx() const; + + void discover_infiniband(); + void add_infiniband(const SmiInfiniBand& infiniband); + const std::vector& infiniband() const; + uint8_t infiniband_num() const; + void collect_vendor_statistics(); + void add_vendor_statistic(struct ethtool_gstrings *strings, struct ethtool_stats *stats); + const std::map& get_vendor_stats_map() const; + void collect_standard_statistics(); + const std::map& get_standard_stats_map() const; + std::optional read_vpd_content() const; + +private: + enum class SmiVendorStat { + TX_PACKETS, + RX_PACKETS, + TX_BYTES, + RX_BYTES, + TX_CSUM_NONE, + RX_CSUM_NONE, + TX_CSUM, + TX_TSO, + TX_TSO_BYTES + }; + + std::string map_vendor_stat_to_string(SmiVendorStat stat) const; + bool vendor_stat_allowed(const std::string& stat_name) const; + + std::string iface_; + std::string bdf_; + NicType type_; + std::string sysfs_class_path_; + std::string sysfs_bus_path_; + std::optional port_num_; + std::vector infiniband_; + std::map vendor_stats_map_; + std::map standard_stats_map_; +}; + +class SmiNic { +public: + SmiNic(const std::string& iface, const std::string& bdf, NicType type = NicType::Unknown, + const std::string& sysfs_class_path = "", const std::string& sysfs_bus_path = "", + NicVendor vendor = NicVendor::Unknown, NicProduct product = NicProduct::Unknown); + virtual ~SmiNic() = default; + + const std::string& interface() const; + const std::string& bdf() const; + NicType type() const; + NicVendor vendor() const; + NicProduct product() const; + const std::string port_type() const; + const std::string& sysfs_class_path() const; + const std::string& sysfs_bus_path() const; + + void add_nic_port(const SmiNicPort& port); + const std::vector& nic_ports() const; + uint8_t nic_ports_num() const; + + std::optional vendor_id() const; + std::optional subvendor_id() const; + std::optional device_id() const; + std::optional subsystem_id() const; + std::optional revision() const; + std::optional perm_address() const; + std::optional pcie_class() const; + std::optional max_pcie_width() const; + std::optional max_pcie_speed() const; + std::optional numa_node() const; + std::optional numa_affinity(uint8_t node) const; + // Vendor specific + virtual std::optional product_name() const; + virtual std::optional vendor_name() const; + virtual std::optional part_number() const; + virtual std::optional serial_number() const; + +protected: + std::string iface_; + std::string bdf_; + NicType type_; + NicVendor vendor_; + NicProduct product_; + std::string sysfs_class_path_; + std::string sysfs_bus_path_; + std::vector ports_; +}; + +class SmiNicPensando : public SmiNic { +public: + SmiNicPensando(const std::string& iface, const std::string& bdf, NicType type = NicType::Unknown, + const std::string& sysfs_class_path = "", const std::string& sysfs_bus_path = "", + NicVendor vendor = NicVendor::AMD, NicProduct product = NicProduct::AINIC); + + std::optional vendor_name() const override; + std::optional product_name() const override; + std::optional part_number() const override; + std::optional serial_number() const override; +}; + +class SmiNicBroadcom : public SmiNic { +public: + SmiNicBroadcom(const std::string& iface, const std::string& bdf, NicType type = NicType::Unknown, + const std::string& sysfs_class_path = "", const std::string& sysfs_bus_path = "", + NicVendor vendor = NicVendor::Broadcom, NicProduct product = NicProduct::Thor); + + std::optional vendor_name() const override; + std::optional product_name() const override; + std::optional part_number() const override; + std::optional serial_number() const override; +}; + +#endif // __SMI_NIC_H__ diff --git a/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/inc/smi_nic_subsystem.h b/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/inc/smi_nic_subsystem.h new file mode 100644 index 00000000000..ed8e03eadc2 --- /dev/null +++ b/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/inc/smi_nic_subsystem.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2025 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef __SMI_NIC_SUBSYSTEM_H__ +#define __SMI_NIC_SUBSYSTEM_H__ + +#include + +#include +#include +#include +#include +#include + +#include "smi_nic.h" + +enum class DriverType { + IONIC, + IONIC_RDMA, +}; + +class SmiNicSubsystem { +public: + virtual ~SmiNicSubsystem() = default; + + virtual void discover(const std::string& pci_path, const std::string& net_path) = 0; + virtual NicVendor vendor() const = 0; + virtual bool driver_loaded(const std::string& bdf, DriverType driver_type) const = 0; + virtual const std::vector>& get_nics() const = 0; +protected: + std::pair read_pci_ids(const std::string& sysfs_bus_path) const; + bool resolve_bdf(const std::string& symlink, std::string& bdf) const; +}; + + +class SmiNicSubsystemPensando : public SmiNicSubsystem { +public: + SmiNicSubsystemPensando() = default; + ~SmiNicSubsystemPensando() override = default; + + void discover(const std::string& pci_path, const std::string& net_path) override; + NicVendor vendor() const override; + const std::vector>& get_nics() const override; +private: + static constexpr uint16_t VENDOR_ID = 0x1dd8; + static constexpr uint16_t DEVICE_ID = 0x0008; + static constexpr uint16_t PORT_ID = 0x1002; + + bool driver_loaded(const std::string& bdf, DriverType driver_type) const override; + bool downstream_port(const std::string& port_bdf, const std::string& bridge_bdf, const std::string& pci_path) const; + void discover_ports(SmiNic& nic, const std::string& bridge_bdf, const std::string& pci_path, const std::string& net_path); + + std::vector> nics_; +}; + +// TODO: broadcom - add subsystem +class SmiNicSubsystemBroadcom : public SmiNicSubsystem { +public: + SmiNicSubsystemBroadcom() = default; + ~SmiNicSubsystemBroadcom() override = default; + + bool driver_loaded(const std::string& bdf, DriverType driver_type) const override; + void discover(const std::string& pci_path, const std::string& net_path) override; + NicVendor vendor() const override; + const std::vector>& get_nics() const override; +private: + // TODO: broadcom + std::vector> nics_; +}; + +#endif // __SMI_NIC_SUBSYSTEM_H__ + diff --git a/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/inc/smi_nic_system.h b/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/inc/smi_nic_system.h new file mode 100644 index 00000000000..a3c833247e2 --- /dev/null +++ b/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/inc/smi_nic_system.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2025 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef __SMI_NIC_SYSTEM_H__ +#define __SMI_NIC_SYSTEM_H__ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "smi_nic.h" +#include "smi_nic_subsystem.h" + +/** + * @brief Convert BDF string format to uint64_t + * + * Converts a BDF string to uint64_t format: + * (domain << 16) | (bus << 8) | (device << 3) | function + * + * @param bdf BDF string + * @return uint64_t BDF value, or 0 if parsing fails + */ +uint64_t parse_bdf(const std::string& bdf); + +class SmiNicSystem { +public: + SmiNicSystem(); + ~SmiNicSystem() = default; + + void register_subsystem(std::unique_ptr subsystem); + void discover_nics(); + bool driver_loaded(const std::string& bdf, DriverType driver_type) const; + + std::vector list_bdfs(); + bool interface_exists(const std::string& iface); + const std::vector& get_nics() const; + const SmiNic* get_nic_by_interface(const std::string& iface) const; + const SmiNic* get_nic_by_bdf(const std::string& bdf) const; + const SmiNic* get_nic_by_bdf(uint64_t bdf) const; + + SmiNicSystem(const SmiNicSystem &) = delete; + SmiNicSystem & operator = (const SmiNicSystem &) = delete; + SmiNicSystem(SmiNicSystem &&) = delete; + SmiNicSystem & operator = (SmiNicSystem &&) = delete; + +private: + std::string net_path_; + std::string pci_path_; + std::vector nics_; + std::vector> subsystems_; +}; + +#endif // __SMI_NIC_SYSTEM_H__ diff --git a/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/inc/smi_sysfs.h b/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/inc/smi_sysfs.h new file mode 100644 index 00000000000..eaa9ee94604 --- /dev/null +++ b/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/inc/smi_sysfs.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2025 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef __SMI_SYSFS_H__ +#define __SMI_SYSFS_H__ + +#include +#include +#include + +class SmiSysfsReader { +public: + using SysfsValue = std::variant; + enum class SysfsStatus { + Success = 0, + FileNotFound, + IOError, + ParseError + }; + + static SysfsStatus readAll(const std::string& filepath, std::vector& content); + static SysfsStatus readLine(const std::string& filepath, SysfsValue& content); + static bool exists(const std::string& filepath); + + SmiSysfsReader() = delete; +}; + +#endif // __SMI_SYSFS_H__ diff --git a/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/interface/smi_nic_interface.h b/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/interface/smi_nic_interface.h new file mode 100644 index 00000000000..5b9345d13fa --- /dev/null +++ b/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/interface/smi_nic_interface.h @@ -0,0 +1,425 @@ +/* + * Copyright (c) 2025 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef __SMI_NIC_INTERFACE_H__ +#define __SMI_NIC_INTERFACE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#define SMI_NIC_MAX_STRING_LENGTH 256 +#define SMI_NIC_MAX_DEVICES 64 +#define SMI_NIC_MAX_STATISTICS 64 +#define SMI_NIC_MAX_PORTS 32 +#define SMI_NIC_MAX_RDMA_DEV 32 + +typedef enum { + SMI_NIC_STATUS_SUCCESS = 0, /**< API completed successfully */ + SMI_NIC_STATUS_ERROR = 1, /**< Generic error */ + SMI_NIC_STATUS_WRONG_PARAM = 2, /**< Wrong parameter provided */ + SMI_NIC_STATUS_NOT_FOUND = 3, /**< NIC not found */ + SMI_NIC_STATUS_NO_RESOURCE = 4, /**< Memory allocation failed */ + SMI_NIC_STATUS_NOT_SUPPORTED = 5, /**< API not supported */ + SMI_NIC_STATUS_NOT_INIT = 6, /**< Not initialized */ + SMI_NIC_STATUS_NO_DATA = 7, /**< Requested data not found */ + SMI_NIC_STATUS_DRIVER_NOT_LOADED = 8 /**< Required driver not loaded */ +} smi_nic_status_t; + +/** + * @struct smi_nic_discovery_t + * @brief Structure about discovered NIC devices + * + * Contains information about detected network interface cards, including their count + * and details for each device such as PCI BDF. + * + * @var smi_nic_discovery_t::count + * Number of NIC devices discovered + * @var smi_nic_discovery_t::devices + * Array containing details for each discovered NIC device + */ +typedef struct { + uint32_t count; + struct { + char bdf[SMI_NIC_MAX_STRING_LENGTH]; /**< PCI BDF */ + } devices[SMI_NIC_MAX_DEVICES]; +} smi_nic_discovery_t; + +/** + * @brief Opaque handle for thread-safe NIC context + * + * This handle represents a thread-safe NIC context. + * Multiple contexts can be created and used concurrently from different threads. + */ +typedef struct smi_nic_ctx *smi_nic_ctx_t; + +/** + * @struct smi_nic_stat_t + * @brief Structure representing a single statistic name-value pair + * + * Contains a statistic name and its corresponding 64-bit value. + */ +typedef struct { + char name[SMI_NIC_MAX_STRING_LENGTH]; + uint64_t value; +} smi_nic_stat_t; + +/** + * @struct smi_nic_stat_info_t + * @brief Structure containing an array of statistics + * + * Contains the count and array of statistic name-value pairs. + */ +typedef struct { + uint32_t count; + smi_nic_stat_t stats[SMI_NIC_MAX_STATISTICS]; +} smi_nic_stat_info_t; + +/** + * @struct smi_nic_driver_info_t + * @brief Structure containing NIC driver information + * + * Contains driver name and version information. + */ +typedef struct { + char name[SMI_NIC_MAX_STRING_LENGTH]; + char version[SMI_NIC_MAX_STRING_LENGTH]; +} smi_nic_driver_info_t; + +/** + * @struct smi_nic_asic_info_t + * @brief Structure containing NIC ASIC information + * + * Contains ASIC information including vendor IDs, device IDs, + * MAC address, product details, and serial number, etc. + */ +typedef struct { + uint16_t vendor_id; + uint16_t subvendor_id; + uint16_t device_id; + uint16_t subsystem_id; + uint8_t revision; + char permanent_address[SMI_NIC_MAX_STRING_LENGTH]; + char product_name[SMI_NIC_MAX_STRING_LENGTH]; + char part_number[SMI_NIC_MAX_STRING_LENGTH]; + char serial_number[SMI_NIC_MAX_STRING_LENGTH]; + char vendor_name[SMI_NIC_MAX_STRING_LENGTH]; +} smi_nic_asic_info_t; + +/** + * @struct smi_nic_bus_info_t + * @brief Structure containing NIC bus/PCIe information + * + * Contains PCIe bus information including width, speed, interface version, and slot type. + */ +typedef struct { + uint64_t bdf; + uint8_t max_pcie_width; + uint32_t max_pcie_speed; + char pcie_interface_version[SMI_NIC_MAX_STRING_LENGTH]; + char slot_type[SMI_NIC_MAX_STRING_LENGTH]; +} smi_nic_bus_info_t; + +/** + * @struct smi_nic_numa_info_t + * @brief Structure containing NIC NUMA information + * + * Contains NUMA node and CPU affinity information. + */ +typedef struct { + uint8_t node; + char affinity[SMI_NIC_MAX_STRING_LENGTH]; +} smi_nic_numa_info_t; + +/** + * @struct smi_nic_port_t + * @brief Structure containing information for a single NIC port + * + * Contains information about a single network port. + */ +typedef struct { + uint64_t bdf; + uint32_t port_num; + char type[SMI_NIC_MAX_STRING_LENGTH]; + char flavour[SMI_NIC_MAX_STRING_LENGTH]; + char netdev[SMI_NIC_MAX_STRING_LENGTH]; + uint8_t ifindex; + char mac_address[SMI_NIC_MAX_STRING_LENGTH]; + uint8_t carrier; + uint16_t mtu; + char link_state[SMI_NIC_MAX_STRING_LENGTH]; + uint32_t link_speed; + uint32_t active_fec; + char autoneg[SMI_NIC_MAX_STRING_LENGTH]; + char pause_autoneg[SMI_NIC_MAX_STRING_LENGTH]; + char pause_rx[SMI_NIC_MAX_STRING_LENGTH]; + char pause_tx[SMI_NIC_MAX_STRING_LENGTH]; +} smi_nic_port_t; + +/** + * @struct smi_nic_port_info_t + * @brief Structure containing information for all NIC ports + * + * Contains the count and array of port information. + */ +typedef struct { + uint32_t num_ports; + smi_nic_port_t ports[SMI_NIC_MAX_PORTS]; +} smi_nic_port_info_t; + +/** + * @struct smi_nic_rdma_port_info_t + * @brief Structure containing information for a single RDMA port + */ +typedef struct { + char netdev[SMI_NIC_MAX_STRING_LENGTH]; + char state[SMI_NIC_MAX_STRING_LENGTH]; + uint8_t rdma_port; + uint16_t max_mtu; + uint16_t active_mtu; +} smi_nic_rdma_port_info_t; + +/** + * @struct smi_nic_rdma_dev_info_t + * @brief Structure containing information for a single RDMA device + */ +typedef struct { + char rdma_dev[SMI_NIC_MAX_STRING_LENGTH]; + char node_guid[SMI_NIC_MAX_STRING_LENGTH]; + char node_type[SMI_NIC_MAX_STRING_LENGTH]; + char sys_image_guid[SMI_NIC_MAX_STRING_LENGTH]; + char fw_ver[SMI_NIC_MAX_STRING_LENGTH]; + uint8_t num_rdma_ports; + smi_nic_rdma_port_info_t rdma_port_info[SMI_NIC_MAX_PORTS]; +} smi_nic_rdma_dev_info_t; + +/** + * @struct smi_nic_rdma_devices_info_t + * @brief Structure containing information for all RDMA devices + */ +typedef struct { + uint8_t num_rdma_dev; + smi_nic_rdma_dev_info_t rdma_dev_info[SMI_NIC_MAX_RDMA_DEV]; +} smi_nic_rdma_devices_info_t; + +/** + * @brief Create a new thread-safe NIC context + * + * Creates a new context handle. Each context maintains its own state and + * can be used concurrently from different threads. + * + * @param[out] ctx Pointer to store the created context handle + * + * @return ::SMI_NIC_STATUS_SUCCESS if context created successfully + * @return ::SMI_NIC_STATUS_WRONG_PARAM if ctx is NULL + * @return ::SMI_NIC_STATUS_NO_RESOURCE if memory allocation failed + * + * @note This function is thread-safe + * @note The context must be destroyed with smi_nic_destroy_context() + * + */ +smi_nic_status_t smi_nic_create_context(smi_nic_ctx_t *ctx); + +/** + * @brief Destroy a NIC context and free its resources + * + * Destroys the specified context and frees all associated resources. + * + * @param[in] ctx Context handle to destroy + * + * @return ::SMI_NIC_STATUS_SUCCESS if context destroyed successfully + * @return ::SMI_NIC_STATUS_WRONG_PARAM if ctx is NULL + * + * @note This function is thread-safe + * @note Do not use the context handle after calling this function + */ +smi_nic_status_t smi_nic_destroy_context(smi_nic_ctx_t ctx); + +/** + * @brief Discover available NICs and their BDFs. + * + * Discovers all available network interface cards. + * + * @param ctx Context handle + * @param discovery Pointer to structure that will be filled with discovered NIC info. + * @return ::smi_nic_status_t | ::SMI_NIC_STATUS_SUCCESS on success, non-zero on fail + * + * @note This function is thread-safe when using separate contexts + * @note Maximum of SMI_NIC_MAX_DEVICES devices can be discovered + */ +smi_nic_status_t smi_discover_nics(smi_nic_ctx_t ctx, smi_nic_discovery_t *discovery); + +/** + * @brief Retrieve NIC driver information. + * + * @param ctx Context handle + * @param device BDF of the network device. + * @param info Pointer to smi_nic_driver_info_t structure to be filled. + * @return ::smi_nic_status_t | ::SMI_NIC_STATUS_SUCCESS on success, non-zero on fail + */ +smi_nic_status_t smi_get_nic_driver_info(smi_nic_ctx_t ctx, uint64_t device, smi_nic_driver_info_t *info); + +/** + * @brief Retrieve NIC ASIC information. + * + * This function retrieves ASIC related information, including + * vendor IDs, device IDs, revision, MAC address, and product information. + * + * @param ctx Context handle + * @param device BDF of the network device. + * @param info Pointer to smi_nic_asic_info_t structure to be filled. + * @return ::smi_nic_status_t | ::SMI_NIC_STATUS_SUCCESS on success, non-zero on fail + */ +smi_nic_status_t smi_get_nic_asic_info(smi_nic_ctx_t ctx, uint64_t device, smi_nic_asic_info_t *info); + +/** + * @brief Retrieve NIC bus/PCIe information. + * + * This function retrieves bus related information, including + * PCIe width, speed, interface version, and slot type. + * + * @param ctx Context handle + * @param device BDF of the network device. + * @param info Pointer to smi_nic_bus_info_t structure to be filled. + * @return ::smi_nic_status_t | ::SMI_NIC_STATUS_SUCCESS on success, non-zero on fail + */ +smi_nic_status_t smi_get_nic_bus_info(smi_nic_ctx_t ctx, uint64_t device, smi_nic_bus_info_t *info); + +/** + * @brief Retrieve NIC NUMA information. + * + * This function retrieves NUMA node and CPU affinity information. + * + * @param ctx Context handle + * @param device BDF of the network device. + * @param info Pointer to smi_nic_numa_info_t structure to be filled. + * @return ::smi_nic_status_t | ::SMI_NIC_STATUS_SUCCESS on success, non-zero on fail + */ +smi_nic_status_t smi_get_nic_numa_info(smi_nic_ctx_t ctx, uint64_t device, smi_nic_numa_info_t *info); + +/** + * @brief Retrieve NIC port information for all ports. + * + * This function retrieves information for all ports on the NIC, including + * interface names, BDFs, link status, speeds, pause settings, etc. + * + * @param ctx Context handle + * @param device BDF of the network device. + * @param info Pointer to smi_nic_port_info_t structure to be filled. + * @return ::smi_nic_status_t | ::SMI_NIC_STATUS_SUCCESS on success, non-zero on fail + */ +smi_nic_status_t smi_get_nic_port_info(smi_nic_ctx_t ctx, uint64_t device, smi_nic_port_info_t *info); + +/** + * @brief Retrieve RDMA device information for a NIC. + * + * @param ctx Context handle + * @param device BDF of the NIC device + * @param info Pointer to structure that will be filled with RDMA device info + * @return ::smi_nic_status_t | ::SMI_NIC_STATUS_SUCCESS on success, non-zero on fail + * + * @note This function aggregates all RDMA/InfiniBand devices across all ports + * @note Returns SMI_NIC_STATUS_DRIVER_NOT_LOADED if no RDMA driver found on any port + * @note Returns SMI_NIC_STATUS_NO_DATA if no RDMA devices found + * @return ::smi_nic_status_t | ::SMI_NIC_STATUS_SUCCESS on success, non-zero on fail + */ +smi_nic_status_t smi_get_nic_rdma_dev_info(smi_nic_ctx_t ctx, uint64_t device, smi_nic_rdma_devices_info_t *info); + +/** + * @brief Get the count of available standard port statistics for a specified NIC port. + * + * @param ctx Context handle + * @param device BDF of the network device. + * @param port_index Index of the NIC port (0-based). + * @param count Pointer to uint32_t to store the number of available statistics. + * @return ::smi_nic_status_t | ::SMI_NIC_STATUS_SUCCESS on success, non-zero on failure. + */ +smi_nic_status_t smi_get_nic_port_statistics_count(smi_nic_ctx_t ctx, uint64_t device, uint32_t port_index, uint32_t *count); + +/** + * @brief Retrieve standard port statistics list for a specified NIC port. + * + * @param ctx Context handle + * @param device BDF of the network device. + * @param port_index Index of the NIC port (0-based). + * @param stats Pointer to smi_nic_stat_info_t structure to be filled. + * @return ::smi_nic_status_t | ::SMI_NIC_STATUS_SUCCESS on success, non-zero on failure. + */ +smi_nic_status_t smi_get_nic_port_statistics_list(smi_nic_ctx_t ctx, uint64_t device, uint32_t port_index, smi_nic_stat_info_t *stats); + +/** + * @brief Get the count of available vendor statistics for a specified NIC port. + * + * @param ctx Context handle + * @param device BDF of the network device. + * @param port_index Index of the NIC port (0-based). + * @param count Pointer to uint32_t to store the number of available statistics. + * @return ::smi_nic_status_t | ::SMI_NIC_STATUS_SUCCESS on success, non-zero on failure. + */ +smi_nic_status_t smi_get_nic_vendor_statistics_count(smi_nic_ctx_t ctx, uint64_t device, uint32_t port_index, uint32_t *count); + +/** + * @brief Retrieve vendor statistics list for a specified NIC port. + * + * @param ctx Context handle + * @param device BDF of the network device. + * @param port_index Index of the NIC port (0-based). + * @param stats Pointer to smi_nic_stat_info_t structure to be filled. + * @return ::smi_nic_status_t | ::SMI_NIC_STATUS_SUCCESS on success, non-zero on failure. + */ +smi_nic_status_t smi_get_nic_vendor_statistics_list(smi_nic_ctx_t ctx, uint64_t device, uint32_t port_index, smi_nic_stat_info_t *stats); + +/** + * @brief Get the count of available RDMA hardware counters for a specified InfiniBand port. + * + * @param ctx Context handle + * @param device BDF of the network device. + * @param port_index Index of the NIC port (0-based). + * @param ib_index Index of the InfiniBand device (0-based). + * @param rdma_port_index Index of the RDMA port (0-based). + * @param count Pointer to uint32_t to store the number of available counters. + * @return ::smi_nic_status_t | ::SMI_NIC_STATUS_SUCCESS on success, non-zero on failure. + */ +smi_nic_status_t smi_get_nic_rdma_port_statistics_count(smi_nic_ctx_t ctx, uint64_t device, uint32_t port_index, uint32_t ib_index, uint32_t rdma_port_index, uint32_t *count); + +/** + * @brief Retrieve RDMA hardware counters list for a specified InfiniBand port. + * + * @param ctx Context handle + * @param device BDF of the network device. + * @param port_index Index of the NIC port (0-based). + * @param ib_index Index of the InfiniBand device (0-based). + * @param rdma_port_index Index of the RDMA port (0-based). + * @param stats Pointer to smi_nic_stat_info_t structure to be filled. + * @return ::smi_nic_status_t | ::SMI_NIC_STATUS_SUCCESS on success, non-zero on failure. + */ +smi_nic_status_t smi_get_nic_rdma_port_statistics_list(smi_nic_ctx_t ctx, uint64_t device, uint32_t port_index, uint32_t ib_index, uint32_t rdma_port_index, smi_nic_stat_info_t *stats); + +#ifdef __cplusplus +} +#endif + +#endif // __SMI_NIC_INTERFACE_H__ diff --git a/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/src/smi_devlink_netlink.cpp b/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/src/smi_devlink_netlink.cpp new file mode 100644 index 00000000000..1049a07a885 --- /dev/null +++ b/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/src/smi_devlink_netlink.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2025 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "smi_devlink_netlink.h" diff --git a/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/src/smi_ethtool_ioctl.cpp b/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/src/smi_ethtool_ioctl.cpp new file mode 100644 index 00000000000..1fba67c7d3f --- /dev/null +++ b/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/src/smi_ethtool_ioctl.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2025 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "smi_ethtool_ioctl.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +template int smi_ethtool_ioctl(const std::string& device, ethtool_stats* data); +template int smi_ethtool_ioctl(const std::string& device, ethtool_gstrings* data); +template int smi_ethtool_ioctl(const std::string& device, ethtool_drvinfo* data); +template int smi_ethtool_ioctl(const std::string& device, ethtool_pauseparam* data); +template int smi_ethtool_ioctl(const std::string& device, ethtool_fecparam* data); +template int smi_ethtool_ioctl(const std::string& device, ethtool_link_settings* data); +template int smi_ethtool_ioctl(const std::string& device, ethtool_perm_addr* data); + +template +int smi_ethtool_ioctl(const std::string& device, T* data) +{ + struct ifreq ifr{}; + + int sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) { + return -1; + } + + device.copy(ifr.ifr_name, IFNAMSIZ - 1); + ifr.ifr_data = reinterpret_cast(data); + + if (ioctl(sock, SIOCETHTOOL, &ifr) == -1) { + close(sock); + return -1; + } + + close(sock); + return 0; +} diff --git a/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/src/smi_nic.cpp b/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/src/smi_nic.cpp new file mode 100644 index 00000000000..5fbf90b69a4 --- /dev/null +++ b/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/src/smi_nic.cpp @@ -0,0 +1,818 @@ +/* + * Copyright (c) 2025 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "smi_nic.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "smi_sysfs.h" +#include "smi_ethtool_ioctl.h" + +static std::string nic_type_to_string(NicType type) +{ + switch (type) { + case NicType::PCIBridge: + return "PCI Bridge"; + case NicType::Ethernet: + return "Ethernet"; + case NicType::InfiniBand: + return "InfiniBand"; + default: + return "Unknown"; + } +} + +template +static std::optional get_sysfs_data(const std::string& path) +{ + SmiSysfsReader::SysfsValue val; + if (SmiSysfsReader::readLine(path, val) == SmiSysfsReader::SysfsStatus::Success) { + if constexpr (std::is_same_v) { + if (std::holds_alternative(val)) { + return std::get(val); + } + if (std::holds_alternative(val)) { + return std::to_string(std::get(val)); + } + } else { + if (std::holds_alternative(val)) { + return static_cast(std::get(val)); + } + if (std::holds_alternative(val)) { + return static_cast(std::stoul(std::get(val), nullptr, 0)); + } + } + } + + return std::nullopt; +} + +// **** SmiNicPort **** + +SmiNicPort::SmiNicPort(const std::string& iface, const std::string& bdf, const std::string& sysfs_class_path, const std::string& sysfs_bus_path) + : iface_(iface), bdf_(bdf), sysfs_class_path_(sysfs_class_path), sysfs_bus_path_(sysfs_bus_path) +{ + port_num_ = get_sysfs_data(sysfs_class_path_ + "/dev_port"); + auto type_value = get_sysfs_data(sysfs_class_path_ + "/type"); + + if (type_value.has_value()) { + if (type_value.value() == ARPHRD_ETHER) { + type_ = NicType::Ethernet; + } else if (type_value.value() == ARPHRD_INFINIBAND) { + type_ = NicType::InfiniBand; + } else { + type_ = NicType::Unknown; + } + } else { + type_ = NicType::Unknown; + } +} + +const std::string& SmiNicPort::interface() const +{ + return iface_; +} + +const std::string& SmiNicPort::bdf() const +{ + return bdf_; +} + +const std::string& SmiNicPort::sysfs_class_path() const +{ + return sysfs_class_path_; +} + +const std::string& SmiNicPort::sysfs_bus_path() const +{ + return sysfs_bus_path_; +} + +std::optional SmiNicPort::mac_address() const +{ + return get_sysfs_data(sysfs_class_path_ + "/address"); +} + +std::optional SmiNicPort::port_num() const +{ + return port_num_; +} + +std::optional SmiNicPort::ifindex() const +{ + return get_sysfs_data(sysfs_class_path_ + "/ifindex"); +} + +std::optional SmiNicPort::carrier() const +{ + return get_sysfs_data(sysfs_class_path_ + "/carrier"); +} + +std::optional SmiNicPort::mtu() const +{ + return get_sysfs_data(sysfs_class_path_ + "/mtu"); +} + +std::optional SmiNicPort::link_state() const +{ + return get_sysfs_data(sysfs_class_path_ + "/operstate"); +} + +std::optional SmiNicPort::link_speed() const +{ + return get_sysfs_data(sysfs_class_path_ + "/speed"); +} + +const std::string SmiNicPort::port_type() const +{ + return nic_type_to_string(type_); +} + +std::string SmiNicPort::flavour() const +{ + return "N/A"; +} + +std::optional SmiNicPort::active_fec() const +{ + struct ethtool_fecparam fec; + fec.cmd = ETHTOOL_GFECPARAM; + + int ret = smi_ethtool_ioctl(iface_, &fec); + if (ret == 0) { + return fec.active_fec; + } + return std::nullopt; +} + +std::optional SmiNicPort::autoneg() const +{ + struct ethtool_link_settings link_settings; + link_settings.cmd = ETHTOOL_GLINKSETTINGS; + + int ret = smi_ethtool_ioctl(iface_, &link_settings); + if (ret == 0) { + return link_settings.autoneg ? "on" : "off"; + } + return std::nullopt; +} + +std::optional SmiNicPort::pause_autoneg() const +{ + struct ethtool_pauseparam pause; + pause.cmd = ETHTOOL_GPAUSEPARAM; + + int ret = smi_ethtool_ioctl(iface_, &pause); + if (ret == 0) { + return pause.autoneg ? "on" : "off"; + } + return std::nullopt; +} + +std::optional SmiNicPort::pause_rx() const +{ + struct ethtool_pauseparam pause; + pause.cmd = ETHTOOL_GPAUSEPARAM; + + int ret = smi_ethtool_ioctl(iface_, &pause); + if (ret == 0) { + return pause.rx_pause ? "on" : "off"; + } + return std::nullopt; +} + +std::optional SmiNicPort::pause_tx() const +{ + struct ethtool_pauseparam pause; + pause.cmd = ETHTOOL_GPAUSEPARAM; + + int ret = smi_ethtool_ioctl(iface_, &pause); + if (ret == 0) { + return pause.tx_pause ? "on" : "off"; + } + return std::nullopt; +} + +void SmiNicPort::discover_infiniband() +{ + std::string infiniband_path = sysfs_bus_path_ + "/infiniband"; + if (!std::filesystem::exists(infiniband_path) || !std::filesystem::is_directory(infiniband_path)) { + return; + } + + for (const auto& entry : std::filesystem::directory_iterator(infiniband_path)) { + if (entry.is_directory()) { + + std::string name = entry.path().filename().string(); + std::string sysfs_path = entry.path().string(); + SmiInfiniBand ib(name, sysfs_path); + + std::string ports_path = sysfs_path + "/ports"; + if (std::filesystem::exists(ports_path) && std::filesystem::is_directory(ports_path)) { + for (const auto& port_entry : std::filesystem::directory_iterator(ports_path)) { + if (port_entry.is_directory()) { + std::string port_name = port_entry.path().filename().string(); + std::string port_sysfs_path = port_entry.path().string(); + SmiInfiniBandPort port(iface_, port_name, port_sysfs_path); + port.collect_hw_counters(); + ib.add_port(port); + } + } + } + add_infiniband(ib); + } + } +} + +void SmiNicPort::add_infiniband(const SmiInfiniBand& infiniband) +{ + infiniband_.push_back(infiniband); +} + +const std::vector& SmiNicPort::infiniband() const +{ + return infiniband_; +} + +uint8_t SmiNicPort::infiniband_num() const +{ + return static_cast(infiniband_.size()); +} + +void SmiNicPort::collect_vendor_statistics() +{ + int ret = 0; + uint32_t stats_num = 0; + + auto drvinfo = std::make_unique(); + drvinfo->cmd = ETHTOOL_GDRVINFO; + + ret = smi_ethtool_ioctl(iface_, drvinfo.get()); + if (ret != 0 || !drvinfo) { + return; + } + stats_num = drvinfo->n_stats; + + size_t strings_len = sizeof(ethtool_gstrings) + stats_num * ETH_GSTRING_LEN; + std::unique_ptr strings( + static_cast(std::calloc(1, strings_len)), &free); + strings->cmd = ETHTOOL_GSTRINGS; + strings->string_set = ETH_SS_STATS; + strings->len = static_cast<__u32>(stats_num); + + ret = smi_ethtool_ioctl(iface_, strings.get()); + if (ret != 0 || !strings) { + return; + } + + size_t stats_len = sizeof(ethtool_stats) + stats_num * sizeof(uint64_t); + std::unique_ptr stats( + static_cast(std::calloc(1, stats_len)), &free); + stats->cmd = ETHTOOL_GSTATS; + stats->n_stats = static_cast<__u32>(stats_num); + + ret = smi_ethtool_ioctl(iface_, stats.get()); + if (ret != 0 || !stats) { + return; + } + + add_vendor_statistic(strings.get(), stats.get()); +} + +void SmiNicPort::add_vendor_statistic(struct ethtool_gstrings *strings, struct ethtool_stats *stats) +{ + if (!strings || !stats) { + return; + } + for (unsigned int i = 0; i < stats->n_stats; ++i) { + std::string key(reinterpret_cast(&strings->data[i * ETH_GSTRING_LEN]), ETH_GSTRING_LEN); + key.erase(std::find(key.begin(), key.end(), '\0'), key.end()); + if (vendor_stat_allowed(key)) { + uint64_t value = stats->data[i]; + vendor_stats_map_[key] = value; + } + } +} + +const std::map& SmiNicPort::get_vendor_stats_map() const +{ + return vendor_stats_map_; +} + +void SmiNicPort::collect_standard_statistics() +{ + std::string stats_path = sysfs_class_path_ + "/statistics"; + + if (!std::filesystem::exists(stats_path) || !std::filesystem::is_directory(stats_path)) { + return; + } + + for (const auto& entry : std::filesystem::directory_iterator(stats_path)) { + if (entry.is_regular_file()) { + std::string stat_name = entry.path().filename().string(); + auto stat_value = get_sysfs_data(entry.path().string()); + if (stat_value.has_value()) { + standard_stats_map_[stat_name] = stat_value.value(); + } + } + } +} + +const std::map& SmiNicPort::get_standard_stats_map() const +{ + return standard_stats_map_; +} + +std::optional SmiNicPort::read_vpd_content() const +{ + auto vpd = get_sysfs_data(sysfs_bus_path_ + "/vpd"); + if (!vpd) { + return std::nullopt; + } + + std::string content = vpd.value(); + content.erase(std::remove_if(content.begin(), content.end(), + [](char c) { return !(std::isprint(static_cast(c)) || c == '\n'); }), content.end()); + + return content; +} + +std::string SmiNicPort::map_vendor_stat_to_string(SmiVendorStat stat) const +{ + static const std::unordered_map stat_map = { + {SmiVendorStat::TX_PACKETS, "tx_packets"}, + {SmiVendorStat::RX_PACKETS, "rx_packets"}, + {SmiVendorStat::TX_BYTES, "tx_bytes"}, + {SmiVendorStat::RX_BYTES, "rx_bytes"}, + {SmiVendorStat::TX_CSUM_NONE, "tx_csum_none"}, + {SmiVendorStat::RX_CSUM_NONE, "rx_csum_none"}, + {SmiVendorStat::TX_CSUM, "tx_csum"}, + {SmiVendorStat::TX_TSO, "tx_tso"}, + {SmiVendorStat::TX_TSO_BYTES, "tx_tso_bytes"} + }; + + auto it = stat_map.find(stat); + return (it != stat_map.end()) ? it->second : ""; +} + +bool SmiNicPort::vendor_stat_allowed(const std::string& stat_name) const +{ + for (int i = static_cast(SmiVendorStat::TX_PACKETS); + i <= static_cast(SmiVendorStat::TX_TSO_BYTES); i++) { + SmiVendorStat stat = static_cast(i); + if (map_vendor_stat_to_string(stat) == stat_name) { + return true; + } + } + return false; +} + +// **** SmiInfiniBandPort **** + +SmiInfiniBandPort::SmiInfiniBandPort(std::string& netdev, std::string& name, const std::string& sysfs_path) + : netdev_(netdev), name_(name), sysfs_path_(sysfs_path) +{ +} + +const std::string& SmiInfiniBandPort::name() const +{ + return name_; +} + +const std::string& SmiInfiniBandPort::netdev() const +{ + return netdev_; +} + +std::optional SmiInfiniBandPort::port_num() const +{ + try { + return static_cast(std::stoul(name_)); + } catch (const std::exception&) { + return std::nullopt; + } +} + +std::optional SmiInfiniBandPort::state() const +{ + auto raw_state = get_sysfs_data(sysfs_path_ + "/state"); + if (!raw_state.has_value()) { + return std::nullopt; + } + + const std::string& state = raw_state.value(); + auto pos = state.find(": "); + + if (pos != std::string::npos) { + return state.substr(pos + 2); + } + + return state; +} + +std::optional SmiInfiniBandPort::max_mtu() const +{ + return get_sysfs_data(sysfs_path_ + "/max_mtu"); +} + +std::optional SmiInfiniBandPort::active_mtu() const +{ + return get_sysfs_data(sysfs_path_ + "/active_mtu"); +} + +void SmiInfiniBandPort::collect_hw_counters() +{ + std::string hw_counters_path = sysfs_path_ + "/hw_counters"; + + if (!std::filesystem::exists(hw_counters_path) || !std::filesystem::is_directory(hw_counters_path)) { + return; + } + + for (const auto& entry : std::filesystem::directory_iterator(hw_counters_path)) { + if (entry.is_regular_file()) { + std::string counter_name = entry.path().filename().string(); + auto counter_value = get_sysfs_data(entry.path().string()); + if (counter_value.has_value()) { + hw_counters_map_[counter_name] = counter_value.value(); + } + } + } +} + +const std::map& SmiInfiniBandPort::get_hw_counters_map() const +{ + return hw_counters_map_; +} + +// **** SmiInfiniBand **** + +SmiInfiniBand::SmiInfiniBand(std::string& name, const std::string& sysfs_path) + : name_(name), sysfs_path_(sysfs_path) +{ +} + +std::string SmiInfiniBand::rdma_dev() const +{ + return name_; +} + +std::optional SmiInfiniBand::node_guid() const +{ + return get_sysfs_data(sysfs_path_ + "/node_guid"); +} + +std::optional SmiInfiniBand::node_type() const +{ + auto raw_node_type = get_sysfs_data(sysfs_path_ + "/node_type"); + if (!raw_node_type.has_value()) { + return std::nullopt; + } + + const std::string& node_type = raw_node_type.value(); + auto pos = node_type.find(": "); + + if (pos != std::string::npos) { + return node_type.substr(pos + 2); + } + + return node_type; +} + +std::optional SmiInfiniBand::sys_image_guid() const +{ + return get_sysfs_data(sysfs_path_ + "/sys_image_guid"); +} + +std::optional SmiInfiniBand::fw_ver() const +{ + return get_sysfs_data(sysfs_path_ + "/fw_ver"); +} + +void SmiInfiniBand::add_port(const SmiInfiniBandPort& port) +{ + ports_.push_back(port); +} + +const std::vector& SmiInfiniBand::ports() const +{ + return ports_; +} + +uint8_t SmiInfiniBand::ports_num() const +{ + return static_cast(ports_.size()); +} + +// **** SmiNic **** + +SmiNic::SmiNic(const std::string& iface, const std::string& bdf, NicType type, + const std::string& sysfs_class_path, const std::string& sysfs_bus_path, + NicVendor vendor, NicProduct product) + : iface_(iface), bdf_(bdf), type_(type), vendor_(vendor), product_(product), + sysfs_class_path_(sysfs_class_path), sysfs_bus_path_(sysfs_bus_path) +{ +} + +const std::string& SmiNic::interface() const +{ + return iface_; +} + +const std::string& SmiNic::bdf() const +{ + return bdf_; +} + +NicType SmiNic::type() const +{ + return type_; +} + +NicVendor SmiNic::vendor() const +{ + return vendor_; +} + +NicProduct SmiNic::product() const +{ + return product_; +} + +const std::string SmiNic::port_type() const +{ + return nic_type_to_string(type_); +} + +const std::string& SmiNic::sysfs_class_path() const +{ + return sysfs_class_path_; +} + +const std::string& SmiNic::sysfs_bus_path() const +{ + return sysfs_bus_path_; +} + +void SmiNic::add_nic_port(const SmiNicPort& port) +{ + ports_.push_back(port); +} + +const std::vector& SmiNic::nic_ports() const +{ + return ports_; +} + +uint8_t SmiNic::nic_ports_num() const +{ + return static_cast(ports_.size()); +} + +std::optional SmiNic::vendor_id() const +{ + return get_sysfs_data(sysfs_bus_path_ + "/vendor"); +} + +std::optional SmiNic::subvendor_id() const +{ + return get_sysfs_data(sysfs_bus_path_ + "/subsystem_vendor"); +} + +std::optional SmiNic::device_id() const +{ + return get_sysfs_data(sysfs_bus_path_ + "/device"); +} + +std::optional SmiNic::subsystem_id() const +{ + return get_sysfs_data(sysfs_bus_path_ + "/subsystem_device"); +} + +std::optional SmiNic::revision() const +{ + return get_sysfs_data(sysfs_bus_path_ + "/revision"); +} + +std::optional SmiNic::perm_address() const +{ + if (ports_.empty()) { + return std::nullopt; + } + + const std::string& port_iface = ports_[0].interface(); + struct ethtool_perm_addr permaddr; + permaddr.cmd = ETHTOOL_GPERMADDR; + permaddr.size = 6; + + int ret = smi_ethtool_ioctl(port_iface, &permaddr); + if (ret != 0) { + return std::nullopt; + } + + if (permaddr.size == 6) { + std::stringstream ss; + ss << std::hex << std::setfill('0'); + for (int i = 0; i < 6; i++) { + if (i > 0) ss << ":"; + ss << std::setw(2) << static_cast(permaddr.data[i]); + } + return ss.str(); + } + + return std::nullopt; +} + +std::optional SmiNic::pcie_class() const +{ + return get_sysfs_data(sysfs_bus_path_ + "/class"); +} + +std::optional SmiNic::max_pcie_width() const { + return get_sysfs_data(sysfs_bus_path_ + "/max_link_width"); +} + +std::optional SmiNic::max_pcie_speed() const { + return get_sysfs_data(sysfs_bus_path_ + "/max_link_speed"); +} + +std::optional SmiNic::numa_node() const { + return get_sysfs_data(sysfs_bus_path_ + "/numa_node"); +} + +std::optional SmiNic::numa_affinity(uint8_t node) const +{ + std::string path = "/sys/devices/system/node/node" + std::to_string(node) + "/cpulist"; + return get_sysfs_data(path); +} + +std::optional SmiNic::product_name() const +{ + return std::nullopt; +} + +std::optional SmiNic::part_number() const +{ + return std::nullopt; +} + +std::optional SmiNic::serial_number() const +{ + return std::nullopt; +} + +std::optional SmiNic::vendor_name() const +{ + return std::nullopt; +} + +// **** SmiNicPensando **** + +SmiNicPensando::SmiNicPensando(const std::string& iface, const std::string& bdf, NicType type, + const std::string& sysfs_class_path, const std::string& sysfs_bus_path, + NicVendor vendor, NicProduct product) + : SmiNic(iface, bdf, type, sysfs_class_path, sysfs_bus_path, vendor, product) +{ +} + +std::optional SmiNicPensando::vendor_name() const +{ + return std::string("AMD Pensando Systems, Inc."); +} + +std::optional SmiNicPensando::product_name() const +{ + if (ports_.empty()) { + return std::nullopt; + } + + auto vpd = ports_[0].read_vpd_content(); + if (!vpd) { + return std::nullopt; + } + + const std::string& content = vpd.value(); + size_t pn_pos = content.find("PN"); + + if (pn_pos != std::string::npos) { + std::string product_name = content.substr(0, pn_pos); + product_name.erase(product_name.find_last_not_of(" \n\r\t") + 1); + product_name.erase(0, product_name.find_first_not_of(" \n\r\t")); + return product_name; + } + + return std::nullopt; +} + +std::optional SmiNicPensando::part_number() const +{ + if (ports_.empty()) { + return std::nullopt; + } + + auto vpd = ports_[0].read_vpd_content(); + if (!vpd) { + return std::nullopt; + } + + const std::string& content = vpd.value(); + size_t pn_pos = content.find("PN"); + size_t sn_pos = content.find("SN", pn_pos); + + if (pn_pos != std::string::npos && sn_pos != std::string::npos) { + std::string part_number = content.substr(pn_pos + 2, sn_pos - (pn_pos + 2)); + part_number.erase(part_number.find_last_not_of(" \n\r\t") + 1); + part_number.erase(0, part_number.find_first_not_of(" \n\r\t")); + return part_number; + } + + return std::nullopt; +} + +std::optional SmiNicPensando::serial_number() const +{ + if (ports_.empty()) { + return std::nullopt; + } + + auto vpd = ports_[0].read_vpd_content(); + if (!vpd) { + return std::nullopt; + } + + const std::string& content = vpd.value(); + size_t sn_pos = content.find("SN"); + size_t mdt_pos = content.find("MDT", sn_pos); + + if (sn_pos != std::string::npos && mdt_pos != std::string::npos) { + std::string serial_number = content.substr(sn_pos + 2, mdt_pos - (sn_pos + 2)); + serial_number.erase(serial_number.find_last_not_of(" \n\r\t") + 1); + serial_number.erase(0, serial_number.find_first_not_of(" \n\r\t")); + return serial_number; + } + + return std::nullopt; +} + +// **** SmiNicBroadcom **** + +SmiNicBroadcom::SmiNicBroadcom(const std::string& iface, const std::string& bdf, NicType type, + const std::string& sysfs_class_path, const std::string& sysfs_bus_path, + NicVendor vendor, NicProduct product) + : SmiNic(iface, bdf, type, sysfs_class_path, sysfs_bus_path, vendor, product) +{ +} + +std::optional SmiNicBroadcom::vendor_name() const +{ + // TODO: broadcom - get vendor name + return std::string("Broadcom Inc."); +} + +std::optional SmiNicBroadcom::product_name() const +{ + // TODO: broadcom - get product name + return std::nullopt; +} + +std::optional SmiNicBroadcom::part_number() const +{ + // TODO: broadcom - get part number + return std::nullopt; +} + +std::optional SmiNicBroadcom::serial_number() const +{ + // TODO: broadcom - get serial number + return std::nullopt; +} diff --git a/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/src/smi_nic_interface.cpp b/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/src/smi_nic_interface.cpp new file mode 100644 index 00000000000..dd23e3cdc7b --- /dev/null +++ b/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/src/smi_nic_interface.cpp @@ -0,0 +1,791 @@ +/* + * Copyright (c) 2025 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "smi_sysfs.h" +#include "smi_ethtool_ioctl.h" +#include "smi_nic_interface.h" +#include "smi_nic_system.h" + +struct smi_nic_ctx { + std::unique_ptr nic_system; + std::mutex ctx_mutex; + std::atomic init; + + smi_nic_ctx() : init(false) {} +}; + +static SmiNicSystem* get_nic_system_from_context(smi_nic_ctx *ctx) +{ + if (!ctx || !ctx->init || !ctx->nic_system) { + return nullptr; + } + return ctx->nic_system.get(); +} + +extern "C" { +smi_nic_status_t smi_nic_create_context(smi_nic_ctx_t *ctx) +{ + try { + if (!ctx) { + return SMI_NIC_STATUS_WRONG_PARAM; + } + + auto context = std::make_unique(); + context->nic_system = std::make_unique(); + context->init = true; + *ctx = context.release(); + + (*ctx)->nic_system->discover_nics(); + + return SMI_NIC_STATUS_SUCCESS; + + } catch (const std::bad_alloc&) { + return SMI_NIC_STATUS_NO_RESOURCE; + } catch (...) { + return SMI_NIC_STATUS_ERROR; + } +} + +smi_nic_status_t smi_nic_destroy_context(smi_nic_ctx_t ctx) +{ + try { + if (!ctx) { + return SMI_NIC_STATUS_WRONG_PARAM; + } + + { + std::lock_guard lock(ctx->ctx_mutex); + ctx->init = false; + ctx->nic_system.reset(); + } + + delete ctx; + return SMI_NIC_STATUS_SUCCESS; + } catch (...) { + return SMI_NIC_STATUS_ERROR; + } +} + +smi_nic_status_t smi_discover_nics(smi_nic_ctx_t ctx, smi_nic_discovery_t *discovery) +{ + if (!ctx || !discovery) { + return SMI_NIC_STATUS_WRONG_PARAM; + } + + discovery->count = 0; + auto* nic_system = get_nic_system_from_context(ctx); + if (!nic_system) { + return SMI_NIC_STATUS_NOT_INIT; + } + + std::lock_guard lock(ctx->ctx_mutex); + + try { + const auto& nics = nic_system->get_nics(); + if (nics.empty()) { + return SMI_NIC_STATUS_NO_DATA; + } + + if (nics.size() > SMI_NIC_MAX_DEVICES) { + return SMI_NIC_STATUS_NO_RESOURCE; + } + + uint32_t index = 0; + for (const auto* nic : nics) { + std::snprintf(discovery->devices[index].bdf, SMI_NIC_MAX_STRING_LENGTH, + "%s", nic->bdf().c_str()); + index++; + } + + discovery->count = static_cast(nics.size()); + return SMI_NIC_STATUS_SUCCESS; + + } catch (const std::exception&) { + discovery->count = 0; + return SMI_NIC_STATUS_ERROR; + } +} + +smi_nic_status_t smi_get_nic_driver_info(smi_nic_ctx_t ctx, uint64_t device, smi_nic_driver_info_t *info) +{ + if (!ctx) { + return SMI_NIC_STATUS_WRONG_PARAM; + } + + if (!info) { + return SMI_NIC_STATUS_WRONG_PARAM; + } + + auto* nic_system = get_nic_system_from_context(ctx); + if (!nic_system) { + return SMI_NIC_STATUS_NOT_INIT; + } + + std::lock_guard lock(ctx->ctx_mutex); + const SmiNic *nic = nic_system->get_nic_by_bdf(device); + if (!nic) { + return SMI_NIC_STATUS_NOT_FOUND; + } + + const auto& ports = nic->nic_ports(); + if (ports.empty()) { + return SMI_NIC_STATUS_DRIVER_NOT_LOADED; + } + + if (!nic_system->driver_loaded(ports[0].bdf(), DriverType::IONIC)) { + return SMI_NIC_STATUS_DRIVER_NOT_LOADED; + } + + *info = {}; + struct ethtool_drvinfo drvinfo{}; + drvinfo.cmd = ETHTOOL_GDRVINFO; + + int ret = smi_ethtool_ioctl(ports[0].interface(), &drvinfo); + if (ret != 0) { + return SMI_NIC_STATUS_ERROR; + } + + std::snprintf(info->name, SMI_NIC_MAX_STRING_LENGTH, "%s", drvinfo.driver); + std::snprintf(info->version, SMI_NIC_MAX_STRING_LENGTH, "%s", drvinfo.version); + + return SMI_NIC_STATUS_SUCCESS; +} + +smi_nic_status_t smi_get_nic_asic_info(smi_nic_ctx_t ctx, uint64_t device, smi_nic_asic_info_t *info) +{ + if (!ctx) { + return SMI_NIC_STATUS_WRONG_PARAM; + } + + if (!info) { + return SMI_NIC_STATUS_WRONG_PARAM; + } + + auto* nic_system = get_nic_system_from_context(ctx); + if (!nic_system) { + return SMI_NIC_STATUS_NOT_INIT; + } + + std::lock_guard lock(ctx->ctx_mutex); + const SmiNic *nic = nic_system->get_nic_by_bdf(device); + if (!nic) { + return SMI_NIC_STATUS_NOT_FOUND; + } + + const auto& ports = nic->nic_ports(); + if (ports.empty()) { + return SMI_NIC_STATUS_DRIVER_NOT_LOADED; + } + + if (!nic_system->driver_loaded(ports[0].bdf(), DriverType::IONIC)) { + return SMI_NIC_STATUS_DRIVER_NOT_LOADED; + } + + *info = {}; + info->vendor_id = nic->vendor_id().value_or(std::numeric_limits::max()); + info->subvendor_id = nic->subvendor_id().value_or(std::numeric_limits::max()); + info->device_id = nic->device_id().value_or(std::numeric_limits::max()); + info->subsystem_id = nic->subsystem_id().value_or(std::numeric_limits::max()); + info->revision = nic->revision().value_or(std::numeric_limits::max()); + + std::snprintf(info->permanent_address, SMI_NIC_MAX_STRING_LENGTH, "%s", + nic->perm_address().value_or("N/A").c_str()); + std::snprintf(info->product_name, SMI_NIC_MAX_STRING_LENGTH, "%s", + nic->product_name().value_or("N/A").c_str()); + std::snprintf(info->vendor_name, SMI_NIC_MAX_STRING_LENGTH, "%s", + nic->vendor_name().value_or("N/A").c_str()); + std::snprintf(info->part_number, SMI_NIC_MAX_STRING_LENGTH, "%s", + nic->part_number().value_or("N/A").c_str()); + std::snprintf(info->serial_number, SMI_NIC_MAX_STRING_LENGTH, "%s", + nic->serial_number().value_or("N/A").c_str()); + + return SMI_NIC_STATUS_SUCCESS; +} + +smi_nic_status_t smi_get_nic_bus_info(smi_nic_ctx_t ctx, uint64_t device, smi_nic_bus_info_t *info) +{ + if (!ctx) { + return SMI_NIC_STATUS_WRONG_PARAM; + } + + if (!info) { + return SMI_NIC_STATUS_WRONG_PARAM; + } + + auto* nic_system = get_nic_system_from_context(ctx); + if (!nic_system) { + return SMI_NIC_STATUS_NOT_INIT; + } + + std::lock_guard lock(ctx->ctx_mutex); + const SmiNic *nic = nic_system->get_nic_by_bdf(device); + if (!nic) { + return SMI_NIC_STATUS_NOT_FOUND; + } + + const auto& ports = nic->nic_ports(); + if (ports.empty()) { + return SMI_NIC_STATUS_DRIVER_NOT_LOADED; + } + + if (!nic_system->driver_loaded(ports[0].bdf(), DriverType::IONIC)) { + return SMI_NIC_STATUS_DRIVER_NOT_LOADED; + } + + *info = {}; + info->bdf = device; + info->max_pcie_width = nic->max_pcie_width().value_or(std::numeric_limits::max()); + info->max_pcie_speed = nic->max_pcie_speed().value_or(std::numeric_limits::max()); + std::snprintf(info->pcie_interface_version, SMI_NIC_MAX_STRING_LENGTH, "%s", "N/A"); + std::snprintf(info->slot_type, SMI_NIC_MAX_STRING_LENGTH, "%s", "N/A"); + + return SMI_NIC_STATUS_SUCCESS; +} + +smi_nic_status_t smi_get_nic_numa_info(smi_nic_ctx_t ctx, uint64_t device, smi_nic_numa_info_t *info) +{ + if (!ctx) { + return SMI_NIC_STATUS_WRONG_PARAM; + } + + if (!info) { + return SMI_NIC_STATUS_WRONG_PARAM; + } + + auto* nic_system = get_nic_system_from_context(ctx); + if (!nic_system) { + return SMI_NIC_STATUS_NOT_INIT; + } + + std::lock_guard lock(ctx->ctx_mutex); + const SmiNic *nic = nic_system->get_nic_by_bdf(device); + if (!nic) { + return SMI_NIC_STATUS_NOT_FOUND; + } + + const auto& ports = nic->nic_ports(); + if (ports.empty()) { + return SMI_NIC_STATUS_DRIVER_NOT_LOADED; + } + + if (!nic_system->driver_loaded(ports[0].bdf(), DriverType::IONIC)) { + return SMI_NIC_STATUS_DRIVER_NOT_LOADED; + } + + *info = {}; + info->node = nic->numa_node().value_or(std::numeric_limits::max()); + std::snprintf(info->affinity, SMI_NIC_MAX_STRING_LENGTH, "%s", + nic->numa_affinity(info->node).value_or("N/A").c_str()); + + return SMI_NIC_STATUS_SUCCESS; +} + +smi_nic_status_t smi_get_nic_port_info(smi_nic_ctx_t ctx, uint64_t device, smi_nic_port_info_t *info) +{ + if (!ctx) { + return SMI_NIC_STATUS_WRONG_PARAM; + } + + if (!info) { + return SMI_NIC_STATUS_WRONG_PARAM; + } + + auto* nic_system = get_nic_system_from_context(ctx); + if (!nic_system) { + return SMI_NIC_STATUS_NOT_INIT; + } + + std::lock_guard lock(ctx->ctx_mutex); + const SmiNic *nic = nic_system->get_nic_by_bdf(device); + if (!nic) { + return SMI_NIC_STATUS_NOT_FOUND; + } + + const auto& ports = nic->nic_ports(); + if (ports.empty()) { + return SMI_NIC_STATUS_DRIVER_NOT_LOADED; + } + + *info = {}; + + uint32_t port_count = 0; + bool driver_not_loaded = false; + for (uint32_t i = 0; i < ports.size() && port_count < SMI_NIC_MAX_PORTS; i++) { + const auto& port = ports[i]; + + if (!nic_system->driver_loaded(port.bdf(), DriverType::IONIC)) { + driver_not_loaded = true; + continue; + } + + smi_nic_port_t *port_info = &info->ports[port_count]; + + port_info->bdf = parse_bdf(port.bdf()); + port_info->port_num = port.port_num().value_or(std::numeric_limits::max()); + + auto port_type = port.port_type(); + std::snprintf(port_info->type, SMI_NIC_MAX_STRING_LENGTH, "%s", + !port_type.empty() ? port_type.c_str() : "N/A"); + + std::string flavour = port.flavour(); + std::snprintf(port_info->flavour, SMI_NIC_MAX_STRING_LENGTH, "%s", + !flavour.empty() ? flavour.c_str() : "N/A"); + + const std::string& netdev = port.interface(); + std::snprintf(port_info->netdev, SMI_NIC_MAX_STRING_LENGTH, "%s", + !netdev.empty() ? netdev.c_str() : "N/A"); + + port_info->ifindex = port.ifindex().value_or(std::numeric_limits::max()); + + auto mac = port.mac_address(); + std::snprintf(port_info->mac_address, SMI_NIC_MAX_STRING_LENGTH, "%s", + (mac.has_value() && !mac.value().empty()) ? mac.value().c_str() : "N/A"); + + port_info->carrier = port.carrier().value_or(std::numeric_limits::max()); + port_info->mtu = port.mtu().value_or(std::numeric_limits::max()); + + auto link_state = port.link_state(); + std::snprintf(port_info->link_state, SMI_NIC_MAX_STRING_LENGTH, "%s", + (link_state.has_value() && !link_state.value().empty()) ? link_state.value().c_str() : "N/A"); + + port_info->link_speed = port.link_speed().value_or(std::numeric_limits::max()); + + struct ethtool_fecparam fecparam_info{}; + fecparam_info.cmd = ETHTOOL_GFECPARAM; + port_info->active_fec = (smi_ethtool_ioctl(port.interface(), &fecparam_info) == 0) + ? fecparam_info.active_fec : std::numeric_limits::max(); + + struct ethtool_link_settings link_settings{}; + link_settings.cmd = ETHTOOL_GLINKSETTINGS; + if (smi_ethtool_ioctl(port.interface(), &link_settings) == 0) { + std::snprintf(port_info->autoneg, SMI_NIC_MAX_STRING_LENGTH, "%s", + link_settings.autoneg ? "ON" : "OFF"); + } else { + std::snprintf(port_info->autoneg, SMI_NIC_MAX_STRING_LENGTH, "%s", "N/A"); + } + + struct ethtool_pauseparam pause_info{}; + pause_info.cmd = ETHTOOL_GPAUSEPARAM; + if (smi_ethtool_ioctl(port.interface(), &pause_info) == 0) { + std::snprintf(port_info->pause_autoneg, SMI_NIC_MAX_STRING_LENGTH, "%s", + pause_info.autoneg ? "ON" : "OFF"); + std::snprintf(port_info->pause_rx, SMI_NIC_MAX_STRING_LENGTH, "%s", + pause_info.rx_pause ? "ON" : "OFF"); + std::snprintf(port_info->pause_tx, SMI_NIC_MAX_STRING_LENGTH, "%s", + pause_info.tx_pause ? "ON" : "OFF"); + } else { + std::snprintf(port_info->pause_autoneg, SMI_NIC_MAX_STRING_LENGTH, "%s", "N/A"); + std::snprintf(port_info->pause_rx, SMI_NIC_MAX_STRING_LENGTH, "%s", "N/A"); + std::snprintf(port_info->pause_tx, SMI_NIC_MAX_STRING_LENGTH, "%s", "N/A"); + } + + port_count++; + } + + info->num_ports = port_count; + if (driver_not_loaded && port_count == 0) { + return SMI_NIC_STATUS_DRIVER_NOT_LOADED; + } + + return SMI_NIC_STATUS_SUCCESS; +} + +smi_nic_status_t smi_get_nic_rdma_dev_info(smi_nic_ctx_t ctx, uint64_t device, smi_nic_rdma_devices_info_t *info) +{ + if (!ctx) { + return SMI_NIC_STATUS_WRONG_PARAM; + } + + if (!info) { + return SMI_NIC_STATUS_WRONG_PARAM; + } + + auto* nic_system = get_nic_system_from_context(ctx); + if (!nic_system) { + return SMI_NIC_STATUS_NOT_INIT; + } + + std::lock_guard lock(ctx->ctx_mutex); + const SmiNic *nic = nic_system->get_nic_by_bdf(device); + if (!nic) { + return SMI_NIC_STATUS_NOT_FOUND; + } + + const auto& ports = nic->nic_ports(); + if (ports.empty()) { + return SMI_NIC_STATUS_DRIVER_NOT_LOADED; + } + + *info = {}; + + uint8_t rdma_count = 0; + bool driver_not_loaded = false; + for (uint32_t i = 0; i < ports.size() && rdma_count < SMI_NIC_MAX_RDMA_DEV; i++) { + const auto& port = ports[i]; + if (!nic_system->driver_loaded(port.bdf(), DriverType::IONIC_RDMA)) { + driver_not_loaded = true; + continue; + } + + const auto& ibs = port.infiniband(); + for (uint8_t j = 0; j < ibs.size() && rdma_count < SMI_NIC_MAX_RDMA_DEV; j++) { + const auto& ib = ibs[j]; + smi_nic_rdma_dev_info_t *rdma_dev = &info->rdma_dev_info[rdma_count]; + + std::snprintf(rdma_dev->rdma_dev, SMI_NIC_MAX_STRING_LENGTH, "%s", + ib.rdma_dev().c_str()); + std::snprintf(rdma_dev->node_guid, SMI_NIC_MAX_STRING_LENGTH, "%s", + ib.node_guid().value_or("N/A").c_str()); + std::snprintf(rdma_dev->node_type, SMI_NIC_MAX_STRING_LENGTH, "%s", + ib.node_type().value_or("N/A").c_str()); + std::snprintf(rdma_dev->sys_image_guid, SMI_NIC_MAX_STRING_LENGTH, "%s", + ib.sys_image_guid().value_or("N/A").c_str()); + std::snprintf(rdma_dev->fw_ver, SMI_NIC_MAX_STRING_LENGTH, "%s", + ib.fw_ver().value_or("N/A").c_str()); + + const auto& ib_ports = ib.ports(); + rdma_dev->num_rdma_ports = ib.ports_num(); + for (uint8_t k = 0; k < ib_ports.size() && k < SMI_NIC_MAX_PORTS; k++) { + const auto& ib_port = ib_ports[k]; + smi_nic_rdma_port_info_t *port_info = &rdma_dev->rdma_port_info[k]; + + std::snprintf(port_info->netdev, SMI_NIC_MAX_STRING_LENGTH, "%s", + port.interface().c_str()); + std::snprintf(port_info->state, SMI_NIC_MAX_STRING_LENGTH, "%s", + ib_port.state().value_or("N/A").c_str()); + port_info->rdma_port = ib_port.port_num().value_or(std::numeric_limits::max()); + port_info->max_mtu = ib_port.max_mtu().value_or(std::numeric_limits::max()); + port_info->active_mtu = ib_port.active_mtu().value_or(std::numeric_limits::max()); + } + rdma_count++; + } + } + + info->num_rdma_dev = rdma_count; + if (driver_not_loaded && rdma_count == 0) { + return SMI_NIC_STATUS_DRIVER_NOT_LOADED; + } + + if (rdma_count == 0) { + return SMI_NIC_STATUS_NO_DATA; + } + + return SMI_NIC_STATUS_SUCCESS; +} + +smi_nic_status_t smi_get_nic_port_statistics_count(smi_nic_ctx_t ctx, uint64_t device, uint32_t port_index, uint32_t *count) +{ + if (!ctx || !count) { + return SMI_NIC_STATUS_WRONG_PARAM; + } + + auto* nic_system = get_nic_system_from_context(ctx); + if (!nic_system) { + return SMI_NIC_STATUS_NOT_INIT; + } + + std::lock_guard lock(ctx->ctx_mutex); + const SmiNic *nic = nic_system->get_nic_by_bdf(device); + if (!nic) { + return SMI_NIC_STATUS_NOT_FOUND; + } + + const auto& ports = nic->nic_ports(); + if (ports.empty()) { + return SMI_NIC_STATUS_DRIVER_NOT_LOADED; + } + + if (port_index >= ports.size()) { + return SMI_NIC_STATUS_WRONG_PARAM; + } + + if (!nic_system->driver_loaded(ports[port_index].bdf(), DriverType::IONIC)) { + return SMI_NIC_STATUS_DRIVER_NOT_LOADED; + } + + const auto& port = ports[port_index]; + const auto& stats_map = port.get_standard_stats_map(); + *count = static_cast(stats_map.size()); + + return SMI_NIC_STATUS_SUCCESS; +} + +smi_nic_status_t smi_get_nic_port_statistics_list(smi_nic_ctx_t ctx, uint64_t device, uint32_t port_index, smi_nic_stat_info_t *stats) +{ + if (!ctx || !stats) { + return SMI_NIC_STATUS_WRONG_PARAM; + } + + auto* nic_system = get_nic_system_from_context(ctx); + if (!nic_system) { + return SMI_NIC_STATUS_NOT_INIT; + } + + std::lock_guard lock(ctx->ctx_mutex); + const SmiNic *nic = nic_system->get_nic_by_bdf(device); + if (!nic) { + return SMI_NIC_STATUS_NOT_FOUND; + } + + const auto& ports = nic->nic_ports(); + if (ports.empty()) { + return SMI_NIC_STATUS_DRIVER_NOT_LOADED; + } + + if (port_index >= ports.size()) { + return SMI_NIC_STATUS_NOT_FOUND; + } + + if (!nic_system->driver_loaded(ports[port_index].bdf(), DriverType::IONIC)) { + return SMI_NIC_STATUS_DRIVER_NOT_LOADED; + } + + const auto& port = ports[port_index]; + const auto& stats_map = port.get_standard_stats_map(); + + if (stats_map.empty()) { + return SMI_NIC_STATUS_NO_DATA; + } + + stats->count = static_cast(std::min(stats_map.size(), (size_t)SMI_NIC_MAX_STATISTICS)); + uint32_t i = 0; + for (const auto& stat_pair : stats_map) { + if (i >= stats->count) { + break; + } + std::snprintf(stats->stats[i].name, SMI_NIC_MAX_STRING_LENGTH, "%s", stat_pair.first.c_str()); + stats->stats[i].value = stat_pair.second; + i++; + } + + return SMI_NIC_STATUS_SUCCESS; +} + +smi_nic_status_t smi_get_nic_vendor_statistics_count(smi_nic_ctx_t ctx, uint64_t device, uint32_t port_index, uint32_t *count) +{ + if (!ctx || !count) { + return SMI_NIC_STATUS_WRONG_PARAM; + } + + auto* nic_system = get_nic_system_from_context(ctx); + if (!nic_system) { + return SMI_NIC_STATUS_NOT_INIT; + } + + std::lock_guard lock(ctx->ctx_mutex); + const SmiNic *nic = nic_system->get_nic_by_bdf(device); + if (!nic) { + return SMI_NIC_STATUS_NOT_FOUND; + } + + const auto& ports = nic->nic_ports(); + if (ports.empty()) { + return SMI_NIC_STATUS_DRIVER_NOT_LOADED; + } + + if (port_index >= ports.size()) { + return SMI_NIC_STATUS_NOT_FOUND; + } + + if (!nic_system->driver_loaded(ports[port_index].bdf(), DriverType::IONIC)) { + return SMI_NIC_STATUS_DRIVER_NOT_LOADED; + } + + + const auto& port = ports[port_index]; + const auto& stats_map = port.get_vendor_stats_map(); + *count = static_cast(stats_map.size()); + + return SMI_NIC_STATUS_SUCCESS; +} + +smi_nic_status_t smi_get_nic_vendor_statistics_list(smi_nic_ctx_t ctx, uint64_t device, uint32_t port_index, smi_nic_stat_info_t *stats) +{ + if (!ctx || !stats) { + return SMI_NIC_STATUS_WRONG_PARAM; + } + + auto* nic_system = get_nic_system_from_context(ctx); + if (!nic_system) { + return SMI_NIC_STATUS_NOT_INIT; + } + + std::lock_guard lock(ctx->ctx_mutex); + const SmiNic *nic = nic_system->get_nic_by_bdf(device); + if (!nic) { + return SMI_NIC_STATUS_NOT_FOUND; + } + + const auto& ports = nic->nic_ports(); + if (ports.empty()) { + return SMI_NIC_STATUS_DRIVER_NOT_LOADED; + } + + if (port_index >= ports.size()) { + return SMI_NIC_STATUS_NOT_FOUND; + } + + if (!nic_system->driver_loaded(ports[port_index].bdf(), DriverType::IONIC)) { + return SMI_NIC_STATUS_DRIVER_NOT_LOADED; + } + + const auto& port = ports[port_index]; + const auto& stats_map = port.get_vendor_stats_map(); + + if (stats_map.empty()) { + return SMI_NIC_STATUS_NO_DATA; + } + + stats->count = static_cast(std::min(stats_map.size(), (size_t)SMI_NIC_MAX_STATISTICS)); + uint32_t i = 0; + for (const auto& stat_pair : stats_map) { + if (i >= stats->count) { + break; + } + std::snprintf(stats->stats[i].name, SMI_NIC_MAX_STRING_LENGTH, "%s", stat_pair.first.c_str()); + stats->stats[i].value = stat_pair.second; + i++; + } + + return SMI_NIC_STATUS_SUCCESS; +} + +smi_nic_status_t smi_get_nic_rdma_port_statistics_count(smi_nic_ctx_t ctx, uint64_t device, uint32_t port_index, uint32_t ib_index, uint32_t rdma_port_index, uint32_t *count) +{ + if (!ctx || !count) { + return SMI_NIC_STATUS_WRONG_PARAM; + } + + auto* nic_system = get_nic_system_from_context(ctx); + if (!nic_system) { + return SMI_NIC_STATUS_NOT_INIT; + } + + std::lock_guard lock(ctx->ctx_mutex); + const SmiNic *nic = nic_system->get_nic_by_bdf(device); + if (!nic) { + return SMI_NIC_STATUS_NOT_FOUND; + } + + const auto& ports = nic->nic_ports(); + if (ports.empty()) { + return SMI_NIC_STATUS_DRIVER_NOT_LOADED; + } + + if (port_index >= ports.size()) { + return SMI_NIC_STATUS_NOT_FOUND; + } + + if (!nic_system->driver_loaded(ports[port_index].bdf(), DriverType::IONIC_RDMA)) { + return SMI_NIC_STATUS_DRIVER_NOT_LOADED; + } + + const auto& ibs = ports[port_index].infiniband(); + if (ib_index >= (uint32_t)ibs.size()) { + return SMI_NIC_STATUS_DRIVER_NOT_LOADED; + } + + const auto& ib_ports = ibs[ib_index].ports(); + if (rdma_port_index >= (uint32_t)ib_ports.size()) { + return SMI_NIC_STATUS_WRONG_PARAM; + } + + const auto& ib_port = ib_ports[rdma_port_index]; + const auto& stats_map = ib_port.get_hw_counters_map(); + *count = static_cast(stats_map.size()); + + return SMI_NIC_STATUS_SUCCESS; +} + +smi_nic_status_t smi_get_nic_rdma_port_statistics_list(smi_nic_ctx_t ctx, uint64_t device, uint32_t port_index, uint32_t ib_index, uint32_t rdma_port_index, smi_nic_stat_info_t *stats) +{ + if (!ctx || !stats) { + return SMI_NIC_STATUS_WRONG_PARAM; + } + + auto* nic_system = get_nic_system_from_context(ctx); + if (!nic_system) { + return SMI_NIC_STATUS_NOT_INIT; + } + + std::lock_guard lock(ctx->ctx_mutex); + const SmiNic *nic = nic_system->get_nic_by_bdf(device); + if (!nic) { + return SMI_NIC_STATUS_NOT_FOUND; + } + + const auto& ports = nic->nic_ports(); + if (ports.empty()) { + return SMI_NIC_STATUS_DRIVER_NOT_LOADED; + } + + if (port_index >= ports.size()) { + return SMI_NIC_STATUS_NOT_FOUND; + } + + if (!nic_system->driver_loaded(ports[port_index].bdf(), DriverType::IONIC_RDMA)) { + return SMI_NIC_STATUS_DRIVER_NOT_LOADED; + } + + const auto& ibs = ports[port_index].infiniband(); + if (ib_index >= (uint32_t)ibs.size()) { + return SMI_NIC_STATUS_NOT_FOUND; + } + + const auto& ib_ports = ibs[ib_index].ports(); + if (rdma_port_index >= (uint32_t)ib_ports.size()) { + return SMI_NIC_STATUS_NOT_FOUND; + } + + const auto& ib_port = ib_ports[rdma_port_index]; + const auto& stats_map = ib_port.get_hw_counters_map(); + + if (stats_map.empty()) { + return SMI_NIC_STATUS_NO_DATA; + } + + stats->count = static_cast(std::min(stats_map.size(), (size_t)SMI_NIC_MAX_STATISTICS)); + uint32_t i = 0; + for (const auto& stat_pair : stats_map) { + if (i >= stats->count) { + break; + } + std::snprintf(stats->stats[i].name, SMI_NIC_MAX_STRING_LENGTH, "%s", stat_pair.first.c_str()); + stats->stats[i].value = stat_pair.second; + i++; + } + + return SMI_NIC_STATUS_SUCCESS; +} + +} // extern "C" diff --git a/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/src/smi_nic_subsystem.cpp b/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/src/smi_nic_subsystem.cpp new file mode 100644 index 00000000000..3100356ae37 --- /dev/null +++ b/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/src/smi_nic_subsystem.cpp @@ -0,0 +1,262 @@ +/* + * Copyright (c) 2025 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "smi_nic_subsystem.h" + +#include +#include + +#include + +#include "smi_sysfs.h" + +namespace fs = std::filesystem; + +std::pair SmiNicSubsystem::read_pci_ids(const std::string& sysfs_bus_path) const +{ + uint16_t vendor_id = 0, device_id = 0; + + std::string vendor_path = sysfs_bus_path + "/vendor"; + std::string device_path = sysfs_bus_path + "/device"; + + SmiSysfsReader::SysfsValue vendor_val, device_val; + if (SmiSysfsReader::readLine(vendor_path, vendor_val) == SmiSysfsReader::SysfsStatus::Success && + SmiSysfsReader::readLine(device_path, device_val) == SmiSysfsReader::SysfsStatus::Success) { + + if (std::holds_alternative(vendor_val)) { + vendor_id = static_cast(std::get(vendor_val)); + } else if (std::holds_alternative(vendor_val)) { + vendor_id = static_cast(std::stoul(std::get(vendor_val), nullptr, 0)); + } + + if (std::holds_alternative(device_val)) { + device_id = static_cast(std::get(device_val)); + } else if (std::holds_alternative(device_val)) { + device_id = static_cast(std::stoul(std::get(device_val), nullptr, 0)); + } + } + + return {vendor_id, device_id}; +} + +bool SmiNicSubsystem::resolve_bdf(const std::string& symlink, std::string& bdf) const +{ + char resolved_path[PATH_MAX]; + ssize_t len = readlink(symlink.c_str(), resolved_path, sizeof(resolved_path) - 1); + + if (len == -1) { + return false; + } + resolved_path[len] = '\0'; + + try { + fs::path symlink_dir = fs::path(symlink).parent_path(); + fs::path target_path = symlink_dir / resolved_path; + std::string full_path = fs::canonical(target_path); + bdf = fs::path(full_path).filename(); + return true; + } catch (const fs::filesystem_error&) { + return false; + } +} + +// PENSANDO + +NicVendor SmiNicSubsystemPensando::vendor() const +{ + return NicVendor::AMD; +} + +bool SmiNicSubsystemPensando::driver_loaded(const std::string& bdf, DriverType driver_type) const +{ + std::error_code ec; + std::string driver_dir; + + switch (driver_type) { + case DriverType::IONIC: + driver_dir = "/sys/bus/pci/drivers/ionic"; + break; + case DriverType::IONIC_RDMA: + driver_dir = "/sys/bus/auxiliary/drivers/ionic_rdma.rdma"; + break; + default: + return false; + } + + if (!fs::exists(driver_dir, ec) || !fs::is_directory(driver_dir, ec)) { + return false; + } + + try { + for (const auto& entry : fs::directory_iterator(driver_dir, ec)) { + if (ec) { + continue; + } + + if (!fs::is_symlink(entry, ec)) { + continue; + } + + std::string symlink_target = fs::read_symlink(entry.path(), ec).string(); + if (ec) { + continue; + } + + if (driver_type == DriverType::IONIC) { + if (entry.path().filename().string() == bdf) { + return true; + } + } + else if (driver_type == DriverType::IONIC_RDMA) { + fs::path full_target_path = entry.path().parent_path() / symlink_target; + std::string canonical_target = fs::canonical(full_target_path, ec).string(); + if (ec) { + continue; + } + + if (canonical_target.find("/" + bdf + "/") != std::string::npos) { + return true; + } + } + } + } catch (const fs::filesystem_error&) { + return false; + } + + return false; +} + +void SmiNicSubsystemPensando::discover(const std::string& pci_path, const std::string& net_path) +{ + nics_.clear(); + std::error_code ec; + + for (const auto& entry : fs::directory_iterator(pci_path, ec)) { + if (ec) { + continue; + } + + std::string bdf = entry.path().filename().string(); + std::string sysfs_bus_path = entry.path().string(); + auto [vendor_id, device_id] = read_pci_ids(sysfs_bus_path); + + if (vendor_id == VENDOR_ID && device_id == DEVICE_ID) { + auto nic = std::make_unique("", bdf, NicType::PCIBridge, "", sysfs_bus_path, + NicVendor::AMD, NicProduct::AINIC); + + discover_ports(*nic, bdf, pci_path, net_path); + if (nic->nic_ports_num() > 0) { + nics_.push_back(std::move(nic)); + } + } + } +} + +const std::vector>& SmiNicSubsystemPensando::get_nics() const +{ + return nics_; +} + +void SmiNicSubsystemPensando::discover_ports(SmiNic& nic, const std::string& bridge_bdf, + const std::string& pci_path, const std::string& net_path) +{ + std::error_code ec; + + for (const auto& net_entry : fs::directory_iterator(net_path, ec)) { + if (ec) { + continue; + } + + const std::string iface_name = net_entry.path().filename().string(); + std::string device_symlink = net_entry.path().string() + "/device"; + std::string sysfs_class_path = net_entry.path().string(); + + if (fs::exists(device_symlink, ec) && fs::is_symlink(device_symlink, ec)) { + std::string port_bdf; + if (resolve_bdf(device_symlink, port_bdf)) { + std::string port_sysfs_bus_path = pci_path + "/" + port_bdf; + auto [port_vendor_id, port_device_id] = read_pci_ids(port_sysfs_bus_path); + + if (port_vendor_id == VENDOR_ID && port_device_id == PORT_ID) { + if (downstream_port(port_bdf, bridge_bdf, pci_path)) { + SmiNicPort port(iface_name, port_bdf, sysfs_class_path, port_sysfs_bus_path); + port.discover_infiniband(); + port.collect_vendor_statistics(); + port.collect_standard_statistics(); + nic.add_nic_port(port); + } + } + } + } + } +} + +bool SmiNicSubsystemPensando::downstream_port(const std::string& port_bdf, const std::string& bridge_bdf, + const std::string& pci_path) const +{ + std::error_code ec; + std::string port_path = pci_path + "/" + port_bdf; + + if (!fs::exists(port_path, ec) || !fs::is_symlink(port_path, ec)) { + return false; + } + + try { + std::string port_canon_path = fs::canonical(port_path, ec).string(); + if (ec) { + return false; + } + + std::string bridge = "/" + bridge_bdf + "/"; + return port_canon_path.find(bridge) != std::string::npos; + + } catch (const fs::filesystem_error&) { + return false; + } +} + +// BROADCOM + +NicVendor SmiNicSubsystemBroadcom::vendor() const +{ + return NicVendor::Broadcom; +} + +bool SmiNicSubsystemBroadcom::driver_loaded(const std::string& bdf, DriverType driver_type) const +{ + (void)bdf; + (void)driver_type; + return false; +} + +void SmiNicSubsystemBroadcom::discover(const std::string& pci_path, const std::string& net_path) +{ + (void)pci_path; + (void)net_path; + nics_.clear(); + // TODO: broadcom - discovery +} + +const std::vector>& SmiNicSubsystemBroadcom::get_nics() const +{ + return nics_; +} diff --git a/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/src/smi_nic_system.cpp b/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/src/smi_nic_system.cpp new file mode 100644 index 00000000000..cd9090d6208 --- /dev/null +++ b/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/src/smi_nic_system.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2025 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "smi_nic_system.h" +#include "smi_nic_subsystem.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include "smi_sysfs.h" + +namespace fs = std::filesystem; + +uint64_t parse_bdf(const std::string& bdf) +{ + if (bdf.length() != 12) { + return 0; + } + + if (bdf[4] != ':' || bdf[7] != ':' || bdf[10] != '.') { + return 0; + } + + try { + uint64_t domain = std::stoul(bdf.substr(0, 4), nullptr, 16); + uint64_t bus = std::stoul(bdf.substr(5, 2), nullptr, 16); + uint64_t device = std::stoul(bdf.substr(8, 2), nullptr, 16); + uint64_t function = std::stoul(bdf.substr(11, 1), nullptr, 16); + + return (domain << 16) | (bus << 8) | (device << 3) | function; + } catch (const std::exception&) { + return 0; + } +} + +SmiNicSystem::SmiNicSystem() : net_path_("/sys/class/net"), pci_path_("/sys/bus/pci/devices") +{ + register_subsystem(std::make_unique()); + // TODO: broadcom + // register_subsystem(std::make_unique()); +} + +void SmiNicSystem::register_subsystem(std::unique_ptr subsystem) +{ + subsystems_.push_back(std::move(subsystem)); +} + +bool SmiNicSystem::interface_exists(const std::string& iface) +{ + std::error_code ec; + return fs::exists(fs::path(net_path_) / fs::path(iface).string(), ec); +} + +bool SmiNicSystem::driver_loaded(const std::string& bdf, DriverType driver_type) const +{ + const SmiNic* nic = nullptr; + + for (const auto& entry : nics_) { + if (entry->bdf() == bdf) { + nic = entry; + break; + } + + for (const auto& port : entry->nic_ports()) { + if (port.bdf() == bdf) { + nic = entry; + break; + } + } + + if (nic) { + break; + } + } + + if (!nic) { + return false; + } + + for (const auto& subsystem : subsystems_) { + if (subsystem->vendor() == nic->vendor()) { + return subsystem->driver_loaded(bdf, driver_type); + } + } + + return false; +} + +void SmiNicSystem::discover_nics() +{ + std::error_code ec; + + if (!fs::exists(pci_path_, ec) || !fs::is_directory(pci_path_, ec)) { + return; + } + + nics_.clear(); + for (auto& subsystem : subsystems_) { + subsystem->discover(pci_path_, net_path_); + const auto& subsys_nics = subsystem->get_nics(); + for (const auto& nic : subsys_nics) { + nics_.push_back(nic.get()); + } + } + + // Sort NICs by BDF + std::sort(nics_.begin(), nics_.end(), [](const SmiNic* x, const SmiNic* y) { + return parse_bdf(x->bdf()) < parse_bdf(y->bdf()); + }); + + // Sort ports within each NIC by BDF for consistency + for (auto* nic : nics_) { + auto& ports = const_cast&>(nic->nic_ports()); + std::sort(ports.begin(), ports.end(), [](const SmiNicPort& x, const SmiNicPort& y) { + return parse_bdf(x.bdf()) < parse_bdf(y.bdf()); + }); + } +} + +const std::vector& SmiNicSystem::get_nics() const +{ + return nics_; +} + +std::vector SmiNicSystem::list_bdfs() +{ + std::vector bdfs; + for (const auto* nic : nics_) { + bdfs.push_back(nic->bdf()); + } + return bdfs; +} + +const SmiNic* SmiNicSystem::get_nic_by_interface(const std::string& iface) const +{ + for (const auto* nic : nics_) { + if (nic->interface() == iface) { + return nic; + } + } + + return nullptr; +} + +const SmiNic* SmiNicSystem::get_nic_by_bdf(const std::string& bdf) const +{ + for (const auto* nic : nics_) { + if (nic->bdf() == bdf) { + return nic; + } + } + + return nullptr; +} + +const SmiNic* SmiNicSystem::get_nic_by_bdf(uint64_t bdf) const +{ + uint64_t function_number = bdf & 0x7; + uint64_t device_number = (bdf >> 3) & 0x1F; + uint64_t bus_number = (bdf >> 8) & 0xFF; + uint64_t domain_number = (bdf >> 16) & 0xFFFFFFFF; + std::ostringstream oss; + + oss << std::hex << std::setfill('0') + << std::setw(4) << domain_number << ":" + << std::setw(2) << bus_number << ":" + << std::setw(2) << device_number << "." + << std::setw(1) << function_number; + + return get_nic_by_bdf(oss.str()); +} diff --git a/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/src/smi_sysfs.cpp b/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/src/smi_sysfs.cpp new file mode 100644 index 00000000000..f6fd36f9349 --- /dev/null +++ b/projects/amdsmi/src/nic/ai-nic/amdsmi_unified/src/smi_sysfs.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2025 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "smi_sysfs.h" +#include +#include +#include +#include + +SmiSysfsReader::SysfsStatus SmiSysfsReader::readAll(const std::string& filepath, std::vector& content) +{ + std::ifstream file(filepath); + std::string line; + + if (!file.is_open()) { + return SmiSysfsReader::SysfsStatus::FileNotFound; + } + + if (!SmiSysfsReader::exists(filepath)) { + return SmiSysfsReader::SysfsStatus::IOError; + } + + content.clear(); + while (std::getline(file, line)) { + if (line.find(' ') != std::string::npos) { + content.push_back(line); + continue; + } + std::stringstream ss(line); + std::string token; + while (ss >> token) { + try { + if (token.find("0x") == 0 || token.find("0X") == 0) { + int hex_value = std::stoi(token, nullptr, 16); + content.push_back(hex_value); + } else if (std::all_of(token.begin(), token.end(), ::isdigit)) { + content.push_back(std::stoi(token)); + } else { + content.push_back(token); + } + } catch (const std::invalid_argument&) { + return SmiSysfsReader::SysfsStatus::ParseError; + } catch (const std::out_of_range&) { + return SmiSysfsReader::SysfsStatus::ParseError; + } + } + } + + return SmiSysfsReader::SysfsStatus::Success; +} + +SmiSysfsReader::SysfsStatus SmiSysfsReader::readLine(const std::string& filepath, SmiSysfsReader::SysfsValue& content) +{ + std::ifstream file(filepath); + std::string line; + + if (!file.is_open()) { + return SmiSysfsReader::SysfsStatus::FileNotFound; + } + + if (!SmiSysfsReader::exists(filepath)) { + return SmiSysfsReader::SysfsStatus::IOError; + } + + if (std::getline(file, line)) { + if (line.find(' ') != std::string::npos) { + content = line; + return SmiSysfsReader::SysfsStatus::Success; + } + std::stringstream ss(line); + std::string token; + if (ss >> token) { + try { + if (token.find("0x") == 0 || token.find("0X") == 0) { + int hex_value = std::stoi(token, nullptr, 16); + content = hex_value; + } else if (std::all_of(token.begin(), token.end(), ::isdigit)) { + content = std::stoi(token); + } else { + content = token; + } + return SmiSysfsReader::SysfsStatus::Success; + } catch (...) { + return SmiSysfsReader::SysfsStatus::ParseError; + } + } + } + + return SmiSysfsReader::SysfsStatus::Success; +} + +bool SmiSysfsReader::exists(const std::string& filepath) +{ + std::ifstream file(filepath); + return file.good(); +} diff --git a/projects/amdsmi/src/nic/brcm-nic/amd_smi_lspci_commands.cc b/projects/amdsmi/src/nic/brcm-nic/amd_smi_lspci_commands.cc new file mode 100644 index 00000000000..30390253807 --- /dev/null +++ b/projects/amdsmi/src/nic/brcm-nic/amd_smi_lspci_commands.cc @@ -0,0 +1,193 @@ +/* + * Copyright (c) Broadcom Inc All Rights Reserved. + * + * Developed by: + * Broadcom Inc + * + * www.broadcom.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include +#include + +#include "amd_smi/impl/nic/amd_smi_lspci_commands.h" +#include "amd_smi/impl/amd_smi_utils.h" + +amdsmi_status_t get_lspci_device_data(std::string bdfStr, std::string search_key, std::string& version) { + std::string lspci_data; + std::string command = "lspci -s " + bdfStr + " -vv | grep -i '" + search_key + "'"; + + if (smi_brcm_execute_cmd_get_data(command, &lspci_data) != AMDSMI_STATUS_SUCCESS){ + std::ostringstream ss; + ss << __PRETTY_FUNCTION__ << " | " + << "Failed to execute command: lspci -s " << bdfStr << " -vv | grep -i " << search_key << "."; + LOG_ERROR(ss); + + return AMDSMI_STATUS_NOT_SUPPORTED; + } + + int pos = lspci_data.find(search_key); + if (pos != std::string::npos) { + version = lspci_data.erase(0, lspci_data.find(search_key) + search_key.length()); + if (!version.empty() && version[version.length() - 1] == '\n') { + version.erase(version.length() - 1); + } + } + else + version = "N/A"; + + return AMDSMI_STATUS_SUCCESS; +} + +amdsmi_status_t get_lspci_root_switch(amdsmi_bdf_t devicehBdf, amdsmi_bdf_t *switchBdf) { + + amdsmi_status_t status = AMDSMI_STATUS_SUCCESS; + std::string lspci_data; + + status = smi_brcm_execute_cmd_get_data("lspci -tvv", &lspci_data); + + if (status != AMDSMI_STATUS_SUCCESS) { + std::ostringstream ss; + ss << __PRETTY_FUNCTION__ << " | " + << "Failed to execute command: lspci -tvv."; + LOG_ERROR(ss); + return status; + } + + std::istringstream lines(lspci_data); + + std::string line; + uint64_t bus_pos, dev_pos, fun_pos; + + std::vector switch_list; + amdsmi_bdf_t temp; + + + // Loop through and get the switch list + while (std::getline(lines, line)) { + + if(line.find("LSI PCIe Switch management endpoint") != std::string::npos){ + //get Bus + bus_pos = line.rfind(']----'); + if (bus_pos == std::string::npos){ + // Check if the Bus position is not found, then continue to the next line + continue; + } + + //Get device + dev_pos = line.rfind('.'); + if (dev_pos == std::string::npos){ + // Check if the device position is not found, then continue to the next line + continue; + } + + //Get function + fun_pos = dev_pos + 1; + + std::ostringstream ss; + ss << __PRETTY_FUNCTION__ << " | " + << "Found switch at " << line.substr(bus_pos - 6, 2) << ":" + << line.substr(dev_pos - 2, 2) << ":" + << line.substr(fun_pos - 2, 1); + LOG_DEBUG(ss); + + try + { + // Parse the BDF + temp.bus_number = std::stoi(line.substr(bus_pos - 6, 2), NULL, 16); + temp.device_number = std::stoi(line.substr(dev_pos - 2, 2), NULL, 16); + temp.function_number = std::stoi(line.substr(fun_pos - 2, 1), NULL, 16); + } + catch (const std::invalid_argument& e) { + std::ostringstream ss; + ss << __PRETTY_FUNCTION__ << " | " << "Invalid input: Not a valid hexadecimal string."; + LOG_ERROR(ss); + } + catch (const std::out_of_range& e) { + std::ostringstream ss; + ss << __PRETTY_FUNCTION__ << " | " << "Invalid input: Number out of range."; + LOG_ERROR(ss); + } + + switch_list.push_back(temp); + } + } + + //Reset Stream + lines.clear(); + lines.seekg(0, std::ios::beg); + + + for (const auto& d : switch_list){ + uint64_t switch_bus_start, switch_bus_end = 0x0 ; + std::stringstream ss; + ss << std::hex << std::setw(2) << std::setfill('0') << d.bus_number; + + while (std::getline(lines, line)) { + + if ((line.rfind('-' + ss.str() + ']') != std::string::npos)) { + switch_bus_end = d.bus_number; + + bus_pos = line.rfind('-' + ss.str() + ']'); + //std::cout << line.substr(bus_pos - 2, 2) << std::endl; + + try + { + switch_bus_start = std::stoi(line.substr(bus_pos - 2, 2), NULL, 16); + } + catch (const std::invalid_argument& e) { + std::ostringstream ss; + ss << __PRETTY_FUNCTION__ << " | " << "Invalid input: Not a valid hexadecimal string."; + LOG_ERROR(ss); + } + catch (const std::out_of_range& e) { + std::ostringstream ss; + ss << __PRETTY_FUNCTION__ << " | " << "Invalid input: Number out of range."; + LOG_ERROR(ss); + + } + + std::ostringstream sst; + sst << __PRETTY_FUNCTION__ << " | " << "Switch bus range: " << switch_bus_start << "-" << switch_bus_end; + LOG_DEBUG(sst); + + break; + } + + } + + if (devicehBdf.bus_number >= switch_bus_start && devicehBdf.bus_number <= switch_bus_end){ + switchBdf->bus_number = d.bus_number; + switchBdf->device_number = d.device_number; + switchBdf->function_number = d.function_number; + std::ostringstream ss; + ss << __PRETTY_FUNCTION__ << " | " << "Found switch at BDF " << d.bus_number << ":" << d.device_number << ":" << d.function_number; + LOG_DEBUG(ss); + + break; + } + } + + return status; + +} diff --git a/projects/amdsmi/src/nic/brcm-nic/amd_smi_nic_device.cc b/projects/amdsmi/src/nic/brcm-nic/amd_smi_nic_device.cc new file mode 100644 index 00000000000..b27ec572612 --- /dev/null +++ b/projects/amdsmi/src/nic/brcm-nic/amd_smi_nic_device.cc @@ -0,0 +1,183 @@ +/* + * Copyright (c) Broadcom Inc All Rights Reserved. + * + * Developed by: + * Broadcom Inc + * + * www.broadcom.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include "amd_smi/impl/nic/amd_smi_nic_device.h" +#include "rocm_smi/rocm_smi_utils.h" + +namespace amd::smi { + +uint32_t AMDSmiNICDevice::get_nic_id() const { + return nic_id_; +} + +std::string& AMDSmiNICDevice::get_nic_path() { + return path_; +} + +amdsmi_bdf_t AMDSmiNICDevice::get_bdf() { + return bdf_; +} + +amdsmi_status_t AMDSmiNICDevice::get_no_drm_data() { + amdsmi_status_t ret; + std::string path; + amdsmi_bdf_t bdf; + + ret = nodrm_.get_device_path_by_index(nic_id_, &path); + if (ret != AMDSMI_STATUS_SUCCESS) { + std::ostringstream ss; + ss << __PRETTY_FUNCTION__ << " | " + << "Failed to get device path for NIC #" << nic_id_ << "."; + LOG_DEBUG(ss); + return AMDSMI_STATUS_NOT_SUPPORTED; + } + ret = nodrm_.get_bdf_by_index(nic_id_, &bdf); + if (ret != AMDSMI_STATUS_SUCCESS) { + std::ostringstream ss; + ss << __PRETTY_FUNCTION__ << " | " + << "Failed to get BDF for NIC #" << nic_id_ << "."; + LOG_DEBUG(ss); + return AMDSMI_STATUS_NOT_SUPPORTED; + } + path_ = path; + + return AMDSMI_STATUS_SUCCESS; +} + +pthread_mutex_t* AMDSmiNICDevice::get_mutex() { + return amd::smi::GetMutex(nic_id_); +} + +amdsmi_status_t AMDSmiNICDevice::amd_query_nic_info(amdsmi_brcm_nic_info_t& info) const { + return nodrm_.amd_query_nic_info(nic_id_, info); +} + +amdsmi_status_t AMDSmiNICDevice::amd_query_nic_temp_info(amdsmi_brcm_nic_temperature_metric_t& info) const { + amdsmi_status_t ret; + std::string hwmonPath; + ret = nodrm_.get_hwmon_path_by_index(nic_id_, &hwmonPath); + if (ret != AMDSMI_STATUS_SUCCESS) { + std::ostringstream ss; + ss << __PRETTY_FUNCTION__ << " | " + << "Failed to get hwmon path for NIC #" << nic_id_ << "."; + LOG_DEBUG(ss); + return AMDSMI_STATUS_NOT_SUPPORTED; + } + + return nodrm_.amd_query_nic_temp(hwmonPath, info); +} + +amdsmi_status_t AMDSmiNICDevice::amd_query_nic_power_info(amdsmi_brcm_nic_hwmon_power_t& info) const { + amdsmi_status_t ret; + std::string hwmonPath; + ret = nodrm_.get_hwmon_path_by_index(nic_id_, &hwmonPath); + if (ret != AMDSMI_STATUS_SUCCESS) { + std::ostringstream ss; + ss << __PRETTY_FUNCTION__ << " | " + << "Failed to get hwmon path for NIC #" << nic_id_ << "."; + LOG_DEBUG(ss); + return AMDSMI_STATUS_NOT_SUPPORTED; + } + + return nodrm_.amd_query_nic_power(hwmonPath, info); +} + +amdsmi_status_t AMDSmiNICDevice::amd_query_nic_device_info(amdsmi_brcm_nic_hwmon_device_t& info) const { + amdsmi_status_t ret; + std::string hwmonPath; + ret = nodrm_.get_hwmon_path_by_index(nic_id_, &hwmonPath); + if (ret != AMDSMI_STATUS_SUCCESS) { + std::ostringstream ss; + ss << __PRETTY_FUNCTION__ << " | " + << "Failed to get hwmon path for NIC #" << nic_id_ << "."; + LOG_DEBUG(ss); + return AMDSMI_STATUS_NOT_SUPPORTED; + } + return nodrm_.amd_query_nic_device(hwmonPath, info); +} + +amdsmi_status_t AMDSmiNICDevice::amd_query_nic_uuid(std::string& version) const { + amdsmi_status_t ret; + std::string devicePath; + ret = nodrm_.get_device_path_by_index(nic_id_, &devicePath); + if (ret != AMDSMI_STATUS_SUCCESS) { + std::ostringstream ss; + ss << __PRETTY_FUNCTION__ << " | " + << "Failed to get device path for NIC #" << nic_id_ << "."; + LOG_DEBUG(ss); + return AMDSMI_STATUS_NOT_SUPPORTED; + } + + return nodrm_.amd_query_nic_uuid(devicePath, version); +} + +amdsmi_status_t AMDSmiNICDevice::amd_query_nic_numa_affinity(int32_t *numa_node) const { + amdsmi_status_t ret; + std::string devicePath; + ret = nodrm_.get_device_path_by_index(nic_id_, &devicePath); + if (ret != AMDSMI_STATUS_SUCCESS) { + std::ostringstream ss; + ss << __PRETTY_FUNCTION__ << " | " + << "Failed to get device path for NIC #" << nic_id_ << "."; + LOG_DEBUG(ss); + return AMDSMI_STATUS_NOT_SUPPORTED; + } + + return nodrm_.amd_query_nic_numa_affinity(devicePath, numa_node); +} + +amdsmi_status_t AMDSmiNICDevice::amd_query_nic_cpu_affinity(std::string& cpu_affinity) const { + char bdf_str[20]; + snprintf(bdf_str, sizeof(bdf_str)-1, "%04lx:%02x", bdf_.domain_number, bdf_.bus_number); + std::stringstream domain_bus_sstream; + domain_bus_sstream << "/sys/class/pci_bus/" << std::string(bdf_str); + + return nodrm_.amd_query_nic_cpu_affinity(domain_bus_sstream.str(), cpu_affinity); +} + +amdsmi_status_t AMDSmiNICDevice::amd_query_nic_firmware_info(amdsmi_brcm_nic_firmware_t& info) const { + amdsmi_status_t ret; + amdsmi_bdf_t bdf = {}; + ret = nodrm_.get_bdf_by_index(nic_id_, &bdf); + + if (ret != AMDSMI_STATUS_SUCCESS) { + std::ostringstream ss; + ss << __PRETTY_FUNCTION__ << " | " + << "Failed to get BDF for NIC #" << nic_id_ << "."; + LOG_DEBUG(ss); + return AMDSMI_STATUS_NOT_SUPPORTED; + } + char bdf_str[20]; + snprintf(bdf_str, sizeof(bdf_str)-1, "%04lx:%02x:%02x.%d", bdf.domain_number, bdf.bus_number, bdf.device_number, + bdf.function_number); + + return nodrm_.amd_query_nic_fw_info(std::string(bdf_str), info); +} + +} // namespace amd::smi + diff --git a/projects/amdsmi/src/nic/brcm-nic/amd_smi_no_drm_nic.cc b/projects/amdsmi/src/nic/brcm-nic/amd_smi_no_drm_nic.cc new file mode 100644 index 00000000000..b7f7360b95c --- /dev/null +++ b/projects/amdsmi/src/nic/brcm-nic/amd_smi_no_drm_nic.cc @@ -0,0 +1,478 @@ +/* + * Copyright (c) Broadcom Inc All Rights Reserved. + * + * Developed by: + * Broadcom Inc + * + * www.broadcom.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "amd_smi/impl/nic/amd_smi_no_drm_nic.h" +#include "amd_smi/impl/nic/amd_smi_lspci_commands.h" +#include "amd_smi/impl/amd_smi_common.h" +#include "amd_smi/impl/amd_smi_utils.h" +#include "rocm_smi/rocm_smi.h" +#include "rocm_smi/rocm_smi_main.h" + +namespace amd::smi { + +amdsmi_status_t AMDSmiNoDrmNIC::init() { + amd::smi::RocmSMI& smi = amd::smi::RocmSMI::getInstance(); + auto devices = smi.nic_devices(); + + bool has_valid_hw_mon = false; + for (uint32_t i=0; i < devices.size(); i++) { + auto rocm_smi_device = devices[i]; + uint64_t bdfid = rocm_smi_device->bdfid(); + amdsmi_bdf_t bdf = {}; + bdf.function_number = bdfid & 0x7; + bdf.device_number = (bdfid >> 3) & 0x1f; + bdf.bus_number = (bdfid >> 8) & 0xff; + bdf.domain_number = (bdfid >> 32) & 0xffffffff; + no_drm_bdfs_.push_back(bdf); + + // get interface name from the path + std::string interface_name = rocm_smi_device->path(); + interface_name = interface_name.substr(interface_name.find_last_of('/') + 1); + interfaces_.push_back(interface_name); + + const std::string nic_dev_folder = rocm_smi_device->path() + "/device"; + device_paths_.push_back(nic_dev_folder); + auto nic_dev_dir = opendir(std::string((nic_dev_folder + "/hwmon")).c_str()); + + if (nic_dev_dir != nullptr) { + auto dentry = readdir(nic_dev_dir); + while (dentry != nullptr) { + if (memcmp(dentry->d_name, "hwmon", strlen("hwmon")) == 0) { + if ((strcmp(dentry->d_name, ".") == 0) || (strcmp(dentry->d_name, "..") == 0)) continue; + const std::string nic_hw_folder = nic_dev_folder + "/hwmon/" + std::string(dentry->d_name); + hwmon_paths_.push_back(nic_hw_folder); + has_valid_hw_mon = true; + break; + } + dentry = readdir(nic_dev_dir); + } + closedir(nic_dev_dir); + } + + // cannot find any valid fds. + if (!has_valid_hw_mon) { + hwmon_paths_.push_back(""); + } + } + + return AMDSMI_STATUS_SUCCESS; +} + +amdsmi_status_t AMDSmiNoDrmNIC::cleanup() { + // Clear the vectors that hold the information about the NICs + device_paths_.clear(); + hwmon_paths_.clear(); + no_drm_bdfs_.clear(); + interfaces_.clear(); + return AMDSMI_STATUS_SUCCESS; +} + +amdsmi_status_t AMDSmiNoDrmNIC::amd_query_nic_info(uint32_t nic_index, amdsmi_brcm_nic_info_t& info) { + // Retrieve information about a specific NIC. + // + // Parameters: + // - nic_index: The index of the NIC in the list of available NICs. + // - info: A reference to an object of type amdsmi_brcm_nic_info_t, + // which will contain information about the NIC. + + amdsmi_status_t ret = AMDSMI_STATUS_SUCCESS; + get_bdf_by_index(nic_index, &info.nic_bdf); + std::string strInterfaceName; + get_interface_name_by_index(nic_index, &strInterfaceName); + snprintf(info.nic_device_name, sizeof(info.nic_device_name)-1, "%s", strInterfaceName.c_str()); + + char bdf_str[20]; + snprintf(bdf_str, sizeof(bdf_str)-1, "%04lx:%02x:%02x.%d", info.nic_bdf.domain_number, info.nic_bdf.bus_number, info.nic_bdf.device_number, + info.nic_bdf.function_number); + + std::string part_number, fw_version; + try { + get_lspci_device_data(std::string(bdf_str), "PN] Part number: ", part_number); + get_lspci_device_data(std::string(bdf_str), "V3] Vendor specific: ", fw_version); + + snprintf(info.nic_part_number, sizeof(info.nic_part_number)-1, "%s", part_number.c_str()); + snprintf(info.nic_firmware_version, sizeof(info.nic_firmware_version)-1, "%s", fw_version.c_str()); + + } catch (const std::invalid_argument &e) { + std::cerr << "AMDSmiNoDrmNIC::amd_query_nic_info - Error: Invalid argument exception caught in std::stoi.\n" + << "Exception message: " << e.what() << std::endl; + } catch (const std::out_of_range &e) { + std::cerr << "AMDSmiNoDrmNIC::amd_query_nic_info - Error: Out of range exception caught in std::stoi.\n" + << "Exception message: " << e.what() << std::endl; + } catch (const std::exception &e) { + std::cerr << "AMDSmiNoDrmNIC::amd_query_nic_info - An error occurred: " << e.what() + << std::endl; + } + + std::string devicePath; + get_device_path_by_index(nic_index, &devicePath); + std::string netPath = devicePath + "/net"; + auto net_node_dir = opendir(netPath.c_str()); + if (net_node_dir != nullptr) { + auto dentry = readdir(net_node_dir); + std::string macPath; + while ((dentry = readdir(net_node_dir)) != nullptr) { + if ((strcmp(dentry->d_name, ".") == 0) || (strcmp(dentry->d_name, "..") == 0)) { + continue; + } + macPath = netPath + "/" + dentry->d_name; + std::string macAddress = "address"; + std::string strUUID = smi_brcm_get_value_string(macPath, macAddress); + snprintf(info.nic_uuid, sizeof(info.nic_uuid)-1, "%s", strUUID.c_str()); + } + closedir(net_node_dir); + } + + return AMDSMI_STATUS_SUCCESS; +} + +amdsmi_status_t AMDSmiNoDrmNIC::amd_query_nic_temp(std::string hwmonPath, + amdsmi_brcm_nic_temperature_metric_t &info) { + // Get nic temperature info + std::string crit_alarm = "temp1_crit_alarm"; + std::string emergency_alarm = "temp1_emergency_alarm"; + std::string shutdown_alarm = "temp1_shutdown_alarm"; + std::string max_alarm = "temp1_max_alarm"; + + std::string nic_crit = "temp1_crit"; + std::string nic_emergency = "temp1_emergency"; + std::string nic_input = "temp1_input"; + std::string nic_max = "temp1_max"; + std::string nic_shutdown = "temp1_shutdown"; + + try { + info.nic_temp_crit_alarm = smi_brcm_get_value_u32(hwmonPath, crit_alarm); + info.nic_temp_emergency_alarm = smi_brcm_get_value_u32(hwmonPath, emergency_alarm); + info.nic_temp_shutdown_alarm = smi_brcm_get_value_u32(hwmonPath, shutdown_alarm); + info.nic_temp_max_alarm = smi_brcm_get_value_u32(hwmonPath, max_alarm); + + info.nic_temp_crit = smi_brcm_get_value_u32(hwmonPath, nic_crit); + info.nic_temp_emergency = smi_brcm_get_value_u32(hwmonPath, nic_emergency); + info.nic_temp_input = smi_brcm_get_value_u32(hwmonPath, nic_input); + info.nic_temp_max = smi_brcm_get_value_u32(hwmonPath, nic_max); + info.nic_temp_shutdown = smi_brcm_get_value_u32(hwmonPath, nic_shutdown); + } catch (const std::exception& e) { + std::cerr << "AMDSmiNoDrmNIC::amd_query_nic_temp - An error occurred: " << e.what() + << std::endl; + } + + return AMDSMI_STATUS_SUCCESS; +} + +amdsmi_status_t AMDSmiNoDrmNIC::amd_query_nic_power(std::string hwmonPath, amdsmi_brcm_nic_hwmon_power_t &info) { + // Get power metrics for a NIC + try { + hwmonPath = hwmonPath+"/power"; + std::string async = "async"; + std::string control = "control"; + std::string runtime_active_kids = "runtime_active_kids"; + std::string runtime_active_time = "runtime_active_time"; + std::string runtime_enabled = "runtime_enabled"; + std::string runtime_status = "runtime_status"; + std::string runtime_suspended_time = "runtime_suspended_time"; + std::string runtime_usage = "runtime_usage"; + + snprintf(info.nic_power_async, sizeof(info.nic_power_async)-1, "%s", smi_brcm_get_value_string(hwmonPath, async).c_str()); + snprintf(info.nic_power_control, sizeof(info.nic_power_control)-1, "%s", smi_brcm_get_value_string(hwmonPath, control).c_str()); + info.nic_power_runtime_active_time = smi_brcm_get_value_u32(hwmonPath, runtime_active_time); + snprintf(info.nic_power_runtime_status, sizeof(info.nic_power_runtime_status)-1, "%s", smi_brcm_get_value_string(hwmonPath, runtime_status).c_str()); + info.nic_power_runtime_usage = smi_brcm_get_value_u32(hwmonPath, runtime_usage); + info.nic_power_runtime_active_kids = smi_brcm_get_value_u32(hwmonPath, runtime_active_kids); + snprintf(info.nic_power_runtime_enabled, sizeof(info.nic_power_runtime_enabled)-1, "%s", smi_brcm_get_value_string(hwmonPath, runtime_enabled).c_str()); + info.nic_power_runtime_suspended_time = smi_brcm_get_value_u32(hwmonPath, runtime_suspended_time); + + } catch (const std::invalid_argument& e) { + printf("AMDSmiNoDrmNIC::amd_query_nic_power - Invalid argument: %s\n", e.what()); + } catch (const std::out_of_range& e) { + printf("Out of range error: %s\n", e.what()); + } catch (...) { + printf("AMDSmiNoDrmNIC::amd_query_nic_power - Error: Exception caught during NIC power query.\n"); + } + return AMDSMI_STATUS_SUCCESS; +} + +amdsmi_status_t AMDSmiNoDrmNIC::amd_query_nic_device(std::string hwmonPath, amdsmi_brcm_nic_hwmon_device_t &info) { + + try { + hwmonPath = hwmonPath+"/device"; + std::string aer_dev_correctable = "aer_dev_correctable"; + std::string aer_dev_fatal = "aer_dev_fatal"; + std::string aer_dev_nonfatal = "aer_dev_nonfatal"; + std::string ari_enabled = "ari_enabled"; + std::string broken_parity_status = "broken_parity_status"; + std::string device_class = "class"; + std::string config = "config"; + std::string consistent_dma_mask_bit = "consistent_dma_mask_bit"; + std::string current_link_speed = "current_link_speed"; + std::string current_link_width = "current_link_width"; + std::string d3cold_allowed = "d3cold_allowed"; + std::string device = "device"; + std::string dma_mask_bits = "dma_mask_bits"; + std::string driver_override = "driver_override"; + std::string enable = "enable"; + std::string irq = "irq"; + std::string local_cpulist = "local_cpulist"; + std::string local_cpus = "local_cpus"; + std::string max_link_speed = "max_link_speed"; + std::string max_link_width = "max_link_width"; + std::string modalias = "modalias"; + std::string msi_bus = "msi_bus"; + std::string numa_node = "numa_node"; + std::string pools = "pools"; + std::string power_state = "power_state"; + std::string reset_method = "reset_method"; + std::string resource = "resource"; + std::string revision = "revision"; + std::string sriov_drivers_autoprobe = "sriov_drivers_autoprobe"; + std::string sriov_numvfs = "sriov_numvfs"; + std::string sriov_offset = "sriov_offset"; + std::string sriov_stride = "sriov_stride"; + std::string sriov_totalvfs = "sriov_totalvfs"; + std::string sriov_vf_device = "sriov_vf_device"; + std::string sriov_vf_total_msix = "sriov_vf_total_msix"; + std::string subsystem_device = "subsystem_device"; + std::string subsystem_vendor = "subsystem_vendor"; + std::string uevent = "uevent"; + std::string vendor = "vendor"; + std::string vpd = "vpd"; + + snprintf(info.nic_device_aer_dev_correctable, sizeof(info.nic_device_aer_dev_correctable)-1, "%s", smi_brcm_get_value_string(hwmonPath, aer_dev_correctable).c_str()); + snprintf(info.nic_device_aer_dev_fatal, sizeof(info.nic_device_aer_dev_fatal)-1, "%s", smi_brcm_get_value_string(hwmonPath, aer_dev_fatal).c_str()); + snprintf(info.nic_device_aer_dev_nonfatal, sizeof(info.nic_device_aer_dev_nonfatal)-1, "%s", smi_brcm_get_value_string(hwmonPath, aer_dev_nonfatal).c_str()); + info.nic_device_ari_enabled = smi_brcm_get_value_u32(hwmonPath, ari_enabled); + info.nic_device_broken_parity_status = smi_brcm_get_value_u32(hwmonPath, broken_parity_status); + snprintf(info.nic_device_class, sizeof(info.nic_device_class)-1, "%s", smi_brcm_get_value_string(hwmonPath, device_class).c_str()); + snprintf(info.nic_device_config, sizeof(info.nic_device_config)-1, "%s", smi_brcm_get_value_string(hwmonPath, config).c_str()); + info.nic_device_consistent_dma_mask_bits = smi_brcm_get_value_u32(hwmonPath, consistent_dma_mask_bit); + snprintf(info.nic_device_current_link_speed, sizeof(info.nic_device_current_link_speed)-1, "%s", smi_brcm_get_value_string(hwmonPath, current_link_speed).c_str()); + info.nic_device_current_link_width = smi_brcm_get_value_u32(hwmonPath, current_link_width); + info.nic_device_d3cold_allowed = smi_brcm_get_value_u32(hwmonPath, d3cold_allowed); + snprintf(info.nic_device_device, sizeof(info.nic_device_device)-1, "%s", smi_brcm_get_value_string(hwmonPath, device).c_str()); + info.nic_device_dma_mask_bits = smi_brcm_get_value_u32(hwmonPath, dma_mask_bits); + snprintf(info.nic_device_driver_override, sizeof(info.nic_device_driver_override)-1, "%s", smi_brcm_get_value_string(hwmonPath, driver_override).c_str()); + info.nic_device_enable = smi_brcm_get_value_u32(hwmonPath, enable); + info.nic_device_irq = smi_brcm_get_value_u32(hwmonPath, irq); + snprintf(info.nic_device_local_cpulist, sizeof(info.nic_device_local_cpulist)-1, "%s", smi_brcm_get_value_string(hwmonPath, local_cpulist).c_str()); + snprintf(info.nic_device_local_cpus, sizeof(info.nic_device_local_cpus)-1, "%s", smi_brcm_get_value_string(hwmonPath, local_cpus).c_str()); + snprintf(info.nic_device_max_link_speed, sizeof(info.nic_device_max_link_speed)-1, "%s", smi_brcm_get_value_string(hwmonPath, max_link_speed).c_str()); + info.nic_device_max_link_width = smi_brcm_get_value_u32(hwmonPath, max_link_width); + snprintf(info.nic_device_modalias, sizeof(info.nic_device_modalias)-1, "%s", smi_brcm_get_value_string(hwmonPath, modalias).c_str()); + info.nic_device_msi_bus = smi_brcm_get_value_u32(hwmonPath, msi_bus); + info.nic_device_numa_node = smi_brcm_get_value_u32(hwmonPath, numa_node); + snprintf(info.nic_device_pools, sizeof(info.nic_device_pools)-1, "%s", smi_brcm_get_value_string(hwmonPath, pools).c_str()); + snprintf(info.nic_device_power_state, sizeof(info.nic_device_power_state)-1, "%s", smi_brcm_get_value_string(hwmonPath, power_state).c_str()); + snprintf(info.nic_device_reset_method, sizeof(info.nic_device_reset_method)-1, "%s", smi_brcm_get_value_string(hwmonPath, reset_method).c_str()); + snprintf(info.nic_device_resource, sizeof(info.nic_device_resource)-1, "%s", smi_brcm_get_value_string(hwmonPath, resource).c_str()); + snprintf(info.nic_device_revision, sizeof(info.nic_device_revision)-1, "%s", smi_brcm_get_value_string(hwmonPath, revision).c_str()); + info.nic_device_sriov_drivers_autoprobe = smi_brcm_get_value_u32(hwmonPath, sriov_drivers_autoprobe); + info.nic_device_sriov_numvfs = smi_brcm_get_value_u32(hwmonPath, sriov_numvfs); + info.nic_device_sriov_offset = smi_brcm_get_value_u32(hwmonPath, sriov_offset); + info.nic_device_sriov_stride = smi_brcm_get_value_u32(hwmonPath, sriov_stride); + info.nic_device_sriov_totalvfs = smi_brcm_get_value_u32(hwmonPath, sriov_totalvfs); + info.nic_device_sriov_vf_device = smi_brcm_get_value_u32(hwmonPath, sriov_vf_device); + info.nic_device_sriov_vf_total_msix = smi_brcm_get_value_u32(hwmonPath, sriov_vf_total_msix); + snprintf(info.nic_device_subsystem_device, sizeof(info.nic_device_subsystem_device-1), "%s", smi_brcm_get_value_string(hwmonPath, subsystem_device).c_str()); + snprintf(info.nic_device_subsystem_vendor, sizeof(info.nic_device_subsystem_vendor-1), "%s", smi_brcm_get_value_string(hwmonPath, subsystem_vendor).c_str()); + snprintf(info.nic_device_uevent, sizeof(info.nic_device_uevent-1), "%s", smi_brcm_get_value_string(hwmonPath, uevent).c_str()); + snprintf(info.nic_device_vendor, sizeof(info.nic_device_vendor-1), "%s", smi_brcm_get_value_string(hwmonPath, vendor).c_str()); + snprintf(info.nic_device_vpd, sizeof(info.nic_device_vpd-1), "%s", smi_brcm_get_value_string(hwmonPath, vpd).c_str()); + + } catch (const std::invalid_argument& e) { + std::cerr << "AMDSmiNoDrmNIC::amd_query_nic_device - Error: Invalid argument exception caught in std::stoi.\n" + << "Exception message: " << e.what() << std::endl; + } catch (const std::out_of_range& e) { + std::cerr << "AMDSmiNoDrmNIC::amd_query_nic_device - Error: Out of range exception caught in std::stoi.\n" + << "Exception message: " << e.what() << std::endl; + } catch (const std::exception& e) { + std::cerr << "AMDSmiNoDrmNIC::amd_query_nic_device - An error occurred: " << e.what() << std::endl; + } + return AMDSMI_STATUS_SUCCESS; +} + +amdsmi_status_t AMDSmiNoDrmNIC::amd_query_nic_fw_info(std::string bdfStr, + amdsmi_brcm_nic_firmware_t &info) { + // Retrieve firmware version information from the NIC. + // + // Args: + // bdfStr (std::string): Bus-Device-Function value of the NIC. + // info (amdsmi_brcm_nic_firmware_t): Structure to hold the firmware + // version information. + std::string fw_pkg_version, fw_efi_version, fw_version, fw_ncsi_version, fw_roce_version; + try { + get_lspci_device_data(bdfStr, "V0] Vendor specific: ", fw_pkg_version); + get_lspci_device_data(bdfStr, "V1] Vendor specific: ", fw_efi_version); + get_lspci_device_data(bdfStr, "V3] Vendor specific: ", fw_version); + get_lspci_device_data(bdfStr, "V8] Vendor specific: ", fw_ncsi_version); + get_lspci_device_data(bdfStr, "VA] Vendor specific: ", fw_roce_version); + + snprintf(info.nic_fw_pkg_version, sizeof(info.nic_fw_pkg_version)-1, "%s", fw_pkg_version.c_str()); + snprintf(info.nic_fw_efi_version, sizeof(info.nic_fw_efi_version)-1, "%s", fw_efi_version.c_str()); + snprintf(info.nic_fw_version, sizeof(info.nic_fw_version)-1, "%s", fw_version.c_str()); + snprintf(info.nic_fw_ncsi_version, sizeof(info.nic_fw_ncsi_version)-1, "%s", fw_ncsi_version.c_str()); + snprintf(info.nic_fw_roce_version, sizeof(info.nic_fw_roce_version)-1, "%s", fw_roce_version.c_str()); + + } catch (const std::invalid_argument &e) { + std::cerr << "AMDSmiNoDrmNIC::amd_query_nic_fw_info - Error: Invalid argument exception caught in std::stoi.\n" + << "Exception message: " << e.what() << std::endl; + } catch (const std::out_of_range &e) { + std::cerr << "AMDSmiNoDrmNIC::amd_query_nic_fw_info - Error: Out of range exception caught in std::stoi.\n" + << "Exception message: " << e.what() << std::endl; + } catch (const std::exception &e) { + std::cerr << "AMDSmiNoDrmNIC::amd_query_nic_fw_info - An error occurred: " << e.what() + << std::endl; + } + return AMDSMI_STATUS_SUCCESS; +} + +amdsmi_status_t AMDSmiNoDrmNIC::get_interface_name_by_index(uint32_t nic_index, std::string* interface_name) const { + // Retrieve the interface name for the given NIC index + if (nic_index + 1 > interfaces_.size()) { + std::ostringstream ss; + ss << __PRETTY_FUNCTION__ << " | " + << "Failed to get interface name for NIC #" << nic_index << ". Error " << AMDSMI_STATUS_NOT_SUPPORTED << "."; + LOG_DEBUG(ss); + return AMDSMI_STATUS_NOT_SUPPORTED; + } + + *interface_name = interfaces_[nic_index]; + return AMDSMI_STATUS_SUCCESS; +} + +amdsmi_status_t AMDSmiNoDrmNIC::get_bdf_by_index(uint32_t nic_index, amdsmi_bdf_t *bdf_info) const { + // Retrieve the BDF for the given NIC index + if (nic_index + 1 > no_drm_bdfs_.size()) { + std::ostringstream ss; + ss << __PRETTY_FUNCTION__ << " | " + << "Failed to get BDF for NIC #" << nic_index << ". Error " << AMDSMI_STATUS_NOT_SUPPORTED << "."; + LOG_DEBUG(ss); + return AMDSMI_STATUS_NOT_SUPPORTED; + } + *bdf_info = no_drm_bdfs_[nic_index]; + return AMDSMI_STATUS_SUCCESS; +} +amdsmi_status_t AMDSmiNoDrmNIC::get_device_path_by_index(uint32_t nic_index, std::string *device_path) const { + // Retrieve the device path for the given NIC index + if (nic_index + 1 > device_paths_.size()) { + std::ostringstream ss; + ss << __PRETTY_FUNCTION__ << " | " + << "Failed to get device path for NIC #" << nic_index << ". Error " << AMDSMI_STATUS_NOT_SUPPORTED << "."; + LOG_DEBUG(ss); + return AMDSMI_STATUS_NOT_SUPPORTED; + } + *device_path = device_paths_[nic_index]; + return AMDSMI_STATUS_SUCCESS; +} + +amdsmi_status_t AMDSmiNoDrmNIC::get_hwmon_path_by_index(uint32_t nic_index, std::string *hwm_path) const { + // Retrieve the hwmon path for the given NIC index + if (nic_index + 1 > hwmon_paths_.size()) { + std::ostringstream ss; + ss << __PRETTY_FUNCTION__ << " | " + << "Failed to get hwmon path for NIC #" << nic_index << ". Error " << AMDSMI_STATUS_NOT_SUPPORTED << "."; + LOG_DEBUG(ss); + return AMDSMI_STATUS_NOT_SUPPORTED; + } + *hwm_path = hwmon_paths_[nic_index]; + return AMDSMI_STATUS_SUCCESS; +} + +std::vector& AMDSmiNoDrmNIC::get_device_paths() { + // Return reference to vector of device paths. + return device_paths_; +} +std::vector& AMDSmiNoDrmNIC::get_hwmon_paths() { + // Return reference to vector of hwmon paths. + return hwmon_paths_; +} + +bool AMDSmiNoDrmNIC::check_if_no_drm_is_supported() { + // Return true if no-drm NIC is supported. + return true; +} + +std::vector AMDSmiNoDrmNIC::get_bdfs() { + // Return reference to vector of BDFs. + return no_drm_bdfs_; +} + + +amdsmi_status_t AMDSmiNoDrmNIC::amd_query_nic_uuid(std::string devicePath, std::string &version) { + // Get NIC MAC address + std::string netPath = devicePath + "/net"; + auto net_node_dir = opendir(netPath.c_str()); + if (net_node_dir == nullptr) { + std::ostringstream ss; + ss << __PRETTY_FUNCTION__ << " | " + << "Failed to open net node directory: " << netPath << ". Error " << AMDSMI_STATUS_FILE_ERROR << "."; + LOG_DEBUG(ss); + + return AMDSMI_STATUS_FILE_ERROR; + } + auto dentry = readdir(net_node_dir); + std::string macPath; + while ((dentry = readdir(net_node_dir)) != nullptr) { + // Skip "." and ".." directories + if ((strcmp(dentry->d_name, ".") == 0) || (strcmp(dentry->d_name, "..") == 0)) { + continue; + } + macPath = netPath + "/" + dentry->d_name; + std::string macAddress = "address"; + version = smi_brcm_get_value_string(macPath, macAddress); + } + closedir(net_node_dir); + + return AMDSMI_STATUS_SUCCESS; +} + +amdsmi_status_t AMDSmiNoDrmNIC::amd_query_nic_numa_affinity(std::string devicePath, int32_t *numa_node) { + // Get NIC NUMA affinity + std::string numaFile = "numa_node"; + uint32_t numa = smi_brcm_get_value_u32(devicePath, numaFile); + *numa_node = numa; + return AMDSMI_STATUS_SUCCESS; +} + +amdsmi_status_t AMDSmiNoDrmNIC::amd_query_nic_cpu_affinity(std::string devicePath, std::string &cpu_affinity) { + // Get NIC CPU affinity + std::string cpuAffFile = "cpulistaffinity"; + cpu_affinity = smi_brcm_get_value_string(devicePath, cpuAffFile); + + return AMDSMI_STATUS_SUCCESS; +} + +} // namespace amd::smi diff --git a/projects/amdsmi/src/nic/brcm-nic/amd_smi_no_drm_switch.cc b/projects/amdsmi/src/nic/brcm-nic/amd_smi_no_drm_switch.cc new file mode 100644 index 00000000000..cba6db56878 --- /dev/null +++ b/projects/amdsmi/src/nic/brcm-nic/amd_smi_no_drm_switch.cc @@ -0,0 +1,327 @@ +/* + * Copyright (c) Broadcom Inc All Rights Reserved. + * + * Developed by: + * Broadcom Inc + * + * www.broadcom.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "amd_smi/impl/nic/amd_smi_no_drm_switch.h" +#include "amd_smi/impl/nic/amd_smi_lspci_commands.h" +#include "amd_smi/impl/amd_smi_common.h" +#include "amd_smi/impl/amd_smi_utils.h" +#include "rocm_smi/rocm_smi.h" +#include "rocm_smi/rocm_smi_main.h" +#include "rocm_smi/rocm_smi_utils.h" + +static const char *kPathPciDevices = "/sys/bus/pci/devices/"; + +namespace amd::smi { + +amdsmi_status_t AMDSmiNoDrmSwitch::init() { + + amd::smi::RocmSMI& smi = amd::smi::RocmSMI::getInstance(); + auto devices = smi.switch_devices(); + + bool has_valid_fds = false; + for (uint32_t i=0; i < devices.size(); i++) { + auto rocm_smi_device = devices[i]; + const std::string switch_host_folder = "/sys/class/scsi_host/host" + std::to_string(rocm_smi_device->index()); + std::string switch_dev_folder = switch_host_folder; + std::vector buf(512); + ssize_t len; + + do { + buf.resize(buf.size() + 128); + len = ::readlink(switch_dev_folder.c_str(), &(buf[0]), buf.size()); + } while (buf.size() == len); + + if (len > 0) { + buf[len] = '\0'; + switch_dev_folder = std::string(&(buf[0])); + std::string suffixDel = "host" + std::to_string(rocm_smi_device->index()) + + "/scsi_host/" + "host" + + std::to_string(rocm_smi_device->index()) + "/"; + switch_dev_folder.erase(switch_dev_folder.length() - suffixDel.length()); + + auto first = switch_dev_folder.begin(); + auto end = switch_dev_folder.begin() + switch_dev_folder.length() - 12; // 12 characters. For example: "0000:45:00.0" + switch_dev_folder.erase(first, end); + + std::string prefixAdd = kPathPciDevices; + switch_dev_folder = prefixAdd.append(switch_dev_folder); + } + + std::ostringstream ss; + std::string vend_path = switch_dev_folder + "/vendor"; + std::string ldev_path = switch_dev_folder + "/device"; + + if (FileExists(vend_path.c_str()) && FileExists(ldev_path.c_str())) { + std::ifstream vfs, dfs; + vfs.open(vend_path); + dfs.open(ldev_path); + + if (vfs.is_open() && dfs.is_open()) { + uint32_t vendor_id; + uint32_t dev_id; + + vfs >> std::hex >> vendor_id; + dfs >> std::hex >> dev_id; + + vfs.close(); + dfs.close(); + + if (vendor_id == 0x1000 && dev_id == 0x00b2) { + device_paths_.push_back(switch_dev_folder); + host_paths_.push_back(switch_host_folder); + has_valid_fds = true; + + uint64_t bdfid = 0; + rsmi_status_t ret = rsmi_switch_dev_pci_id_get(i, &bdfid); + if (ret != RSMI_STATUS_SUCCESS) { + continue; + } + amdsmi_bdf_t bdf = {}; + bdf.function_number = bdfid & 0x7; + bdf.device_number = (bdfid >> 3) & 0x1f; + bdf.bus_number = (bdfid >> 8) & 0xff; + bdf.domain_number = (bdfid >> 32) & 0xffffffff; + no_drm_bdfs_.push_back(bdf); + } + } + } + } + + // cannot find any valid fds. + if (!has_valid_fds) { + no_drm_bdfs_.clear(); + return AMDSMI_STATUS_INIT_ERROR; + } + + return AMDSMI_STATUS_SUCCESS; +} + +amdsmi_status_t AMDSmiNoDrmSwitch::cleanup() { + device_paths_.clear(); + host_paths_.clear(); + no_drm_bdfs_.clear(); + return AMDSMI_STATUS_SUCCESS; +} + +amdsmi_status_t AMDSmiNoDrmSwitch::amd_query_switch_link( std::string devicePath, + amdsmi_brcm_switch_link_metric_t &info) { + + std::string current_speed = "current_link_speed"; + std::string max_speed = "max_link_speed"; + std::string current_width = "current_link_width"; + std::string max_width = "max_link_width"; + + snprintf(info.current_link_speed, sizeof(info.current_link_speed)-1, "%s", smi_brcm_get_value_string(devicePath, current_speed).c_str()); + snprintf(info.max_link_speed, sizeof(info.max_link_speed)-1, "%s", smi_brcm_get_value_string(devicePath, max_speed).c_str()); + snprintf(info.current_link_width, sizeof(info.current_link_width)-1, "%s", smi_brcm_get_value_string(devicePath, current_width).c_str()); + snprintf(info.max_link_width, sizeof(info.max_link_width)-1, "%s", smi_brcm_get_value_string(devicePath, max_width).c_str()); + + return AMDSMI_STATUS_SUCCESS; +} + +amdsmi_status_t AMDSmiNoDrmSwitch::amd_query_switch_uuid(std::string bdfStr, std::string& serial) { + + get_lspci_device_data(bdfStr, "Device Serial Number ", serial); + + return AMDSMI_STATUS_SUCCESS; +} + +amdsmi_status_t AMDSmiNoDrmSwitch::amd_query_switch_numa_affinity(std::string devicePath, int32_t *numa_node) { + std::string numaFile = "numa_node"; + uint32_t numa = smi_brcm_get_value_u32(devicePath, numaFile); + *numa_node = numa; + return AMDSMI_STATUS_SUCCESS; +} + +amdsmi_status_t AMDSmiNoDrmSwitch::amd_query_switch_cpu_affinity(std::string devicePath, std::string &cpu_affinity) { + std::string cpuAffFile = "cpulistaffinity"; + cpu_affinity = smi_brcm_get_value_string(devicePath, cpuAffFile); + + return AMDSMI_STATUS_SUCCESS; +} + +amdsmi_status_t AMDSmiNoDrmSwitch::amd_query_switch_device( std::string devicePath, + amdsmi_brcm_switch_device_metric_t &info) { + + std::string brcm_device_aer_dev_correctable = "aer_dev_correctable"; + std::string brcm_device_aer_dev_fatal = "aer_dev_fatal"; + std::string brcm_device_aer_dev_nonfatal = "aer_dev_nonfatal"; + std::string brcm_device_ari_enabled = "ari_enabled"; + std::string brcm_device_broken_parity_status = "broken_parity_status"; + std::string brcm_device_class = "class"; + std::string brcm_device_config = "config"; + std::string brcm_device_consistent_dma_mask_bits = "consistent_dma_mask_bits"; + std::string brcm_device_current_link_speed = "current_link_speed"; + std::string brcm_device_current_link_width = "current_link_width"; + std::string brcm_device_d3cold_allowed = "d3cold_allowed"; + std::string brcm_device_device = "device"; + std::string brcm_device_dma_mask_bits = "dma_mask_bits"; + std::string brcm_device_driver_override = "driver_override"; + std::string brcm_device_enable = "enable"; + std::string brcm_device_irq = "irq"; + std::string brcm_device_local_cpulist = "local_cpulist"; + std::string brcm_device_local_cpus = "local_cpus"; + std::string brcm_device_max_link_speed = "max_link_speed"; + std::string brcm_device_max_link_width = "max_link_width"; + std::string brcm_device_modalias = "modalias"; + std::string brcm_device_msi_bus = "msi_bus"; + std::string brcm_device_numa_node = "numa_node"; + std::string brcm_device_pools = "pools"; + std::string brcm_device_power_state = "power_state"; + std::string brcm_device_remove = "remove"; + std::string brcm_device_rescan = "rescan"; + std::string brcm_device_reset = "reset"; + std::string brcm_device_reset_method = "reset_method"; + std::string brcm_device_resource = "resource"; + std::string brcm_device_resource0 = "resource0"; + std::string brcm_device_resource0_wc = "resource0_wc"; + std::string brcm_device_revision = "revision"; + std::string brcm_device_subsystem_device = "subsystem_device"; + std::string brcm_device_subsystem_vendor = "subsystem_vendor"; + std::string brcm_device_uevent = "uevent"; + std::string brcm_device_vendor = "vendor"; + + snprintf(info.brcm_device_aer_dev_correctable, sizeof(info.brcm_device_aer_dev_correctable)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_device_aer_dev_correctable).c_str()); + snprintf(info.brcm_device_aer_dev_fatal, sizeof(info.brcm_device_aer_dev_fatal)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_device_aer_dev_fatal).c_str()); + snprintf(info.brcm_device_aer_dev_nonfatal, sizeof(info.brcm_device_aer_dev_nonfatal)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_device_aer_dev_nonfatal).c_str()); + snprintf(info.brcm_device_ari_enabled, sizeof(info.brcm_device_ari_enabled)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_device_ari_enabled).c_str()); + snprintf(info.brcm_device_broken_parity_status, sizeof(info.brcm_device_broken_parity_status)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_device_broken_parity_status).c_str()); + snprintf(info.brcm_device_class, sizeof(info.brcm_device_class)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_device_class).c_str()); + snprintf(info.brcm_device_config, sizeof(info.brcm_device_config)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_device_config).c_str()); + snprintf(info.brcm_device_consistent_dma_mask_bits, sizeof(info.brcm_device_consistent_dma_mask_bits)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_device_consistent_dma_mask_bits).c_str()); + snprintf(info.brcm_device_current_link_speed, sizeof(info.brcm_device_current_link_speed)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_device_current_link_speed).c_str()); + snprintf(info.brcm_device_current_link_width, sizeof(info.brcm_device_current_link_width)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_device_current_link_width).c_str()); + snprintf(info.brcm_device_d3cold_allowed, sizeof(info.brcm_device_d3cold_allowed)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_device_d3cold_allowed).c_str()); + snprintf(info.brcm_device_device, sizeof(info.brcm_device_device)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_device_device).c_str()); + snprintf(info.brcm_device_dma_mask_bits, sizeof(info.brcm_device_dma_mask_bits)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_device_dma_mask_bits).c_str()); + snprintf(info.brcm_device_driver_override, sizeof(info.brcm_device_driver_override)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_device_driver_override).c_str()); + snprintf(info.brcm_device_enable, sizeof(info.brcm_device_enable)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_device_enable).c_str()); + snprintf(info.brcm_device_irq, sizeof(info.brcm_device_irq)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_device_irq).c_str()); + snprintf(info.brcm_device_local_cpulist, sizeof(info.brcm_device_local_cpulist)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_device_local_cpulist).c_str()); + snprintf(info.brcm_device_local_cpus, sizeof(info.brcm_device_local_cpus)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_device_local_cpus).c_str()); + snprintf(info.brcm_device_max_link_speed, sizeof(info.brcm_device_max_link_speed)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_device_max_link_speed).c_str()); + snprintf(info.brcm_device_max_link_width, sizeof(info.brcm_device_max_link_width)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_device_max_link_width).c_str()); + snprintf(info.brcm_device_modalias, sizeof(info.brcm_device_modalias)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_device_modalias).c_str()); + snprintf(info.brcm_device_msi_bus, sizeof(info.brcm_device_msi_bus)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_device_msi_bus).c_str()); + snprintf(info.brcm_device_numa_node, sizeof(info.brcm_device_numa_node)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_device_numa_node).c_str()); + snprintf(info.brcm_device_pools, sizeof(info.brcm_device_pools)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_device_pools).c_str()); + snprintf(info.brcm_device_power_state, sizeof(info.brcm_device_power_state)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_device_power_state).c_str()); + snprintf(info.brcm_device_reset_method, sizeof(info.brcm_device_reset_method)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_device_reset_method).c_str()); + snprintf(info.brcm_device_resource, sizeof(info.brcm_device_resource)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_device_resource).c_str()); + snprintf(info.brcm_device_revision, sizeof(info.brcm_device_revision)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_device_revision).c_str()); + snprintf(info.brcm_device_subsystem_device, sizeof(info.brcm_device_subsystem_device)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_device_subsystem_device).c_str()); + snprintf(info.brcm_device_subsystem_vendor, sizeof(info.brcm_device_subsystem_vendor)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_device_subsystem_vendor).c_str()); + snprintf(info.brcm_device_uevent, sizeof(info.brcm_device_uevent)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_device_uevent).c_str()); + snprintf(info.brcm_device_vendor, sizeof(info.brcm_device_vendor)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_device_vendor).c_str()); + + return AMDSMI_STATUS_SUCCESS; +} + +amdsmi_status_t AMDSmiNoDrmSwitch::amd_query_switch_power( std::string devicePath, + amdsmi_brcm_switch_power_metric_t &info) { + + devicePath = devicePath+"/power"; + std::string brcm_power_async = "async"; + std::string brcm_power_control = "control"; + std::string brcm_power_runtime_active_kids = "runtime_active_kids"; + std::string brcm_power_runtime_active_time = "runtime_active_time"; + std::string brcm_power_runtime_enabled = "runtime_enabled"; + std::string brcm_power_runtime_status = "runtime_status"; + std::string brcm_power_runtime_suspended_time = "runtime_suspended_time"; + std::string brcm_power_runtime_usage = "runtime_usage"; + std::string brcm_power_wakeup = "wakeup"; + std::string brcm_power_wakeup_abort_count = "wakeup_abort_count"; + std::string brcm_power_wakeup_active = "wakeup_active"; + std::string brcm_power_wakeup_active_count = "wakeup_active_count"; + std::string brcm_power_wakeup_count = "wakeup_count"; + std::string brcm_power_wakeup_expire_count = "wakeup_expire_count"; + std::string brcm_power_wakeup_last_time_ms = "wakeup_last_time_ms"; + std::string brcm_power_wakeup_max_time_ms = "wakeup_max_time_ms"; + std::string brcm_power_wakeup_total_time_ms = "wakeup_total_time_ms"; + + snprintf(info.brcm_power_async, sizeof(info.brcm_power_async)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_power_async).c_str()); + snprintf(info.brcm_power_control, sizeof(info.brcm_power_control)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_power_control).c_str()); + snprintf(info.brcm_power_runtime_active_kids, sizeof(info.brcm_power_runtime_active_kids)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_power_runtime_active_kids).c_str()); + snprintf(info.brcm_power_runtime_active_time, sizeof(info.brcm_power_runtime_active_time)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_power_runtime_active_time).c_str()); + snprintf(info.brcm_power_runtime_enabled, sizeof(info.brcm_power_runtime_enabled)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_power_runtime_enabled).c_str()); + snprintf(info.brcm_power_runtime_status, sizeof(info.brcm_power_runtime_status)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_power_runtime_status).c_str()); + snprintf(info.brcm_power_runtime_suspended_time, sizeof(info.brcm_power_runtime_suspended_time)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_power_runtime_suspended_time).c_str()); + snprintf(info.brcm_power_runtime_usage, sizeof(info.brcm_power_runtime_usage)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_power_runtime_usage).c_str()); + snprintf(info.brcm_power_wakeup, sizeof(info.brcm_power_wakeup)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_power_wakeup).c_str()); + snprintf(info.brcm_power_wakeup_abort_count, sizeof(info.brcm_power_wakeup_abort_count)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_power_wakeup_abort_count).c_str()); + snprintf(info.brcm_power_wakeup_active, sizeof(info.brcm_power_wakeup_active)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_power_wakeup_active).c_str()); + snprintf(info.brcm_power_wakeup_active_count, sizeof(info.brcm_power_wakeup_active_count)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_power_wakeup_active_count).c_str()); + snprintf(info.brcm_power_wakeup_count, sizeof(info.brcm_power_wakeup_count)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_power_wakeup_count).c_str()); + snprintf(info.brcm_power_wakeup_expire_count, sizeof(info.brcm_power_wakeup_expire_count)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_power_wakeup_expire_count).c_str()); + snprintf(info.brcm_power_wakeup_last_time_ms, sizeof(info.brcm_power_wakeup_last_time_ms)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_power_wakeup_last_time_ms).c_str()); + snprintf(info.brcm_power_wakeup_max_time_ms, sizeof(info.brcm_power_wakeup_max_time_ms)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_power_wakeup_max_time_ms).c_str()); + snprintf(info.brcm_power_wakeup_total_time_ms, sizeof(info.brcm_power_wakeup_total_time_ms)-1, "%s", smi_brcm_get_value_string(devicePath, brcm_power_wakeup_total_time_ms).c_str()); + + return AMDSMI_STATUS_SUCCESS; +} + +amdsmi_status_t AMDSmiNoDrmSwitch::get_bdf_by_index(uint32_t switch_index, amdsmi_bdf_t *bdf_info) const { + if (switch_index + 1 > no_drm_bdfs_.size()) return AMDSMI_STATUS_NOT_SUPPORTED; + *bdf_info = no_drm_bdfs_[switch_index]; + return AMDSMI_STATUS_SUCCESS; +} + +amdsmi_status_t AMDSmiNoDrmSwitch::get_device_path_by_index(uint32_t switch_index, std::string *device_path) const { + if (switch_index + 1 > device_paths_.size()) return AMDSMI_STATUS_NOT_SUPPORTED; + *device_path = device_paths_[switch_index]; + return AMDSMI_STATUS_SUCCESS; +} + +amdsmi_status_t AMDSmiNoDrmSwitch::get_hwmon_path_by_index(uint32_t switch_index, std::string *hwm_path) const { + if (switch_index + 1 > host_paths_.size()) return AMDSMI_STATUS_NOT_SUPPORTED; + *hwm_path = host_paths_[switch_index]; + return AMDSMI_STATUS_SUCCESS; +} + +std::vector& AMDSmiNoDrmSwitch::get_device_paths() { return device_paths_; } +std::vector &AMDSmiNoDrmSwitch::get_hwmon_paths() { return host_paths_; } + +bool AMDSmiNoDrmSwitch::check_if_no_drm_is_supported() { return true; } + +std::vector AMDSmiNoDrmSwitch::get_bdfs() { + return no_drm_bdfs_; +} + +} // namespace amd::smi + diff --git a/projects/amdsmi/src/nic/brcm-nic/amd_smi_switch_device.cc b/projects/amdsmi/src/nic/brcm-nic/amd_smi_switch_device.cc new file mode 100644 index 00000000000..73f418f8f78 --- /dev/null +++ b/projects/amdsmi/src/nic/brcm-nic/amd_smi_switch_device.cc @@ -0,0 +1,123 @@ +/* + * Copyright (c) Broadcom Inc All Rights Reserved. + * + * Developed by: + * Broadcom Inc + * + * www.broadcom.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include "amd_smi/impl/nic/amd_smi_switch_device.h" +#include "rocm_smi/rocm_smi_utils.h" + +namespace amd::smi { + +uint32_t AMDSmiSWITCHDevice::get_switch_id() const { + return switch_id_; +} + +std::string& AMDSmiSWITCHDevice::get_switch_path() { + return path_; +} + +amdsmi_bdf_t AMDSmiSWITCHDevice::get_bdf() { + return bdf_; +} + +amdsmi_status_t AMDSmiSWITCHDevice::get_no_drm_data() { + amdsmi_status_t ret; + std::string path; + amdsmi_bdf_t bdf; + ret = nodrm_.get_device_path_by_index(switch_id_, &path); + if (ret != AMDSMI_STATUS_SUCCESS) return AMDSMI_STATUS_NOT_SUPPORTED; + ret = nodrm_.get_bdf_by_index(switch_id_, &bdf); + if (ret != AMDSMI_STATUS_SUCCESS) return AMDSMI_STATUS_NOT_SUPPORTED; + path_ = path; + + return AMDSMI_STATUS_SUCCESS; +} + +pthread_mutex_t* AMDSmiSWITCHDevice::get_mutex() { + return amd::smi::GetMutex(switch_id_); +} + +amdsmi_status_t AMDSmiSWITCHDevice::amd_query_switch_link_info(amdsmi_brcm_switch_link_metric_t& info) const { + amdsmi_status_t ret; + std::string devicePath; + ret = nodrm_.get_device_path_by_index(switch_id_, &devicePath); + if (ret != AMDSMI_STATUS_SUCCESS) return AMDSMI_STATUS_NOT_SUPPORTED; + + return nodrm_.amd_query_switch_link(devicePath, info); +} + +amdsmi_status_t AMDSmiSWITCHDevice::amd_query_switch_power_info(amdsmi_brcm_switch_power_metric_t& info) const { + amdsmi_status_t ret; + std::string devicePath; //sys/bus/pci/devices/0000:9b:00.0 + ret = nodrm_.get_device_path_by_index(switch_id_, &devicePath); + if (ret != AMDSMI_STATUS_SUCCESS) return AMDSMI_STATUS_NOT_SUPPORTED; + + return nodrm_.amd_query_switch_power(devicePath, info); +} + +amdsmi_status_t AMDSmiSWITCHDevice::amd_query_switch_device_info(amdsmi_brcm_switch_device_metric_t& info) const { + amdsmi_status_t ret; + std::string devicePath; + ret = nodrm_.get_device_path_by_index(switch_id_, &devicePath); + if (ret != AMDSMI_STATUS_SUCCESS) return AMDSMI_STATUS_NOT_SUPPORTED; + + return nodrm_.amd_query_switch_device(devicePath, info); +} + +amdsmi_status_t AMDSmiSWITCHDevice::amd_query_switch_uuid(std::string& serial) const { + amdsmi_status_t ret; + amdsmi_bdf_t bdf = {}; + ret = nodrm_.get_bdf_by_index(switch_id_, &bdf); + + if (ret != AMDSMI_STATUS_SUCCESS) return AMDSMI_STATUS_NOT_SUPPORTED; + + char bdf_str[20]; + snprintf(bdf_str, sizeof(bdf_str)-1, "%04lx:%02x:%02x.%d", bdf.domain_number, bdf.bus_number, + bdf.device_number, bdf.function_number); + + return nodrm_.amd_query_switch_uuid(std::string(bdf_str), serial); +} + +amdsmi_status_t AMDSmiSWITCHDevice::amd_query_switch_numa_affinity(int32_t *numa_node) const { + amdsmi_status_t ret; + std::string devicePath; + ret = nodrm_.get_device_path_by_index(switch_id_, &devicePath); + if (ret != AMDSMI_STATUS_SUCCESS) return AMDSMI_STATUS_NOT_SUPPORTED; + + return nodrm_.amd_query_switch_numa_affinity(devicePath, numa_node); +} + +amdsmi_status_t AMDSmiSWITCHDevice::amd_query_switch_cpu_affinity(std::string& cpu_affinity) const { + char bdf_str[20]; + snprintf(bdf_str, sizeof(bdf_str)-1, "%04lx:%02x", bdf_.domain_number, bdf_.bus_number); + std::stringstream domain_bus_sstream; + domain_bus_sstream << "/sys/class/pci_bus/" << std::string(bdf_str); + + return nodrm_.amd_query_switch_cpu_affinity(domain_bus_sstream.str(), cpu_affinity); +} + +} // namespace amd::smi + diff --git a/projects/amdsmi/tests/amd_smi_test/CMakeLists.txt b/projects/amdsmi/tests/amd_smi_test/CMakeLists.txt index 2dbb5305396..f6c0c0e1d80 100644 --- a/projects/amdsmi/tests/amd_smi_test/CMakeLists.txt +++ b/projects/amdsmi/tests/amd_smi_test/CMakeLists.txt @@ -55,13 +55,15 @@ set(TEST "amdsmitst") # Source files aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} tstSources) - # Header file include path -include_directories(${TEST} ${CMAKE_CURRENT_SOURCE_DIR}/.. ${ROCM_INC_DIR}/..) +include_directories(${TEST} +${CMAKE_CURRENT_SOURCE_DIR}/.. +${ROCM_INC_DIR}/.. +) # Build rules add_executable(${TEST} ${tstSources} ${functionalSources}) -target_link_libraries(${TEST} ${AMD_SMI} GTest::gtest c stdc++ pthread ${FILESYSTEM_LIB}) +target_link_libraries(${TEST} ${AMD_SMI} GTest::gtest c stdc++ pthread ${AMDSMINIC_PATH} ${FILESYSTEM_LIB}) # Install tests install( diff --git a/projects/amdsmi/tests/amd_smi_test/functional/ainic.cc.disabled b/projects/amdsmi/tests/amd_smi_test/functional/ainic.cc.disabled new file mode 100644 index 00000000000..9feac3468dc --- /dev/null +++ b/projects/amdsmi/tests/amd_smi_test/functional/ainic.cc.disabled @@ -0,0 +1,401 @@ +/* + * Copyright (c) Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace { + +struct RAII { + RAII(std::function init, std::function finish) + : _finish(finish) { + init(); + } + ~RAII() { + _finish(); + } + std::function _finish; +}; + +void dump_ainic_info(int idx, const amd::smi::AMDSmiAINICDevice::AINICInfo &ainic_info) { +#if 0//Expected Output: +NIC: 0 + ASIC: + VENDOR_ID: 0x1dd8 + SUBVENDOR_ID: 0x1dd8 + DEVICE_ID: 0x8 + SUBSYSTEM_ID: 0x5201 + REVISION: 0x0 + PERMANENT_ADDRESS: 04:90:81:01:09:38 + PRODUCT_NAME: ,Pensando DSC2-200 50/100/200G 2p QSFP56 Card + PART_NUMBER: DSC2-2Q200-32R32F64P-R4 + SERIAL_NUMBER: FPF2316002EEC0V2 + VENDOR_NAME: AMD Pensando Systems, Inc. + BUS: + BDF: 0000:e2:00.0 + MAX_PCIE_WIDTH: 16 + MAX_PCIE_SPEED: 32 + PCIE_INTERFACE_VERSION: N/A + SLOT_TYPE: N/A + DRIVER: + NAME: ionic + VERSION: 25.08.2.001 + FW_VERSION: 1.97.0-A-5 + BUS_INFO: 0000:83:00.0 + NUMA: + NODE: 1 + AFFINITY: 16-31,48-63 + VERSIONS: + RUNNING: + fw: ?? + fw.heartbeat: ?? + fw.status: ?? + PORTS: + PORT_0: + BDF: 0 + TYPE: Ethernet + FLAVOUR: N/A + NETDEV: enp131s0 + IFINDEX: 11 + MAC_ADDRESS: 04:90:81:01:09:38 + CARRIER: 0 + MTU: 1500B + LINK_STATE: down + LINK_SPEED: 0 Mb/s + ACTIVE_FEC: Off + AUTONEG: off + PAUSE_AUTONEG: off + PAUSE_RX: on + PAUSE_TX: on + PORT_1: + BDF: 0 + TYPE: Ethernet + FLAVOUR: N/A + NETDEV: enp132s0 + IFINDEX: 12 + MAC_ADDRESS: 04:90:81:01:09:39 + CARRIER: 0 + MTU: 1500B + LINK_STATE: down + LINK_SPEED: 0 Mb/s + ACTIVE_FEC: Off + AUTONEG: off + PAUSE_AUTONEG: off + PAUSE_RX: on + PAUSE_TX: on + PORT_2: + BDF: 0 + TYPE: Ethernet + FLAVOUR: N/A + NETDEV: enp229s0 + IFINDEX: 14 + MAC_ADDRESS: 04:90:81:2c:77:b0 + CARRIER: 0 + MTU: 1500B + LINK_STATE: down + LINK_SPEED: 0 Mb/s + ACTIVE_FEC: Off + AUTONEG: off + PAUSE_AUTONEG: off + PAUSE_RX: off + PAUSE_TX: off + RDMA_DEVICES: + RDMA_DEVICES: + RDMA_DEVICE_0: + NAME: rocep131s0 + NODE_GUID: 0690:81ff:fe01:0938 + NODE_TYPE: 1: CA + SYS_IMAGE_GUID: 0690:81ff:fe01:0938 + FW_VER: 1.97.0-A-5 + PORTS: + PORT_0: + NETDEV: + PORT_NUM: 1 + STATE: DOWN + MAX_MTU: 65535 + ACTIVE_MTU: 65535 + RDMA_DEVICES: + RDMA_DEVICE_0: + NAME: rocep132s0 + NODE_GUID: 0690:81ff:fe01:0939 + NODE_TYPE: 1: CA + SYS_IMAGE_GUID: 0690:81ff:fe01:0939 + FW_VER: 1.97.0-A-5 + PORTS: + PORT_0: + NETDEV: + PORT_NUM: 1 + STATE: DOWN + MAX_MTU: 65535 + ACTIVE_MTU: 65535 + RDMA_DEVICES: + RDMA_DEVICE_0: + NAME: rocep229s0 + NODE_GUID: 0690:81ff:fe2c:77b0 + NODE_TYPE: 1: CA + SYS_IMAGE_GUID: 0690:81ff:fe2c:77b0 + FW_VER: 1.110.1-a-1 + PORTS: + PORT_0: + NETDEV: + PORT_NUM: 1 + STATE: DOWN + MAX_MTU: 65535 + ACTIVE_MTU: 65535 +#endif//0 + std::ostringstream oss; + oss << std::hex << + std::setw(4) << std::setfill('0') << ainic_info.bus.bdf.domain_number << ":" << + std::setw(2) << std::setfill('0') << ainic_info.bus.bdf.bus_number << ":" << + std::setw(2) << std::setfill('0') << ainic_info.bus.bdf.device_number << "." << + std::setw(2) << std::setfill('0') << ainic_info.bus.bdf.function_number; + std::string bdf_str = oss.str(); + oss.str(""); + oss << "===============================================\n" << + "NIC: " << idx << "\n" << + " ASIC:" << "\n" << + " VENDOR_ID: 0x" << std::hex << ainic_info.asic.vendor_id << std::dec << "\n" << + " SUBVENDOR_ID: 0x" << std::hex << ainic_info.asic.subvendor_id << std::dec << "\n" << + " DEVICE_ID: 0x" << std::hex << ainic_info.asic.device_id << std::dec << "\n" << + " SUBSYSTEM_ID: 0x" << std::hex << ainic_info.asic.subsystem_id << std::dec << "\n" << + " REVISION: 0x" << std::hex << static_cast(ainic_info.asic.revision) << std::dec << "\n" << + " PERMANENT_ADDRESS: " << ainic_info.asic.permanent_address << "\n" << + " PRODUCT_NAME: " << ainic_info.asic.product_name << "\n" << + " PART_NUMBER: " << ainic_info.asic.part_number << "\n" << + " SERIAL_NUMBER: " << ainic_info.asic.serial_number << "\n" << + " VENDOR_NAME: " << ainic_info.asic.vendor_name << "\n" << + " BUS:" << "\n" << + " BDF: " << bdf_str << "\n" << + " MAX_PCIE_WIDTH: " << static_cast(ainic_info.bus.max_pcie_width) << "\n" << + " MAX_PCIE_SPEED: " << ainic_info.bus.max_pcie_speed << "\n" << + " PCIE_INTERFACE_VERSION: " << ainic_info.bus.pcie_interface_version << "\n" << + " SLOT_TYPE: " << ainic_info.bus.slot_type << "\n" << + " DRIVER:" << "\n" << + " NAME: " << ainic_info.driver.name << "\n" << + " VERSION: " << ainic_info.driver.version << "\n" << + // " FW_VERSION: " << ainic_info.driver.version << "\n" << + // " BUS_INFO: " << ainic_info.driver.bus_info << "\n" << + " NUMA:" << "\n" << + " NODE: " << static_cast(ainic_info.numa.node) << "\n" << + " AFFINITY: " << ainic_info.numa.affinity << "\n" << + // " LIMIT:" << "\n" << //can be fetched through https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface or https://github.com/lm-sensors/lm-sensors + // " MAX_POWER: ?? W" << "\n" << + // " MAX_TEMPERATURE: ?? C" << "\n" << + // " VERSIONS:" << "\n" << //these can be obtained only through netlink interface which we currently don't have. It requires 3rd party library dependency or manually netlink hdr/msg construction which is hard to write and maintain/test. So, netlink interface implementation is postponed. + // " RUNNING:" << "\n" << + // " fw: ??" << ainic_info.versions.running.fw << "\n" << + // " fw.heartbeat: ??" << ainic_info.versions.running.fw_heartbeat << "\n" << + // " fw.status: ??" << ainic_info.versions.running.fw_status << "\n" + " PORTS:" << "\n"; + for(uint32_t port_idx = 0; port_idx < ainic_info.port.num_ports; ++port_idx) { + oss << + " PORT_" << static_cast(port_idx) << ":\n" << + " BDF: " << 0 << "\n" << + " TYPE: " << ainic_info.port.ports[port_idx].type << "\n" << + " FLAVOUR: " << ainic_info.port.ports[port_idx].flavour << "\n" << + " NETDEV: " << ainic_info.port.ports[port_idx].netdev << "\n" << + " IFINDEX: " << static_cast(ainic_info.port.ports[port_idx].ifindex) << "\n" << + " MAC_ADDRESS: " << ainic_info.port.ports[port_idx].mac_address << "\n" << + " CARRIER: " << static_cast(ainic_info.port.ports[port_idx].carrier) << "\n" << + " MTU: " << ainic_info.port.ports[port_idx].mtu << "B\n" << + " LINK_STATE: " << ainic_info.port.ports[port_idx].link_state << "\n" << + " LINK_SPEED: " << ainic_info.port.ports[port_idx].link_speed << " Mb/s\n" << + " ACTIVE_FEC: " << ainic_info.port.ports[port_idx].active_fec << "\n" << + " AUTONEG: " << ainic_info.port.ports[port_idx].autoneg << "\n" << + " PAUSE_AUTONEG: " << ainic_info.port.ports[port_idx].pause_autoneg << "\n" << + " PAUSE_RX: " << ainic_info.port.ports[port_idx].pause_rx << "\n" << + " PAUSE_TX: " << ainic_info.port.ports[port_idx].pause_tx << "\n"; + }//port + oss << + " RDMA_DEVICES: " << "\n"; + for(uint32_t port_idx = 0; port_idx < ainic_info.port.num_ports; ++port_idx) { + oss << + " RDMA_DEVICES: " << "\n"; + for(uint8_t rdma_dev_idx = 0; rdma_dev_idx < ainic_info.rdma_dev.num_rdma_dev; ++rdma_dev_idx) { + oss << + " RDMA_DEVICE_" << static_cast(rdma_dev_idx) << ":\n" << + " NAME: " <(rdma_port_idx) << ":\n" << + " NETDEV: " <(ainic_info.port.ports[port_idx].rdma_dev[rdma_dev_idx].rdma_port_info[rdma_port_idx].port_num) << "\n" << + " STATE: " <(ainic_info.port.ports[port_idx].rdma_dev[rdma_dev_idx].rdma_port_info[rdma_port_idx].max_mtu) << "\n" << + // " ACTIVE_MTU: " << static_cast(ainic_info.port.ports[port_idx].rdma_dev[rdma_dev_idx].rdma_port_info[rdma_port_idx].active_mtu) << + "\n"; + } + }//num_infiniband + + } + std::cout << oss.str(); +} + +std::optional> get_nics() { + + auto &amdsmi = amd::smi::AMDSmiSystem::getInstance(); + (void)amdsmi;//unused variable warning suppression, but needed to ensure amdsmi system is initialized. + uint32_t soc_count = 10; + std::vector sockets(soc_count); + // Get the sockets of the system + amdsmi_status_t status = amdsmi_get_socket_handles(&soc_count, &sockets[0]); + if (status != AMDSMI_STATUS_SUCCESS){ + return std::nullopt; + } + std::cout << "Got " << soc_count << " socket(s)\n"; + + std::vector nics; + for (uint32_t index = 0 ; index < soc_count; index++){ + uint32_t processor_count = 0; + status = amdsmi_get_processor_handles_by_type( + sockets[index], + AMDSMI_PROCESSOR_TYPE_AMD_NIC, + nullptr, &processor_count); + if (status != AMDSMI_STATUS_SUCCESS){ + return std::nullopt; + } + std::cout << "Got " << processor_count << " processors for socket " << index << ":\n"; + std::vector processor_handles(processor_count); + status = amdsmi_get_processor_handles_by_type( + sockets[index], + AMDSMI_PROCESSOR_TYPE_AMD_NIC, + processor_handles.data(), &processor_count); + if (status != AMDSMI_STATUS_SUCCESS){ + return std::nullopt; + } + + for(uint32_t idx = 0; idx < processor_count; ++idx){ + amd::smi::AMDSmiAINICDevice::AINICInfo ainic_info = {}; + status = amdsmi_get_ainic_info(processor_handles[idx], &ainic_info); + if (status != AMDSMI_STATUS_SUCCESS) { + return std::nullopt; + } + dump_ainic_info(idx, ainic_info); + nics.emplace_back(ainic_info); + } + } + return nics; +} + +std::map port_stats() { + amdsmi_processor_handle processor_handle = nullptr; + amdsmi_status_t status = smi_amdgpu_get_ainic_processor_handle_by_index(0, &processor_handle); + if(status != AMDSMI_STATUS_SUCCESS) { + return {}; + } + + uint32_t rdma_port_index = 0; + uint32_t num_stats = 0; + std::unique_ptr stats; + amdsmi_get_nic_rdma_port_statistics( + processor_handle, + rdma_port_index, + &num_stats, + nullptr); + + std::cout << "[" << __FILE__ << ":" << __LINE__ << "]\nnum_stats: " << num_stats << "\n"; + stats = std::make_unique(num_stats); + amdsmi_get_nic_rdma_port_statistics( + processor_handle, + rdma_port_index, + &num_stats, + stats.get()); + + std::map values; + for(uint32_t idx = 0; idx < num_stats; ++idx) { + std::cout << "Stat " << idx << ": " << stats[idx].name << " = " << stats[idx].value << std::endl; + values[stats[idx].name] = stats[idx].value; + } + return values; +} +}//namespace + +TEST(TestAINIC, DISABLED_ListAiNicPorts) { + RAII _([]() {amdsmi_init(AMDSMI_INIT_AMD_NICS);}, []() { amdsmi_shut_down(); }); + auto nics = get_nics(); + ASSERT_TRUE(nics); + ASSERT_GE(nics->size(), 1); +} + +TEST(TestAINIC, DISABLED_GetAiNicPortStatistics) { + RAII _([]() {amdsmi_init(AMDSMI_INIT_AMD_NICS);}, []() { amdsmi_shut_down(); }); + auto stats = port_stats(); + constexpr int expected_num_stats = 43; + ASSERT_EQ(stats.size(), expected_num_stats); + ASSERT_NE(stats.find("resp_rx_outof_atomic"), stats.end()); + ASSERT_NE(stats.find("rx_rdma_ucast_pkts"), stats.end()); + ASSERT_NE(stats.find("req_rx_cqe_flush"), stats.end()); + ASSERT_NE(stats.find("req_tx_loc_acc_err"), stats.end()); + ASSERT_NE(stats.find("tx_rdma_ucast_bytes"), stats.end()); + ASSERT_NE(stats.find("rx_rdma_ecn_pkts"), stats.end()); + ASSERT_NE(stats.find("tx_rdma_mcast_bytes"), stats.end()); + ASSERT_NE(stats.find("resp_tx_rnr_retry_err"), stats.end()); + ASSERT_NE(stats.find("req_rx_rmt_req_err"), stats.end()); + ASSERT_NE(stats.find("req_rx_rmt_acc_err"), stats.end()); + ASSERT_NE(stats.find("lifespan"), stats.end()); + ASSERT_NE(stats.find("resp_tx_rmt_acc_err"), stats.end()); + ASSERT_NE(stats.find("req_rx_pkt_seq_err"), stats.end()); + ASSERT_NE(stats.find("resp_rx_outouf_seq"), stats.end()); + ASSERT_NE(stats.find("tx_rdma_mcast_pkts"), stats.end()); + ASSERT_NE(stats.find("resp_tx_pkt_seq_err"), stats.end()); + ASSERT_NE(stats.find("req_rx_inval_pkts"), stats.end()); + ASSERT_NE(stats.find("rx_rdma_mcast_pkts"), stats.end()); + ASSERT_NE(stats.find("rx_rdma_ucast_bytes"), stats.end()); + ASSERT_NE(stats.find("req_tx_loc_oper_err"), stats.end()); + ASSERT_NE(stats.find("resp_rx_cqe_flush"), stats.end()); + ASSERT_NE(stats.find("resp_rx_loc_len_err"), stats.end()); + ASSERT_NE(stats.find("rx_rdma_mcast_bytes"), stats.end()); + ASSERT_NE(stats.find("resp_tx_loc_sgl_inv_err"), stats.end()); + ASSERT_NE(stats.find("resp_tx_rmt_oper_err"), stats.end()); + ASSERT_NE(stats.find("tx_rdma_cnp_pkts"), stats.end()); + ASSERT_NE(stats.find("req_rx_cqe_err"), stats.end()); + ASSERT_NE(stats.find("req_rx_dup_response"), stats.end()); + ASSERT_NE(stats.find("resp_rx_dup_request"), stats.end()); + ASSERT_NE(stats.find("resp_rx_cqe_err"), stats.end()); + ASSERT_NE(stats.find("req_tx_loc_sgl_inv_err"), stats.end()); + ASSERT_NE(stats.find("req_tx_mem_mgmt_err"), stats.end()); + ASSERT_NE(stats.find("resp_rx_inval_request"), stats.end()); + ASSERT_NE(stats.find("req_tx_retry_excd_err"), stats.end()); + ASSERT_NE(stats.find("req_rx_rnr_retry_err"), stats.end()); + ASSERT_NE(stats.find("resp_rx_s0_table_err"), stats.end()); + ASSERT_NE(stats.find("resp_rx_loc_oper_err"), stats.end()); + ASSERT_NE(stats.find("resp_rx_outof_buf"), stats.end()); + ASSERT_NE(stats.find("rx_rdma_cnp_pkts"), stats.end()); + ASSERT_NE(stats.find("req_rx_oper_err"), stats.end()); + ASSERT_NE(stats.find("resp_tx_rmt_inval_req_err"), stats.end()); + ASSERT_NE(stats.find("req_rx_impl_nak_seq_err"), stats.end()); + ASSERT_NE(stats.find("tx_rdma_ucast_pkts"), stats.end()); +} diff --git a/projects/amdsmi/tests/amd_smi_test/functional/dynamic_metrics_test.cc b/projects/amdsmi/tests/amd_smi_test/functional/dynamic_metrics_test.cc index 4d29c0c4b0b..ee87668f1d9 100644 --- a/projects/amdsmi/tests/amd_smi_test/functional/dynamic_metrics_test.cc +++ b/projects/amdsmi/tests/amd_smi_test/functional/dynamic_metrics_test.cc @@ -131,7 +131,7 @@ TEST(AmdSmiDynamicMetricTest, GPUMetricDynamicVersionSupported) { const auto* header = reinterpret_cast(blob.data()); const auto flag = amd::smi::translate_header_to_flag_version(*header, is_partition_metrics, fake_path.string()); - EXPECT_EQ(flag, GetExpectedMetricVersionFlag(1, ver, is_partition_metrics)) + EXPECT_EQ(flag, GetExpectedMetricVersionFlag(1, static_cast(ver), is_partition_metrics)) << "Version 1." << ver << " should be treated as supported"; auto gpu_metrics_ptr = @@ -175,7 +175,7 @@ TEST(AmdSmiDynamicMetricTest, XCPMetricDynamicVersionSupported) { const auto* header = reinterpret_cast(blob.data()); const auto flag = amd::smi::translate_header_to_flag_version(*header, is_partition_metrics, fake_path.string()); - EXPECT_EQ(flag, GetExpectedMetricVersionFlag(1, ver, is_partition_metrics)) + EXPECT_EQ(flag, GetExpectedMetricVersionFlag(1, static_cast(ver), is_partition_metrics)) << "Version 1." << ver << " should be treated as supported"; auto xcp_metrics_ptr = diff --git a/projects/amdsmi/tests/amd_smi_test/functional/power_cap_read_write.cc b/projects/amdsmi/tests/amd_smi_test/functional/power_cap_read_write.cc index 6725ada3328..f620c9ce813 100644 --- a/projects/amdsmi/tests/amd_smi_test/functional/power_cap_read_write.cc +++ b/projects/amdsmi/tests/amd_smi_test/functional/power_cap_read_write.cc @@ -134,7 +134,6 @@ void TestPowerCapReadWrite::Run(void) { PrintDeviceHeader(processor_handles_[dv_ind]); // verify amdsmi_get_supported_power_cap_info() works - amdsmi_power_cap_info_t info; uint32_t sensor_count = 0; uint32_t sensor_inds[2]; amdsmi_power_cap_type_t sensor_types[2]; diff --git a/projects/amdsmi/tests/amd_smi_test/functional/sys_info_read.cc b/projects/amdsmi/tests/amd_smi_test/functional/sys_info_read.cc index e2e14ac7d6d..0e09c9482a3 100644 --- a/projects/amdsmi/tests/amd_smi_test/functional/sys_info_read.cc +++ b/projects/amdsmi/tests/amd_smi_test/functional/sys_info_read.cc @@ -131,7 +131,102 @@ void TestSysInfoRead::Run(void) { // Verify api support checking functionality is working err = amdsmi_get_gpu_topo_numa_affinity(processor_handles_[i], nullptr); ASSERT_EQ(err, AMDSMI_STATUS_INVAL); +#ifdef BRCM_NIC + // cpu_affinity + char cpu_aff_data[1024] = {}; + unsigned int cpu_aff_length = sizeof(cpu_aff_data); + err = amdsmi_get_gpu_topo_cpu_affinity(processor_handles_[i], &cpu_aff_length, cpu_aff_data); + if (err == AMDSMI_STATUS_NOT_SUPPORTED) { + std::cout << + "\t**amdsmi_get_gpu_topo_cpu_affinity() is not supported" + " on this machine" << std::endl; + } else { + CHK_ERR_ASRT(err) + IF_VERB(STANDARD) { + std::cout << "\t**CPU AFFINITY: " << cpu_aff_data << std::endl; + } + } + + // nic_topo_numa_affinity + int32_t numa_node = -1; + err = amdsmi_get_nic_topo_numa_affinity(processor_handles_[i], &numa_node); + if (err == AMDSMI_STATUS_NOT_SUPPORTED) { + std::cout << + "\t**amdsmi_get_nic_topo_numa_affinity() is not supported" + " on this machine" << std::endl; + } else { + CHK_ERR_ASRT(err) + IF_VERB(STANDARD) { + std::cout << "\t**NUMA NODE (NIC): " << numa_node << std::endl; + } + } + + // nic_topo_cpu_affinity + char nic_cpu_aff_data[1024] = {}; + unsigned int nic_cpu_aff_length = sizeof(cpu_aff_data); + err = amdsmi_get_nic_topo_cpu_affinity(processor_handles_[i], &nic_cpu_aff_length, nic_cpu_aff_data); + if (err == AMDSMI_STATUS_NOT_SUPPORTED) { + std::cout << + "\t**amdsmi_get_nic_topo_cpu_affinity() is not supported" + " on this machine" << std::endl; + } else { + CHK_ERR_ASRT(err) + IF_VERB(STANDARD) { + std::cout << "\t**CPU AFFINITY (NIC): " << nic_cpu_aff_data << std::endl; + } + } + // switch_topo_numa_affinity + int32_t switch_numa_node = -1; + err = amdsmi_get_switch_topo_numa_affinity(processor_handles_[i], &switch_numa_node); + if (err == AMDSMI_STATUS_NOT_SUPPORTED) { + std::cout << + "\t**amdsmi_get_switch_topo_numa_affinity() is not supported" + " on this machine" << std::endl; + } else { + CHK_ERR_ASRT(err) + IF_VERB(STANDARD) { + std::cout << "\t**NUMA NODE (SWITCH): " << switch_numa_node << std::endl; + } + } + + // switch_topo_cpu_affinity + char switch_cpu_aff_data[1024] = {}; + size_t switch_cpu_aff_length = sizeof(switch_cpu_aff_data); + err = amdsmi_get_switch_topo_cpu_affinity(processor_handles_[i], + &switch_cpu_aff_length, + switch_cpu_aff_data); + if (err == AMDSMI_STATUS_NOT_SUPPORTED) { + std::cout << + "\t**amdsmi_get_switch_topo_cpu_affinity() is not supported" + " on this machine" << std::endl; + } else { + CHK_ERR_ASRT(err) + IF_VERB(STANDARD) { + std::cout << "\t**CPU AFFINITY (SWITCH): " + << switch_cpu_aff_data << std::endl; + } + } + + // nic_gpu_topo_info + char nic_gpu_topo_info[1024] = {}; + size_t nic_gpu_topo_info_length = sizeof(nic_gpu_topo_info); + err = amdsmi_get_nic_gpu_topo_info(processor_handles_[i], + processor_handles_[i], + &nic_gpu_topo_info_length, + nic_gpu_topo_info); + if (err == AMDSMI_STATUS_NOT_SUPPORTED) { + std::cout << + "\t**amdsmi_get_nic_gpu_topo_info() is not supported" + " on this machine" << std::endl; + } else { + CHK_ERR_ASRT(err) + IF_VERB(STANDARD) { + std::cout << "\t**NIC_GPU_TOPO_INFO: " + << nic_gpu_topo_info << std::endl; + } + } +#endif//BRCM_NIC // vendor_id, unique_id, target_gfx_version amdsmi_asic_info_t asic_info = {}; err = amdsmi_get_gpu_asic_info(processor_handles_[i], &asic_info); diff --git a/projects/amdsmi/tests/python_unittest/common.py b/projects/amdsmi/tests/python_unittest/common.py index 83c591801bb..c3dc100445a 100644 --- a/projects/amdsmi/tests/python_unittest/common.py +++ b/projects/amdsmi/tests/python_unittest/common.py @@ -531,3 +531,24 @@ def check_ret(self, msg, exc, expected_code_name=None, printIt=True): print(f'{status_msg}', flush=True) return status_ret + def _skip_if_missing(self, names): + def has_attr_recursive(obj, name): + """Check if an attribute exists in obj or its submodules.""" + if hasattr(obj, name): + return True + # Try to find it in submodules + for attr_name in dir(obj): + try: + attr = getattr(obj, attr_name) + if hasattr(attr, '__dict__') and hasattr(attr, name): + return True + except (AttributeError, ImportError): + pass + return False + + missing = [name for name in names if not has_attr_recursive(amdsmi, name)] + if missing: + test_name = self.id().split('.')[-1] + print_missing_msg = f"{test_name} | Missing amdsmi API(s) in amdsmi_interface.py: " + ", ".join(missing) + print(f"\n") + self.skipTest(f"{str(print_missing_msg)}") diff --git a/projects/amdsmi/tests/python_unittest/integration_test.py b/projects/amdsmi/tests/python_unittest/integration_test.py index 39c01046923..ad55e9aba6a 100755 --- a/projects/amdsmi/tests/python_unittest/integration_test.py +++ b/projects/amdsmi/tests/python_unittest/integration_test.py @@ -25,6 +25,7 @@ import sys import threading import unittest +import common amdsmi_path = os.environ.get("AMDSMI_PATH", "/opt/rocm/share/amd_smi") @@ -303,6 +304,52 @@ def test_bdf_device_id(self): print(" uuid is: {}".format(uuid)) print("\n") + def test_nic_bdf_device_id(self): + common.Common._skip_if_missing(self, [ + "amdsmi_get_nic_processor_handles", + "amdsmi_get_nic_info", + "amdsmi_get_nic_device_uuid", + ]) + self.setUp() + processors = amdsmi.amdsmi_get_nic_processor_handles() + self.assertGreaterEqual(len(processors), 1) + self.assertLessEqual(len(processors), 32) + for i in range(0, len(processors)): + bdf = "" + nic_info = amdsmi.amdsmi_get_nic_info(processors[i]) + if nic_info: + bdf = nic_info['bdf'] + print("\n\n###Test nic Processor {}, bdf: {}".format(i, bdf)) + print("\n###Test amdsmi_get_processor_handle_from_bdf \n") + processor = amdsmi.amdsmi_get_processor_handle_from_bdf(bdf) + print("\n###Test amdsmi_get_nic_device_uuid \n") + uuid = amdsmi.amdsmi_get_nic_device_uuid(processor) + print(" uuid is: {}".format(uuid)) + print() + self.tearDown() + + def test_switch_bdf_device_id(self): + common.Common._skip_if_missing(self, [ + "amdsmi_get_switch_processor_handles", + "amdsmi_get_switch_device_bdf", + "amdsmi_get_device_id", + ]) + self.setUp() + processors = amdsmi.amdsmi_get_switch_processor_handles() + self.assertGreaterEqual(len(processors), 1) + self.assertLessEqual(len(processors), 32) + for i in range(0, len(processors)): + bdf = amdsmi.amdsmi_get_switch_device_bdf(processors[i]) + print("\n\n###Test switch Processor {}, bdf: {}".format(i, bdf)) + print("\n###Test amdsmi_get_processor_handle_from_bdf \n") + processor = amdsmi.amdsmi_get_processor_handle_from_bdf(bdf) + print("\n###Test amdsmi_get_device_id \n") + device_id = amdsmi.amdsmi_get_device_id(processor) + print(" device_id is: {}".format(device_id)) + print() + self.tearDown() + + def test_board_info(self): processors = amdsmi.amdsmi_get_processor_handles() self.assertGreaterEqual(len(processors), 1)