Skip to content

Commit a50d774

Browse files
committed
refactor: get topology
Signed-off-by: thxCode <[email protected]>
1 parent f4e0fd6 commit a50d774

14 files changed

+335
-215
lines changed

gpustack_runtime/detector/__utils__.py

Lines changed: 51 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,30 @@ def get_numa_node_cpu_mapping() -> dict[int, list[int]]:
695695
return numa_cpu_mapping
696696

697697

698+
@lru_cache(maxsize=128)
699+
def get_numa_node_by_bdf(bdf: str) -> str:
700+
"""
701+
Get the NUMA node for a given PCI device BDF (Bus:Device.Function) address.
702+
703+
Args:
704+
bdf:
705+
The PCI device BDF address (e.g., "0000:00:1f.0").
706+
707+
Returns:
708+
The NUMA node index if found, otherwise blank string.
709+
710+
"""
711+
if bdf:
712+
with contextlib.suppress(Exception):
713+
with Path(f"/sys/bus/pci/devices/{bdf}/numa_node").open("r") as f:
714+
s = f.read().strip()
715+
numa_node = int(s)
716+
if numa_node >= 0:
717+
return str(numa_node)
718+
719+
return ""
720+
721+
698722
@lru_cache
699723
def map_cpu_affinity_to_numa_node(cpu_affinity: int | str | None) -> str:
700724
"""
@@ -715,6 +739,8 @@ def map_cpu_affinity_to_numa_node(cpu_affinity: int | str | None) -> str:
715739
if isinstance(cpu_affinity, int):
716740
cpu_indices = bitmask_to_list(cpu_affinity)
717741
else:
742+
if not cpu_affinity:
743+
return ""
718744
cpu_indices: list[int] = []
719745
for part in cpu_affinity.split(","):
720746
if "-" in part:
@@ -764,6 +790,8 @@ def map_numa_node_to_cpu_affinity(numa_node: int | str | None) -> str:
764790
if isinstance(numa_node, int):
765791
numa_indices = bitmask_to_list(numa_node)
766792
else:
793+
if not numa_node:
794+
return ""
767795
numa_indices: list[int] = []
768796
for part in numa_node.split(","):
769797
if "-" in part:
@@ -810,29 +838,6 @@ def bitmask_to_list(bitmask: int, offset: int = 0) -> list[int]:
810838
return indices
811839

812840

813-
def bitmask_to_str(bitmask_list: list) -> str:
814-
"""
815-
Convert a bitmask to a comma-separated string of set bit indices.
816-
817-
Args:
818-
bitmask_list:
819-
An integer list stores each item in bitmask.
820-
821-
Returns:
822-
If a bitmask are contiguous, returns a range string (e.g., "2-5"),
823-
Otherwise, returns a comma-separated string of indices (e.g., "0,2-4").
824-
825-
"""
826-
bits_lists = []
827-
offset = 0
828-
for bitmask in bitmask_list:
829-
if bitmask != 0:
830-
bits_lists.extend(bitmask_to_list(bitmask, offset))
831-
offset += get_bits_size()
832-
833-
return list_to_range_str(sorted(bits_lists))
834-
835-
836841
def list_to_range_str(indices: list[int]) -> str:
837842
"""
838843
Convert a list of indices to a comma-separated string with ranges.
@@ -872,3 +877,26 @@ def list_to_range_str(indices: list[int]) -> str:
872877
range_str = ",".join(range_str_parts)
873878

874879
return range_str
880+
881+
882+
def bitmask_to_str(bitmask_list: list) -> str:
883+
"""
884+
Convert a bitmask to a comma-separated string of set bit indices.
885+
886+
Args:
887+
bitmask_list:
888+
An integer list stores each item in bitmask.
889+
890+
Returns:
891+
If a bitmask are contiguous, returns a range string (e.g., "2-5"),
892+
Otherwise, returns a comma-separated string of indices (e.g., "0,2-4").
893+
894+
"""
895+
bits_lists = []
896+
offset = 0
897+
for bitmask in bitmask_list:
898+
if bitmask != 0:
899+
bits_lists.extend(bitmask_to_list(bitmask, offset))
900+
offset += get_bits_size()
901+
902+
return list_to_range_str(sorted(bits_lists))

