A Linux shell script that analyzes PCIe devices on a system and groups them by their NUMA (Non-Uniform Memory Access) node. This tool is useful for understanding system topology and optimizing performance by identifying which PCIe devices are local to which NUMA nodes.
- Lists physical PCIe devices (NVMe, NICs, GPUs, storage controllers, etc.) by default
- Automatically filters out infrastructure devices (bridges, IOMMU, system peripherals)
- Groups devices by their associated NUMA node
- Displays device information including PCI address and description
- Shows the bound kernel driver and kernel device names (e.g.,
nvme0n1,ens1f0,sda) for each device; flags devices with no driver bound - Maps devices to their physical motherboard slot designation (e.g.,
CPU SLOT3 PCI-E 4.0 X16) using SMBIOS data when run as root - Verbose mode shows PCIe link speed/width and flags links that trained below their maximum (
DEGRADED) - Provides summary statistics with filtered device count
- Colored output for better readability
- Multiple output modes (all, verbose, summary-only)
- Linux operating system with sysfs support
- Bash shell
- Optional:
lspciutility for detailed device descriptions (part ofpciutilspackage) - Optional:
dmidecodeand root privileges for motherboard slot designations
Simply clone this repository or download the script:
git clone <repository-url>
cd numatopo
chmod +x pcie-numa-analyzer.sh./pcie-numa-analyzer.shBy default, this shows only physical/endpoint devices (NVMe drives, NICs, GPUs, storage controllers, USB controllers, etc.), filtering out infrastructure like bridges, IOMMU, and system peripherals.
-h, --help: Display help message-a, --all: Show all devices including bridges and infrastructure-b, --by-slot: Group devices by physical motherboard slot — a top-down view of the system. Each slot heading notes its NUMA node; onboard devices (no slot) are grouped by NUMA node in a section below-v, --verbose: Show verbose output with additional device details (vendor/device IDs, class codes, PCIe link speed/width)-n, --no-color: Disable colored output (useful for piping to files)-s, --summary: Show only summary statistics
Physical devices only (default):
./pcie-numa-analyzer.shAll devices including bridges:
./pcie-numa-analyzer.sh -aVerbose output with all device details:
./pcie-numa-analyzer.sh -vSummary only:
./pcie-numa-analyzer.sh -sWithout colors (for logging):
./pcie-numa-analyzer.sh -n > pcie-topology.txtEach PCIe device is displayed with:
- PCI Address: The bus:device.function address (e.g.,
0000:00:1f.2) - Description: Human-readable device name (from lspci)
- Driver and kernel names: The bound kernel driver and associated kernel device names, e.g.
[nvme: nvme3n1],[tg3: eno1]; a yellow[no driver]marks devices with nothing bound - Motherboard slot: The physical slot designation from SMBIOS, e.g.
[slot: CPU SLOT3 PCI-E 4.0 X16]— matching what is printed on the motherboard silkscreen. Shown only for devices in slots the BIOS reports (onboard devices have no slot). Requiresdmidecodeand root; silently omitted otherwise. All functions of a multi-function card (e.g., both ports of a dual-port NIC) receive the same slot tag. Bifurcated slots (e.g., an x16 riser carrying multiple NVMe drives) are handled by joining the kernel's per-port slot numbers from/sys/bus/pci/slotswith the SMBIOS slot IDs, so every device in the slot is labeled even though SMBIOS records only one bus address per slot. When a bifurcated port has no BIOS slot record of its own, the slot is inferred from topology: a bifurcated slot spans consecutive sibling root ports (functions of one parent device) starting at the port SMBIOS's Bus Address names, and the record's Data Bus Width bounds how many x4 devices fit. Each slot label extends upward from its anchor port to unlabeled devices of the same class within that lane budget, stopping at any port that already carries a label or a different device class. Connectors the BIOS describes only via PCIe Slot Capabilities (e.g., SlimSAS ports) get a rawSlot Nlabel for their own bus — those firmware-internal numbers are never joined to SMBIOS slot IDs, since they need not match - Onboard designation: Devices named in SMBIOS type 41 (Onboard Devices Extended Information) are tagged e.g.
[onboard: ASPEED Video AST2500]; disabled entries are ignored - Vendor/Device IDs: In verbose mode (e.g.,
0x8086:0x9d03) - Class Code: In verbose mode (e.g.,
0x010601for SATA controller) - Link speed/width: In verbose mode, e.g.
[16.0GT/s x8]; a link that trained below its maximum is flagged asDEGRADED(max ...). Note: a DEGRADED flag can be transient on power-managed (ASPM) links — verify under I/O load before reseating hardware
- NUMA Node N: Devices are grouped under their respective NUMA node number (0, 1, 2, etc.)
- NUMA Node N/A: Devices that don't have NUMA information or are on systems without NUMA support
By default, the script hides infrastructure devices based on PCI base class codes:
| Base Class | Description | Filtered |
|---|---|---|
0x05 |
Memory controller | Yes |
0x06 |
Bridge (host, PCI-PCI, ISA, etc.) | Yes |
0x08 |
System peripheral (IOMMU, PIC, DMA) | Yes |
0x10 |
Encryption controller (e.g., AMD PTDMA/PSP) | Yes |
0x13 |
Non-Essential Instrumentation | Yes |
| All others | Storage, network, display, USB, etc. | No (shown) |
Use -a to see all devices including infrastructure.
From an AMD EPYC Milan server with 4 NUMA nodes:
========================================
PCIe Devices Grouped by NUMA Node
========================================
NUMA Node: 0
0000:c1:00.0 - Non-Volatile memory controller: Micron Technology Inc 7500 PRO NVMe SSD
0000:c2:00.0 - Non-Volatile memory controller: Micron Technology Inc 7500 PRO NVMe SSD
0000:c3:00.0 - Non-Volatile memory controller: Intel Corporation PCIe Data Center SSD
0000:c4:00.0 - Non-Volatile memory controller: Intel Corporation PCIe Data Center SSD
NUMA Node: 2
0000:81:00.0 - Non-Volatile memory controller: Intel Corporation PCIe Data Center SSD
0000:82:00.0 - Non-Volatile memory controller: Intel Corporation PCIe Data Center SSD
0000:83:00.0 - Non-Volatile memory controller: Micron Technology Inc 7500 PRO NVMe SSD
0000:84:00.0 - Non-Volatile memory controller: Micron Technology Inc 7500 PRO NVMe SSD
NUMA Node: 4
0000:41:00.0 - Non-Volatile memory controller: Intel Corporation PCIe Data Center SSD
0000:47:00.0 - USB controller: ASMedia Technology Inc. ASM1042A USB 3.0 Host Controller
0000:49:00.0 - VGA compatible controller: ASPEED Technology, Inc. ASPEED Graphics Family
0000:4b:00.0 - Ethernet controller: Broadcom Inc. NetXtreme BCM5720 Gigabit Ethernet PCIe
0000:4b:00.1 - Ethernet controller: Broadcom Inc. NetXtreme BCM5720 Gigabit Ethernet PCIe
0000:4e:00.0 - SATA controller: Advanced Micro Devices, Inc. [AMD] FCH SATA Controller
NUMA Node: 6
0000:01:00.0 - Non-Volatile memory controller: Intel Corporation PCIe Data Center SSD
0000:02:00.0 - Non-Volatile memory controller: Intel Corporation PCIe Data Center SSD
0000:06:00.3 - USB controller: Advanced Micro Devices, Inc. [AMD] Starship USB 3.0 Host Controller
========================================
Summary
========================================
NUMA Node 0: 4 device(s)
NUMA Node 2: 4 device(s)
NUMA Node 4: 6 device(s)
NUMA Node 6: 3 device(s)
Total PCIe devices: 17
(95 infrastructure device(s) hidden, use -a to show all)
The script reads information from the Linux sysfs virtual filesystem:
- Device enumeration: Scans
/sys/bus/pci/devices/for all PCIe devices - NUMA node detection: Reads
/sys/bus/pci/devices/<device>/numa_nodefor each device - Infrastructure filtering: Checks the PCI base class code from
/sys/bus/pci/devices/<device>/classto filter out bridges and system peripherals (unless-ais used) - Device details: Retrieves vendor, device, and class information from sysfs
- Description lookup: Uses
lspciif available for human-readable device names - Grouping and display: Organizes devices by NUMA node and displays them
NUMA (Non-Uniform Memory Access) is a memory architecture used in multi-processor systems where memory access time depends on the memory location relative to a processor. PCIe devices are typically attached to specific NUMA nodes, and for optimal performance:
- Processes should run on CPUs in the same NUMA node as the devices they interact with
- Memory allocations should be from the same NUMA node as the accessing CPU/device
- Network and storage workloads benefit significantly from NUMA-aware placement
If you get permission denied errors, the script may need elevated privileges:
sudo ./pcie-numa-analyzer.shIf all devices show "NUMA Node N/A":
- Your system may not have NUMA configured (common on single-socket systems)
- NUMA support may be disabled in BIOS
- The kernel may not have NUMA support enabled
If device descriptions show as "Unknown (lspci not available)":
- Install pciutils:
sudo apt-get install pciutils(Debian/Ubuntu) orsudo yum install pciutils(RHEL/CentOS)
- Performance optimization: Identify which devices are local to which NUMA nodes for workload placement
- System auditing: Document PCIe device topology
- Capacity planning: Understand device distribution across NUMA nodes
- Troubleshooting: Diagnose performance issues related to cross-NUMA access
- HPC and high-performance networking: Optimize device-to-CPU affinity
This project is provided as-is for system administration and performance optimization purposes.
Contributions are welcome! Please feel free to submit issues or pull requests.