Files
TheRock/rocm-systems/projects/amdsmi/amdsmi_cli/subcommands/topology.py
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

1106 lines
50 KiB
Python

#!/usr/bin/env python3
#
# Copyright (C) Advanced Micro Devices. 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.
import logging
from amdsmi import amdsmi_exception, amdsmi_interface
class TopologyCommands:
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
is_single_nic_request = False # -N option
is_single_switch_request = False # --switch option
is_single_gpu_request = False # -g option
gpucount = 0
niccount = 0
switchcount = 0
# The topology parser conditionally registers --nic / --switch based on
# is_brcm_nic_initialized() / is_brcm_switch_initialized(). On hosts
# where one is gated off, the corresponding argparse attribute is
# absent on the namespace; getattr() guards the unconditional access
# below to prevent AttributeError on single-subsystem hosts.
if getattr(args, "nic", None) is None:
args.nic = self.device_handles_brcm_nics
if not isinstance(args.nic, list):
args.nic = [args.nic]
if len(args.nic) == 1:
is_single_nic_request = True
niccount = len(args.nic)
if getattr(args, "switch", None) is None:
args.switch = self.device_handles_switchs
if not isinstance(args.switch, list):
args.switch = [args.switch]
if len(args.switch) == 1:
is_single_switch_request = True
switchcount = len(args.switch)
if args.gpu is None:
args.gpu = self.device_handles
if not isinstance(args.gpu, list):
args.gpu = [args.gpu]
if len(args.gpu) == 1:
is_single_gpu_request = True
gpucount = len(args.gpu)
# 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)
try:
status = amdsmi_interface.amdsmi_get_nic_gpu_topo_info(dest_nic, gpu_dest)
except amdsmi_exception.AmdSmiLibraryException:
status = "N/A"
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 is_single_nic_request:
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 is_single_nic_request:
gpucount = 0
niccount = 1
switchcount = 0
if is_single_switch_request:
gpucount = 0
niccount = 0
switchcount = 1
if is_single_gpu_request:
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(gpu_bdf)
# 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(nic_bdf)
# 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,
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
access (bool) - Value override for args.access
weight (bool) - Value override for args.weight
hops (bool) - Value override for args.hops
type (bool) - Value override for args.type
numa_bw (bool) - Value override for args.numa_bw
coherent (bool) - Value override for args.coherent
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
"""
# Set args.* to passed in arguments
if gpu:
args.gpu = gpu
if access:
args.access = access
if weight:
args.weight = weight
if hops:
args.hops = hops
if link_type:
args.link_type = link_type
if numa_bw:
args.numa_bw = numa_bw
if coherent:
args.coherent = coherent
if atomics:
args.atomics = atomics
if dma:
args.dma = dma
if bi_dir:
args.bi_dir = bi_dir
if nic:
args.nic = nic
if switch:
args.switch = switch
# nic_topo / nic_switch are only registered on the parser when their
# respective subsystem is initialized, so guard every namespace access
# with getattr() to stay safe on single-subsystem hosts.
nic_topo_req = getattr(args, "nic_topo", False)
nic_switch_req = getattr(args, "nic_switch", False)
if (self.helpers.is_brcm_nic_initialized() and (nic_topo or nic_topo_req)) or (
self.helpers.is_brcm_switch_initialized() and (nic_switch or nic_switch_req)
):
self.topology_nic(
args,
multiple_devices,
args.gpu,
getattr(args, "nic", None),
nic_topo_req,
nic_switch_req,
multiple_device_enabled,
getattr(args, "switch", None),
)
return
# Handle No GPU passed
if args.gpu == None:
args.gpu = self.device_handles
if not isinstance(args.gpu, list):
args.gpu = [args.gpu]
# Handle all args being false
if not any(
[
args.access,
args.weight,
args.hops,
args.link_type,
args.numa_bw,
args.coherent,
args.atomics,
args.dma,
args.bi_dir,
]
):
args.access = args.weight = args.hops = args.link_type = args.numa_bw = (
args.coherent
) = args.atomics = args.dma = args.bi_dir = True
# Clear the table header
self.logger.table_header = "".rjust(12)
if not self.group_check_printed:
self.helpers.check_required_groups()
self.group_check_printed = True
p2p_status_cache = {}
def get_cached_p2p_status(src_gpu, dest_gpu):
# Get P2P status with caching to avoid duplicate calls
src_gpu_id = self.helpers.get_gpu_id_from_device_handle(src_gpu)
dest_gpu_id = self.helpers.get_gpu_id_from_device_handle(dest_gpu)
key = (src_gpu_id, dest_gpu_id)
if key not in p2p_status_cache:
try:
if src_gpu == dest_gpu:
p2p_status_cache[key] = {
"cap": {
"is_iolink_coherent": -1,
"is_iolink_atomics_32bit": -1,
"is_iolink_atomics_64bit": -1,
"is_iolink_dma": -1,
"is_iolink_bi_directional": -1,
}
}
else:
p2p_status_cache[key] = amdsmi_interface.amdsmi_topo_get_p2p_status(
src_gpu, dest_gpu
)
except amdsmi_exception.AmdSmiLibraryException as e:
logging.debug(
"Failed to get link status for %s to %s | %s",
src_gpu_id,
dest_gpu_id,
e.get_error_info(),
)
p2p_status_cache[key] = {
"cap": {
"is_iolink_coherent": -1,
"is_iolink_atomics_32bit": -1,
"is_iolink_atomics_64bit": -1,
"is_iolink_dma": -1,
"is_iolink_bi_directional": -1,
}
}
return p2p_status_cache[key]
# Populate the possible gpus
topo_values = []
for src_gpu_index, src_gpu in enumerate(args.gpu):
src_gpu_id = self.helpers.get_gpu_id_from_device_handle(src_gpu)
topo_values.append({"gpu": src_gpu_id})
src_gpu_bdf = amdsmi_interface.amdsmi_get_gpu_device_bdf(src_gpu)
topo_values[src_gpu_index]["bdf"] = src_gpu_bdf
self.logger.table_header += src_gpu_bdf.rjust(13)
if not self.logger.is_json_format():
continue # below is for JSON format only
##########################
# JSON formatting start #
##########################
links = []
# create json obj for data alignment
# dest_gpu_links = {
# "gpu": GPU #
# "bdf": BDF identification
# "weight": 0 - self (current node); weight >= 0 correlated with hops (GPU-CPU, GPU-GPU, GPU-CPU-CPU-GPU, etc..)
# "link_status": "ENABLED" - devices linked; "DISABLED" - devices not linked; Correlated to access
# "link_type": "SELF" - current node, "PCIE", "XGMI", "N/A" - no link,"UNKNOWN" - unidentified link type
# "num_hops": num_hops - # of hops between devices
# "bandwidth": numa_bw - The NUMA "minimum bandwidth-maximum bandwidth" between src and dest nodes
# "N/A" - self node or not connected devices
# "coherent": coherent - Coherent / Non-Coherent io links
# "atomics": atomics - 32 and 64-bit atomic io link capability between nodes
# "dma": dma - P2P direct memory access (DMA) link capability between nodes
# "bi_dir": bi_dir - P2P bi-directional link capability between nodes
# }
for dest_gpu_index, dest_gpu in enumerate(args.gpu):
link_type = "SELF"
if src_gpu != dest_gpu:
link_type = amdsmi_interface.amdsmi_topo_get_link_type(src_gpu, dest_gpu)[
"type"
]
if isinstance(link_type, int):
if link_type == amdsmi_interface.amdsmi_wrapper.AMDSMI_LINK_TYPE_INTERNAL:
link_type = "UNKNOWN"
elif link_type == amdsmi_interface.amdsmi_wrapper.AMDSMI_LINK_TYPE_PCIE:
link_type = "PCIE"
elif link_type == amdsmi_interface.amdsmi_wrapper.AMDSMI_LINK_TYPE_XGMI:
link_type = "XGMI"
else:
link_type = "N/A"
numa_bw = "N/A"
if src_gpu != dest_gpu:
try:
bw_dict = amdsmi_interface.amdsmi_get_minmax_bandwidth_between_processors(
src_gpu, dest_gpu
)
numa_bw = f"{bw_dict['min_bandwidth']}-{bw_dict['max_bandwidth']}"
except amdsmi_exception.AmdSmiLibraryException as e:
logging.debug(
"Failed to get min max bandwidth for %s to %s | %s",
self.helpers.get_gpu_id_from_device_handle(src_gpu),
self.helpers.get_gpu_id_from_device_handle(dest_gpu),
e.get_error_info(),
)
weight = 0
num_hops = 0
if src_gpu != dest_gpu:
weight = amdsmi_interface.amdsmi_topo_get_link_weight(src_gpu, dest_gpu)
num_hops = amdsmi_interface.amdsmi_topo_get_link_type(src_gpu, dest_gpu)["hops"]
link_status = amdsmi_interface.amdsmi_is_P2P_accessible(src_gpu, dest_gpu)
if link_status:
link_status = "ENABLED"
else:
link_status = "DISABLED"
link_coherent = "SELF"
link_atomics = "SELF"
link_dma = "SELF"
link_bi_dir = "SELF"
if src_gpu != dest_gpu:
try:
cap = get_cached_p2p_status(src_gpu, dest_gpu)["cap"]
link_coherent = (
"C"
if cap["is_iolink_coherent"] == 1
else "NC"
if cap["is_iolink_coherent"] == 0
else "N/A"
)
link_atomics = (
"64,32"
if cap["is_iolink_atomics_32bit"] == 1
and cap["is_iolink_atomics_64bit"] == 1
else "32"
if cap["is_iolink_atomics_32bit"] == 1
else "64"
if cap["is_iolink_atomics_64bit"] == 1
else "N/A"
)
link_dma = (
"T"
if cap["is_iolink_dma"] == 1
else "F"
if cap["is_iolink_dma"] == 0
else "N/A"
)
link_bi_dir = (
"T"
if cap["is_iolink_bi_directional"] == 1
else "F"
if cap["is_iolink_bi_directional"] == 0
else "N/A"
)
except amdsmi_exception.AmdSmiLibraryException as e:
logging.debug(
"Failed to get link status for %s to %s | %s",
self.helpers.get_gpu_id_from_device_handle(src_gpu),
self.helpers.get_gpu_id_from_device_handle(dest_gpu),
e.get_error_info(),
)
# link_status = amdsmi_is_P2P_accessible(src,dest)
dest_gpu_links = {
"gpu": self.helpers.get_gpu_id_from_device_handle(dest_gpu),
"bdf": amdsmi_interface.amdsmi_get_gpu_device_bdf(dest_gpu),
"weight": weight,
"link_status": link_status,
"link_type": link_type,
"num_hops": num_hops,
"bandwidth": numa_bw,
"coherent": link_coherent,
"atomics": link_atomics,
"dma": link_dma,
"bi_dir": link_bi_dir,
}
if not args.access:
del dest_gpu_links["link_status"]
if not args.weight:
del dest_gpu_links["weight"]
if not args.link_type:
del dest_gpu_links["link_type"]
if not args.hops:
del dest_gpu_links["num_hops"]
if not args.numa_bw:
del dest_gpu_links["bandwidth"]
if not args.coherent:
del dest_gpu_links["coherent"]
if not args.atomics:
del dest_gpu_links["atomics"]
if not args.dma:
del dest_gpu_links["dma"]
if not args.bi_dir:
del dest_gpu_links["bi_dir"]
links.append(dest_gpu_links)
dest_end = dest_gpu_index + 1 == len(args.gpu)
isEndOfSrc = src_gpu_index + 1 == len(args.gpu)
if dest_end:
topo_values[src_gpu_index]["links"] = links
continue
if isEndOfSrc:
self.logger.multiple_device_output = topo_values
self.logger.print_output(multiple_device_enabled=True, tabular=True)
return
##########################
# JSON formatting end #
##########################
if args.access:
tabular_output = []
for src_gpu_index, src_gpu in enumerate(args.gpu):
src_gpu_bdf = amdsmi_interface.amdsmi_get_gpu_device_bdf(src_gpu)
if self.logger.is_human_readable_format():
tabular_output_dict = {"gpu": f"{src_gpu_bdf} "}
else:
tabular_output_dict = {"gpu": src_gpu_bdf}
src_gpu_links = {}
for dest_gpu in args.gpu:
dest_gpu_id = self.helpers.get_gpu_id_from_device_handle(dest_gpu)
dest_gpu_key = f"gpu_{dest_gpu_id}"
try:
dest_gpu_link_status = amdsmi_interface.amdsmi_is_P2P_accessible(
src_gpu, dest_gpu
)
if dest_gpu_link_status:
src_gpu_links[dest_gpu_key] = "ENABLED"
else:
src_gpu_links[dest_gpu_key] = "DISABLED"
except amdsmi_exception.AmdSmiLibraryException as e:
src_gpu_links[dest_gpu_key] = "N/A"
logging.debug(
"Failed to get link status for %s to %s | %s",
self.helpers.get_gpu_id_from_device_handle(src_gpu),
self.helpers.get_gpu_id_from_device_handle(dest_gpu),
e.get_error_info(),
)
topo_values[src_gpu_index]["link_accessibility"] = src_gpu_links
tabular_output_dict.update(src_gpu_links)
tabular_output.append(tabular_output_dict)
if self.logger.is_human_readable_format():
self.logger.multiple_device_output = tabular_output
self.logger.table_title = "ACCESS TABLE"
self.logger.print_output(multiple_device_enabled=True, tabular=True)
if args.weight:
tabular_output = []
for src_gpu_index, src_gpu in enumerate(args.gpu):
src_gpu_bdf = amdsmi_interface.amdsmi_get_gpu_device_bdf(src_gpu)
if self.logger.is_human_readable_format():
tabular_output_dict = {"gpu": f"{src_gpu_bdf} "}
else:
tabular_output_dict = {"gpu": src_gpu_bdf}
src_gpu_weight = {}
for dest_gpu in args.gpu:
dest_gpu_id = self.helpers.get_gpu_id_from_device_handle(dest_gpu)
dest_gpu_key = f"gpu_{dest_gpu_id}"
if src_gpu == dest_gpu:
src_gpu_weight[dest_gpu_key] = 0
continue
try:
dest_gpu_link_weight = amdsmi_interface.amdsmi_topo_get_link_weight(
src_gpu, dest_gpu
)
src_gpu_weight[dest_gpu_key] = dest_gpu_link_weight
except amdsmi_exception.AmdSmiLibraryException as e:
src_gpu_weight[dest_gpu_key] = "N/A"
logging.debug(
"Failed to get link weight for %s to %s | %s",
self.helpers.get_gpu_id_from_device_handle(src_gpu),
self.helpers.get_gpu_id_from_device_handle(dest_gpu),
e.get_error_info(),
)
topo_values[src_gpu_index]["weight"] = src_gpu_weight
tabular_output_dict.update(src_gpu_weight)
tabular_output.append(tabular_output_dict)
if self.logger.is_human_readable_format():
self.logger.multiple_device_output = tabular_output
self.logger.table_title = "WEIGHT TABLE"
self.logger.print_output(multiple_device_enabled=True, tabular=True)
if args.hops:
tabular_output = []
for src_gpu_index, src_gpu in enumerate(args.gpu):
src_gpu_bdf = amdsmi_interface.amdsmi_get_gpu_device_bdf(src_gpu)
if self.logger.is_human_readable_format():
tabular_output_dict = {"gpu": f"{src_gpu_bdf} "}
else:
tabular_output_dict = {"gpu": src_gpu_bdf}
src_gpu_hops = {}
for dest_gpu in args.gpu:
dest_gpu_id = self.helpers.get_gpu_id_from_device_handle(dest_gpu)
dest_gpu_key = f"gpu_{dest_gpu_id}"
if src_gpu == dest_gpu:
src_gpu_hops[dest_gpu_key] = 0
continue
try:
dest_gpu_hops = amdsmi_interface.amdsmi_topo_get_link_type(
src_gpu, dest_gpu
)["hops"]
src_gpu_hops[dest_gpu_key] = dest_gpu_hops
except amdsmi_exception.AmdSmiLibraryException as e:
src_gpu_hops[dest_gpu_key] = "N/A"
logging.debug(
"Failed to get link hops for %s to %s | %s",
self.helpers.get_gpu_id_from_device_handle(src_gpu),
self.helpers.get_gpu_id_from_device_handle(dest_gpu),
e.get_error_info(),
)
topo_values[src_gpu_index]["hops"] = src_gpu_hops
tabular_output_dict.update(src_gpu_hops)
tabular_output.append(tabular_output_dict)
if self.logger.is_human_readable_format():
self.logger.multiple_device_output = tabular_output
self.logger.table_title = "HOPS TABLE"
self.logger.print_output(multiple_device_enabled=True, tabular=True)
if args.link_type:
tabular_output = []
for src_gpu_index, src_gpu in enumerate(args.gpu):
src_gpu_bdf = amdsmi_interface.amdsmi_get_gpu_device_bdf(src_gpu)
if self.logger.is_human_readable_format():
tabular_output_dict = {"gpu": f"{src_gpu_bdf} "}
else:
tabular_output_dict = {"gpu": src_gpu_bdf}
src_gpu_link_type = {}
for dest_gpu in args.gpu:
dest_gpu_id = self.helpers.get_gpu_id_from_device_handle(dest_gpu)
dest_gpu_key = f"gpu_{dest_gpu_id}"
if src_gpu == dest_gpu:
src_gpu_link_type[dest_gpu_key] = "SELF"
continue
try:
link_type = amdsmi_interface.amdsmi_topo_get_link_type(src_gpu, dest_gpu)[
"type"
]
if isinstance(link_type, int):
if (
link_type
== amdsmi_interface.amdsmi_wrapper.AMDSMI_LINK_TYPE_INTERNAL
):
src_gpu_link_type[dest_gpu_key] = "UNKNOWN"
elif link_type == amdsmi_interface.amdsmi_wrapper.AMDSMI_LINK_TYPE_PCIE:
src_gpu_link_type[dest_gpu_key] = "PCIE"
elif link_type == amdsmi_interface.amdsmi_wrapper.AMDSMI_LINK_TYPE_XGMI:
src_gpu_link_type[dest_gpu_key] = "XGMI"
except amdsmi_exception.AmdSmiLibraryException as e:
src_gpu_link_type[dest_gpu_key] = "N/A"
logging.debug(
"Failed to get link type for %s to %s | %s",
self.helpers.get_gpu_id_from_device_handle(src_gpu),
self.helpers.get_gpu_id_from_device_handle(dest_gpu),
e.get_error_info(),
)
topo_values[src_gpu_index]["link_type"] = src_gpu_link_type
tabular_output_dict.update(src_gpu_link_type)
tabular_output.append(tabular_output_dict)
if self.logger.is_human_readable_format():
self.logger.multiple_device_output = tabular_output
self.logger.table_title = "LINK TYPE TABLE"
self.logger.print_output(multiple_device_enabled=True, tabular=True)
if args.numa_bw:
tabular_output = []
for src_gpu_index, src_gpu in enumerate(args.gpu):
src_gpu_bdf = amdsmi_interface.amdsmi_get_gpu_device_bdf(src_gpu)
if self.logger.is_human_readable_format():
tabular_output_dict = {"gpu": f"{src_gpu_bdf} "}
else:
tabular_output_dict = {"gpu": src_gpu_bdf}
src_gpu_link_type = {}
for dest_gpu in args.gpu:
dest_gpu_id = self.helpers.get_gpu_id_from_device_handle(dest_gpu)
dest_gpu_key = f"gpu_{dest_gpu_id}"
if src_gpu == dest_gpu:
src_gpu_link_type[dest_gpu_key] = "N/A"
continue
try:
link_type = amdsmi_interface.amdsmi_topo_get_link_type(src_gpu, dest_gpu)[
"type"
]
if isinstance(link_type, int):
if link_type != amdsmi_interface.amdsmi_wrapper.AMDSMI_LINK_TYPE_XGMI:
# non_xgmi = True
src_gpu_link_type[dest_gpu_key] = "N/A"
continue
except amdsmi_exception.AmdSmiLibraryException as e:
src_gpu_link_type[dest_gpu_key] = "N/A"
logging.debug(
"Failed to get link type for %s to %s | %s",
self.helpers.get_gpu_id_from_device_handle(src_gpu),
self.helpers.get_gpu_id_from_device_handle(dest_gpu),
e.get_error_info(),
)
try:
bw_dict = amdsmi_interface.amdsmi_get_minmax_bandwidth_between_processors(
src_gpu, dest_gpu
)
src_gpu_link_type[dest_gpu_key] = (
f"{bw_dict['min_bandwidth']}-{bw_dict['max_bandwidth']}"
)
except amdsmi_exception.AmdSmiLibraryException as e:
src_gpu_link_type[dest_gpu_key] = e.get_error_info()
logging.debug(
"Failed to get min max bandwidth for %s to %s | %s",
self.helpers.get_gpu_id_from_device_handle(src_gpu),
self.helpers.get_gpu_id_from_device_handle(dest_gpu),
e.get_error_info(),
)
topo_values[src_gpu_index]["numa_bandwidth"] = src_gpu_link_type
tabular_output_dict.update(src_gpu_link_type)
tabular_output.append(tabular_output_dict)
if self.logger.is_human_readable_format():
self.logger.multiple_device_output = tabular_output
self.logger.table_title = "NUMA BW TABLE"
self.logger.print_output(multiple_device_enabled=True, tabular=True)
if args.coherent:
tabular_output = []
for src_gpu_index, src_gpu in enumerate(args.gpu):
src_gpu_bdf = amdsmi_interface.amdsmi_get_gpu_device_bdf(src_gpu)
if self.logger.is_human_readable_format():
tabular_output_dict = {"gpu": f"{src_gpu_bdf} "}
else:
tabular_output_dict = {"gpu": src_gpu_bdf}
src_gpu_coherent = {}
for dest_gpu in args.gpu:
dest_gpu_id = self.helpers.get_gpu_id_from_device_handle(dest_gpu)
dest_gpu_key = f"gpu_{dest_gpu_id}"
if src_gpu == dest_gpu:
src_gpu_coherent[dest_gpu_key] = "SELF"
continue
try:
iolink_coherent = get_cached_p2p_status(src_gpu, dest_gpu)["cap"][
"is_iolink_coherent"
]
src_gpu_coherent[dest_gpu_key] = (
"C" if iolink_coherent == 1 else "NC" if iolink_coherent == 0 else "N/A"
)
except amdsmi_exception.AmdSmiLibraryException as e:
src_gpu_coherent[dest_gpu_key] = "N/A"
logging.debug(
"Failed to get link coherent for %s to %s | %s",
self.helpers.get_gpu_id_from_device_handle(src_gpu),
self.helpers.get_gpu_id_from_device_handle(dest_gpu),
e.get_error_info(),
)
topo_values[src_gpu_index]["coherent"] = src_gpu_coherent
tabular_output_dict.update(src_gpu_coherent)
tabular_output.append(tabular_output_dict)
if self.logger.is_human_readable_format():
self.logger.multiple_device_output = tabular_output
self.logger.table_title = "CACHE COHERENCY TABLE"
self.logger.print_output(multiple_device_enabled=True, tabular=True)
if args.atomics:
tabular_output = []
for src_gpu_index, src_gpu in enumerate(args.gpu):
src_gpu_bdf = amdsmi_interface.amdsmi_get_gpu_device_bdf(src_gpu)
if self.logger.is_human_readable_format():
tabular_output_dict = {"gpu": f"{src_gpu_bdf} "}
else:
tabular_output_dict = {"gpu": src_gpu_bdf}
src_gpu_atomics = {}
for dest_gpu in args.gpu:
dest_gpu_id = self.helpers.get_gpu_id_from_device_handle(dest_gpu)
dest_gpu_key = f"gpu_{dest_gpu_id}"
if src_gpu == dest_gpu:
src_gpu_atomics[dest_gpu_key] = "SELF"
continue
try:
cap = get_cached_p2p_status(src_gpu, dest_gpu)["cap"]
src_gpu_atomics[dest_gpu_key] = (
"64,32"
if cap["is_iolink_atomics_32bit"] == 1
and cap["is_iolink_atomics_64bit"] == 1
else "32"
if cap["is_iolink_atomics_32bit"] == 1
else "64"
if cap["is_iolink_atomics_64bit"] == 1
else "N/A"
)
except amdsmi_exception.AmdSmiLibraryException as e:
src_gpu_atomics[dest_gpu_key] = "N/A"
logging.debug(
"Failed to get link atomics for %s to %s | %s",
self.helpers.get_gpu_id_from_device_handle(src_gpu),
self.helpers.get_gpu_id_from_device_handle(dest_gpu),
e.get_error_info(),
)
topo_values[src_gpu_index]["atomics"] = src_gpu_atomics
tabular_output_dict.update(src_gpu_atomics)
tabular_output.append(tabular_output_dict)
if self.logger.is_human_readable_format():
self.logger.multiple_device_output = tabular_output
self.logger.table_title = "ATOMICS TABLE"
self.logger.print_output(multiple_device_enabled=True, tabular=True)
if args.dma:
tabular_output = []
for src_gpu_index, src_gpu in enumerate(args.gpu):
src_gpu_bdf = amdsmi_interface.amdsmi_get_gpu_device_bdf(src_gpu)
if self.logger.is_human_readable_format():
tabular_output_dict = {"gpu": f"{src_gpu_bdf} "}
else:
tabular_output_dict = {"gpu": src_gpu_bdf}
src_gpu_dma = {}
for dest_gpu in args.gpu:
dest_gpu_id = self.helpers.get_gpu_id_from_device_handle(dest_gpu)
dest_gpu_key = f"gpu_{dest_gpu_id}"
if src_gpu == dest_gpu:
src_gpu_dma[dest_gpu_key] = "SELF"
continue
try:
iolink_dma = get_cached_p2p_status(src_gpu, dest_gpu)["cap"][
"is_iolink_dma"
]
src_gpu_dma[dest_gpu_key] = (
"T" if iolink_dma == 1 else "F" if iolink_dma == 0 else "N/A"
)
except amdsmi_exception.AmdSmiLibraryException as e:
src_gpu_dma[dest_gpu_key] = "N/A"
logging.debug(
"Failed to get link dma for %s to %s | %s",
self.helpers.get_gpu_id_from_device_handle(src_gpu),
self.helpers.get_gpu_id_from_device_handle(dest_gpu),
e.get_error_info(),
)
topo_values[src_gpu_index]["dma"] = src_gpu_dma
tabular_output_dict.update(src_gpu_dma)
tabular_output.append(tabular_output_dict)
if self.logger.is_human_readable_format():
self.logger.multiple_device_output = tabular_output
self.logger.table_title = "DMA TABLE"
self.logger.print_output(multiple_device_enabled=True, tabular=True)
if args.bi_dir:
tabular_output = []
for src_gpu_index, src_gpu in enumerate(args.gpu):
src_gpu_bdf = amdsmi_interface.amdsmi_get_gpu_device_bdf(src_gpu)
if self.logger.is_human_readable_format():
tabular_output_dict = {"gpu": f"{src_gpu_bdf} "}
else:
tabular_output_dict = {"gpu": src_gpu_bdf}
src_gpu_bi_dir = {}
for dest_gpu in args.gpu:
dest_gpu_id = self.helpers.get_gpu_id_from_device_handle(dest_gpu)
dest_gpu_key = f"gpu_{dest_gpu_id}"
if src_gpu == dest_gpu:
src_gpu_bi_dir[dest_gpu_key] = "SELF"
continue
try:
iolink_bi_dir = get_cached_p2p_status(src_gpu, dest_gpu)["cap"][
"is_iolink_bi_directional"
]
src_gpu_bi_dir[dest_gpu_key] = (
"T" if iolink_bi_dir == 1 else "F" if iolink_bi_dir == 0 else "N/A"
)
except amdsmi_exception.AmdSmiLibraryException as e:
src_gpu_bi_dir[dest_gpu_key] = "N/A"
logging.debug(
"Failed to get link bi-directional for %s to %s | %s",
self.helpers.get_gpu_id_from_device_handle(src_gpu),
self.helpers.get_gpu_id_from_device_handle(dest_gpu),
e.get_error_info(),
)
topo_values[src_gpu_index]["bi_dir"] = src_gpu_bi_dir
tabular_output_dict.update(src_gpu_bi_dir)
tabular_output.append(tabular_output_dict)
if self.logger.is_human_readable_format():
self.logger.multiple_device_output = tabular_output
self.logger.table_title = "BI-DIRECTIONAL 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:",
" SELF = Current GPU",
" ENABLED / DISABLED = Link is enabled or disabled",
" N/A = Not supported",
" T/F = True / False",
" C/NC = Coherent / Non-Coherent io links",
" 64,32 = 64 bit and 32 bit atomic support",
" <BW from>-<BW to>",
]
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")
self.logger.multiple_device_output = topo_values
if self.logger.is_csv_format():
new_output = []
for elem in self.logger.multiple_device_output:
new_output.append(self.logger.flatten_dict(elem, topology_override=True))
self.logger.multiple_device_output = new_output
if not self.logger.is_human_readable_format():
self.logger.print_output(multiple_device_enabled=True)