gpustack_runtime/detector/amd.py

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
PCIDevice,
1313
byte_to_mebibyte,
1414
get_brief_version,
15+
get_numa_node_by_bdf,
1516
get_pci_devices,
1617
get_utilization,
1718
map_numa_node_to_cpu_affinity,
@@ -216,7 +217,7 @@ def detect(self) -> Devices | None:
216217

217218
with contextlib.suppress(pyamdsmi.AmdSmiException):
218219
dev_bdf = pyamdsmi.amdsmi_get_gpu_device_bdf(dev)
219-
dev_appendix["bdf"] = str(dev_bdf)
220+
dev_appendix["bdf"] = str(dev_bdf).lower()
220221

221222
ret.append(
222223
Device(
@@ -277,10 +278,8 @@ def get_topology(self, devices: Devices | None = None) -> Topology | None:
277278
devs_mapping = None
278279

279280
def get_device_handle(dev: Device):
280-
if "bdf" in dev.appendix:
281-
return pyamdsmi.amdsmi_get_processor_handle_from_bdf(
282-
dev.appendix["bdf"],
283-
)
281+
if bdf := dev.appendix.get("bdf", None):
282+
return pyamdsmi.amdsmi_get_processor_handle_from_bdf(bdf)
284283
nonlocal devs_mapping
285284
if devs_mapping is None:
286285
devs = pyamdsmi.amdsmi_get_processor_handles()
@@ -293,24 +292,32 @@ def get_device_handle(dev: Device):
293292
for i, dev_i in enumerate(devices):
294293
dev_i_handle = get_device_handle(dev_i)
295294

296-
# Get NUMA affinity.
297-
try:
298-
dev_i_numa_node = pyamdsmi.amdsmi_topo_get_numa_node_number(
299-
dev_i_handle,
295+
# Get affinity with PCIe BDF if possible.
296+
if dev_i_bdf := dev_i.appendix.get("bdf", ""):
297+
numa_node = get_numa_node_by_bdf(dev_i_bdf)
298+
topology.devices_numa_affinities[i] = numa_node
299+
topology.devices_cpu_affinities[i] = map_numa_node_to_cpu_affinity(
300+
numa_node,
300301
)
301-
topology.devices_numa_affinities[i] = str(dev_i_numa_node)
302-
except pyamdsmi.AmdSmiException:
303-
debug_log_exception(
304-
logger,
305-
"Failed to get NUMA affinity for device %d",
306-
dev_i.index,
302+
# Otherwise, get affinity via AMD SMI.
303+
if not topology.devices_cpu_affinities[i]:
304+
# Get NUMA affinity.
305+
try:
306+
dev_i_numa_node = pyamdsmi.amdsmi_topo_get_numa_node_number(
307+
dev_i_handle,
308+
)
309+
topology.devices_numa_affinities[i] = str(dev_i_numa_node)
310+
except pyamdsmi.AmdSmiException:
311+
debug_log_exception(
312+
logger,
313+
"Failed to get NUMA affinity for device %d",
314+
dev_i.index,
315+
)
316+
# Get CPU affinity.
317+
topology.devices_cpu_affinities[i] = map_numa_node_to_cpu_affinity(
318+
numa_node=topology.devices_numa_affinities[i],
307319
)
308320

309-
# Get CPU affinity.
310-
topology.devices_cpu_affinities[i] = map_numa_node_to_cpu_affinity(
311-
numa_node=topology.devices_numa_affinities[i],
312-
)
313-
314321
# Get distances to other devices.
315322
for j, dev_j in enumerate(devices):
316323
if i == j:

gpustack_runtime/detector/ascend.py

Lines changed: 49 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@
1818
from .__utils__ import (
1919
PCIDevice,
2020
get_brief_version,
21+
get_numa_node_by_bdf,
2122
get_pci_devices,
2223
get_utilization,
2324
map_cpu_affinity_to_numa_node,
25+
map_numa_node_to_cpu_affinity,
2426
)
2527

2628
logger = logging.getLogger(__name__)
@@ -111,9 +113,6 @@ def detect(self) -> Devices | None:
111113

112114
_, card_list = pydcmi.dcmi_get_card_list()
113115
for dev_card_id in card_list:
114-
device_id_max_in_card, _, _ = pydcmi.dcmi_get_device_id_in_card(
115-
dev_card_id,
116-
)
117116
device_num_in_card = pydcmi.dcmi_get_device_num_in_card(dev_card_id)
118117
for dev_device_id in range(device_num_in_card):
119118
dev_is_vgpu = False
@@ -193,7 +192,7 @@ def detect(self) -> Devices | None:
193192
"vgpu": dev_is_vgpu,
194193
"card_id": dev_card_id,
195194
"device_id": dev_device_id,
196-
"device_id_max": device_id_max_in_card - 1,
195+
"device_id_max": device_num_in_card - 1,
197196
}
198197

199198
dev_roce_ip, dev_roce_mask, dev_roce_gateway = (
@@ -209,6 +208,13 @@ def detect(self) -> Devices | None:
209208
if dev_roce_gateway:
210209
dev_appendix["roce_gateway"] = str(dev_roce_gateway)
211210

211+
with contextlib.suppress(pydcmi.DCMIError):
212+
dev_bdf = pydcmi.dcmi_get_device_bdf(
213+
dev_card_id,
214+
dev_device_id,
215+
)
216+
dev_appendix["bdf"] = str(dev_bdf).lower()
217+
212218
ret.append(
213219
Device(
214220
manufacturer=self.manufacturer,
@@ -265,39 +271,58 @@ def get_topology(self, devices: Devices | None = None) -> Topology | None:
265271
pydcmi.dcmi_init()
266272

267273
for i, dev_i in enumerate(devices):
268-
# Get CPU affinity.
269-
try:
270-
cpu_affinity = pydcmi.dcmi_get_affinity_cpu_info_by_device_id(
271-
dev_i.appendix["card_id"],
272-
dev_i.appendix["device_id"],
274+
dev_i_card_id = dev_i.appendix["card_id"]
275+
dev_i_device_id = dev_i.appendix["device_id"]
276+
277+
# Get affinity with PCIe BDF if possible.
278+
if dev_i_bdf := dev_i.appendix.get("bdf", ""):
279+
numa_node = get_numa_node_by_bdf(dev_i_bdf)
280+
topology.devices_numa_affinities[i] = numa_node
281+
topology.devices_cpu_affinities[i] = map_numa_node_to_cpu_affinity(
282+
numa_node,
273283
)
274-
topology.devices_cpu_affinities[i] = cpu_affinity
275-
except pydcmi.DCMIError:
276-
debug_log_exception(
277-
slogger,
278-
"Failed to get CPU affinity for device %d",
279-
dev_i.index,
284+
# Otherwise, get affinity via DCMI.
285+
if not topology.devices_cpu_affinities[i]:
286+
# Get CPU affinity.
287+
try:
288+
cpu_affinity = pydcmi.dcmi_get_affinity_cpu_info_by_device_id(
289+
dev_i.appendix["card_id"],
290+
dev_i.appendix["device_id"],
291+
)
292+
topology.devices_cpu_affinities[i] = cpu_affinity
293+
except pydcmi.DCMIError:
294+
debug_log_exception(
295+
slogger,
296+
"Failed to get CPU affinity for device %d",
297+
dev_i.index,
298+
)
299+
# Get NUMA affinity.
300+
topology.devices_numa_affinities[i] = map_cpu_affinity_to_numa_node(
301+
cpu_affinity=topology.devices_cpu_affinities[i],
280302
)
281303

282-
# Get NUMA affinity.
283-
topology.devices_numa_affinities[i] = map_cpu_affinity_to_numa_node(
284-
cpu_affinity=topology.devices_cpu_affinities[i],
285-
)
286-
287304
# Get distances to other devices.
288305
for j, dev_j in enumerate(devices):
289306
if i == j:
290307
continue
291308
if topology.devices_distances[i][j] != 0:
292309
continue
293310

311+
dev_j_card_id = dev_j.appendix["card_id"]
312+
dev_j_device_id = dev_j.appendix["device_id"]
313+
314+
# If two devices are the same card,
315+
# skip distance calculation.
316+
if dev_i_card_id == dev_j_card_id:
317+
continue
318+
294319
distance = TopologyDistanceEnum.UNK
295320
try:
296321
topo_type = pydcmi.dcmi_get_topo_info_by_device_id(
297-
dev_i.appendix["card_id"],
298-
dev_i.appendix["device_id"],
299-
dev_j.appendix["card_id"],
300-
dev_j.appendix["device_id"],
322+
dev_i_card_id,
323+
dev_i_device_id,
324+
dev_j_card_id,
325+
dev_j_device_id,
301326
)
302327
distance = _TOPO_TYPE_DISTANCE_MAPPING.get(topo_type, distance)
303328
except pydcmi.DCMIError:

0 commit comments

Comments
 (0)