diff --git a/src/3rdparty/hwloc/NEWS b/src/3rdparty/hwloc/NEWS index bf5f1f6f..cf12c5e4 100644 --- a/src/3rdparty/hwloc/NEWS +++ b/src/3rdparty/hwloc/NEWS @@ -1,5 +1,5 @@ Copyright © 2009 CNRS -Copyright © 2009-2024 Inria. All rights reserved. +Copyright © 2009-2025 Inria. All rights reserved. Copyright © 2009-2013 Université Bordeaux Copyright © 2009-2011 Cisco Systems, Inc. All rights reserved. Copyright © 2020 Hewlett Packard Enterprise. All rights reserved. @@ -17,6 +17,52 @@ bug fixes (and other actions) for each version of hwloc since version 0.9. +Version 2.12.1 +-------------- +* Add hwloc-calc's --default-nodes option to hwloc-bind and hwloc-info. +* Improve the --best-memattr "default" fallback, try to use "default" + memory nodes, and add verbose messages and warnings if some + performance info are incomplete or missing. + Thanks to Antoine Morvan for the report. +* Fix CPU and memory binding on different locations, + thanks to Antoine Morvan for the report. +* Add HWLOC_LOCAL_NUMANODE_FLAG_INTERSECT_LOCALITY and enable it by + default in hwloc-calc --local-memory for finding local NUMA nodes + that do not exactly match input locations. + Thanks to Antoine Morvan for the report. +* Fix a possible crash in the x86 backend when Qemu is configured to + expose multicore/thread CPUs that are actually single-core/thread. + Thanks to Georg Pfuetzenreuter. + + +Version 2.12.0 +-------------- +* Add hwloc_topology_get_default_nodeset() for the set of default + NUMA nodes. + - hwloc-calc now has --default-nodes option. +* Rework oneAPI LevelZero support to use zesInit() and avoid the need + to set ZES_ENABLE_SYSMAN=1 in the environment. + - zesDriverGetDeviceByUuidExp() is now required in the L0 runtime. + - ZES/Sysman variants were added in hwloc/levelzero.h to specifically + handle ZES/Sysman device handles. +* Fix the locality of AMD GPU partitions, thanks to Edgar Leon for + reporting and debugging the issue. +* Better detect Cray Slingshot NICs, thanks to Edgar Leon. +* Add support for Die objects and Module groups on Windows. +* Only filter-out Dies that are identical to their Packages + when it applies to all Dies. +* Improve hwloc-calc to handle CPU-less NUMA nodes or platforms with + heterogeneous memory without requiring --nodeset-output. +* hwloc-calc now accepts counting/listing cpukinds and memory tiers + with -N and -I cpukind/memorytier. +* The systemd-dbus-api output of hwloc-calc has changed, and + --nodeset-output-format was added, to support NUMA node outputs. + Thanks to Pierre Neyron. +* Update NVLink bandwidth and CUDA capabilities up to NVIDIA Blackwell. +* Fix some NUMA syscalls on Linux for platforms with old libc headers. +* Some minor fixes in distances. + + Version 2.11.2 -------------- * Add missing CPU info attrs on aarch64 on Linux. diff --git a/src/3rdparty/hwloc/VERSION b/src/3rdparty/hwloc/VERSION index 25c0cc54..7f6675cb 100644 --- a/src/3rdparty/hwloc/VERSION +++ b/src/3rdparty/hwloc/VERSION @@ -8,8 +8,8 @@ # Please update HWLOC_VERSION* in contrib/windows/hwloc_config.h too. major=2 -minor=11 -release=2 +minor=12 +release=1 # greek is used for alpha or beta release tags. If it is non-empty, # it will be appended to the version number. It does not have to be @@ -22,7 +22,7 @@ greek= # The date when this release was created -date="Sep 26, 2024" +date="May 12, 2025" # If snapshot=1, then use the value from snapshot_version as the # entire hwloc version (i.e., ignore major, minor, release, and @@ -41,6 +41,6 @@ snapshot_version=${major}.${minor}.${release}${greek}-git # 2. Version numbers are described in the Libtool current:revision:age # format. -libhwloc_so_version=23:1:8 +libhwloc_so_version=25:0:10 # Please also update the lines in contrib/windows/libhwloc.vcxproj diff --git a/src/3rdparty/hwloc/include/hwloc.h b/src/3rdparty/hwloc/include/hwloc.h index d52e9900..bcae3257 100644 --- a/src/3rdparty/hwloc/include/hwloc.h +++ b/src/3rdparty/hwloc/include/hwloc.h @@ -1,6 +1,6 @@ /* * Copyright © 2009 CNRS - * Copyright © 2009-2024 Inria. All rights reserved. + * Copyright © 2009-2025 Inria. All rights reserved. * Copyright © 2009-2012 Université Bordeaux * Copyright © 2009-2020 Cisco Systems, Inc. All rights reserved. * See COPYING in top-level directory. @@ -112,7 +112,7 @@ extern "C" { * Two stable releases of the same series usually have the same ::HWLOC_API_VERSION * even if their HWLOC_VERSION are different. */ -#define HWLOC_API_VERSION 0x00020b00 +#define HWLOC_API_VERSION 0x00020c00 /** \brief Indicate at runtime which hwloc API version was used at build time. * @@ -346,9 +346,10 @@ typedef enum { * * Some operating systems (e.g. Linux) may expose a single die per package * even if the hardware does not support dies at all. To avoid showing - * such non-existing dies, the corresponding hwloc backend may filter them out. + * such non-existing dies, hwloc will filter them out if all of them are + * identical to packages. * This is functionally equivalent to ::HWLOC_TYPE_FILTER_KEEP_STRUCTURE - * being enforced. + * being enforced for Dies versus Packages. */ HWLOC_OBJ_TYPE_MAX /**< \private Sentinel value */ @@ -1047,7 +1048,7 @@ HWLOC_DECLSPEC const char * hwloc_obj_type_string (hwloc_obj_type_t type) __hwlo * If \p size is 0, \p string may safely be \c NULL. * * \return the number of characters that were actually written if not truncating, - * or that would have been written (not including the ending \\0). + * or that would have been written (not including the ending \c \0). */ HWLOC_DECLSPEC int hwloc_obj_type_snprintf(char * __hwloc_restrict string, size_t size, hwloc_obj_t obj, @@ -1062,7 +1063,7 @@ HWLOC_DECLSPEC int hwloc_obj_type_snprintf(char * __hwloc_restrict string, size_ * If \p size is 0, \p string may safely be \c NULL. * * \return the number of characters that were actually written if not truncating, - * or that would have been written (not including the ending \\0). + * or that would have been written (not including the ending \c \0). */ HWLOC_DECLSPEC int hwloc_obj_attr_snprintf(char * __hwloc_restrict string, size_t size, hwloc_obj_t obj, const char * __hwloc_restrict separator, @@ -2002,7 +2003,7 @@ HWLOC_DECLSPEC int hwloc_topology_set_xml(hwloc_topology_t __hwloc_restrict topo * a file, as with hwloc_topology_set_xml()). * * Gather topology information from the XML memory buffer given at - * \p buffer and of length \p size (including an ending \0). + * \p buffer and of length \p size (including an ending \c \0). * This buffer may have been filled earlier with * hwloc_topology_export_xmlbuffer() in hwloc/export.h. * diff --git a/src/3rdparty/hwloc/include/hwloc/autogen/config.h b/src/3rdparty/hwloc/include/hwloc/autogen/config.h index b9084182..deadcf11 100644 --- a/src/3rdparty/hwloc/include/hwloc/autogen/config.h +++ b/src/3rdparty/hwloc/include/hwloc/autogen/config.h @@ -1,6 +1,6 @@ /* * Copyright © 2009 CNRS - * Copyright © 2009-2024 Inria. All rights reserved. + * Copyright © 2009-2025 Inria. All rights reserved. * Copyright © 2009-2012 Université Bordeaux * Copyright © 2009-2011 Cisco Systems, Inc. All rights reserved. * See COPYING in top-level directory. @@ -11,10 +11,10 @@ #ifndef HWLOC_CONFIG_H #define HWLOC_CONFIG_H -#define HWLOC_VERSION "2.11.2" +#define HWLOC_VERSION "2.12.1" #define HWLOC_VERSION_MAJOR 2 -#define HWLOC_VERSION_MINOR 11 -#define HWLOC_VERSION_RELEASE 2 +#define HWLOC_VERSION_MINOR 12 +#define HWLOC_VERSION_RELEASE 1 #define HWLOC_VERSION_GREEK "" #define __hwloc_restrict diff --git a/src/3rdparty/hwloc/include/hwloc/bitmap.h b/src/3rdparty/hwloc/include/hwloc/bitmap.h index 6b56bcb9..8ce19057 100644 --- a/src/3rdparty/hwloc/include/hwloc/bitmap.h +++ b/src/3rdparty/hwloc/include/hwloc/bitmap.h @@ -1,6 +1,6 @@ /* * Copyright © 2009 CNRS - * Copyright © 2009-2023 Inria. All rights reserved. + * Copyright © 2009-2024 Inria. All rights reserved. * Copyright © 2009-2012 Université Bordeaux * Copyright © 2009-2011 Cisco Systems, Inc. All rights reserved. * See COPYING in top-level directory. @@ -113,51 +113,88 @@ HWLOC_DECLSPEC int hwloc_bitmap_copy(hwloc_bitmap_t dst, hwloc_const_bitmap_t sr * Bitmap/String Conversion */ -/** \brief Stringify a bitmap. +/** \brief Stringify a bitmap in the default hwloc format. + * + * Note that if the bitmap is a CPU or nodeset, it contains physical indexes. + * + * Print the bits set inside a bitmap as a comma-separated list of hexadecimal 32-bit blocks. + * A bitmap containing bits 1, 33, 34, and all from 64 to 95 is printed as "0xffffffff,0x00000006,0x00000002". * * Up to \p buflen characters may be written in buffer \p buf. * * If \p buflen is 0, \p buf may safely be \c NULL. * * \return the number of characters that were actually written if not truncating, - * or that would have been written (not including the ending \\0). + * or that would have been written (not including the ending \c \0). + * \return -1 on error. */ HWLOC_DECLSPEC int hwloc_bitmap_snprintf(char * __hwloc_restrict buf, size_t buflen, hwloc_const_bitmap_t bitmap); -/** \brief Stringify a bitmap into a newly allocated string. +/** \brief Stringify a bitmap into a newly allocated string in the default hwloc format. * - * \return 0 on success, -1 on error. + * Note that if the bitmap is a CPU or nodeset, it contains physical indexes. + * + * Print the bits set inside a bitmap as a comma-separated list of hexadecimal 32-bit blocks. + * A bitmap containing bits 1, 33, 34, and all from 64 to 95 is printed as "0xffffffff,0x00000006,0x00000002". + * + * \return the number of characters that were written (not including the ending \c \0). + * \return -1 on error, for instance with \p errno set to \c ENOMEM on failure to allocate the output string. */ HWLOC_DECLSPEC int hwloc_bitmap_asprintf(char ** strp, hwloc_const_bitmap_t bitmap); -/** \brief Parse a bitmap string and stores it in bitmap \p bitmap. +/** \brief Parse a bitmap string as the default hwloc format and stores it in bitmap \p bitmap. + * + * Note that if the bitmap is a CPU or nodeset, the input string must contain physical indexes. + * + * The input string should be a comma-separared list of hexadecimal 32-bit blocks. + * String "0xffffffff,0x6,0x2" is parsed as a bitmap containing all bits between 64 and 95, + * and bits 33, 34 and 1. * * \return 0 on success, -1 on error. */ HWLOC_DECLSPEC int hwloc_bitmap_sscanf(hwloc_bitmap_t bitmap, const char * __hwloc_restrict string); /** \brief Stringify a bitmap in the list format. + * + * Note that if the bitmap is a CPU or nodeset, it contains physical indexes. * * Lists are comma-separated indexes or ranges. * Ranges are dash separated indexes. - * The last range may not have an ending indexes if the bitmap is infinitely set. + * A bitmap containing bits 1, 33, 34, and all from 64 to 95 is printed as "1,33-34,64-95". + * The last range may not have an ending index if the bitmap is infinitely set. * * Up to \p buflen characters may be written in buffer \p buf. * * If \p buflen is 0, \p buf may safely be \c NULL. * * \return the number of characters that were actually written if not truncating, - * or that would have been written (not including the ending \\0). + * or that would have been written (not including the ending \c \0). + * \return -1 on error. */ HWLOC_DECLSPEC int hwloc_bitmap_list_snprintf(char * __hwloc_restrict buf, size_t buflen, hwloc_const_bitmap_t bitmap); /** \brief Stringify a bitmap into a newly allocated list string. * - * \return 0 on success, -1 on error. + * Note that if the bitmap is a CPU or nodeset, it contains physical indexes. + * + * Lists are comma-separated indexes or ranges. + * Ranges are dash separated indexes. + * A bitmap containing bits 1, 33, 34, and all from 64 to 95 is printed as "1,33-34,64-95". + * The last range may not have an ending index if the bitmap is infinitely set. + * + * \return the number of characters that were written (not including the ending \c \0). + * \return -1 on error, for instance with \p errno set to \c ENOMEM on failure to allocate the output string. */ HWLOC_DECLSPEC int hwloc_bitmap_list_asprintf(char ** strp, hwloc_const_bitmap_t bitmap); /** \brief Parse a list string and stores it in bitmap \p bitmap. + * + * Note that if the bitmap is a CPU or nodeset, the input string must contain physical indexes. + * + * Lists are comma-separated indexes or ranges. + * Ranges are dash separated indexes. + * String "1,33-34,64-95" is parsed as a bitmap containing bits 1, 33, 34, and all from 64 to 95. + * The last range may not have an ending index if the bitmap is infinitely set. * * \return 0 on success, -1 on error. */ @@ -165,25 +202,43 @@ HWLOC_DECLSPEC int hwloc_bitmap_list_sscanf(hwloc_bitmap_t bitmap, const char * /** \brief Stringify a bitmap in the taskset-specific format. * - * The taskset command manipulates bitmap strings that contain a single + * Note that if the bitmap is a CPU or nodeset, it contains physical indexes. + * + * The taskset program manipulates bitmap strings that contain a single * (possible very long) hexadecimal number starting with 0x. + * A bitmap containing bits 1, 33, 34, and all from 64 to 95 is printed as "0xffffffff0000000600000002". * * Up to \p buflen characters may be written in buffer \p buf. * * If \p buflen is 0, \p buf may safely be \c NULL. * * \return the number of characters that were actually written if not truncating, - * or that would have been written (not including the ending \\0). + * or that would have been written (not including the ending \c \0). + * \return -1 on error. */ HWLOC_DECLSPEC int hwloc_bitmap_taskset_snprintf(char * __hwloc_restrict buf, size_t buflen, hwloc_const_bitmap_t bitmap); /** \brief Stringify a bitmap into a newly allocated taskset-specific string. * - * \return 0 on success, -1 on error. + * Note that if the bitmap is a CPU or nodeset, it contains physical indexes. + * + * The taskset program manipulates bitmap strings that contain a single + * (possible very long) hexadecimal number starting with 0x. + * A bitmap containing bits 1, 33, 34, and all from 64 to 95 is printed as "0xffffffff0000000600000002". + * + * \return the number of characters that were written (not including the ending \c \0). + * \return -1 on error, for instance with \p errno set to \c ENOMEM on failure to allocate the output string. */ HWLOC_DECLSPEC int hwloc_bitmap_taskset_asprintf(char ** strp, hwloc_const_bitmap_t bitmap); /** \brief Parse a taskset-specific bitmap string and stores it in bitmap \p bitmap. + * + * Note that if the bitmap is a CPU or nodeset, the input string must contain physical indexes. + * + * The taskset program manipulates bitmap strings that contain a single + * (possible very long) hexadecimal number starting with 0x. + * String "0xffffffff0000000600000002" is parsed as a bitmap containing all bits between 64 and 95, + * and bits 33, 34 and 1. * * \return 0 on success, -1 on error. */ diff --git a/src/3rdparty/hwloc/include/hwloc/diff.h b/src/3rdparty/hwloc/include/hwloc/diff.h index 4d822434..a9afa8f7 100644 --- a/src/3rdparty/hwloc/include/hwloc/diff.h +++ b/src/3rdparty/hwloc/include/hwloc/diff.h @@ -1,5 +1,5 @@ /* - * Copyright © 2013-2023 Inria. All rights reserved. + * Copyright © 2013-2024 Inria. All rights reserved. * See COPYING in top-level directory. */ @@ -258,7 +258,7 @@ HWLOC_DECLSPEC int hwloc_topology_diff_export_xml(hwloc_topology_diff_t diff, co /** \brief Load a list of topology differences from a XML buffer. * * Build a list of differences from the XML memory buffer given - * at \p xmlbuffer and of length \p buflen (including an ending \0). + * at \p xmlbuffer and of length \p buflen (including an ending \c \0). * This buffer may have been filled earlier with * hwloc_topology_diff_export_xmlbuffer(). * @@ -284,7 +284,7 @@ HWLOC_DECLSPEC int hwloc_topology_diff_load_xmlbuffer(const char *xmlbuffer, int * that contains the reference topology. * This attribute is given back when reading the diff from XML. * - * The returned buffer ends with a \0 that is included in the returned + * The returned buffer ends with a \c \0 that is included in the returned * length. * * \return 0 on success, -1 on error. diff --git a/src/3rdparty/hwloc/include/hwloc/distances.h b/src/3rdparty/hwloc/include/hwloc/distances.h index 5b0db873..15b2dc8a 100644 --- a/src/3rdparty/hwloc/include/hwloc/distances.h +++ b/src/3rdparty/hwloc/include/hwloc/distances.h @@ -1,5 +1,5 @@ /* - * Copyright © 2010-2024 Inria. All rights reserved. + * Copyright © 2010-2025 Inria. All rights reserved. * See COPYING in top-level directory. */ @@ -227,17 +227,24 @@ enum hwloc_distances_transform_e { HWLOC_DISTANCES_TRANSFORM_LINKS = 1, /** \brief Merge switches with multiple ports into a single object. - * This currently only applies to NVSwitches where GPUs seem connected to different - * separate switch ports in the NVLinkBandwidth matrix. This transformation will - * replace all of them with the same port connected to all GPUs. - * Other ports are removed by applying ::HWLOC_DISTANCES_TRANSFORM_REMOVE_NULL internally. + * + * This currently only applies to NVSwitches where GPUs seem connected + * to different switch ports. Switch ports must be objects with subtype + * "NVSwitch" as in the NVLinkBandwidth matrix. + * + * This transformation will replace all ports with only the first one, + * now connected to all GPUs. Other ports are removed by applying + * ::HWLOC_DISTANCES_TRANSFORM_REMOVE_NULL internally. * \hideinitializer */ HWLOC_DISTANCES_TRANSFORM_MERGE_SWITCH_PORTS = 2, /** \brief Apply a transitive closure to the matrix to connect objects across switches. - * This currently only applies to GPUs and NVSwitches in the NVLinkBandwidth matrix. - * All pairs of GPUs will be reported as directly connected. + * + * All pairs of GPUs will be reported as directly connected instead GPUs being + * only connected to switches. + * + * Switch ports must be objects with subtype "NVSwitch" as in the NVLinkBandwidth matrix. * \hideinitializer */ HWLOC_DISTANCES_TRANSFORM_TRANSITIVE_CLOSURE = 3 diff --git a/src/3rdparty/hwloc/include/hwloc/levelzero.h b/src/3rdparty/hwloc/include/hwloc/levelzero.h index dcdcf1fb..c1b43845 100644 --- a/src/3rdparty/hwloc/include/hwloc/levelzero.h +++ b/src/3rdparty/hwloc/include/hwloc/levelzero.h @@ -1,5 +1,5 @@ /* - * Copyright © 2021-2023 Inria. All rights reserved. + * Copyright © 2021-2024 Inria. All rights reserved. * See COPYING in top-level directory. */ @@ -32,7 +32,8 @@ extern "C" { /** \defgroup hwlocality_levelzero Interoperability with the oneAPI Level Zero interface. * * This interface offers ways to retrieve topology information about - * devices managed by the Level Zero API. + * devices managed by the Level Zero API, both for main Core devices (ZE API) + * and the Sysman devices (ZES API). * * @{ */ @@ -44,9 +45,68 @@ extern "C" { * the Level Zero device \p device. * * Topology \p topology and device \p device must match the local machine. + * The Level Zero library must have been initialized with zeInit(). + * I/O devices detection and the Level Zero component are not needed in the + * topology. + * + * The function only returns the locality of the device. + * If more information about the device is needed, OS objects should + * be used instead, see hwloc_levelzero_get_device_osdev(). + * + * This function is currently only implemented in a meaningful way for + * Linux; other systems will simply get a full cpuset. + * + * \return 0 on success. + * \return -1 on error, for instance if device information could not be found. + * + * \note zeDevicePciGetPropertiesExt() must be supported, or the entire machine + * locality will be returned. + */ +static __hwloc_inline int +hwloc_levelzero_get_device_cpuset(hwloc_topology_t topology __hwloc_attribute_unused, + ze_device_handle_t device, hwloc_cpuset_t set) +{ +#ifdef HWLOC_LINUX_SYS + /* If we're on Linux, use the sysfs mechanism to get the local cpus */ +#define HWLOC_LEVELZERO_DEVICE_SYSFS_PATH_MAX 128 + char path[HWLOC_LEVELZERO_DEVICE_SYSFS_PATH_MAX]; + ze_pci_ext_properties_t pci; + ze_result_t res; + + if (!hwloc_topology_is_thissystem(topology)) { + errno = EINVAL; + return -1; + } + + pci.stype = ZE_STRUCTURE_TYPE_PCI_EXT_PROPERTIES; + pci.pNext = NULL; + res = zeDevicePciGetPropertiesExt(device, &pci); + if (res != ZE_RESULT_SUCCESS) { + errno = EINVAL; + return -1; + } + + sprintf(path, "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/local_cpus", + pci.address.domain, pci.address.bus, pci.address.device, pci.address.function); + if (hwloc_linux_read_path_as_cpumask(path, set) < 0 + || hwloc_bitmap_iszero(set)) + hwloc_bitmap_copy(set, hwloc_topology_get_complete_cpuset(topology)); +#else + /* Non-Linux systems simply get a full cpuset */ + hwloc_bitmap_copy(set, hwloc_topology_get_complete_cpuset(topology)); +#endif + return 0; +} + +/** \brief Get the CPU set of logical processors that are physically + * close to the Level Zero Sysman device \p device + * + * Store in \p set the CPU-set describing the locality of + * the Level Zero device \p device. + * + * Topology \p topology and device \p device must match the local machine. * The Level Zero library must have been initialized with Sysman enabled - * (by calling zesInit(0) if supported, - * or by setting ZES_ENABLE_SYSMAN=1 in the environment). + * with zesInit(). * I/O devices detection and the Level Zero component are not needed in the * topology. * @@ -61,15 +121,14 @@ extern "C" { * \return -1 on error, for instance if device information could not be found. */ static __hwloc_inline int -hwloc_levelzero_get_device_cpuset(hwloc_topology_t topology __hwloc_attribute_unused, - ze_device_handle_t device, hwloc_cpuset_t set) +hwloc_levelzero_get_sysman_device_cpuset(hwloc_topology_t topology __hwloc_attribute_unused, + zes_device_handle_t device, hwloc_cpuset_t set) { #ifdef HWLOC_LINUX_SYS /* If we're on Linux, use the sysfs mechanism to get the local cpus */ #define HWLOC_LEVELZERO_DEVICE_SYSFS_PATH_MAX 128 char path[HWLOC_LEVELZERO_DEVICE_SYSFS_PATH_MAX]; zes_pci_properties_t pci; - zes_device_handle_t sdevice = device; ze_result_t res; if (!hwloc_topology_is_thissystem(topology)) { @@ -77,7 +136,7 @@ hwloc_levelzero_get_device_cpuset(hwloc_topology_t topology __hwloc_attribute_un return -1; } - res = zesDevicePciGetProperties(sdevice, &pci); + res = zesDevicePciGetProperties(device, &pci); if (res != ZE_RESULT_SUCCESS) { errno = EINVAL; return -1; @@ -102,17 +161,90 @@ hwloc_levelzero_get_device_cpuset(hwloc_topology_t topology __hwloc_attribute_un * \return \c NULL if none could be found. * * Topology \p topology and device \p dv_ind must match the local machine. + * The Level Zero library must have been initialized with zeInit(). * I/O devices detection and the Level Zero component must be enabled in the * topology. If not, the locality of the object may still be found using * hwloc_levelzero_get_device_cpuset(). * + * \note If the input ZE device is actually a subdevice, then its parent + * (root device) is actually translated, i.e. the main hwloc OS device + * is returned instead of one of its children. + * + * \note The corresponding hwloc PCI device may be found by looking + * at the result parent pointer (unless PCI devices are filtered out). + * + * \note zeDevicePciGetPropertiesExt() must be supported. + */ +static __hwloc_inline hwloc_obj_t +hwloc_levelzero_get_device_osdev(hwloc_topology_t topology, ze_device_handle_t device) +{ + ze_pci_ext_properties_t pci; + ze_result_t res; + hwloc_obj_t osdev; + + if (!hwloc_topology_is_thissystem(topology)) { + errno = EINVAL; + return NULL; + } + + pci.stype = ZE_STRUCTURE_TYPE_PCI_EXT_PROPERTIES; + pci.pNext = NULL; + res = zeDevicePciGetPropertiesExt(device, &pci); + if (res != ZE_RESULT_SUCCESS) { + errno = EINVAL; + return NULL; + } + + osdev = NULL; + while ((osdev = hwloc_get_next_osdev(topology, osdev)) != NULL) { + hwloc_obj_t pcidev; + + if (strncmp(osdev->name, "ze", 2)) + continue; + + pcidev = osdev; + while (pcidev && pcidev->type != HWLOC_OBJ_PCI_DEVICE) + pcidev = pcidev->parent; + if (!pcidev) + continue; + + if (pcidev + && pcidev->type == HWLOC_OBJ_PCI_DEVICE + && pcidev->attr->pcidev.domain == pci.address.domain + && pcidev->attr->pcidev.bus == pci.address.bus + && pcidev->attr->pcidev.dev == pci.address.device + && pcidev->attr->pcidev.func == pci.address.function) + return osdev; + + /* FIXME: when we'll have serialnumber, try it in case PCI is filtered-out */ + } + + return NULL; +} + +/** \brief Get the hwloc OS device object corresponding to Level Zero Sysman device + * \p device. + * + * \return The hwloc OS device object that describes the given Level Zero device \p device. + * \return \c NULL if none could be found. + * + * Topology \p topology and device \p dv_ind must match the local machine. + * The Level Zero library must have been initialized with Sysman enabled + * with zesInit(). + * I/O devices detection and the Level Zero component must be enabled in the + * topology. If not, the locality of the object may still be found using + * hwloc_levelzero_get_device_cpuset(). + * + * \note If the input ZES device is actually a subdevice, then its parent + * (root device) is actually translated, i.e. the main hwloc OS device + * is returned instead of one of its children. + * * \note The corresponding hwloc PCI device may be found by looking * at the result parent pointer (unless PCI devices are filtered out). */ static __hwloc_inline hwloc_obj_t -hwloc_levelzero_get_device_osdev(hwloc_topology_t topology, ze_device_handle_t device) +hwloc_levelzero_get_sysman_device_osdev(hwloc_topology_t topology, zes_device_handle_t device) { - zes_device_handle_t sdevice = device; zes_pci_properties_t pci; ze_result_t res; hwloc_obj_t osdev; @@ -122,20 +254,25 @@ hwloc_levelzero_get_device_osdev(hwloc_topology_t topology, ze_device_handle_t d return NULL; } - res = zesDevicePciGetProperties(sdevice, &pci); + res = zesDevicePciGetProperties(device, &pci); if (res != ZE_RESULT_SUCCESS) { - /* L0 was likely initialized without sysman, don't bother */ errno = EINVAL; return NULL; } osdev = NULL; while ((osdev = hwloc_get_next_osdev(topology, osdev)) != NULL) { - hwloc_obj_t pcidev = osdev->parent; + hwloc_obj_t pcidev; if (strncmp(osdev->name, "ze", 2)) continue; + pcidev = osdev; + while (pcidev && pcidev->type != HWLOC_OBJ_PCI_DEVICE) + pcidev = pcidev->parent; + if (!pcidev) + continue; + if (pcidev && pcidev->type == HWLOC_OBJ_PCI_DEVICE && pcidev->attr->pcidev.domain == pci.address.domain diff --git a/src/3rdparty/hwloc/include/hwloc/memattrs.h b/src/3rdparty/hwloc/include/hwloc/memattrs.h index 81b85d64..ad9278cc 100644 --- a/src/3rdparty/hwloc/include/hwloc/memattrs.h +++ b/src/3rdparty/hwloc/include/hwloc/memattrs.h @@ -1,5 +1,5 @@ /* - * Copyright © 2019-2024 Inria. All rights reserved. + * Copyright © 2019-2025 Inria. All rights reserved. * See COPYING in top-level directory. */ @@ -58,6 +58,11 @@ extern "C" { * an easy way to distinguish NUMA nodes of different kinds, as explained * in \ref heteromem. * + * Beside tiers, hwloc defines a set of "default" nodes where normal memory + * allocations should be made from (see hwloc_topology_get_default_nodeset()). + * This is also useful for dividing the machine into a set of non-overlapping + * NUMA domains, for instance for binding tasks per domain. + * * \sa An example is available in doc/examples/memory-attributes.c in the source tree. * * \note The API also supports specific objects as initiator, @@ -245,6 +250,16 @@ enum hwloc_local_numanode_flag_e { */ HWLOC_LOCAL_NUMANODE_FLAG_SMALLER_LOCALITY = (1UL<<1), + /** \breif Select NUMA nodes whose locality intersects the given cpuset. + * This includes larger and smaller localities as well as localities + * that are partially included. + * For instance, if the locality is one core of both packages, a NUMA node + * local to one package is neither larger nor smaller than this locality, + * but it intersects it. + * \hideinitializer + */ + HWLOC_LOCAL_NUMANODE_FLAG_INTERSECT_LOCALITY = (1UL<<3), + /** \brief Select all NUMA nodes in the topology. * The initiator \p initiator is ignored. * \hideinitializer @@ -290,7 +305,57 @@ hwloc_get_local_numanode_objs(hwloc_topology_t topology, hwloc_obj_t *nodes, unsigned long flags); - +/** \brief Return the set of default NUMA nodes + * + * In machines with heterogeneous memory, some NUMA nodes are considered + * the default ones, i.e. where basic allocations should be made from. + * These are usually DRAM nodes. + * + * Other nodes may be reserved for specific use (I/O device memory, e.g. GPU memory), + * small but high performance (HBM), large but slow memory (NVM), etc. + * Buffers should usually not be allocated from there unless explicitly required. + * + * This function fills \p nodeset with the bits of NUMA nodes considered default. + * + * It is guaranteed that these nodes have non-intersecting CPU sets, + * i.e. cores may not have multiple local NUMA nodes anymore. + * Hence this may be used to iterate over the platform divided into separate + * NUMA localities, for instance for binding one task per NUMA domain. + * + * Any core that had some local NUMA node(s) in the initial topology should + * still have one in the default nodeset. Corner cases where this would be + * wrong consist in asymmetric platforms with missing DRAM nodes, or topologies + * that were already restricted to less NUMA nodes. + * + * The returned nodeset may be passed to hwloc_topology_restrict() with + * ::HWLOC_RESTRICT_FLAG_BYNODESET to remove all non-default nodes from + * the topology. The resulting topology will be easier to use when iterating + * over (now homogeneous) NUMA nodes. + * + * The heuristics for finding default nodes relies on memory tiers and subtypes + * (see \ref heteromem) as well as the assumption that hardware vendors list + * default nodes first in hardware tables. + * + * \p flags must be \c 0 for now. + * + * \return 0 on success. + * \return -1 on error. + * + * \note The returned nodeset usually contains all nodes from a single memory + * tier, likely the DRAM one. + * + * \note The returned nodeset is included in the list of available nodes + * returned by hwloc_topology_get_topology_nodeset(). It is strictly smaller + * if the machine has heterogeneous memory. + * + * \note The heuristics may return a suboptimal set of nodes if hwloc could + * not guess memory types and/or if some default nodes were removed earlier + * from the topology (e.g. with hwloc_topology_restrict()). + */ +HWLOC_DECLSPEC int +hwloc_topology_get_default_nodeset(hwloc_topology_t topology, + hwloc_nodeset_t nodeset, + unsigned long flags); /** \brief Return an attribute value for a specific target NUMA node. * diff --git a/src/3rdparty/hwloc/include/hwloc/plugins.h b/src/3rdparty/hwloc/include/hwloc/plugins.h index 95e68195..96f95f1f 100644 --- a/src/3rdparty/hwloc/include/hwloc/plugins.h +++ b/src/3rdparty/hwloc/include/hwloc/plugins.h @@ -26,7 +26,7 @@ struct hwloc_backend; -/** \defgroup hwlocality_disc_components Components and Plugins: Discovery components +/** \defgroup hwlocality_disc_components Components and Plugins: Discovery components and backends * * \note These structures and functions may change when ::HWLOC_COMPONENT_ABI is modified. * @@ -90,18 +90,6 @@ struct hwloc_disc_component { struct hwloc_disc_component * next; }; -/** @} */ - - - - -/** \defgroup hwlocality_disc_backends Components and Plugins: Discovery backends - * - * \note These structures and functions may change when ::HWLOC_COMPONENT_ABI is modified. - * - * @{ - */ - /** \brief Discovery phase */ typedef enum hwloc_disc_phase_e { /** \brief xml or synthetic, platform-specific components such as bgq. @@ -313,6 +301,64 @@ struct hwloc_component { void * data; }; +/** \brief Make sure that plugins can lookup core symbols. + * + * This is a sanity check to avoid lazy-lookup failures when libhwloc + * is loaded within a plugin, and later tries to load its own plugins. + * This may fail (and abort the program) if libhwloc symbols are in a + * private namespace. + * + * \return 0 on success. + * \return -1 if the plugin cannot be successfully loaded. The caller + * plugin init() callback should return a negative error code as well. + * + * Plugins should call this function in their init() callback to avoid + * later crashes if lazy symbol resolution is used by the upper layer that + * loaded hwloc (e.g. OpenCL implementations using dlopen with RTLD_LAZY). + * + * \note The build system must define HWLOC_INSIDE_PLUGIN if and only if + * building the caller as a plugin. + * + * \note This function should remain inline so plugins can call it even + * when they cannot find libhwloc symbols. + */ +static __hwloc_inline int +hwloc_plugin_check_namespace(const char *pluginname __hwloc_attribute_unused, const char *symbol __hwloc_attribute_unused) +{ +#ifdef HWLOC_INSIDE_PLUGIN + void *sym; +#ifdef HWLOC_HAVE_LTDL + lt_dlhandle handle = lt_dlopen(NULL); +#else + void *handle = dlopen(NULL, RTLD_NOW|RTLD_LOCAL); +#endif + if (!handle) + /* cannot check, assume things will work */ + return 0; +#ifdef HWLOC_HAVE_LTDL + sym = lt_dlsym(handle, symbol); + lt_dlclose(handle); +#else + sym = dlsym(handle, symbol); + dlclose(handle); +#endif + if (!sym) { + static int verboseenv_checked = 0; + static int verboseenv_value = 0; + if (!verboseenv_checked) { + const char *verboseenv = getenv("HWLOC_PLUGINS_VERBOSE"); + verboseenv_value = verboseenv ? atoi(verboseenv) : 0; + verboseenv_checked = 1; + } + if (verboseenv_value) + fprintf(stderr, "Plugin `%s' disabling itself because it cannot find the `%s' core symbol.\n", + pluginname, symbol); + return -1; + } +#endif /* HWLOC_INSIDE_PLUGIN */ + return 0; +} + /** @} */ @@ -422,64 +468,6 @@ HWLOC_DECLSPEC int hwloc_obj_add_children_sets(hwloc_obj_t obj); */ HWLOC_DECLSPEC int hwloc_topology_reconnect(hwloc_topology_t topology, unsigned long flags __hwloc_attribute_unused); -/** \brief Make sure that plugins can lookup core symbols. - * - * This is a sanity check to avoid lazy-lookup failures when libhwloc - * is loaded within a plugin, and later tries to load its own plugins. - * This may fail (and abort the program) if libhwloc symbols are in a - * private namespace. - * - * \return 0 on success. - * \return -1 if the plugin cannot be successfully loaded. The caller - * plugin init() callback should return a negative error code as well. - * - * Plugins should call this function in their init() callback to avoid - * later crashes if lazy symbol resolution is used by the upper layer that - * loaded hwloc (e.g. OpenCL implementations using dlopen with RTLD_LAZY). - * - * \note The build system must define HWLOC_INSIDE_PLUGIN if and only if - * building the caller as a plugin. - * - * \note This function should remain inline so plugins can call it even - * when they cannot find libhwloc symbols. - */ -static __hwloc_inline int -hwloc_plugin_check_namespace(const char *pluginname __hwloc_attribute_unused, const char *symbol __hwloc_attribute_unused) -{ -#ifdef HWLOC_INSIDE_PLUGIN - void *sym; -#ifdef HWLOC_HAVE_LTDL - lt_dlhandle handle = lt_dlopen(NULL); -#else - void *handle = dlopen(NULL, RTLD_NOW|RTLD_LOCAL); -#endif - if (!handle) - /* cannot check, assume things will work */ - return 0; -#ifdef HWLOC_HAVE_LTDL - sym = lt_dlsym(handle, symbol); - lt_dlclose(handle); -#else - sym = dlsym(handle, symbol); - dlclose(handle); -#endif - if (!sym) { - static int verboseenv_checked = 0; - static int verboseenv_value = 0; - if (!verboseenv_checked) { - const char *verboseenv = getenv("HWLOC_PLUGINS_VERBOSE"); - verboseenv_value = verboseenv ? atoi(verboseenv) : 0; - verboseenv_checked = 1; - } - if (verboseenv_value) - fprintf(stderr, "Plugin `%s' disabling itself because it cannot find the `%s' core symbol.\n", - pluginname, symbol); - return -1; - } -#endif /* HWLOC_INSIDE_PLUGIN */ - return 0; -} - /** @} */ diff --git a/src/3rdparty/hwloc/include/hwloc/rename.h b/src/3rdparty/hwloc/include/hwloc/rename.h index fca397fc..7fddc600 100644 --- a/src/3rdparty/hwloc/include/hwloc/rename.h +++ b/src/3rdparty/hwloc/include/hwloc/rename.h @@ -1,6 +1,6 @@ /* * Copyright © 2009-2011 Cisco Systems, Inc. All rights reserved. - * Copyright © 2010-2024 Inria. All rights reserved. + * Copyright © 2010-2025 Inria. All rights reserved. * See COPYING in top-level directory. */ @@ -409,8 +409,10 @@ extern "C" { #define hwloc_local_numanode_flag_e HWLOC_NAME(local_numanode_flag_e) #define HWLOC_LOCAL_NUMANODE_FLAG_LARGER_LOCALITY HWLOC_NAME_CAPS(LOCAL_NUMANODE_FLAG_LARGER_LOCALITY) #define HWLOC_LOCAL_NUMANODE_FLAG_SMALLER_LOCALITY HWLOC_NAME_CAPS(LOCAL_NUMANODE_FLAG_SMALLER_LOCALITY) +#define HWLOC_LOCAL_NUMANODE_FLAG_INTERSECT_LOCALITY HWLOC_NAME_CAPS(LOCAL_NUMANODE_FLAG_INTERSECT_LOCALITY) #define HWLOC_LOCAL_NUMANODE_FLAG_ALL HWLOC_NAME_CAPS(LOCAL_NUMANODE_FLAG_ALL) #define hwloc_get_local_numanode_objs HWLOC_NAME(get_local_numanode_objs) +#define hwloc_topology_get_default_nodeset HWLOC_NAME(topology_get_default_nodeset) #define hwloc_memattr_get_name HWLOC_NAME(memattr_get_name) #define hwloc_memattr_get_flags HWLOC_NAME(memattr_get_flags) @@ -599,7 +601,9 @@ extern "C" { /* levelzero.h */ #define hwloc_levelzero_get_device_cpuset HWLOC_NAME(levelzero_get_device_cpuset) +#define hwloc_levelzero_get_sysman_device_cpuset HWLOC_NAME(levelzero_get_sysman_device_cpuset) #define hwloc_levelzero_get_device_osdev HWLOC_NAME(levelzero_get_device_osdev) +#define hwloc_levelzero_get_sysman_device_osdev HWLOC_NAME(levelzero_get_sysman_device_osdev) /* gl.h */ @@ -813,6 +817,8 @@ extern "C" { #define hwloc_topology_setup_defaults HWLOC_NAME(topology_setup_defaults) #define hwloc_topology_clear HWLOC_NAME(topology_clear) +#define hwloc__reconnect HWLOC_NAME(_reconnect) + #define hwloc__attach_memory_object HWLOC_NAME(insert_memory_object) #define hwloc_get_obj_by_type_and_gp_index HWLOC_NAME(get_obj_by_type_and_gp_index) diff --git a/src/3rdparty/hwloc/include/private/autogen/config.h b/src/3rdparty/hwloc/include/private/autogen/config.h index 3002ac5e..5bf22fac 100644 --- a/src/3rdparty/hwloc/include/private/autogen/config.h +++ b/src/3rdparty/hwloc/include/private/autogen/config.h @@ -1,6 +1,6 @@ /* * Copyright © 2009, 2011, 2012 CNRS. All rights reserved. - * Copyright © 2009-2020 Inria. All rights reserved. + * Copyright © 2009-2021 Inria. All rights reserved. * Copyright © 2009, 2011, 2012, 2015 Université Bordeaux. All rights reserved. * Copyright © 2009-2020 Cisco Systems, Inc. All rights reserved. * $COPYRIGHT$ @@ -17,10 +17,6 @@ #define HWLOC_HAVE_MSVC_CPUIDEX 1 -/* #undef HAVE_MKSTEMP */ - -#define HWLOC_HAVE_X86_CPUID 1 - /* Define to 1 if the system has the type `CACHE_DESCRIPTOR'. */ #define HAVE_CACHE_DESCRIPTOR 0 @@ -132,7 +128,8 @@ #define HAVE_DECL__SC_PAGE_SIZE 0 /* Define to 1 if you have the header file. */ -/* #undef HAVE_DIRENT_H */ +/* #define HAVE_DIRENT_H 1 */ +#undef HAVE_DIRENT_H /* Define to 1 if you have the header file. */ /* #undef HAVE_DLFCN_H */ @@ -285,7 +282,7 @@ #define HAVE_STRING_H 1 /* Define to 1 if you have the `strncasecmp' function. */ -/* #undef HAVE_STRNCASECMP */ +#define HAVE_STRNCASECMP 1 /* Define to '1' if sysctl is present and usable */ /* #undef HAVE_SYSCTL */ @@ -326,7 +323,8 @@ /* #undef HAVE_UNAME */ /* Define to 1 if you have the header file. */ -/* #undef HAVE_UNISTD_H */ +/* #define HAVE_UNISTD_H 1 */ +#undef HAVE_UNISTD_H /* Define to 1 if you have the `uselocale' function. */ /* #undef HAVE_USELOCALE */ @@ -661,7 +659,7 @@ #define hwloc_pid_t HANDLE /* Define this to either strncasecmp or strncmp */ -/* #undef hwloc_strncasecmp */ +#define hwloc_strncasecmp strncasecmp /* Define this to the thread ID type */ #define hwloc_thread_t HANDLE diff --git a/src/3rdparty/hwloc/include/private/misc.h b/src/3rdparty/hwloc/include/private/misc.h index b5ee196c..823e8a2d 100644 --- a/src/3rdparty/hwloc/include/private/misc.h +++ b/src/3rdparty/hwloc/include/private/misc.h @@ -186,7 +186,7 @@ hwloc_ffsl_from_ffs32(unsigned long x) /** * flsl helpers. */ -#ifdef __GNUC_____ +#ifdef __GNUC__ # if (__GNUC__ >= 4) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4)) # define hwloc_flsl(x) ((x) ? (8*sizeof(long) - __builtin_clzl(x)) : 0) diff --git a/src/3rdparty/hwloc/include/private/private.h b/src/3rdparty/hwloc/include/private/private.h index 3e3f71d9..0c262aa6 100644 --- a/src/3rdparty/hwloc/include/private/private.h +++ b/src/3rdparty/hwloc/include/private/private.h @@ -1,6 +1,6 @@ /* * Copyright © 2009 CNRS - * Copyright © 2009-2023 Inria. All rights reserved. + * Copyright © 2009-2025 Inria. All rights reserved. * Copyright © 2009-2012, 2020 Université Bordeaux * Copyright © 2009-2011 Cisco Systems, Inc. All rights reserved. * @@ -302,6 +302,9 @@ extern void hwloc__reorder_children(hwloc_obj_t parent); extern void hwloc_topology_setup_defaults(struct hwloc_topology *topology); extern void hwloc_topology_clear(struct hwloc_topology *topology); +#define _HWLOC_RECONNECT_FLAG_KEEPSTRUCTURE (1UL<<0) +extern int hwloc__reconnect(struct hwloc_topology *topology, unsigned long flags); + /* insert memory object as memory child of normal parent */ extern struct hwloc_obj * hwloc__attach_memory_object(struct hwloc_topology *topology, hwloc_obj_t parent, hwloc_obj_t obj, const char *reason); diff --git a/src/3rdparty/hwloc/src/distances.c b/src/3rdparty/hwloc/src/distances.c index 6dab5113..28c586c7 100644 --- a/src/3rdparty/hwloc/src/distances.c +++ b/src/3rdparty/hwloc/src/distances.c @@ -1,5 +1,5 @@ /* - * Copyright © 2010-2024 Inria. All rights reserved. + * Copyright © 2010-2025 Inria. All rights reserved. * Copyright © 2011-2012 Université Bordeaux * Copyright © 2011 Cisco Systems, Inc. All rights reserved. * See COPYING in top-level directory. @@ -699,7 +699,7 @@ hwloc_distances_add_commit(hwloc_topology_t topology, } /* in case we added some groups, see if we need to reconnect */ - hwloc_topology_reconnect(topology, 0); + hwloc__reconnect(topology, 0); return 0; @@ -1387,19 +1387,12 @@ static __hwloc_inline int is_nvswitch(hwloc_obj_t obj) } static int -hwloc__distances_transform_merge_switch_ports(hwloc_topology_t topology, - struct hwloc_distances_s *distances) +hwloc__distances_transform_merge_switch_ports(struct hwloc_distances_s *distances) { - struct hwloc_internal_distances_s *dist = hwloc__internal_distances_from_public(topology, distances); hwloc_obj_t *objs = distances->objs; hwloc_uint64_t *values = distances->values; unsigned first, i, j, nbobjs = distances->nbobjs; - if (strcmp(dist->name, "NVLinkBandwidth")) { - errno = EINVAL; - return -1; - } - /* find the first port */ first = (unsigned) -1; for(i=0; iobjs; hwloc_uint64_t *values = distances->values; unsigned nbobjs = distances->nbobjs; unsigned i, j, k; - if (strcmp(dist->name, "NVLinkBandwidth")) { - errno = EINVAL; - return -1; - } - for(i=0; i bw_sw2j ? bw_sw2j : bw_i2sw; + /* bandwidth from i to j now gets indirect bandwidth too, min(i2sw,sw2j) */ + values[i*nbobjs+j] += bw_i2sw > bw_sw2j ? bw_sw2j : bw_i2sw; } } @@ -1476,7 +1462,7 @@ hwloc__distances_transform_transitive_closure(hwloc_topology_t topology, } int -hwloc_distances_transform(hwloc_topology_t topology, +hwloc_distances_transform(hwloc_topology_t topology __hwloc_attribute_unused, struct hwloc_distances_s *distances, enum hwloc_distances_transform_e transform, void *transform_attr, @@ -1495,13 +1481,13 @@ hwloc_distances_transform(hwloc_topology_t topology, case HWLOC_DISTANCES_TRANSFORM_MERGE_SWITCH_PORTS: { int err; - err = hwloc__distances_transform_merge_switch_ports(topology, distances); + err = hwloc__distances_transform_merge_switch_ports(distances); if (!err) err = hwloc__distances_transform_remove_null(distances); return err; } case HWLOC_DISTANCES_TRANSFORM_TRANSITIVE_CLOSURE: - return hwloc__distances_transform_transitive_closure(topology, distances); + return hwloc__distances_transform_transitive_closure(distances); default: errno = EINVAL; return -1; diff --git a/src/3rdparty/hwloc/src/memattrs.c b/src/3rdparty/hwloc/src/memattrs.c index 112cbcf9..f2c27565 100644 --- a/src/3rdparty/hwloc/src/memattrs.c +++ b/src/3rdparty/hwloc/src/memattrs.c @@ -1,5 +1,5 @@ /* - * Copyright © 2020-2024 Inria. All rights reserved. + * Copyright © 2020-2025 Inria. All rights reserved. * See COPYING in top-level directory. */ @@ -1158,6 +1158,8 @@ match_local_obj_cpuset(hwloc_obj_t node, hwloc_cpuset_t cpuset, unsigned long fl { if (flags & HWLOC_LOCAL_NUMANODE_FLAG_ALL) return 1; + if (flags & HWLOC_LOCAL_NUMANODE_FLAG_INTERSECT_LOCALITY) + return hwloc_bitmap_intersects(node->cpuset, cpuset); if ((flags & HWLOC_LOCAL_NUMANODE_FLAG_LARGER_LOCALITY) && hwloc_bitmap_isincluded(cpuset, node->cpuset)) return 1; @@ -1180,6 +1182,7 @@ hwloc_get_local_numanode_objs(hwloc_topology_t topology, if (flags & ~(HWLOC_LOCAL_NUMANODE_FLAG_SMALLER_LOCALITY |HWLOC_LOCAL_NUMANODE_FLAG_LARGER_LOCALITY + |HWLOC_LOCAL_NUMANODE_FLAG_INTERSECT_LOCALITY | HWLOC_LOCAL_NUMANODE_FLAG_ALL)) { errno = EINVAL; return -1; @@ -1226,6 +1229,93 @@ hwloc_get_local_numanode_objs(hwloc_topology_t topology, return 0; } +static int compare_nodes_by_os_index(const void *_a, const void *_b) +{ + const hwloc_obj_t * a = _a, * b = _b; + return (*a)->os_index - (*b)->os_index; +} + +int +hwloc_topology_get_default_nodeset(hwloc_topology_t topology, + hwloc_nodeset_t nodeset, + unsigned long flags) +{ + hwloc_obj_t *nodes; + hwloc_bitmap_t remainingcpuset; + unsigned nrnodes, i; + const char *first_subtype; + + if (flags) { + errno = EINVAL; + goto out; + } + + remainingcpuset = hwloc_bitmap_dup(topology->levels[0][0]->cpuset); + if (!remainingcpuset) + goto out; + + nrnodes = topology->slevels[HWLOC_SLEVEL_NUMANODE].nbobjs; + nodes = malloc(nrnodes * sizeof(*nodes)); + if (!nodes) + goto out_with_remainingcpuset; + + memcpy(nodes, topology->slevels[HWLOC_SLEVEL_NUMANODE].objs, nrnodes * sizeof(*nodes)); + qsort(nodes, nrnodes, sizeof(*nodes), compare_nodes_by_os_index); + + hwloc_bitmap_zero(nodeset); + + /* always take the first node (FIXME: except if unexpected subtype?) */ + first_subtype = nodes[0]->subtype; + hwloc_bitmap_set(nodeset, nodes[0]->os_index); + hwloc_bitmap_andnot(remainingcpuset, remainingcpuset, nodes[0]->cpuset); + + /* use all non-intersecting nodes with same subtype */ + for(i=1; isubtype || strcmp(first_subtype, nodes[i]->subtype)) + continue; + } else if (nodes[i]->subtype) { + continue; + } + /* take non-overlapping nodes */ + if (hwloc_bitmap_isincluded(nodes[i]->cpuset, remainingcpuset) /* can be empty */) { + hwloc_bitmap_set(nodeset, nodes[i]->os_index); + hwloc_bitmap_andnot(remainingcpuset, remainingcpuset, nodes[i]->cpuset); + } + /* more needed? */ + if (hwloc_bitmap_iszero(remainingcpuset)) + goto done; + } + + /* find more nodes to cover the entire topology cpuset. + * only take what's necessary: first nodes, non-empty */ + for(i=1; icpuset, remainingcpuset) + && !hwloc_bitmap_iszero(nodes[i]->cpuset)) { + hwloc_bitmap_set(nodeset, nodes[i]->os_index); + hwloc_bitmap_andnot(remainingcpuset, remainingcpuset, nodes[i]->cpuset); + } + /* more needed? */ + if (hwloc_bitmap_iszero(remainingcpuset)) + goto done; + } + + done: + free(nodes); + hwloc_bitmap_free(remainingcpuset); + return 0; + + out_with_remainingcpuset: + hwloc_bitmap_free(remainingcpuset); + out: + return -1; +} + /************************************** * Using memattrs to identify HBM/DRAM @@ -1433,10 +1523,15 @@ hwloc__group_memory_tiers(hwloc_topology_t topology, } } - /* Sort nodes. - * We could also sort by the existing subtype. - * KNL is the only case where subtypes are set in backends, but we set memattrs as well there. - * Also HWLOC_MEMTIERS_REFRESH would be a special value to ignore existing subtypes. + /* Sort nodes by tier type and bandwidth. + * + * We could also use the existing subtype but it's not clear it'd be better. + * For NVIDIA GPU, "GPUMemory" is set in the Linux backend, and used above to set tier type anyway. + * For KNL, the Linux backend sets subtypes and memattrs, sorting by memattrs already works fine. + * Existing subtypes could have been imported from XML, usually mostly OK except maybe SPM (fallback for I don't know)? + * An envvar (or HWLOC_MEMTIERS_REFRESH special value?) could be passed to ignore existing subtypes, + * but "GPUMemory" wouldn't be available anymore, we'd have to use something else like "PCIBusId", + * but that one might not always be specific to GPU-backed NUMA nodes? */ hwloc_debug("Sorting memory node infos...\n"); qsort(nodeinfos, n, sizeof(*nodeinfos), compare_node_infos_by_type_and_bw); diff --git a/src/3rdparty/hwloc/src/topology-windows.c b/src/3rdparty/hwloc/src/topology-windows.c index a8d6e014..8aa19aeb 100644 --- a/src/3rdparty/hwloc/src/topology-windows.c +++ b/src/3rdparty/hwloc/src/topology-windows.c @@ -1,6 +1,6 @@ /* * Copyright © 2009 CNRS - * Copyright © 2009-2024 Inria. All rights reserved. + * Copyright © 2009-2025 Inria. All rights reserved. * Copyright © 2009-2012, 2020 Université Bordeaux * Copyright © 2011 Cisco Systems, Inc. All rights reserved. * See COPYING in top-level directory. @@ -56,6 +56,9 @@ typedef enum _LOGICAL_PROCESSOR_RELATIONSHIP { RelationCache, RelationProcessorPackage, RelationGroup, + RelationProcessorDie, + RelationNumaNodeEx, /* only used to *request* extended numa info only, but included in RelationAll, never returned on output */ + RelationProcessorModule, RelationAll = 0xffff } LOGICAL_PROCESSOR_RELATIONSHIP; #else /* HAVE_LOGICAL_PROCESSOR_RELATIONSHIP */ @@ -64,6 +67,11 @@ typedef enum _LOGICAL_PROCESSOR_RELATIONSHIP { # define RelationGroup 4 # define RelationAll 0xffff # endif /* HAVE_RELATIONPROCESSORPACKAGE */ +# ifndef HAVE_RELATIONPROCESSORDIE +# define RelationProcessorDie 5 +# define RelationNumaNodeEx 6 +# define RelationProcessorModule 7 +# endif #endif /* HAVE_LOGICAL_PROCESSOR_RELATIONSHIP */ #ifndef HAVE_GROUP_AFFINITY @@ -366,7 +374,7 @@ hwloc_win_get_processor_groups(void) hwloc_debug("found %lu windows processor groups\n", nr_processor_groups); if (nr_processor_groups > 1 && SIZEOF_VOID_P == 4) { - if (HWLOC_SHOW_ALL_ERRORS()) + if (HWLOC_SHOW_CRITICAL_ERRORS()) fprintf(stderr, "hwloc/windows: multiple processor groups found on 32bits Windows, topology may be invalid/incomplete.\n"); } @@ -1068,6 +1076,7 @@ hwloc_look_windows(struct hwloc_backend *backend, struct hwloc_disc_status *dsta id = HWLOC_UNKNOWN_INDEX; switch (procInfo->Relationship) { + case RelationNumaNodeEx: /* only used on input anyway */ case RelationNumaNode: type = HWLOC_OBJ_NUMANODE; /* Starting with Windows 11 and Server 2022, the GroupCount field is valid and >=1 @@ -1087,9 +1096,19 @@ hwloc_look_windows(struct hwloc_backend *backend, struct hwloc_disc_status *dsta break; case RelationProcessorPackage: type = HWLOC_OBJ_PACKAGE; + num = procInfo->Processor.GroupCount; + GroupMask = procInfo->Processor.GroupMask; + break; + case RelationProcessorDie: + type = HWLOC_OBJ_DIE; num = procInfo->Processor.GroupCount; GroupMask = procInfo->Processor.GroupMask; - break; + break; + case RelationProcessorModule: + type = HWLOC_OBJ_GROUP; + num = procInfo->Processor.GroupCount; + GroupMask = procInfo->Processor.GroupMask; + break; case RelationCache: type = (procInfo->Cache.Type == CacheInstruction ? HWLOC_OBJ_L1ICACHE : HWLOC_OBJ_L1CACHE) + procInfo->Cache.Level - 1; /* GroupCount added approximately with NumaNode.GroupCount above */ @@ -1211,6 +1230,19 @@ hwloc_look_windows(struct hwloc_backend *backend, struct hwloc_disc_status *dsta continue; } break; + case HWLOC_OBJ_GROUP: + switch (procInfo->Relationship) { + case RelationGroup: + obj->attr->group.kind = HWLOC_GROUP_KIND_WINDOWS_PROCESSOR_GROUP; + break; + case RelationProcessorModule: + obj->attr->group.kind = HWLOC_GROUP_KIND_INTEL_MODULE; + obj->subtype = strdup("Module"); + break; + default: + obj->attr->group.kind = HWLOC_GROUP_KIND_WINDOWS_RELATIONSHIP_UNKNOWN; + } + break; default: break; } diff --git a/src/3rdparty/hwloc/src/topology-x86.c b/src/3rdparty/hwloc/src/topology-x86.c index 22f65843..42248f68 100644 --- a/src/3rdparty/hwloc/src/topology-x86.c +++ b/src/3rdparty/hwloc/src/topology-x86.c @@ -1,5 +1,5 @@ /* - * Copyright © 2010-2024 Inria. All rights reserved. + * Copyright © 2010-2025 Inria. All rights reserved. * Copyright © 2010-2013 Université Bordeaux * Copyright © 2010-2011 Cisco Systems, Inc. All rights reserved. * See COPYING in top-level directory. @@ -653,7 +653,13 @@ static void look_proc(struct hwloc_backend *backend, struct procinfo *infos, uns cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump); infos->apicid = ebx >> 24; if (edx & (1 << 28)) { - legacy_max_log_proc = 1 << hwloc_flsl(((ebx >> 16) & 0xff) - 1); + unsigned ebx_16_23 = (ebx >> 16) & 0xff; + if (ebx_16_23) { + legacy_max_log_proc = 1 << hwloc_flsl(ebx_16_23 - 1); + } else { + hwloc_debug("HTT bit set in CPUID 0x01.edx, but legacy_max_proc = 0 in ebx, assuming legacy_max_log_proc = 1\n"); + legacy_max_log_proc = 1; + } } else { hwloc_debug("HTT bit not set in CPUID 0x01.edx, assuming legacy_max_log_proc = 1\n"); legacy_max_log_proc = 1; @@ -1742,7 +1748,7 @@ hwloc_x86_discover(struct hwloc_backend *backend, struct hwloc_disc_status *dsta if (topology->levels[0][0]->cpuset) { /* somebody else discovered things, reconnect levels so that we can look at them */ - hwloc_topology_reconnect(topology, 0); + hwloc__reconnect(topology, 0); if (topology->nb_levels == 2 && topology->level_nbobjects[1] == data->nbprocs) { /* only PUs were discovered, as much as we would, complete the topology with everything else */ alreadypus = 1; diff --git a/src/3rdparty/hwloc/src/topology-xml.c b/src/3rdparty/hwloc/src/topology-xml.c index 67c62349..67f56025 100644 --- a/src/3rdparty/hwloc/src/topology-xml.c +++ b/src/3rdparty/hwloc/src/topology-xml.c @@ -1,6 +1,6 @@ /* * Copyright © 2009 CNRS - * Copyright © 2009-2024 Inria. All rights reserved. + * Copyright © 2009-2025 Inria. All rights reserved. * Copyright © 2009-2011, 2020 Université Bordeaux * Copyright © 2009-2018 Cisco Systems, Inc. All rights reserved. * See COPYING in top-level directory. @@ -415,6 +415,20 @@ hwloc__xml_import_object_attr(struct hwloc_topology *topology, } } + else if (!strcmp(name, "numanode_type")) { + switch (obj->type) { + case HWLOC_OBJ_NUMANODE: { + /* ignored for now, here for possible forward compat */ + break; + } + default: + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: ignoring numanode_type attribute for non-NUMA object\n", + state->global->msgprefix); + break; + } + } + else if (data->version_major < 2) { /************************ * deprecated from 1.x @@ -876,14 +890,19 @@ hwloc__xml_import_object(hwloc_topology_t topology, /* deal with possible future type */ obj->type = HWLOC_OBJ_GROUP; obj->attr->group.kind = HWLOC_GROUP_KIND_LINUX_CLUSTER; - } else if (!strcasecmp(attrvalue, "MemCache")) { + } +#if 0 + /* reenable if there's ever a future type that should be ignored without being an error */ + else if (!strcasecmp(attrvalue, "MemCache")) { /* ignore possible future type */ obj->type = _HWLOC_OBJ_FUTURE; ignored = 1; if (hwloc__xml_verbose()) fprintf(stderr, "%s: %s object not-supported, will be ignored\n", state->global->msgprefix, attrvalue); - } else { + } +#endif + else { if (hwloc__xml_verbose()) fprintf(stderr, "%s: unrecognized object type string %s\n", state->global->msgprefix, attrvalue); @@ -958,22 +977,22 @@ hwloc__xml_import_object(hwloc_topology_t topology, if (hwloc__obj_type_is_normal(obj->type)) { if (!hwloc__obj_type_is_normal(parent->type)) { if (hwloc__xml_verbose()) - fprintf(stderr, "normal object %s cannot be child of non-normal parent %s\n", - hwloc_obj_type_string(obj->type), hwloc_obj_type_string(parent->type)); + fprintf(stderr, "%s: normal object %s cannot be child of non-normal parent %s\n", + state->global->msgprefix, hwloc_obj_type_string(obj->type), hwloc_obj_type_string(parent->type)); goto error_with_object; } } else if (hwloc__obj_type_is_memory(obj->type)) { if (hwloc__obj_type_is_io(parent->type) || HWLOC_OBJ_MISC == parent->type) { if (hwloc__xml_verbose()) - fprintf(stderr, "Memory object %s cannot be child of non-normal-or-memory parent %s\n", - hwloc_obj_type_string(obj->type), hwloc_obj_type_string(parent->type)); + fprintf(stderr, "%s: Memory object %s cannot be child of non-normal-or-memory parent %s\n", + state->global->msgprefix, hwloc_obj_type_string(obj->type), hwloc_obj_type_string(parent->type)); goto error_with_object; } } else if (hwloc__obj_type_is_io(obj->type)) { if (hwloc__obj_type_is_memory(parent->type) || HWLOC_OBJ_MISC == parent->type) { if (hwloc__xml_verbose()) - fprintf(stderr, "I/O object %s cannot be child of non-normal-or-I/O parent %s\n", - hwloc_obj_type_string(obj->type), hwloc_obj_type_string(parent->type)); + fprintf(stderr, "%s: I/O object %s cannot be child of non-normal-or-I/O parent %s\n", + state->global->msgprefix, hwloc_obj_type_string(obj->type), hwloc_obj_type_string(parent->type)); goto error_with_object; } } diff --git a/src/3rdparty/hwloc/src/topology.c b/src/3rdparty/hwloc/src/topology.c index 305f807a..2d0eb832 100644 --- a/src/3rdparty/hwloc/src/topology.c +++ b/src/3rdparty/hwloc/src/topology.c @@ -1,6 +1,6 @@ /* * Copyright © 2009 CNRS - * Copyright © 2009-2023 Inria. All rights reserved. + * Copyright © 2009-2025 Inria. All rights reserved. * Copyright © 2009-2012, 2020 Université Bordeaux * Copyright © 2009-2011 Cisco Systems, Inc. All rights reserved. * Copyright © 2022 IBM Corporation. All rights reserved. @@ -54,56 +54,6 @@ #endif -#ifdef HWLOC_HAVE_LEVELZERO -/* - * Define ZES_ENABLE_SYSMAN=1 early so that the LevelZero backend gets Sysman enabled. - * - * Only if the levelzero was enabled in this build so that we don't enable sysman - * for external levelzero users when hwloc doesn't need it. If somebody ever loads - * an external levelzero plugin in a hwloc library built without levelzero (unlikely), - * he may have to manually set ZES_ENABLE_SYSMAN=1. - * - * Use the constructor if supported and/or the Windows DllMain callback. - * Do it in the main hwloc library instead of the levelzero component because - * the latter could be loaded later as a plugin. - * - * L0 seems to be using getenv() to check this variable on Windows - * (at least in the Intel Compute-Runtime of March 2021), - * but setenv() doesn't seem to exist on Windows, hence use putenv() to set the variable. - * - * For the record, Get/SetEnvironmentVariable() is not exactly the same as getenv/putenv(): - * - getenv() doesn't see what was set with SetEnvironmentVariable() - * - GetEnvironmentVariable() doesn't see putenv() in cygwin (while it does in MSVC and MinGW). - * Hence, if L0 ever switches from getenv() to GetEnvironmentVariable(), - * it will break in cygwin, we'll have to use both putenv() and SetEnvironmentVariable(). - * Hopefully L0 will provide a way to enable Sysman without env vars before it happens. - */ -#if HWLOC_HAVE_ATTRIBUTE_CONSTRUCTOR -static void hwloc_constructor(void) __attribute__((constructor)); -static void hwloc_constructor(void) -{ - if (!getenv("ZES_ENABLE_SYSMAN")) -#ifdef HWLOC_WIN_SYS - putenv("ZES_ENABLE_SYSMAN=1"); -#else - setenv("ZES_ENABLE_SYSMAN", "1", 1); -#endif -} -#endif -#ifdef HWLOC_WIN_SYS -BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) -{ - if (fdwReason == DLL_PROCESS_ATTACH) { - if (!getenv("ZES_ENABLE_SYSMAN")) - /* Windows does not have a setenv, so use putenv. */ - putenv((char *) "ZES_ENABLE_SYSMAN=1"); - } - return TRUE; -} -#endif -#endif /* HWLOC_HAVE_LEVELZERO */ - - unsigned hwloc_get_api_version(void) { return HWLOC_API_VERSION; @@ -179,7 +129,7 @@ static void report_insert_error(hwloc_obj_t new, hwloc_obj_t old, const char *ms report_insert_error_format_obj(oldstr, sizeof(oldstr), old); fprintf(stderr, "****************************************************************************\n"); - fprintf(stderr, "* hwloc %s received invalid information from the operating system.\n", HWLOC_VERSION); + fprintf(stderr, "* hwloc %s received invalid information.\n", HWLOC_VERSION); fprintf(stderr, "*\n"); fprintf(stderr, "* Failed with error: %s\n", msg); fprintf(stderr, "* while inserting %s\n", newstr); @@ -1966,6 +1916,51 @@ static void hwloc_set_group_depth(hwloc_topology_t topology); static void hwloc_connect_children(hwloc_obj_t parent); static int hwloc_connect_levels(hwloc_topology_t topology); static int hwloc_connect_special_levels(hwloc_topology_t topology); +static int hwloc_filter_levels_keep_structure(hwloc_topology_t topology); + +/* reconnect children and levels, + * and optionnally merged identical levels while keeping structure. + */ +int +hwloc__reconnect(struct hwloc_topology *topology, unsigned long flags) +{ + int merged_levels = 0; + + if (topology->modified) { + hwloc_connect_children(topology->levels[0][0]); + + if (hwloc_connect_levels(topology) < 0) + return -1; + } + + if (flags & _HWLOC_RECONNECT_FLAG_KEEPSTRUCTURE) { + merged_levels = hwloc_filter_levels_keep_structure(topology); + /* If > 0, we merged some levels, + * some child+parent special children list may have been merged, + * hence specials level might need reordering, + * So reconnect special levels only here at the end. + */ + } + + if (topology->modified || merged_levels) { + if (hwloc_connect_special_levels(topology) < 0) + return -1; + } + + topology->modified = 0; + return 0; +} + +int +hwloc_topology_reconnect(struct hwloc_topology *topology, unsigned long flags) +{ + if (flags) { + errno = EINVAL; + return -1; + } + + return hwloc__reconnect(topology, 0); +} hwloc_obj_t hwloc_topology_insert_group_object(struct hwloc_topology *topology, hwloc_obj_t obj) @@ -2058,7 +2053,10 @@ hwloc_topology_insert_group_object(struct hwloc_topology *topology, hwloc_obj_t /* properly inserted */ hwloc_obj_add_children_sets(res); - if (hwloc_topology_reconnect(topology, 0) < 0) + /* reconnect levels. + * no need to filter levels keep_structure because groups are either auto-merged + * or have the dont_merge attribute */ + if (hwloc__reconnect(topology, 0) < 0) return NULL; /* Compute group total_memory. */ @@ -2550,26 +2548,13 @@ hwloc_compare_levels_structure(hwloc_topology_t topology, unsigned i) return 0; } -/* return > 0 if any level was removed. - * performs its own reconnect internally if needed - */ +/* return > 0 if any level was removed. */ static int hwloc_filter_levels_keep_structure(hwloc_topology_t topology) { unsigned i, j; int res = 0; - if (topology->modified) { - /* WARNING: hwloc_topology_reconnect() is duplicated partially here - * and at the end of this function: - * - we need normal levels before merging. - * - and we'll need to update special levels after merging. - */ - hwloc_connect_children(topology->levels[0][0]); - if (hwloc_connect_levels(topology) < 0) - return -1; - } - /* start from the bottom since we'll remove intermediate levels */ for(i=topology->nb_levels-1; i>0; i--) { int replacechild = 0, replaceparent = 0; @@ -2591,9 +2576,15 @@ hwloc_filter_levels_keep_structure(hwloc_topology_t topology) if (type1 == HWLOC_OBJ_GROUP && hwloc_dont_merge_group_level(topology, i)) replacechild = 0; } - if (!replacechild && !replaceparent) + if (!replacechild && !replaceparent) { + /* always merge Die into Package when levels are identical */ + if (type1 == HWLOC_OBJ_PACKAGE && type2 == HWLOC_OBJ_DIE) + replacechild = 1; + } + if (!replacechild && !replaceparent) { /* no ignoring */ continue; + } /* Decide which one to actually replace */ if (replaceparent && replacechild) { /* If both may be replaced, look at obj_type_priority */ @@ -2736,20 +2727,6 @@ hwloc_filter_levels_keep_structure(hwloc_topology_t topology) } } - - if (res > 0 || topology-> modified) { - /* WARNING: hwloc_topology_reconnect() is duplicated partially here - * and at the beginning of this function. - * If we merged some levels, some child+parent special children lisst - * may have been merged, hence specials level might need reordering, - * So reconnect special levels only here at the end - * (it's not needed at the beginning of this function). - */ - if (hwloc_connect_special_levels(topology) < 0) - return -1; - topology->modified = 0; - } - return 0; } @@ -3278,33 +3255,6 @@ hwloc_connect_levels(hwloc_topology_t topology) return 0; } -int -hwloc_topology_reconnect(struct hwloc_topology *topology, unsigned long flags) -{ - /* WARNING: when updating this function, the replicated code must - * also be updated inside hwloc_filter_levels_keep_structure() - */ - - if (flags) { - errno = EINVAL; - return -1; - } - if (!topology->modified) - return 0; - - hwloc_connect_children(topology->levels[0][0]); - - if (hwloc_connect_levels(topology) < 0) - return -1; - - if (hwloc_connect_special_levels(topology) < 0) - return -1; - - topology->modified = 0; - - return 0; -} - /* for regression testing, make sure the order of io devices * doesn't change with the dentry order in the filesystem * @@ -3561,32 +3511,13 @@ hwloc_discover(struct hwloc_topology *topology, hwloc_debug_print_objects(0, topology->levels[0][0]); } - /* see if we should ignore the root now that we know how many children it has */ - if (!hwloc_filter_check_keep_object(topology, topology->levels[0][0]) - && topology->levels[0][0]->first_child && !topology->levels[0][0]->first_child->next_sibling) { - hwloc_obj_t oldroot = topology->levels[0][0]; - hwloc_obj_t newroot = oldroot->first_child; - /* switch to the new root */ - newroot->parent = NULL; - topology->levels[0][0] = newroot; - /* move oldroot memory/io/misc children before newroot children */ - if (oldroot->memory_first_child) - prepend_siblings_list(&newroot->memory_first_child, oldroot->memory_first_child, newroot); - if (oldroot->io_first_child) - prepend_siblings_list(&newroot->io_first_child, oldroot->io_first_child, newroot); - if (oldroot->misc_first_child) - prepend_siblings_list(&newroot->misc_first_child, oldroot->misc_first_child, newroot); - /* destroy oldroot and use the new one */ - hwloc_free_unlinked_object(oldroot); - } - /* * All object cpusets and nodesets are properly set now. */ /* Now connect handy pointers to make remaining discovery easier. */ hwloc_debug("%s", "\nOk, finished tweaking, now connect\n"); - if (hwloc_topology_reconnect(topology, 0) < 0) + if (hwloc__reconnect(topology, 0) < 0) return -1; hwloc_debug_print_objects(0, topology->levels[0][0]); @@ -3642,12 +3573,12 @@ hwloc_discover(struct hwloc_topology *topology, } hwloc_debug_print_objects(0, topology->levels[0][0]); + /* reconnect all (new groups might have appears, IO added, etc), + * and (now that everything was added) remove identical levels while keeping structure + */ hwloc_debug("%s", "\nRemoving levels with HWLOC_TYPE_FILTER_KEEP_STRUCTURE\n"); - if (hwloc_filter_levels_keep_structure(topology) < 0) + if (hwloc__reconnect(topology, _HWLOC_RECONNECT_FLAG_KEEPSTRUCTURE) < 0) return -1; - /* takes care of reconnecting children/levels internally, - * because it needs normal levels. - * and it's often needed below because of Groups inserted for I/Os anyway */ hwloc_debug_print_objects(0, topology->levels[0][0]); /* accumulate children memory in total_memory fields (only once parent is set) */ @@ -4494,7 +4425,7 @@ hwloc_topology_restrict(struct hwloc_topology *topology, hwloc_const_bitmap_t se hwloc_bitmap_free(droppedcpuset); hwloc_bitmap_free(droppednodeset); - if (hwloc_filter_levels_keep_structure(topology) < 0) /* takes care of reconnecting internally */ + if (hwloc__reconnect(topology, _HWLOC_RECONNECT_FLAG_KEEPSTRUCTURE) < 0) goto out; /* some objects may have disappeared and sets were modified, @@ -5116,6 +5047,8 @@ hwloc_topology_check(struct hwloc_topology *topology) unsigned i; int j, depth; + assert(!topology->modified); + /* make sure we can use ranges to check types */ /* hwloc__obj_type_is_{,d,i}cache() want cache types to be ordered like this */