From ffadc0c8c64eb19b4f1e91da152d8a2ef8b9a1c7 Mon Sep 17 00:00:00 2001 From: Jon Chesterfield Date: Thu, 19 Mar 2020 01:04:28 +0000 Subject: [PATCH] Replace libpci with new parser. libpci was only used to find a marketing string for a device. This patch looks for a pci.ids on disk and parses it to extract the same string, using 'Device xxxx' as the fallback on file i/o error or missing data from the text file. Tested by checking every vendor/ device pair against the values returned from libpci. Change-Id: I21af3157472c1824d57fcee31393c6ee8ce07330 Signed-off-by: Jon Chesterfield [ROCm/ROCR-Runtime commit: 0a1718b7533e2ce482100b9bb8154470e2bcd044] --- projects/rocr-runtime/CMakeLists.txt | 13 +- projects/rocr-runtime/src/fmm.c | 9 +- projects/rocr-runtime/src/libhsakmt.h | 4 +- projects/rocr-runtime/src/pci_ids.c | 304 ++++++++++++++++++++++++++ projects/rocr-runtime/src/pci_ids.h | 46 ++++ projects/rocr-runtime/src/perfctr.c | 8 +- projects/rocr-runtime/src/topology.c | 15 +- 7 files changed, 373 insertions(+), 26 deletions(-) create mode 100644 projects/rocr-runtime/src/pci_ids.c create mode 100644 projects/rocr-runtime/src/pci_ids.h diff --git a/projects/rocr-runtime/CMakeLists.txt b/projects/rocr-runtime/CMakeLists.txt index cc186fd7d0..aa2c4c609c 100644 --- a/projects/rocr-runtime/CMakeLists.txt +++ b/projects/rocr-runtime/CMakeLists.txt @@ -105,6 +105,7 @@ set ( HSAKMT_SRC "src/debug.c" "src/libhsakmt.c" "src/memory.c" "src/openclose.c" + "src/pci_ids.c" "src/perfctr.c" "src/pmc_table.c" "src/queues.c" @@ -127,10 +128,8 @@ set_property ( TARGET ${HSAKMT_TARGET} PROPERTY VERSION "${LIB_VERSION_STRING}" set_property ( TARGET ${HSAKMT_TARGET} PROPERTY SOVERSION "${LIB_VERSION_MAJOR}" ) find_package(PkgConfig) -pkg_check_modules(PC_LIBPCI REQUIRED libpci) -include_directories ( ${PC_LIBPCI_INCLUDEDIR} ) target_link_libraries ( ${HSAKMT_TARGET} - pthread rt numa ${PC_LIBPCI_LIBRARIES} + pthread rt numa ) ## Define default variable and variables for the optional build target hsakmt-dev @@ -195,15 +194,15 @@ endif() # Debian package specific variables set ( CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://github.com/RadeonOpenCompute/ROCT-Thunk-Interface" ) set ( CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_CURRENT_SOURCE_DIR}/DEBIAN/postinst;${CMAKE_CURRENT_SOURCE_DIR}/DEBIAN/prerm" ) -set ( CPACK_DEBIAN_PACKAGE_DEPENDS "libc6, libnuma1, libpci3, zlib1g, libudev1" ) +set ( CPACK_DEBIAN_PACKAGE_DEPENDS "libc6, libnuma1, zlib1g, libudev1" ) # RPM package specific variables if( DISTRO_ID MATCHES "sles" ) - set ( CPACK_RPM_PACKAGE_REQUIRES "glibc, libnuma-devel, pciutils, libgcc_s1") + set ( CPACK_RPM_PACKAGE_REQUIRES "glibc, libnuma-devel, libgcc_s1") elseif( DISTRO_ID MATCHES "centos" AND DISTRO_RELEASE MATCHES "6" ) - set ( CPACK_RPM_PACKAGE_REQUIRES "glibc, numactl, pciutils-libs, libgcc" ) + set ( CPACK_RPM_PACKAGE_REQUIRES "glibc, numactl, libgcc" ) else() - set ( CPACK_RPM_PACKAGE_REQUIRES "glibc, numactl-libs, pciutils-libs, libgcc" ) + set ( CPACK_RPM_PACKAGE_REQUIRES "glibc, numactl-libs, libgcc" ) endif() set ( CPACK_RPM_PRE_INSTALL_SCRIPT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/RPM/rpm_post" ) set ( CPACK_RPM_POST_UNINSTALL_SCRIPT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/RPM/rpm_postun" ) diff --git a/projects/rocr-runtime/src/fmm.c b/projects/rocr-runtime/src/fmm.c index 4442876e43..fab57af6b5 100644 --- a/projects/rocr-runtime/src/fmm.c +++ b/projects/rocr-runtime/src/fmm.c @@ -36,7 +36,7 @@ #include #include #include -#include + #include #include #include "rbtree.h" @@ -2119,7 +2119,7 @@ HSAKMT_STATUS fmm_init_process_apertures(unsigned int NumNodes) HSAKMT_STATUS ret = HSAKMT_STATUS_SUCCESS; char *disableCache, *pagedUserptr, *checkUserptr, *guardPagesStr, *reserveSvm; unsigned int guardPages = 1; - struct pci_access *pacc; + struct pci_ids pacc; uint64_t svm_base = 0, svm_limit = 0; uint32_t svm_alignment = 0; @@ -2166,8 +2166,7 @@ HSAKMT_STATUS fmm_init_process_apertures(unsigned int NumNodes) * gets called before hsaKmtAcquireSystemProperties() is called. */ - pacc = pci_alloc(); - pci_init(pacc); + pacc = pci_ids_create(); for (i = 0; i < NumNodes; i++) { memset(&props, 0, sizeof(props)); ret = topology_sysfs_get_node_props(i, &props, &gpu_id, pacc); @@ -2204,7 +2203,7 @@ HSAKMT_STATUS fmm_init_process_apertures(unsigned int NumNodes) gpu_mem_count++; } } - pci_cleanup(pacc); + pci_ids_destroy(pacc); /* The ioctl will also return Number of Nodes if * args.kfd_process_device_apertures_ptr is set to NULL. This is not diff --git a/projects/rocr-runtime/src/libhsakmt.h b/projects/rocr-runtime/src/libhsakmt.h index 411cdec512..8d38eef80c 100644 --- a/projects/rocr-runtime/src/libhsakmt.h +++ b/projects/rocr-runtime/src/libhsakmt.h @@ -27,10 +27,10 @@ #define LIBHSAKMT_H_INCLUDED #include "hsakmt.h" +#include "pci_ids.h" #include #include #include -#include extern int kfd_fd; extern unsigned long kfd_open_count; @@ -155,7 +155,7 @@ HSAKMT_STATUS validate_nodeid_array(uint32_t **gpu_id_array, uint32_t NumberOfNodes, uint32_t *NodeArray); HSAKMT_STATUS topology_sysfs_get_node_props(uint32_t node_id, HsaNodeProperties *props, - uint32_t *gpu_id, struct pci_access* pacc); + uint32_t *gpu_id, struct pci_ids pacc); HSAKMT_STATUS topology_sysfs_get_system_props(HsaSystemProperties *props); bool topology_is_dgpu(uint16_t device_id); bool topology_is_svm_needed(uint16_t device_id); diff --git a/projects/rocr-runtime/src/pci_ids.c b/projects/rocr-runtime/src/pci_ids.c new file mode 100644 index 0000000000..1353cfa23d --- /dev/null +++ b/projects/rocr-runtime/src/pci_ids.c @@ -0,0 +1,304 @@ +/* + * Copyright © 2020 Advanced Micro Devices, Inc. + * + * 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 (including + * the next paragraph) 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. + */ + +/* + * Parse a pci.ids text file to extract 'device_name' + * # Vendors, devices and subsystems. Please keep sorted. + * # Syntax: + * # vendor vendor_name + * # device device_name <-- single tab + * # subvendor subdevice subsystem_name <-- two tabs + */ + +/* + * Example section of file. Searching for 1002/130c + * + * 1002 Advanced Micro Devices, Inc. [AMD/ATI] + * # fields elided + * 130a Kaveri [Radeon R6 Graphics] + * 130b Kaveri [Radeon R4 Graphics] + * + * 130c Kaveri [Radeon R7 Graphics] <- result + * # ^-------------------------^ + * + * 130d Kaveri [Radeon R6 Graphics] + * 130e Kaveri [Radeon R5 Graphics] + * # fields elided + * # next vendor region starts + * 1003 ULSI Systems + * 0201 US201 + */ + +#define _GNU_SOURCE +#include + +#include "pci_ids.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static const char *const pci_ids_paths[] = { + "/usr/share/hwdata/pci.ids", // update-pciids + "/usr/share/misc/pci.ids", // debian + "/usr/share/pci.ids", // redhat + "/var/lib/pciutils/pci.ids", // also debian + "pci.ids", +}; + +static struct pci_ids pci_ids_create_from_file(const char *path) +{ + struct pci_ids failure = { + .fd = -1, + }; + + int fd = open(path, O_RDONLY, 0); + + if (fd == -1) + return failure; + + struct stat sb; + + fstat(fd, &sb); + size_t sz = sb.st_size; + + if (sz == 0) { + close(fd); + return failure; + } + + sz = (sz < UINT32_MAX) ? sz : UINT32_MAX; + void *addr = mmap(0, sz, PROT_READ, MAP_PRIVATE, fd, 0); + + if (addr == MAP_FAILED) { + close(fd); + return failure; + } + + return (struct pci_ids){ + .fd = fd, + .addr = addr, + .size = sz, + }; +} + +struct pci_ids pci_ids_create(void) +{ + size_t sz = sizeof(pci_ids_paths) / sizeof(pci_ids_paths[0]); + + for (size_t i = 0; i < sz; i++) { + struct pci_ids res = pci_ids_create_from_file(pci_ids_paths[i]); + + if (res.fd != -1) + return res; + } + + return (struct pci_ids){ .fd = -1 }; +} + +void pci_ids_destroy(struct pci_ids f) +{ + if (f.fd != -1) { + munmap(f.addr, f.size); + close(f.fd); + } +} + +struct range { + // Iterator pair, start <= end. Can dereference [start end) + unsigned char *start; + unsigned char *end; +}; +static bool empty_range(struct range r) +{ + return r.start == r.end; +} + +static void write_as_hex(uint16_t x, char *b) +{ + static const char digits[] = "0123456789abcdef"; + + for (unsigned int i = 0; i < 4; i++) { + unsigned int index = 0xf & (x >> 4 * (3 - i)); + + b[i] = digits[index]; + } +} + +static struct range find_vendor(struct range r, uint16_t VendorId) + +{ + if (empty_range(r)) + return r; + + char needle[5] = { '\n' }; + + write_as_hex(VendorId, &needle[1]); + unsigned char *s = + memmem(r.start, r.end - r.start, needle, sizeof(needle)); + + if (s) { + r.start = s; + } else { + r.start = r.end; + assert(empty_range(r)); + } + return r; +} + +static struct range trim_whitespace(struct range r) +{ + while (!empty_range(r) && isspace(r.start[0])) + r.start++; + + while (!empty_range(r) && isspace(r.end[-1])) + r.end--; + + return r; +} + +static struct range skip_vendor_id(struct range r) +{ + const struct range failure = { 0, 0 }; + + assert(!empty_range(r)); + // Expect an initial newline to skip over + if (r.start[0] != '\n') + return failure; + + r.start++; + if (empty_range(r)) + return failure; + + // Skip rest of line + r.start = memchr(r.start, '\n', r.end - r.start); + if (!r.start) + return failure; + + return r; +} + +static struct range find_device(struct range r, uint16_t DeviceId) +{ + struct range failure = { 0, 0 }; + + if (empty_range(r)) + return failure; + + r = skip_vendor_id(r); + if (empty_range(r)) + return failure; + + assert(r.start[0] == '\n'); + + char needle[6] = { '\n', '\t' }; + + write_as_hex(DeviceId, &needle[2]); + + for (;;) { + size_t width = r.end - r.start; + + if (width < sizeof(needle)) + return failure; + + unsigned char *line_end = memchr(r.start + 1, '\n', width - 1); + + if (!line_end) { + // File may not end with a newline + line_end = r.end; + } + + if (memcmp(r.start, needle, sizeof(needle)) == 0) { + // Success + r.start += sizeof(needle); + r.end = line_end; + return trim_whitespace(r); + } + + if (isxdigit(r.start[1])) { + // Reached the end of this region + return failure; + } + + // Otherwise ignore whatever is on the line, e.g. '#' + r.start = line_end; + } +} + +static void copy_range_to_buffer(struct range r, char *buf, size_t size) +{ + assert(!empty_range(r)); + size_t to_copy = (r.end - r.start); + + to_copy = to_copy < (size - 1) ? to_copy : size - 1; + + memcpy(buf, r.start, to_copy); + buf[to_copy] = '\0'; +} + +static void write_fallback_to_buffer(char *buf, size_t size, uint16_t DeviceId) +{ + char tmp[] = "Device xxxx"; + + static_assert(sizeof(tmp) == 12, ""); + write_as_hex(DeviceId, &tmp[7]); + + size_t to_copy = (sizeof(tmp) <= size) ? sizeof(tmp) : size; + + memcpy(buf, tmp, to_copy); + buf[size - 1] = '\0'; +} + +char *pci_ids_lookup(struct pci_ids f, char *buf, size_t size, + uint16_t VendorId, uint16_t DeviceId) +{ + if (f.fd == -1) { + write_fallback_to_buffer(buf, size, DeviceId); + return buf; + } + + struct range whole_file = { + .start = f.addr, + .end = (unsigned char *)f.addr + f.size, + }; + + struct range vendor = find_vendor(whole_file, VendorId); + + struct range device = find_device(vendor, DeviceId); + + if (!empty_range(device)) + copy_range_to_buffer(device, buf, size); + else + write_fallback_to_buffer(buf, size, DeviceId); + + return buf; +} diff --git a/projects/rocr-runtime/src/pci_ids.h b/projects/rocr-runtime/src/pci_ids.h new file mode 100644 index 0000000000..4c0d44c57e --- /dev/null +++ b/projects/rocr-runtime/src/pci_ids.h @@ -0,0 +1,46 @@ +/* + * Copyright © 2020 Advanced Micro Devices, Inc. + * + * 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 (including + * the next paragraph) 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 SRC_PCI_IDS_H_ +#define SRC_PCI_IDS_H_ + +#include +#include + +struct pci_ids { + int32_t fd; // -1 if file access failed + uint32_t size; + void *addr; +}; + +// Sixteen byte struct is passed in registers. Avoids calling malloc/free. +struct pci_ids pci_ids_create(void); +void pci_ids_destroy(struct pci_ids pacc); + +// Writes to buf. Returns buf. Does not fail. +char *pci_ids_lookup(struct pci_ids pacc, char *buf, size_t buf_size, + uint16_t VendorId, uint16_t DeviceId); + +#endif // SRC_PCI_IDS_H_ diff --git a/projects/rocr-runtime/src/perfctr.c b/projects/rocr-runtime/src/perfctr.c index 73bab19f45..546dd2358b 100644 --- a/projects/rocr-runtime/src/perfctr.c +++ b/projects/rocr-runtime/src/perfctr.c @@ -77,11 +77,11 @@ struct perf_shared_table { struct perf_counts_values { union { struct { - u64 val; - u64 ena; - u64 run; + uint64_t val; + uint64_t ena; + uint64_t run; }; - u64 values[3]; + uint64_t values[3]; }; }; diff --git a/projects/rocr-runtime/src/topology.c b/projects/rocr-runtime/src/topology.c index f718ddaa05..2817b035ff 100644 --- a/projects/rocr-runtime/src/topology.c +++ b/projects/rocr-runtime/src/topology.c @@ -31,7 +31,7 @@ #include #include #include -#include + #include #include @@ -910,7 +910,7 @@ exit: HSAKMT_STATUS topology_sysfs_get_node_props(uint32_t node_id, HsaNodeProperties *props, uint32_t *gpu_id, - struct pci_access *pacc) + struct pci_ids pacc) { FILE *fd; char *read_buf, *p, *envvar, dummy; @@ -1068,7 +1068,7 @@ HSAKMT_STATUS topology_sysfs_get_node_props(uint32_t node_id, * Retrieve the marketing name of the node using pcilib, * convert UTF8 to UTF16 */ - name = pci_lookup_name(pacc, namebuf, sizeof(namebuf), PCI_LOOKUP_DEVICE, + name = pci_ids_lookup(pacc, namebuf, sizeof(namebuf), props->VendorId, props->DeviceId); for (i = 0; name[i] != 0 && i < HSA_PUBLIC_NAME_SIZE - 1; i++) props->MarketingName[i] = name[i]; @@ -1731,7 +1731,7 @@ HSAKMT_STATUS topology_take_snapshot(void) HsaSystemProperties sys_props; node_props_t *temp_props = 0; HSAKMT_STATUS ret = HSAKMT_STATUS_SUCCESS; - struct pci_access *pacc; + struct pci_ids pacc; struct proc_cpuinfo *cpuinfo; const uint32_t num_procs = get_nprocs(); @@ -1755,8 +1755,7 @@ retry: ret = HSAKMT_STATUS_NO_MEMORY; goto err; } - pacc = pci_alloc(); - pci_init(pacc); + pacc = pci_ids_create(); for (i = 0; i < sys_props.NumNodes; i++) { ret = topology_sysfs_get_node_props(i, &temp_props[i].node, @@ -1826,7 +1825,7 @@ retry: * remote node (node_to) is not accessible */ while (sys_link_id < temp_props[i].node.NumIOLinks && - link_id < sys_props.NumNodes - 1) { + link_id < sys_props.NumNodes - 1) { ret = topology_sysfs_get_iolink_props(i, sys_link_id++, &temp_props[i].link[link_id]); if (ret == HSAKMT_STATUS_NOT_SUPPORTED) { @@ -1842,7 +1841,7 @@ retry: temp_props[i].node.NumIOLinks = link_id; } } - pci_cleanup(pacc); + pci_ids_destroy(pacc); } /* All direct IO links are created in the kernel. Here we need to