2 * Copyright © 2009 CNRS
3 * Copyright © 2009-2018 Inria. All rights reserved.
4 * Copyright © 2009-2011, 2013 Université Bordeaux
5 * Copyright © 2014-2018 Cisco Systems, Inc. All rights reserved.
6 * Copyright © 2015 Research Organization for Information Science
7 * and Technology (RIST). All rights reserved.
8 * See COPYING in top-level directory.
11 #include <private/autogen/config.h>
13 #include <hwloc/helper.h>
14 #include <hwloc/plugins.h>
16 /* private headers allowed for convenience because this plugin is built within hwloc */
17 #include <private/debug.h>
18 #include <private/misc.h>
25 #ifdef HWLOC_LINUX_SYS
29 #include <pciaccess.h>
31 #ifndef PCI_HEADER_TYPE
32 #define PCI_HEADER_TYPE 0x0e
34 #ifndef PCI_HEADER_TYPE_BRIDGE
35 #define PCI_HEADER_TYPE_BRIDGE 1
38 #ifndef PCI_CLASS_DEVICE
39 #define PCI_CLASS_DEVICE 0x0a
41 #ifndef PCI_CLASS_BRIDGE_PCI
42 #define PCI_CLASS_BRIDGE_PCI 0x0604
45 #ifndef PCI_REVISION_ID
46 #define PCI_REVISION_ID 0x08
49 #ifndef PCI_SUBSYSTEM_VENDOR_ID
50 #define PCI_SUBSYSTEM_VENDOR_ID 0x2c
52 #ifndef PCI_SUBSYSTEM_ID
53 #define PCI_SUBSYSTEM_ID 0x2e
56 #ifndef PCI_PRIMARY_BUS
57 #define PCI_PRIMARY_BUS 0x18
59 #ifndef PCI_SECONDARY_BUS
60 #define PCI_SECONDARY_BUS 0x19
62 #ifndef PCI_SUBORDINATE_BUS
63 #define PCI_SUBORDINATE_BUS 0x1a
66 #ifndef PCI_CAP_ID_EXP
67 #define PCI_CAP_ID_EXP 0x10
70 #ifndef PCI_CAP_NORMAL
71 #define PCI_CAP_NORMAL 1
74 #define CONFIG_SPACE_CACHESIZE 256
77 #error pciaccess locking currently not implemented on Windows
79 #elif defined HWLOC_HAVE_PTHREAD_MUTEX
80 /* pthread mutex if available (except on windows) */
82 static pthread_mutex_t hwloc_pciaccess_mutex
= PTHREAD_MUTEX_INITIALIZER
;
83 #define HWLOC_PCIACCESS_LOCK() pthread_mutex_lock(&hwloc_pciaccess_mutex)
84 #define HWLOC_PCIACCESS_UNLOCK() pthread_mutex_unlock(&hwloc_pciaccess_mutex)
86 #else /* HWLOC_WIN_SYS || HWLOC_HAVE_PTHREAD_MUTEX */
87 #error No mutex implementation available
91 hwloc_look_pci(struct hwloc_backend
*backend
)
93 struct hwloc_topology
*topology
= backend
->topology
;
94 struct hwloc_obj
*first_obj
= NULL
, *last_obj
= NULL
;
96 struct pci_device_iterator
*iter
;
97 struct pci_device
*pcidev
;
98 #ifdef HWLOC_LINUX_SYS
102 if (!(hwloc_topology_get_flags(topology
) & (HWLOC_TOPOLOGY_FLAG_IO_DEVICES
|HWLOC_TOPOLOGY_FLAG_WHOLE_IO
)))
105 if (hwloc_get_next_pcidev(topology
, NULL
)) {
106 hwloc_debug("%s", "PCI objects already added, ignoring pci backend.\n");
110 if (!hwloc_topology_is_thissystem(topology
)) {
111 hwloc_debug("%s", "\nno PCI detection (not thissystem)\n");
115 hwloc_debug("%s", "\nScanning PCI buses...\n");
117 /* pciaccess isn't thread-safe. it uses a single global variable that doesn't have
118 * refcounting, and is dynamically reallocated when vendor/device names are needed, etc.
120 HWLOC_PCIACCESS_LOCK();
122 /* initialize PCI scanning */
123 ret
= pci_system_init();
125 HWLOC_PCIACCESS_UNLOCK();
126 hwloc_debug("%s", "Can not initialize libpciaccess\n");
130 iter
= pci_slot_match_iterator_create(NULL
);
132 /* iterate over devices */
133 for (pcidev
= pci_device_next(iter
);
135 pcidev
= pci_device_next(iter
))
137 const char *vendorname
, *devicename
, *fullname
;
138 unsigned char config_space_cache
[CONFIG_SPACE_CACHESIZE
];
139 struct hwloc_obj
*obj
;
142 unsigned device_class
;
143 unsigned short tmp16
;
147 /* initialize the config space in case we fail to read it (missing permissions, etc). */
148 memset(config_space_cache
, 0xff, CONFIG_SPACE_CACHESIZE
);
149 pci_device_probe(pcidev
);
150 pci_device_cfg_read(pcidev
, config_space_cache
, 0, CONFIG_SPACE_CACHESIZE
, NULL
);
152 /* try to read the domain */
153 domain
= pcidev
->domain
;
155 /* try to read the device_class */
156 device_class
= pcidev
->device_class
>> 8;
158 /* fixup SR-IOV buggy VF device/vendor IDs */
159 if (0xffff == pcidev
->vendor_id
&& 0xffff == pcidev
->device_id
) {
160 /* SR-IOV puts ffff:ffff in Virtual Function config space.
161 * The actual VF device ID is stored at a special (dynamic) location in the Physical Function config space.
162 * VF and PF have the same vendor ID.
164 * libpciaccess just returns ffff:ffff, needs to be fixed.
165 * linuxpci is OK because sysfs files are already fixed the kernel.
166 * (pciutils is OK when it uses those Linux sysfs files.)
168 * Reading these files is an easy way to work around the libpciaccess issue on Linux,
169 * but we have no way to know if this is caused by SR-IOV or not.
172 * If PF has CAP_ID_PCIX or CAP_ID_EXP (offset>0),
173 * look for extended capability PCI_EXT_CAP_ID_SRIOV (need extended config space (more than 256 bytes)),
174 * then read the VF device ID after it (PCI_IOV_DID bytes later).
175 * Needs access to extended config space (needs root on Linux).
177 * Add string info attributes in VF and PF objects?
179 #ifdef HWLOC_LINUX_SYS
180 /* Workaround for Linux (the kernel returns the VF device/vendor IDs). */
186 snprintf(path
, sizeof(path
), "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/vendor",
187 domain
, pcidev
->bus
, pcidev
->dev
, pcidev
->func
);
188 file
= fopen(path
, "r");
190 bytes_read
= fread(value
, 1, sizeof(value
), file
);
193 /* fixup the pciaccess struct so that pci_device_get_vendor_name() is correct later. */
194 pcidev
->vendor_id
= strtoul(value
, NULL
, 16);
197 snprintf(path
, sizeof(path
), "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/device",
198 domain
, pcidev
->bus
, pcidev
->dev
, pcidev
->func
);
199 file
= fopen(path
, "r");
201 bytes_read
= fread(value
, 1, sizeof(value
), file
);
204 /* fixup the pciaccess struct so that pci_device_get_device_name() is correct later. */
205 pcidev
->device_id
= strtoul(value
, NULL
, 16);
210 /* might be useful for debugging (note that domain might be truncated) */
211 os_index
= (domain
<< 20) + (pcidev
->bus
<< 12) + (pcidev
->dev
<< 4) + pcidev
->func
;
213 obj
= hwloc_alloc_setup_object(HWLOC_OBJ_PCI_DEVICE
, os_index
);
214 obj
->attr
->pcidev
.domain
= domain
;
215 obj
->attr
->pcidev
.bus
= pcidev
->bus
;
216 obj
->attr
->pcidev
.dev
= pcidev
->dev
;
217 obj
->attr
->pcidev
.func
= pcidev
->func
;
218 obj
->attr
->pcidev
.vendor_id
= pcidev
->vendor_id
;
219 obj
->attr
->pcidev
.device_id
= pcidev
->device_id
;
220 obj
->attr
->pcidev
.class_id
= device_class
;
221 obj
->attr
->pcidev
.revision
= config_space_cache
[PCI_REVISION_ID
];
223 obj
->attr
->pcidev
.linkspeed
= 0; /* unknown */
224 offset
= hwloc_pci_find_cap(config_space_cache
, PCI_CAP_ID_EXP
);
226 if (offset
> 0 && offset
+ 20 /* size of PCI express block up to link status */ <= CONFIG_SPACE_CACHESIZE
) {
227 hwloc_pci_find_linkspeed(config_space_cache
, offset
, &obj
->attr
->pcidev
.linkspeed
);
228 #ifdef HWLOC_LINUX_SYS
230 /* if not available from config-space (extended part is root-only), look in Linux sysfs files added in 4.13 */
237 snprintf(path
, sizeof(path
), "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/current_link_speed",
238 domain
, pcidev
->bus
, pcidev
->dev
, pcidev
->func
);
239 file
= fopen(path
, "r");
241 bytes_read
= fread(value
, 1, sizeof(value
), file
);
244 speed
= hwloc_linux_pci_link_speed_from_string(value
);
246 snprintf(path
, sizeof(path
), "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/current_link_width",
247 domain
, pcidev
->bus
, pcidev
->dev
, pcidev
->func
);
248 file
= fopen(path
, "r");
250 bytes_read
= fread(value
, 1, sizeof(value
), file
);
255 obj
->attr
->pcidev
.linkspeed
= speed
*width
/8;
259 if (hwloc_pci_prepare_bridge(obj
, config_space_cache
) < 0)
262 if (obj
->type
== HWLOC_OBJ_PCI_DEVICE
) {
263 memcpy(&tmp16
, &config_space_cache
[PCI_SUBSYSTEM_VENDOR_ID
], sizeof(tmp16
));
264 obj
->attr
->pcidev
.subvendor_id
= tmp16
;
265 memcpy(&tmp16
, &config_space_cache
[PCI_SUBSYSTEM_ID
], sizeof(tmp16
));
266 obj
->attr
->pcidev
.subdevice_id
= tmp16
;
269 * bridge must lookup PCI_CAP_ID_SSVID and then look at offset+PCI_SSVID_VENDOR/DEVICE_ID
270 * cardbus must look at PCI_CB_SUBSYSTEM_VENDOR_ID and PCI_CB_SUBSYSTEM_ID
274 /* get the vendor name */
275 vendorname
= pci_device_get_vendor_name(pcidev
);
276 if (vendorname
&& *vendorname
)
277 hwloc_obj_add_info(obj
, "PCIVendor", vendorname
);
279 /* get the device name */
280 devicename
= pci_device_get_device_name(pcidev
);
281 if (devicename
&& *devicename
)
282 hwloc_obj_add_info(obj
, "PCIDevice", devicename
);
284 /* generate or get the fullname */
285 snprintf(name
, sizeof(name
), "%s%s%s",
286 vendorname
? vendorname
: "",
287 vendorname
&& devicename
? " " : "",
288 devicename
? devicename
: "");
291 obj
->name
= strdup(name
);
292 hwloc_debug(" %04x:%02x:%02x.%01x %04x %04x:%04x %s\n",
293 domain
, pcidev
->bus
, pcidev
->dev
, pcidev
->func
,
294 device_class
, pcidev
->vendor_id
, pcidev
->device_id
,
295 fullname
&& *fullname
? fullname
: "??");
297 /* queue the object for now */
299 last_obj
->next_sibling
= obj
;
305 /* finalize device scanning */
306 pci_iterator_destroy(iter
);
307 pci_system_cleanup();
308 HWLOC_PCIACCESS_UNLOCK();
310 #ifdef HWLOC_LINUX_SYS
311 dir
= opendir("/sys/bus/pci/slots/");
313 struct dirent
*dirent
;
314 while ((dirent
= readdir(dir
)) != NULL
) {
318 if (dirent
->d_name
[0] == '.')
320 err
= snprintf(path
, sizeof(path
), "/sys/bus/pci/slots/%s/address", dirent
->d_name
);
321 if ((size_t) err
< sizeof(path
)) {
322 file
= fopen(path
, "r");
324 unsigned domain
, bus
, dev
;
325 if (fscanf(file
, "%x:%x:%x", &domain
, &bus
, &dev
) == 3) {
326 hwloc_obj_t obj
= first_obj
;
328 if (obj
->attr
->pcidev
.domain
== domain
329 && obj
->attr
->pcidev
.bus
== bus
330 && obj
->attr
->pcidev
.dev
== dev
) {
331 hwloc_obj_add_info(obj
, "PCISlot", dirent
->d_name
);
333 obj
= obj
->next_sibling
;
344 return hwloc_insert_pci_device_list(backend
, first_obj
);
347 static struct hwloc_backend
*
348 hwloc_pci_component_instantiate(struct hwloc_disc_component
*component
,
349 const void *_data1 __hwloc_attribute_unused
,
350 const void *_data2 __hwloc_attribute_unused
,
351 const void *_data3 __hwloc_attribute_unused
)
353 struct hwloc_backend
*backend
;
355 /* thissystem may not be fully initialized yet, we'll check flags in discover() */
357 backend
= hwloc_backend_alloc(component
);
360 backend
->flags
= HWLOC_BACKEND_FLAG_NEED_LEVELS
;
361 #ifdef HWLOC_SOLARIS_SYS
362 if ((uid_t
)0 != geteuid())
363 backend
->discover
= NULL
;
366 backend
->discover
= hwloc_look_pci
;
370 static struct hwloc_disc_component hwloc_pci_disc_component
= {
371 HWLOC_DISC_COMPONENT_TYPE_MISC
,
373 HWLOC_DISC_COMPONENT_TYPE_GLOBAL
,
374 hwloc_pci_component_instantiate
,
380 hwloc_pci_component_init(unsigned long flags
)
384 if (hwloc_plugin_check_namespace("pci", "hwloc_backend_alloc") < 0)
389 #ifdef HWLOC_INSIDE_PLUGIN
390 HWLOC_DECLSPEC
extern const struct hwloc_component hwloc_pci_component
;
393 const struct hwloc_component hwloc_pci_component
= {
395 hwloc_pci_component_init
, NULL
,
396 HWLOC_COMPONENT_TYPE_DISC
,
398 &hwloc_pci_disc_component