From 450b9ec19a77eaaf1332ebe8c0a5e231bdf2f245 Mon Sep 17 00:00:00 2001 From: SChernykh Date: Tue, 3 Dec 2019 20:25:51 +0100 Subject: [PATCH 01/31] Update VirtualMemory_unix.cpp --- src/crypto/common/VirtualMemory_unix.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/crypto/common/VirtualMemory_unix.cpp b/src/crypto/common/VirtualMemory_unix.cpp index 3d099c76..ffa4b137 100644 --- a/src/crypto/common/VirtualMemory_unix.cpp +++ b/src/crypto/common/VirtualMemory_unix.cpp @@ -58,12 +58,24 @@ void *xmrig::VirtualMemory::allocateExecutableMemory(size_t size) void *xmrig::VirtualMemory::allocateLargePagesMemory(size_t size) { + int flag_1gb = 0; + # if defined(__APPLE__) void *mem = mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, VM_FLAGS_SUPERPAGE_SIZE_2MB, 0); # elif defined(__FreeBSD__) void *mem = mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_ALIGNED_SUPER | MAP_PREFAULT_READ, -1, 0); # else - void *mem = mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB | MAP_POPULATE, 0, 0); + +# if defined(MAP_HUGE_1GB) + flag_1gb = (size > (1UL << 30)) ? MAP_HUGE_1GB : 0; +# elif defined(MAP_HUGE_SHIFT) + flag_1gb = (size > (1UL << 30)) ? (30 << MAP_HUGE_SHIFT) : 0; +# endif + + void *mem = mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB | MAP_POPULATE | flag_1gb, 0, 0); + if (mem == MAP_FAILED) { + mem = mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB | MAP_POPULATE, 0, 0); + } # endif return mem == MAP_FAILED ? nullptr : mem; From 99d995fdabccd575e619704251b873e1bcec26f4 Mon Sep 17 00:00:00 2001 From: XMRig Date: Thu, 5 Dec 2019 12:16:05 +0700 Subject: [PATCH 02/31] v5.1.2-dev --- src/version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/version.h b/src/version.h index 086378eb..2cb9fef4 100644 --- a/src/version.h +++ b/src/version.h @@ -28,7 +28,7 @@ #define APP_ID "xmrig" #define APP_NAME "XMRig" #define APP_DESC "XMRig miner" -#define APP_VERSION "5.1.1" +#define APP_VERSION "5.1.2-dev" #define APP_DOMAIN "xmrig.com" #define APP_SITE "www.xmrig.com" #define APP_COPYRIGHT "Copyright (C) 2016-2019 xmrig.com" @@ -36,7 +36,7 @@ #define APP_VER_MAJOR 5 #define APP_VER_MINOR 1 -#define APP_VER_PATCH 1 +#define APP_VER_PATCH 2 #ifdef _MSC_VER # if (_MSC_VER >= 1920) From a066f9a49c8ad4dd25a4fd369341249d35250aab Mon Sep 17 00:00:00 2001 From: XMRig Date: Thu, 5 Dec 2019 12:47:31 +0700 Subject: [PATCH 03/31] hwloc for MSVC updated to v2.1.0. --- cmake/flags.cmake | 4 +- src/3rdparty/hwloc/AUTHORS | 1 + src/3rdparty/hwloc/CMakeLists.txt | 2 +- src/3rdparty/hwloc/NEWS | 153 ++- src/3rdparty/hwloc/VERSION | 8 +- src/3rdparty/hwloc/include/hwloc.h | 188 ++- .../hwloc/include/hwloc/autogen/config.h | 6 +- src/3rdparty/hwloc/include/hwloc/bitmap.h | 29 +- src/3rdparty/hwloc/include/hwloc/cuda.h | 8 +- src/3rdparty/hwloc/include/hwloc/cudart.h | 8 +- src/3rdparty/hwloc/include/hwloc/deprecated.h | 4 +- src/3rdparty/hwloc/include/hwloc/distances.h | 53 +- src/3rdparty/hwloc/include/hwloc/gl.h | 2 +- .../hwloc/include/hwloc/glibc-sched.h | 5 +- src/3rdparty/hwloc/include/hwloc/helper.h | 32 +- src/3rdparty/hwloc/include/hwloc/intel-mic.h | 10 +- .../hwloc/include/hwloc/linux-libnuma.h | 3 +- src/3rdparty/hwloc/include/hwloc/linux.h | 3 +- src/3rdparty/hwloc/include/hwloc/nvml.h | 8 +- src/3rdparty/hwloc/include/hwloc/opencl.h | 119 +- .../hwloc/include/hwloc/openfabrics-verbs.h | 6 +- src/3rdparty/hwloc/include/hwloc/plugins.h | 170 ++- src/3rdparty/hwloc/include/hwloc/rename.h | 66 +- src/3rdparty/hwloc/include/hwloc/shmem.h | 2 +- .../hwloc/include/private/components.h | 12 +- src/3rdparty/hwloc/include/private/debug.h | 4 +- .../include/private/internal-components.h | 3 +- src/3rdparty/hwloc/include/private/misc.h | 23 +- src/3rdparty/hwloc/include/private/private.h | 101 +- src/3rdparty/hwloc/include/private/xml.h | 5 +- src/3rdparty/hwloc/src/base64.c | 2 +- src/3rdparty/hwloc/src/bind.c | 13 +- src/3rdparty/hwloc/src/bitmap.c | 55 +- src/3rdparty/hwloc/src/components.c | 370 ++++-- src/3rdparty/hwloc/src/diff.c | 15 +- src/3rdparty/hwloc/src/distances.c | 367 ++++-- src/3rdparty/hwloc/src/misc.c | 25 +- src/3rdparty/hwloc/src/pci-common.c | 401 ++++--- src/3rdparty/hwloc/src/shmem.c | 17 +- src/3rdparty/hwloc/src/topology-noos.c | 30 +- src/3rdparty/hwloc/src/topology-synthetic.c | 84 +- src/3rdparty/hwloc/src/topology-windows.c | 34 +- src/3rdparty/hwloc/src/topology-x86.c | 905 +++++++++------ .../hwloc/src/topology-xml-nolibxml.c | 48 +- src/3rdparty/hwloc/src/topology-xml.c | 470 ++++++-- src/3rdparty/hwloc/src/topology.c | 1024 ++++++++++++----- src/3rdparty/hwloc/src/traversal.c | 131 ++- 47 files changed, 3479 insertions(+), 1550 deletions(-) diff --git a/cmake/flags.cmake b/cmake/flags.cmake index e9533eed..d2bc70d0 100644 --- a/cmake/flags.cmake +++ b/cmake/flags.cmake @@ -57,9 +57,9 @@ if (CMAKE_CXX_COMPILER_ID MATCHES GNU) add_definitions(/DHAVE_BUILTIN_CLEAR_CACHE) elseif (CMAKE_CXX_COMPILER_ID MATCHES MSVC) + set(CMAKE_C_FLAGS_RELEASE "/MT /O2 /Oi /DNDEBUG /GL") + set(CMAKE_CXX_FLAGS_RELEASE "/MT /O2 /Oi /DNDEBUG /GL") - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /Ox /Ot /Oi /MT /GL") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Ox /Ot /Oi /MT /GL") add_definitions(/D_CRT_SECURE_NO_WARNINGS) add_definitions(/D_CRT_NONSTDC_NO_WARNINGS) add_definitions(/DNOMINMAX) diff --git a/src/3rdparty/hwloc/AUTHORS b/src/3rdparty/hwloc/AUTHORS index 7187a723..b4809d15 100644 --- a/src/3rdparty/hwloc/AUTHORS +++ b/src/3rdparty/hwloc/AUTHORS @@ -21,6 +21,7 @@ Nathalie Furmento CNRS Bryon Gloden Brice Goglin Inria Gilles Gouaillardet RIST +Valentin Hoyet Inria Joshua Hursey UWL Alexey Kardashevskiy IBM Rob Latham ANL diff --git a/src/3rdparty/hwloc/CMakeLists.txt b/src/3rdparty/hwloc/CMakeLists.txt index 431c11eb..0e56b6fc 100644 --- a/src/3rdparty/hwloc/CMakeLists.txt +++ b/src/3rdparty/hwloc/CMakeLists.txt @@ -5,7 +5,7 @@ include_directories(include) include_directories(src) add_definitions(/D_CRT_SECURE_NO_WARNINGS) -set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /MT") +set(CMAKE_C_FLAGS_RELEASE "/MT /O2 /Ob2 /DNDEBUG") set(HEADERS include/hwloc.h diff --git a/src/3rdparty/hwloc/NEWS b/src/3rdparty/hwloc/NEWS index 664c8d55..99809e6a 100644 --- a/src/3rdparty/hwloc/NEWS +++ b/src/3rdparty/hwloc/NEWS @@ -13,8 +13,96 @@ $HEADER$ This file contains the main features as well as overviews of specific bug fixes (and other actions) for each version of hwloc since version -0.9 (as initially released as "libtopology", then re-branded to "hwloc" -in v0.9.1). +0.9. + + +Version 2.1.0 +------------- +* API + + Add a new "Die" object (HWLOC_OBJ_DIE) for upcoming x86 processors + with multiple dies per package, in the x86 and Linux backends. + + Add the new HWLOC_OBJ_MEMCACHE object type for memory-side caches. + - They are filtered-out by default, except in command-line tools. + - They are only available on very recent platforms running Linux 5.2+ + and uptodate ACPI tables. + - The KNL MCDRAM in cache mode is still exposed as a L3 unless + HWLOC_KNL_MSCACHE_L3=0 in the environment. + + Add HWLOC_RESTRICT_FLAG_BYNODESET and _REMOVE_MEMLESS for restricting + topologies based on some memory nodes. + + Add hwloc_topology_set_components() for blacklisting some components + from being enabled in a topology. + + Add hwloc_bitmap_nr_ulongs() and hwloc_bitmap_from/to_ulongs(), + thanks to Junchao Zhang for the suggestion. + + Improve the API for dealing with disallowed resources + - HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM is replaced with FLAG_INCLUDE_DISALLOWED + and --whole-system command-line options with --disallowed. + . Former names are still accepted for backward compatibility. + - Add hwloc_topology_allow() for changing allowed sets after load(). + - Add the HWLOC_ALLOW=all environment variable to totally ignore + administrative restrictions such as Linux Cgroups. + - Add disallowed_pu and disallowed_numa bits to the discovery support + structure. + + Group objects have a new "dont_merge" attribute to prevent them from + being automatically merged with identical parent or children. + + Add more distances-related features: + - Add hwloc_distances_get_name() to retrieve a string describing + what a distances structure contain. + - Add hwloc_distances_get_by_name() to retrieve distances structures + based on their name. + - Add hwloc_distances_release_remove() + - Distances may now cover objects of different types with new kind + HWLOC_DISTANCES_KIND_HETEROGENEOUS_TYPES. +* Backends + + Add support for Linux 5.3 new sysfs cpu topology files with Die information. + + Add support for Intel v2 Extended Topology Enumeration in the x86 backend. + + Improve memory locality on Linux by using HMAT initiators (exposed + since Linux 5.2+), and NUMA distances for CPU-less NUMA nodes. + + The x86 backend now properly handles offline CPUs. + + Detect the locality of NVIDIA GPU OpenCL devices. + + Ignore NUMA nodes that correspond to NVIDIA GPU by default. + - They may be unignored if HWLOC_KEEP_NVIDIA_GPU_NUMA_NODES=1 in the environment. + - Fix their CPU locality and add info attributes to identify them. + Thanks to Max Katz and Edgar Leon for the help. + + Add support for IBM S/390 drawers. + + Rework the heuristics for discovering KNL Cluster and Memory modes + to stop assuming all CPUs are online (required for mOS support). + Thanks to Sharath K Bhat for testing patches. + + Ignore NUMA node information from AMD topoext in the x86 backend, + unless HWLOC_X86_TOPOEXT_NUMANODES=1 is set in the environment. + + Expose Linux DAX devices as hwloc Block OS devices. + + Remove support for /proc/cpuinfo-only topology discovery in Linux + kernel prior to 2.6.16. + + Disable POWER device-tree-based topology on Linux by default. + - It may be reenabled by setting HWLOC_USE_DT=1 in the environment. + + Discovery components are now divided in phases that may be individually + blacklisted. + - The linuxio component has been merged back into the linux component. +* Tools + + lstopo + - lstopo factorizes objects by default in the graphical output when + there are more than 4 identical children. + . New options --no-factorize and --factorize may be used to configure this. + . Hit the 'f' key to disable factorizing in interactive outputs. + - Both logical and OS/physical indexes are now displayed by default + for PU and NUMA nodes. + - The X11 and Windows interactive outputs support many keyboard + shortcuts to dynamically customize the attributes, legend, etc. + - Add --linespacing and change default margins and linespacing. + - Add --allow for changing allowed sets. + - Add a native SVG backend. Its graphical output may be slightly less + pretty than Cairo (still used by default if available) but the SVG + code provides attributes to manipulate objects from HTML/JS. + See dynamic_SVG_example.html for an example. + + Add --nodeset options to hwloc-calc for converting between cpusets and + nodesets. + + Add --no-smt to lstopo, hwloc-bind and hwloc-calc to ignore multiple + PU in SMT cores. + + hwloc-annotate may annotate multiple locations at once. + + Add a HTML/JS version of hwloc-ps. See contrib/hwloc-ps.www/README. + + Add bash completions. +* Misc + + Add several FAQ entries in "Compatibility between hwloc versions" + about API version, ABI, XML, Synthetic strings, and shmem topologies. Version 2.0.4 (also included in 1.11.13 when appropriate) @@ -214,6 +302,54 @@ Version 2.0.0 + hwloc now requires a C99 compliant compiler. +Version 1.11.13 (also included in 2.0.4) +--------------- +* Add support for Linux 5.3 new sysfs cpu topology files with Die information. +* Add support for Intel v2 Extended Topology Enumeration in the x86 backend. +* Tiles, Modules and Dies are exposed as Groups for now. + + HWLOC_DONT_MERGE_DIE_GROUPS=1 may be set in the environment to prevent + Die groups from being automatically merged with identical parent or children. +* Ignore NUMA node information from AMD topoext in the x86 backend, + unless HWLOC_X86_TOPOEXT_NUMANODES=1 is set in the environment. +* Group objects have a new "dont_merge" attribute to prevent them from + being automatically merged with identical parent or children. + + +Version 1.11.12 (also included in 2.0.3) +--------------- +* Fix a corner case of hwloc_topology_restrict() where children would + become out-of-order. +* Fix the return length of export_xmlbuffer() functions to always + include the ending \0. + + +Version 1.11.11 (also included in 2.0.2) +--------------- +* Add support for Hygon Dhyana processors in the x86 backend, + thanks to Pu Wen for the patch. +* Fix symbol renaming to also rename internal components, + thanks to Evan Ramos for the patch. +* Fix build on HP-UX, thanks to Richard Lloyd for reporting the issues. +* Detect PCI link speed without being root on Linux >= 4.13. + + +Version 1.11.10 (also included in 2.0.1) +--------------- +* Fix detection of cores and hyperthreads on Mac OS X. +* Serialize pciaccess discovery to fix concurrent topology loads in + multiple threads. +* Fix first touch area memory binding on Linux when thread memory + binding is different. +* Some minor fixes to memory binding. +* Fix hwloc-dump-hwdata to only process SMBIOS information that correspond + to the KNL and KNM configuration. +* Add a heuristic for guessing KNL/KNM memory and cluster modes when + hwloc-dump-hwdata could not run as root earlier. +* Fix discovery of NVMe OS devices on Linux >= 4.0. +* Add get_area_memlocation() on Windows. +* Add CPUVendor, Model, ... attributes on Mac OS X. + + Version 1.11.9 -------------- * Add support for Zhaoxin ZX-C and ZX-D processors in the x86 backend, @@ -941,7 +1077,7 @@ Version 1.6.0 + Add a section about Synthetic topologies in the documentation. -Version 1.5.2 (some of these changes are in v1.6.2 but not in v1.6) +Version 1.5.2 (some of these changes are in 1.6.2 but not in 1.6) ------------- * Use libpciaccess instead of pciutils/libpci by default for I/O discovery. pciutils/libpci is only used if --enable-libpci is given to configure @@ -1076,9 +1212,8 @@ Version 1.4.2 for most of them. -Version 1.4.1 +Version 1.4.1 (contains all 1.3.2 changes) ------------- -* This release contains all changes from v1.3.2. * Fix hwloc_alloc_membind, thanks Karl Napf for reporting the issue. * Fix memory leaks in some get_membind() functions. * Fix helpers converting from Linux libnuma to hwloc (hwloc/linux-libnuma.h) @@ -1091,7 +1226,7 @@ Version 1.4.1 issues. -Version 1.4.0 (does not contain all v1.3.2 changes) +Version 1.4.0 (does not contain all 1.3.2 changes) ------------- * Major features + Add "custom" interface and "assembler" tools to build multi-node @@ -1536,7 +1671,7 @@ Version 1.0.0 Version 0.9.4 (unreleased) --------------------------- +------------- * Fix reseting colors to normal in lstopo -.txt output. * Fix Linux pthread_t binding error report. @@ -1593,7 +1728,7 @@ Version 0.9.1 the physical location of IB devices. -Version 0.9 (libtopology) -------------------------- +Version 0.9 (formerly named "libtopology") +----------- * First release. diff --git a/src/3rdparty/hwloc/VERSION b/src/3rdparty/hwloc/VERSION index 5ebc6bb4..9035ed9c 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=0 -release=4 +minor=1 +release=0 # 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="Jun 03, 2019" +date="Sep 30, 2019" # If snapshot=1, then use the value from snapshot_version as the # entire hwloc version (i.e., ignore major, minor, release, and @@ -41,7 +41,7 @@ snapshot_version=${major}.${minor}.${release}${greek}-git # 2. Version numbers are described in the Libtool current:revision:age # format. -libhwloc_so_version=15:3:0 +libhwloc_so_version=16:0:1 libnetloc_so_version=0:0:0 # 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 ee6da6fd..e106e9cc 100644 --- a/src/3rdparty/hwloc/include/hwloc.h +++ b/src/3rdparty/hwloc/include/hwloc.h @@ -53,7 +53,8 @@ #ifndef HWLOC_H #define HWLOC_H -#include +#include "hwloc/autogen/config.h" + #include #include #include @@ -62,13 +63,13 @@ /* * Symbol transforms */ -#include +#include "hwloc/rename.h" /* * Bitmap definitions */ -#include +#include "hwloc/bitmap.h" #ifdef __cplusplus @@ -86,13 +87,13 @@ extern "C" { * actually modifies the API. * * Users may check for available features at build time using this number - * (see \ref faq_upgrade). + * (see \ref faq_version_api). * * \note This should not be confused with HWLOC_VERSION, the library version. * 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 0x00020000 +#define HWLOC_API_VERSION 0x00020100 /** \brief Indicate at runtime which hwloc API version was used at build time. * @@ -101,7 +102,7 @@ extern "C" { HWLOC_DECLSPEC unsigned hwloc_get_api_version(void); /** \brief Current component and plugin ABI version (see hwloc/plugins.h) */ -#define HWLOC_COMPONENT_ABI 5 +#define HWLOC_COMPONENT_ABI 6 /** @} */ @@ -186,7 +187,8 @@ typedef enum { HWLOC_OBJ_PACKAGE, /**< \brief Physical package. * The physical package that usually gets inserted * into a socket on the motherboard. - * A processor package usually contains multiple cores. + * A processor package usually contains multiple cores, + * and possibly some dies. */ HWLOC_OBJ_CORE, /**< \brief Core. * A computation unit (may be shared by several @@ -233,6 +235,10 @@ typedef enum { * It is usually close to some cores (the corresponding objects * are descendants of the NUMA node object in the hwloc tree). * + * This is the smallest object representing Memory resources, + * it cannot have any child except Misc objects. + * However it may have Memory-side cache parents. + * * There is always at least one such object in the topology * even if the machine is not NUMA. * @@ -279,6 +285,24 @@ typedef enum { * Misc objects have NULL CPU and node sets. */ + HWLOC_OBJ_MEMCACHE, /**< \brief Memory-side cache (filtered out by default). + * A cache in front of a specific NUMA node. + * + * This object always has at least one NUMA node as a memory child. + * + * Memory objects are not listed in the main children list, + * but rather in the dedicated Memory children list. + * + * Memory-side cache have a special depth ::HWLOC_TYPE_DEPTH_MEMCACHE + * instead of a normal depth just like other objects in the + * main tree. + */ + + HWLOC_OBJ_DIE, /**< \brief Die within a physical package. + * A subpart of the physical package, that contains multiple cores. + * \hideinitializer + */ + HWLOC_OBJ_TYPE_MAX /**< \private Sentinel value */ } hwloc_obj_type_t; @@ -297,8 +321,8 @@ typedef enum hwloc_obj_bridge_type_e { /** \brief Type of a OS device. */ typedef enum hwloc_obj_osdev_type_e { - HWLOC_OBJ_OSDEV_BLOCK, /**< \brief Operating system block device. - * For instance "sda" on Linux. */ + HWLOC_OBJ_OSDEV_BLOCK, /**< \brief Operating system block device, or non-volatile memory device. + * For instance "sda" or "dax2.0" on Linux. */ HWLOC_OBJ_OSDEV_GPU, /**< \brief Operating system GPU device. * For instance ":0.0" for a GL display, * "card0" for a Linux DRM device. */ @@ -434,9 +458,15 @@ struct hwloc_obj { * These children are listed in \p memory_first_child. */ struct hwloc_obj *memory_first_child; /**< \brief First Memory child. - * NUMA nodes are listed here (\p memory_arity and \p memory_first_child) + * NUMA nodes and Memory-side caches are listed here + * (\p memory_arity and \p memory_first_child) * instead of in the normal children list. * See also hwloc_obj_type_is_memory(). + * + * A memory hierarchy starts from a normal CPU-side object + * (e.g. Package) and ends with NUMA nodes as leaves. + * There might exist some memory-side caches between them + * in the middle of the memory subtree. */ /**@}*/ @@ -471,7 +501,7 @@ struct hwloc_obj { * object and known how (the children path between this object and the PU * objects). * - * If the ::HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM configuration flag is set, + * If the ::HWLOC_TOPOLOGY_FLAG_INCLUDE_DISALLOWED configuration flag is set, * some of these CPUs may not be allowed for binding, * see hwloc_topology_get_allowed_cpuset(). * @@ -483,7 +513,7 @@ struct hwloc_obj { * * This may include not only the same as the cpuset field, but also some CPUs for * which topology information is unknown or incomplete, some offlines CPUs, and - * the CPUs that are ignored when the ::HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM flag + * the CPUs that are ignored when the ::HWLOC_TOPOLOGY_FLAG_INCLUDE_DISALLOWED flag * is not set. * Thus no corresponding PU object may be found in the topology, because the * precise position is undefined. It is however known that it would be somewhere @@ -501,7 +531,7 @@ struct hwloc_obj { * * In the end, these nodes are those that are close to the current object. * - * If the ::HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM configuration flag is set, + * If the ::HWLOC_TOPOLOGY_FLAG_INCLUDE_DISALLOWED configuration flag is set, * some of these nodes may not be allowed for allocation, * see hwloc_topology_get_allowed_nodeset(). * @@ -516,7 +546,7 @@ struct hwloc_obj { * * This may include not only the same as the nodeset field, but also some NUMA * nodes for which topology information is unknown or incomplete, some offlines - * nodes, and the nodes that are ignored when the ::HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM + * nodes, and the nodes that are ignored when the ::HWLOC_TOPOLOGY_FLAG_INCLUDE_DISALLOWED * flag is not set. * Thus no corresponding NUMA node object may be found in the topology, because the * precise position is undefined. It is however known that it would be @@ -770,7 +800,8 @@ enum hwloc_get_type_depth_e { HWLOC_TYPE_DEPTH_BRIDGE = -4, /**< \brief Virtual depth for bridge object level. \hideinitializer */ HWLOC_TYPE_DEPTH_PCI_DEVICE = -5, /**< \brief Virtual depth for PCI device object level. \hideinitializer */ HWLOC_TYPE_DEPTH_OS_DEVICE = -6, /**< \brief Virtual depth for software device object level. \hideinitializer */ - HWLOC_TYPE_DEPTH_MISC = -7 /**< \brief Virtual depth for Misc object. \hideinitializer */ + HWLOC_TYPE_DEPTH_MISC = -7, /**< \brief Virtual depth for Misc object. \hideinitializer */ + HWLOC_TYPE_DEPTH_MEMCACHE = -8 /**< \brief Virtual depth for MemCache object. \hideinitializer */ }; /** \brief Return the depth of parents where memory objects are attached. @@ -1781,6 +1812,31 @@ HWLOC_DECLSPEC int hwloc_topology_set_xml(hwloc_topology_t __hwloc_restrict topo */ HWLOC_DECLSPEC int hwloc_topology_set_xmlbuffer(hwloc_topology_t __hwloc_restrict topology, const char * __hwloc_restrict buffer, int size); +/** \brief Flags to be passed to hwloc_topology_set_components() + */ +enum hwloc_topology_components_flag_e { + /** \brief Blacklist the target component from being used. + * \hideinitializer + */ + HWLOC_TOPOLOGY_COMPONENTS_FLAG_BLACKLIST = (1UL<<0) +}; + +/** \brief Prevent a discovery component from being used for a topology. + * + * \p name is the name of the discovery component that should not be used + * when loading topology \p topology. The name is a string such as "cuda". + * + * For components with multiple phases, it may also be suffixed with the name + * of a phase, for instance "linux:io". + * + * \p flags should be ::HWLOC_TOPOLOGY_COMPONENTS_FLAG_BLACKLIST. + * + * This may be used to avoid expensive parts of the discovery process. + * For instance, CUDA-specific discovery may be expensive and unneeded + * while generic I/O discovery could still be useful. + */ +HWLOC_DECLSPEC int hwloc_topology_set_components(hwloc_topology_t __hwloc_restrict topology, unsigned long flags, const char * __hwloc_restrict name); + /** @} */ @@ -1800,28 +1856,27 @@ HWLOC_DECLSPEC int hwloc_topology_set_xmlbuffer(hwloc_topology_t __hwloc_restric * They may also be returned by hwloc_topology_get_flags(). */ enum hwloc_topology_flags_e { - /** \brief Detect the whole system, ignore reservations. + /** \brief Detect the whole system, ignore reservations, include disallowed objects. * * Gather all resources, even if some were disabled by the administrator. * For instance, ignore Linux Cgroup/Cpusets and gather all processors and memory nodes. * * When this flag is not set, PUs and NUMA nodes that are disallowed are not added to the topology. * Parent objects (package, core, cache, etc.) are added only if some of their children are allowed. + * All existing PUs and NUMA nodes in the topology are allowed. + * hwloc_topology_get_allowed_cpuset() and hwloc_topology_get_allowed_nodeset() + * are equal to the root object cpuset and nodeset. * * When this flag is set, the actual sets of allowed PUs and NUMA nodes are given * by hwloc_topology_get_allowed_cpuset() and hwloc_topology_get_allowed_nodeset(). * They may be smaller than the root object cpuset and nodeset. * - * When this flag is not set, all existing PUs and NUMA nodes in the topology - * are allowed. hwloc_topology_get_allowed_cpuset() and hwloc_topology_get_allowed_nodeset() - * are equal to the root object cpuset and nodeset. - * * If the current topology is exported to XML and reimported later, this flag * should be set again in the reimported topology so that disallowed resources * are reimported as well. * \hideinitializer */ - HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM = (1UL<<0), + HWLOC_TOPOLOGY_FLAG_INCLUDE_DISALLOWED = (1UL<<0), /** \brief Assume that the selected backend provides the topology for the * system on which we are running. @@ -1901,6 +1956,10 @@ struct hwloc_topology_discovery_support { unsigned char numa; /** \brief Detecting the amount of memory in NUMA nodes is supported. */ unsigned char numa_memory; + /** \brief Detecting and identifying PU objects that are not available to the current process is supported. */ + unsigned char disallowed_pu; + /** \brief Detecting and identifying NUMA nodes that are not available to the current process is supported. */ + unsigned char disallowed_numa; }; /** \brief Flags describing actual PU binding support for this topology. @@ -1998,7 +2057,7 @@ HWLOC_DECLSPEC const struct hwloc_topology_support *hwloc_topology_get_support(h * * By default, most objects are kept (::HWLOC_TYPE_FILTER_KEEP_ALL). * Instruction caches, I/O and Misc objects are ignored by default (::HWLOC_TYPE_FILTER_KEEP_NONE). - * Group levels are ignored unless they bring structure (::HWLOC_TYPE_FILTER_KEEP_STRUCTURE). + * Die and Group levels are ignored unless they bring structure (::HWLOC_TYPE_FILTER_KEEP_STRUCTURE). * * Note that group objects are also ignored individually (without the entire level) * when they do not bring structure. @@ -2063,11 +2122,15 @@ HWLOC_DECLSPEC int hwloc_topology_get_type_filter(hwloc_topology_t topology, hwl */ HWLOC_DECLSPEC int hwloc_topology_set_all_types_filter(hwloc_topology_t topology, enum hwloc_type_filter_e filter); -/** \brief Set the filtering for all cache object types. +/** \brief Set the filtering for all CPU cache object types. + * + * Memory-side caches are not involved since they are not CPU caches. */ HWLOC_DECLSPEC int hwloc_topology_set_cache_types_filter(hwloc_topology_t topology, enum hwloc_type_filter_e filter); -/** \brief Set the filtering for all instruction cache object types. +/** \brief Set the filtering for all CPU instruction cache object types. + * + * Memory-side caches are not involved since they are not CPU caches. */ HWLOC_DECLSPEC int hwloc_topology_set_icache_types_filter(hwloc_topology_t topology, enum hwloc_type_filter_e filter); @@ -2110,6 +2173,19 @@ enum hwloc_restrict_flags_e { */ HWLOC_RESTRICT_FLAG_REMOVE_CPULESS = (1UL<<0), + /** \brief Restrict by nodeset instead of CPU set. + * Only keep objects whose nodeset is included or partially included in the given set. + * This flag may not be used with ::HWLOC_RESTRICT_FLAG_BYNODESET. + */ + HWLOC_RESTRICT_FLAG_BYNODESET = (1UL<<3), + + /** \brief Remove all objects that became Memory-less. + * By default, only objects that contain no PU and no memory are removed. + * This flag may only be used with ::HWLOC_RESTRICT_FLAG_BYNODESET. + * \hideinitializer + */ + HWLOC_RESTRICT_FLAG_REMOVE_MEMLESS = (1UL<<4), + /** \brief Move Misc objects to ancestors if their parents are removed during restriction. * If this flag is not set, Misc objects are removed when their parents are removed. * \hideinitializer @@ -2123,28 +2199,70 @@ enum hwloc_restrict_flags_e { HWLOC_RESTRICT_FLAG_ADAPT_IO = (1UL<<2) }; -/** \brief Restrict the topology to the given CPU set. +/** \brief Restrict the topology to the given CPU set or nodeset. * * Topology \p topology is modified so as to remove all objects that - * are not included (or partially included) in the CPU set \p cpuset. + * are not included (or partially included) in the CPU set \p set. * All objects CPU and node sets are restricted accordingly. * + * If ::HWLOC_RESTRICT_FLAG_BYNODESET is passed in \p flags, + * \p set is considered a nodeset instead of a CPU set. + * * \p flags is a OR'ed set of ::hwloc_restrict_flags_e. * * \note This call may not be reverted by restricting back to a larger - * cpuset. Once dropped during restriction, objects may not be brought + * set. Once dropped during restriction, objects may not be brought * back, except by loading another topology with hwloc_topology_load(). * * \return 0 on success. * - * \return -1 with errno set to EINVAL if the input cpuset is invalid. + * \return -1 with errno set to EINVAL if the input set is invalid. * The topology is not modified in this case. * * \return -1 with errno set to ENOMEM on failure to allocate internal data. * The topology is reinitialized in this case. It should be either * destroyed with hwloc_topology_destroy() or configured and loaded again. */ -HWLOC_DECLSPEC int hwloc_topology_restrict(hwloc_topology_t __hwloc_restrict topology, hwloc_const_cpuset_t cpuset, unsigned long flags); +HWLOC_DECLSPEC int hwloc_topology_restrict(hwloc_topology_t __hwloc_restrict topology, hwloc_const_bitmap_t set, unsigned long flags); + +/** \brief Flags to be given to hwloc_topology_allow(). */ +enum hwloc_allow_flags_e { + /** \brief Mark all objects as allowed in the topology. + * + * \p cpuset and \p nođeset given to hwloc_topology_allow() must be \c NULL. + * \hideinitializer */ + HWLOC_ALLOW_FLAG_ALL = (1UL<<0), + + /** \brief Only allow objects that are available to the current process. + * + * The topology must have ::HWLOC_TOPOLOGY_FLAG_IS_THISSYSTEM so that the set + * of available resources can actually be retrieved from the operating system. + * + * \p cpuset and \p nođeset given to hwloc_topology_allow() must be \c NULL. + * \hideinitializer */ + HWLOC_ALLOW_FLAG_LOCAL_RESTRICTIONS = (1UL<<1), + + /** \brief Allow a custom set of objects, given to hwloc_topology_allow() as \p cpuset and/or \p nodeset parameters. + * \hideinitializer */ + HWLOC_ALLOW_FLAG_CUSTOM = (1UL<<2) +}; + +/** \brief Change the sets of allowed PUs and NUMA nodes in the topology. + * + * This function only works if the ::HWLOC_TOPOLOGY_FLAG_INCLUDE_DISALLOWED + * was set on the topology. It does not modify any object, it only changes + * the sets returned by hwloc_topology_get_allowed_cpuset() and + * hwloc_topology_get_allowed_nodeset(). + * + * It is notably useful when importing a topology from another process + * running in a different Linux Cgroup. + * + * \p flags must be set to one flag among ::hwloc_allow_flags_e. + * + * \note Removing objects from a topology should rather be performed with + * hwloc_topology_restrict(). + */ +HWLOC_DECLSPEC int hwloc_topology_allow(hwloc_topology_t __hwloc_restrict topology, hwloc_const_cpuset_t cpuset, hwloc_const_nodeset_t nodeset, unsigned long flags); /** \brief Add a MISC object as a leaf of the topology * @@ -2250,21 +2368,21 @@ HWLOC_DECLSPEC int hwloc_obj_add_other_obj_sets(hwloc_obj_t dst, hwloc_obj_t src /* high-level helpers */ -#include +#include "hwloc/helper.h" /* inline code of some functions above */ -#include +#include "hwloc/inlines.h" /* exporting to XML or synthetic */ -#include +#include "hwloc/export.h" /* distances */ -#include +#include "hwloc/distances.h" /* topology diffs */ -#include +#include "hwloc/diff.h" /* deprecated headers */ -#include +#include "hwloc/deprecated.h" #endif /* HWLOC_H */ diff --git a/src/3rdparty/hwloc/include/hwloc/autogen/config.h b/src/3rdparty/hwloc/include/hwloc/autogen/config.h index 14d4481d..36669de5 100644 --- a/src/3rdparty/hwloc/include/hwloc/autogen/config.h +++ b/src/3rdparty/hwloc/include/hwloc/autogen/config.h @@ -11,10 +11,10 @@ #ifndef HWLOC_CONFIG_H #define HWLOC_CONFIG_H -#define HWLOC_VERSION "2.0.4" +#define HWLOC_VERSION "2.1.0" #define HWLOC_VERSION_MAJOR 2 -#define HWLOC_VERSION_MINOR 0 -#define HWLOC_VERSION_RELEASE 4 +#define HWLOC_VERSION_MINOR 1 +#define HWLOC_VERSION_RELEASE 0 #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 bae623c8..d5b0ea02 100644 --- a/src/3rdparty/hwloc/include/hwloc/bitmap.h +++ b/src/3rdparty/hwloc/include/hwloc/bitmap.h @@ -13,7 +13,8 @@ #ifndef HWLOC_BITMAP_H #define HWLOC_BITMAP_H -#include +#include "hwloc/autogen/config.h" + #include @@ -198,6 +199,9 @@ HWLOC_DECLSPEC int hwloc_bitmap_from_ulong(hwloc_bitmap_t bitmap, unsigned long /** \brief Setup bitmap \p bitmap from unsigned long \p mask used as \p i -th subset */ HWLOC_DECLSPEC int hwloc_bitmap_from_ith_ulong(hwloc_bitmap_t bitmap, unsigned i, unsigned long mask); +/** \brief Setup bitmap \p bitmap from unsigned longs \p masks used as first \p nr subsets */ +HWLOC_DECLSPEC int hwloc_bitmap_from_ulongs(hwloc_bitmap_t bitmap, unsigned nr, const unsigned long *masks); + /* * Modifying bitmaps. @@ -256,6 +260,29 @@ HWLOC_DECLSPEC unsigned long hwloc_bitmap_to_ulong(hwloc_const_bitmap_t bitmap) /** \brief Convert the \p i -th subset of bitmap \p bitmap into unsigned long mask */ HWLOC_DECLSPEC unsigned long hwloc_bitmap_to_ith_ulong(hwloc_const_bitmap_t bitmap, unsigned i) __hwloc_attribute_pure; +/** \brief Convert the first \p nr subsets of bitmap \p bitmap into the array of \p nr unsigned long \p masks + * + * \p nr may be determined earlier with hwloc_bitmap_nr_ulongs(). + * + * \return 0 + */ +HWLOC_DECLSPEC int hwloc_bitmap_to_ulongs(hwloc_const_bitmap_t bitmap, unsigned nr, unsigned long *masks); + +/** \brief Return the number of unsigned longs required for storing bitmap \p bitmap entirely + * + * This is the number of contiguous unsigned longs from the very first bit of the bitmap + * (even if unset) up to the last set bit. + * This is useful for knowing the \p nr parameter to pass to hwloc_bitmap_to_ulongs() + * (or which calls to hwloc_bitmap_to_ith_ulong() are needed) + * to entirely convert a bitmap into multiple unsigned longs. + * + * When called on the output of hwloc_topology_get_topology_cpuset(), + * the returned number is large enough for all cpusets of the topology. + * + * \return -1 if \p bitmap is infinite. + */ +HWLOC_DECLSPEC int hwloc_bitmap_nr_ulongs(hwloc_const_bitmap_t bitmap) __hwloc_attribute_pure; + /** \brief Test whether index \p id is part of bitmap \p bitmap. * * \return 1 if the bit at index \p id is set in bitmap \p bitmap, 0 otherwise. diff --git a/src/3rdparty/hwloc/include/hwloc/cuda.h b/src/3rdparty/hwloc/include/hwloc/cuda.h index 77c8473e..6f0cda4c 100644 --- a/src/3rdparty/hwloc/include/hwloc/cuda.h +++ b/src/3rdparty/hwloc/include/hwloc/cuda.h @@ -16,11 +16,11 @@ #ifndef HWLOC_CUDA_H #define HWLOC_CUDA_H -#include -#include -#include +#include "hwloc.h" +#include "hwloc/autogen/config.h" +#include "hwloc/helper.h" #ifdef HWLOC_LINUX_SYS -#include +#include "hwloc/linux.h" #endif #include diff --git a/src/3rdparty/hwloc/include/hwloc/cudart.h b/src/3rdparty/hwloc/include/hwloc/cudart.h index 63c7f59c..688b8421 100644 --- a/src/3rdparty/hwloc/include/hwloc/cudart.h +++ b/src/3rdparty/hwloc/include/hwloc/cudart.h @@ -16,11 +16,11 @@ #ifndef HWLOC_CUDART_H #define HWLOC_CUDART_H -#include -#include -#include +#include "hwloc.h" +#include "hwloc/autogen/config.h" +#include "hwloc/helper.h" #ifdef HWLOC_LINUX_SYS -#include +#include "hwloc/linux.h" #endif #include /* for CUDA_VERSION */ diff --git a/src/3rdparty/hwloc/include/hwloc/deprecated.h b/src/3rdparty/hwloc/include/hwloc/deprecated.h index 8f3b1459..4a231f50 100644 --- a/src/3rdparty/hwloc/include/hwloc/deprecated.h +++ b/src/3rdparty/hwloc/include/hwloc/deprecated.h @@ -1,6 +1,6 @@ /* * Copyright © 2009 CNRS - * Copyright © 2009-2017 Inria. All rights reserved. + * Copyright © 2009-2018 Inria. All rights reserved. * Copyright © 2009-2012 Université Bordeaux * Copyright © 2009-2010 Cisco Systems, Inc. All rights reserved. * See COPYING in top-level directory. @@ -21,6 +21,8 @@ extern "C" { #endif +/* backward compat with v2.0 before WHOLE_SYSTEM renaming */ +#define HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM HWLOC_TOPOLOGY_FLAG_INCLUDE_DISALLOWED /* backward compat with v1.11 before System removal */ #define HWLOC_OBJ_SYSTEM HWLOC_OBJ_MACHINE /* backward compat with v1.10 before Socket->Package renaming */ diff --git a/src/3rdparty/hwloc/include/hwloc/distances.h b/src/3rdparty/hwloc/include/hwloc/distances.h index d523f29f..b7baed8a 100644 --- a/src/3rdparty/hwloc/include/hwloc/distances.h +++ b/src/3rdparty/hwloc/include/hwloc/distances.h @@ -87,7 +87,12 @@ enum hwloc_distances_kind_e { * Such values are currently ignored for distance-based grouping. * \hideinitializer */ - HWLOC_DISTANCES_KIND_MEANS_BANDWIDTH = (1UL<<3) + HWLOC_DISTANCES_KIND_MEANS_BANDWIDTH = (1UL<<3), + + /** \brief This distances structure covers objects of different types. + * \hideinitializer + */ + HWLOC_DISTANCES_KIND_HETEROGENEOUS_TYPES = (1UL<<4) }; /** \brief Retrieve distance matrices. @@ -131,20 +136,32 @@ hwloc_distances_get_by_depth(hwloc_topology_t topology, int depth, * * Identical to hwloc_distances_get() with the additional \p type filter. */ -static __hwloc_inline int +HWLOC_DECLSPEC int hwloc_distances_get_by_type(hwloc_topology_t topology, hwloc_obj_type_t type, unsigned *nr, struct hwloc_distances_s **distances, - unsigned long kind, unsigned long flags) -{ - int depth = hwloc_get_type_depth(topology, type); - if (depth == HWLOC_TYPE_DEPTH_UNKNOWN || depth == HWLOC_TYPE_DEPTH_MULTIPLE) { - *nr = 0; - return 0; - } - return hwloc_distances_get_by_depth(topology, depth, nr, distances, kind, flags); -} + unsigned long kind, unsigned long flags); -/** \brief Release a distance matrix structure previously returned by hwloc_distances_get(). */ +/** \brief Retrieve a distance matrix with the given name. + * + * Usually only one distances structure may match a given name. + */ +HWLOC_DECLSPEC int +hwloc_distances_get_by_name(hwloc_topology_t topology, const char *name, + unsigned *nr, struct hwloc_distances_s **distances, + unsigned long flags); + +/** \brief Get a description of what a distances structure contains. + * + * For instance "NUMALatency" for hardware-provided NUMA distances (ACPI SLIT), + * or NULL if unknown. + */ +HWLOC_DECLSPEC const char * +hwloc_distances_get_name(hwloc_topology_t topology, struct hwloc_distances_s *distances); + +/** \brief Release a distance matrix structure previously returned by hwloc_distances_get(). + * + * \note This function is not required if the structure is removed with hwloc_distances_release_remove(). + */ HWLOC_DECLSPEC void hwloc_distances_release(hwloc_topology_t topology, struct hwloc_distances_s *distances); @@ -221,11 +238,11 @@ enum hwloc_distances_add_flag_e { * The distance from object i to object j is in slot i*nbobjs+j. * * \p kind specifies the kind of distance as a OR'ed set of ::hwloc_distances_kind_e. + * Kind ::HWLOC_DISTANCES_KIND_HETEROGENEOUS_TYPES will be automatically added + * if objects of different types are given. * * \p flags configures the behavior of the function using an optional OR'ed set of * ::hwloc_distances_add_flag_e. - * - * Objects must be of the same type. They cannot be of type Group. */ HWLOC_DECLSPEC int hwloc_distances_add(hwloc_topology_t topology, unsigned nbobjs, hwloc_obj_t *objs, hwloc_uint64_t *values, @@ -237,7 +254,7 @@ HWLOC_DECLSPEC int hwloc_distances_add(hwloc_topology_t topology, * gathered through the OS. * * If these distances were used to group objects, these additional - *Group objects are not removed from the topology. + * Group objects are not removed from the topology. */ HWLOC_DECLSPEC int hwloc_distances_remove(hwloc_topology_t topology); @@ -260,6 +277,12 @@ hwloc_distances_remove_by_type(hwloc_topology_t topology, hwloc_obj_type_t type) return hwloc_distances_remove_by_depth(topology, depth); } +/** \brief Release and remove the given distance matrice from the topology. + * + * This function includes a call to hwloc_distances_release(). + */ +HWLOC_DECLSPEC int hwloc_distances_release_remove(hwloc_topology_t topology, struct hwloc_distances_s *distances); + /** @} */ diff --git a/src/3rdparty/hwloc/include/hwloc/gl.h b/src/3rdparty/hwloc/include/hwloc/gl.h index 3e643fa9..897ef784 100644 --- a/src/3rdparty/hwloc/include/hwloc/gl.h +++ b/src/3rdparty/hwloc/include/hwloc/gl.h @@ -14,7 +14,7 @@ #ifndef HWLOC_GL_H #define HWLOC_GL_H -#include +#include "hwloc.h" #include #include diff --git a/src/3rdparty/hwloc/include/hwloc/glibc-sched.h b/src/3rdparty/hwloc/include/hwloc/glibc-sched.h index 1f9ba7cd..99659e03 100644 --- a/src/3rdparty/hwloc/include/hwloc/glibc-sched.h +++ b/src/3rdparty/hwloc/include/hwloc/glibc-sched.h @@ -17,8 +17,9 @@ #ifndef HWLOC_GLIBC_SCHED_H #define HWLOC_GLIBC_SCHED_H -#include -#include +#include "hwloc.h" +#include "hwloc/helper.h" + #include #if !defined _GNU_SOURCE || !defined _SCHED_H || (!defined CPU_SETSIZE && !defined sched_priority) diff --git a/src/3rdparty/hwloc/include/hwloc/helper.h b/src/3rdparty/hwloc/include/hwloc/helper.h index d48df15f..bc27be59 100644 --- a/src/3rdparty/hwloc/include/hwloc/helper.h +++ b/src/3rdparty/hwloc/include/hwloc/helper.h @@ -527,30 +527,36 @@ hwloc_obj_type_is_io(hwloc_obj_type_t type); * * Memory objects are objects attached to their parents * in the Memory children list. - * This current only includes NUMA nodes. + * This current includes NUMA nodes and Memory-side caches. * * \return 1 if an object of type \p type is a Memory object, 0 otherwise. */ HWLOC_DECLSPEC int hwloc_obj_type_is_memory(hwloc_obj_type_t type); -/** \brief Check whether an object type is a Cache (Data, Unified or Instruction). +/** \brief Check whether an object type is a CPU Cache (Data, Unified or Instruction). + * + * Memory-side caches are not CPU caches. * * \return 1 if an object of type \p type is a Cache, 0 otherwise. */ HWLOC_DECLSPEC int hwloc_obj_type_is_cache(hwloc_obj_type_t type); -/** \brief Check whether an object type is a Data or Unified Cache. +/** \brief Check whether an object type is a CPU Data or Unified Cache. * - * \return 1 if an object of type \p type is a Data or Unified Cache, 0 otherwise. + * Memory-side caches are not CPU caches. + * + * \return 1 if an object of type \p type is a CPU Data or Unified Cache, 0 otherwise. */ HWLOC_DECLSPEC int hwloc_obj_type_is_dcache(hwloc_obj_type_t type); -/** \brief Check whether an object type is a Instruction Cache, +/** \brief Check whether an object type is a CPU Instruction Cache, * - * \return 1 if an object of type \p type is a Instruction Cache, 0 otherwise. + * Memory-side caches are not CPU caches. + * + * \return 1 if an object of type \p type is a CPU Instruction Cache, 0 otherwise. */ HWLOC_DECLSPEC int hwloc_obj_type_is_icache(hwloc_obj_type_t type); @@ -914,7 +920,7 @@ hwloc_topology_get_complete_cpuset(hwloc_topology_t topology) __hwloc_attribute_ * \note The returned cpuset is not newly allocated and should thus not be * changed or freed; hwloc_bitmap_dup() must be used to obtain a local copy. * - * \note This is equivalent to retrieving the root object complete CPU-set. + * \note This is equivalent to retrieving the root object CPU-set. */ HWLOC_DECLSPEC hwloc_const_cpuset_t hwloc_topology_get_topology_cpuset(hwloc_topology_t topology) __hwloc_attribute_pure; @@ -923,11 +929,11 @@ hwloc_topology_get_topology_cpuset(hwloc_topology_t topology) __hwloc_attribute_ * * \return the CPU set of allowed logical processors of the system. * - * \note If the topology flag ::HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM was not set, + * \note If the topology flag ::HWLOC_TOPOLOGY_FLAG_INCLUDE_DISALLOWED was not set, * this is identical to hwloc_topology_get_topology_cpuset(), which means * all PUs are allowed. * - * \note If ::HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM was set, applying + * \note If ::HWLOC_TOPOLOGY_FLAG_INCLUDE_DISALLOWED was set, applying * hwloc_bitmap_intersects() on the result of this function and on an object * cpuset checks whether there are allowed PUs inside that object. * Applying hwloc_bitmap_and() returns the list of these allowed PUs. @@ -945,7 +951,7 @@ hwloc_topology_get_allowed_cpuset(hwloc_topology_t topology) __hwloc_attribute_p * \note The returned nodeset is not newly allocated and should thus not be * changed or freed; hwloc_bitmap_dup() must be used to obtain a local copy. * - * \note This is equivalent to retrieving the root object complete CPU-set. + * \note This is equivalent to retrieving the root object complete nodeset. */ HWLOC_DECLSPEC hwloc_const_nodeset_t hwloc_topology_get_complete_nodeset(hwloc_topology_t topology) __hwloc_attribute_pure; @@ -959,7 +965,7 @@ hwloc_topology_get_complete_nodeset(hwloc_topology_t topology) __hwloc_attribute * \note The returned nodeset is not newly allocated and should thus not be * changed or freed; hwloc_bitmap_dup() must be used to obtain a local copy. * - * \note This is equivalent to retrieving the root object complete CPU-set. + * \note This is equivalent to retrieving the root object nodeset. */ HWLOC_DECLSPEC hwloc_const_nodeset_t hwloc_topology_get_topology_nodeset(hwloc_topology_t topology) __hwloc_attribute_pure; @@ -968,11 +974,11 @@ hwloc_topology_get_topology_nodeset(hwloc_topology_t topology) __hwloc_attribute * * \return the node set of allowed memory of the system. * - * \note If the topology flag ::HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM was not set, + * \note If the topology flag ::HWLOC_TOPOLOGY_FLAG_INCLUDE_DISALLOWED was not set, * this is identical to hwloc_topology_get_topology_nodeset(), which means * all NUMA nodes are allowed. * - * \note If ::HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM was set, applying + * \note If ::HWLOC_TOPOLOGY_FLAG_INCLUDE_DISALLOWED was set, applying * hwloc_bitmap_intersects() on the result of this function and on an object * nodeset checks whether there are allowed NUMA nodes inside that object. * Applying hwloc_bitmap_and() returns the list of these allowed NUMA nodes. diff --git a/src/3rdparty/hwloc/include/hwloc/intel-mic.h b/src/3rdparty/hwloc/include/hwloc/intel-mic.h index 6f6f9d1b..c504cd7e 100644 --- a/src/3rdparty/hwloc/include/hwloc/intel-mic.h +++ b/src/3rdparty/hwloc/include/hwloc/intel-mic.h @@ -13,11 +13,13 @@ #ifndef HWLOC_INTEL_MIC_H #define HWLOC_INTEL_MIC_H -#include -#include -#include +#include "hwloc.h" +#include "hwloc/autogen/config.h" +#include "hwloc/helper.h" + #ifdef HWLOC_LINUX_SYS -#include +#include "hwloc/linux.h" + #include #include #endif diff --git a/src/3rdparty/hwloc/include/hwloc/linux-libnuma.h b/src/3rdparty/hwloc/include/hwloc/linux-libnuma.h index 7cea4166..0e2cc19f 100644 --- a/src/3rdparty/hwloc/include/hwloc/linux-libnuma.h +++ b/src/3rdparty/hwloc/include/hwloc/linux-libnuma.h @@ -15,7 +15,8 @@ #ifndef HWLOC_LINUX_LIBNUMA_H #define HWLOC_LINUX_LIBNUMA_H -#include +#include "hwloc.h" + #include diff --git a/src/3rdparty/hwloc/include/hwloc/linux.h b/src/3rdparty/hwloc/include/hwloc/linux.h index c409e1c2..ecc86be3 100644 --- a/src/3rdparty/hwloc/include/hwloc/linux.h +++ b/src/3rdparty/hwloc/include/hwloc/linux.h @@ -15,7 +15,8 @@ #ifndef HWLOC_LINUX_H #define HWLOC_LINUX_H -#include +#include "hwloc.h" + #include diff --git a/src/3rdparty/hwloc/include/hwloc/nvml.h b/src/3rdparty/hwloc/include/hwloc/nvml.h index 19710866..1bc2599f 100644 --- a/src/3rdparty/hwloc/include/hwloc/nvml.h +++ b/src/3rdparty/hwloc/include/hwloc/nvml.h @@ -13,11 +13,11 @@ #ifndef HWLOC_NVML_H #define HWLOC_NVML_H -#include -#include -#include +#include "hwloc.h" +#include "hwloc/autogen/config.h" +#include "hwloc/helper.h" #ifdef HWLOC_LINUX_SYS -#include +#include "hwloc/linux.h" #endif #include diff --git a/src/3rdparty/hwloc/include/hwloc/opencl.h b/src/3rdparty/hwloc/include/hwloc/opencl.h index 058968d7..ebf09848 100644 --- a/src/3rdparty/hwloc/include/hwloc/opencl.h +++ b/src/3rdparty/hwloc/include/hwloc/opencl.h @@ -14,19 +14,17 @@ #ifndef HWLOC_OPENCL_H #define HWLOC_OPENCL_H -#include -#include -#include +#include "hwloc.h" +#include "hwloc/autogen/config.h" +#include "hwloc/helper.h" #ifdef HWLOC_LINUX_SYS -#include +#include "hwloc/linux.h" #endif #ifdef __APPLE__ #include -#include #else #include -#include #endif #include @@ -37,17 +35,75 @@ extern "C" { #endif +/* OpenCL extensions aren't always shipped with default headers, and + * they don't always reflect what the installed implementations support. + * Try everything and let the implementation return errors when non supported. + */ +/* Copyright (c) 2008-2018 The Khronos Group Inc. */ + +/* needs "cl_amd_device_attribute_query" device extension, but not strictly required for clGetDeviceInfo() */ +#define HWLOC_CL_DEVICE_TOPOLOGY_AMD 0x4037 +typedef union { + struct { cl_uint type; cl_uint data[5]; } raw; + struct { cl_uint type; cl_char unused[17]; cl_char bus; cl_char device; cl_char function; } pcie; +} hwloc_cl_device_topology_amd; +#define HWLOC_CL_DEVICE_TOPOLOGY_TYPE_PCIE_AMD 1 + +/* needs "cl_nv_device_attribute_query" device extension, but not strictly required for clGetDeviceInfo() */ +#define HWLOC_CL_DEVICE_PCI_BUS_ID_NV 0x4008 +#define HWLOC_CL_DEVICE_PCI_SLOT_ID_NV 0x4009 + + /** \defgroup hwlocality_opencl Interoperability with OpenCL * * This interface offers ways to retrieve topology information about * OpenCL devices. * - * Only the AMD OpenCL interface currently offers useful locality information - * about its devices. + * Only AMD and NVIDIA OpenCL implementations currently offer useful locality + * information about their devices. * * @{ */ +/** \brief Return the domain, bus and device IDs of the OpenCL device \p device. + * + * Device \p device must match the local machine. + */ +static __hwloc_inline int +hwloc_opencl_get_device_pci_busid(cl_device_id device, + unsigned *domain, unsigned *bus, unsigned *dev, unsigned *func) +{ + hwloc_cl_device_topology_amd amdtopo; + cl_uint nvbus, nvslot; + cl_int clret; + + clret = clGetDeviceInfo(device, HWLOC_CL_DEVICE_TOPOLOGY_AMD, sizeof(amdtopo), &amdtopo, NULL); + if (CL_SUCCESS == clret + && HWLOC_CL_DEVICE_TOPOLOGY_TYPE_PCIE_AMD == amdtopo.raw.type) { + *domain = 0; /* can't do anything better */ + *bus = (unsigned) amdtopo.pcie.bus; + *dev = (unsigned) amdtopo.pcie.device; + *func = (unsigned) amdtopo.pcie.function; + return 0; + } + + clret = clGetDeviceInfo(device, HWLOC_CL_DEVICE_PCI_BUS_ID_NV, sizeof(nvbus), &nvbus, NULL); + if (CL_SUCCESS == clret) { + clret = clGetDeviceInfo(device, HWLOC_CL_DEVICE_PCI_SLOT_ID_NV, sizeof(nvslot), &nvslot, NULL); + if (CL_SUCCESS == clret) { + /* FIXME: PCI bus only uses 8bit, assume nvidia hardcodes the domain in higher bits */ + *domain = nvbus >> 8; + *bus = nvbus & 0xff; + /* non-documented but used in many other projects */ + *dev = nvslot >> 3; + *func = nvslot & 0x7; + return 0; + } + } + + return -1; +} + /** \brief Get the CPU set of logical processors that are physically * close to OpenCL device \p device. * @@ -62,7 +118,7 @@ extern "C" { * and hwloc_opencl_get_device_osdev_by_index(). * * This function is currently only implemented in a meaningful way for - * Linux with the AMD OpenCL implementation; other systems will simply + * Linux with the AMD or NVIDIA OpenCL implementation; other systems will simply * get a full cpuset. */ static __hwloc_inline int @@ -70,35 +126,28 @@ hwloc_opencl_get_device_cpuset(hwloc_topology_t topology __hwloc_attribute_unuse cl_device_id device __hwloc_attribute_unused, hwloc_cpuset_t set) { -#if (defined HWLOC_LINUX_SYS) && (defined CL_DEVICE_TOPOLOGY_AMD) - /* If we're on Linux + AMD OpenCL, use the AMD extension + the sysfs mechanism to get the local cpus */ +#if (defined HWLOC_LINUX_SYS) + /* If we're on Linux, try AMD/NVIDIA extensions + the sysfs mechanism to get the local cpus */ #define HWLOC_OPENCL_DEVICE_SYSFS_PATH_MAX 128 char path[HWLOC_OPENCL_DEVICE_SYSFS_PATH_MAX]; - cl_device_topology_amd amdtopo; - cl_int clret; + unsigned pcidomain, pcibus, pcidev, pcifunc; if (!hwloc_topology_is_thissystem(topology)) { errno = EINVAL; return -1; } - clret = clGetDeviceInfo(device, CL_DEVICE_TOPOLOGY_AMD, sizeof(amdtopo), &amdtopo, NULL); - if (CL_SUCCESS != clret) { - hwloc_bitmap_copy(set, hwloc_topology_get_complete_cpuset(topology)); - return 0; - } - if (CL_DEVICE_TOPOLOGY_TYPE_PCIE_AMD != amdtopo.raw.type) { + if (hwloc_opencl_get_device_pci_busid(device, &pcidomain, &pcibus, &pcidev, &pcifunc) < 0) { hwloc_bitmap_copy(set, hwloc_topology_get_complete_cpuset(topology)); return 0; } - sprintf(path, "/sys/bus/pci/devices/0000:%02x:%02x.%01x/local_cpus", - (unsigned) amdtopo.pcie.bus, (unsigned) amdtopo.pcie.device, (unsigned) amdtopo.pcie.function); + sprintf(path, "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/local_cpus", pcidomain, pcibus, pcidev, pcifunc); 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 + AMD OpenCL systems simply get a full cpuset */ + /* Non-Linux systems simply get a full cpuset */ hwloc_bitmap_copy(set, hwloc_topology_get_complete_cpuset(topology)); #endif return 0; @@ -140,8 +189,8 @@ hwloc_opencl_get_device_osdev_by_index(hwloc_topology_t topology, * Use OpenCL device attributes to find the corresponding hwloc OS device object. * Return NULL if there is none or if useful attributes are not available. * - * This function currently only works on AMD OpenCL devices that support - * the CL_DEVICE_TOPOLOGY_AMD extension. hwloc_opencl_get_device_osdev_by_index() + * This function currently only works on AMD and NVIDIA OpenCL devices that support + * relevant OpenCL extensions. hwloc_opencl_get_device_osdev_by_index() * should be preferred whenever possible, i.e. when platform and device index * are known. * @@ -159,17 +208,10 @@ static __hwloc_inline hwloc_obj_t hwloc_opencl_get_device_osdev(hwloc_topology_t topology __hwloc_attribute_unused, cl_device_id device __hwloc_attribute_unused) { -#ifdef CL_DEVICE_TOPOLOGY_AMD hwloc_obj_t osdev; - cl_device_topology_amd amdtopo; - cl_int clret; + unsigned pcidomain, pcibus, pcidevice, pcifunc; - clret = clGetDeviceInfo(device, CL_DEVICE_TOPOLOGY_AMD, sizeof(amdtopo), &amdtopo, NULL); - if (CL_SUCCESS != clret) { - errno = EINVAL; - return NULL; - } - if (CL_DEVICE_TOPOLOGY_TYPE_PCIE_AMD != amdtopo.raw.type) { + if (hwloc_opencl_get_device_pci_busid(device, &pcidomain, &pcibus, &pcidevice, &pcifunc) < 0) { errno = EINVAL; return NULL; } @@ -181,18 +223,15 @@ hwloc_opencl_get_device_osdev(hwloc_topology_t topology __hwloc_attribute_unused continue; if (pcidev && pcidev->type == HWLOC_OBJ_PCI_DEVICE - && pcidev->attr->pcidev.domain == 0 - && pcidev->attr->pcidev.bus == amdtopo.pcie.bus - && pcidev->attr->pcidev.dev == amdtopo.pcie.device - && pcidev->attr->pcidev.func == amdtopo.pcie.function) + && pcidev->attr->pcidev.domain == pcidomain + && pcidev->attr->pcidev.bus == pcibus + && pcidev->attr->pcidev.dev == pcidevice + && pcidev->attr->pcidev.func == pcifunc) return osdev; /* if PCI are filtered out, we need a info attr to match on */ } return NULL; -#else - return NULL; -#endif } /** @} */ diff --git a/src/3rdparty/hwloc/include/hwloc/openfabrics-verbs.h b/src/3rdparty/hwloc/include/hwloc/openfabrics-verbs.h index 174ab4a5..d247a8b1 100644 --- a/src/3rdparty/hwloc/include/hwloc/openfabrics-verbs.h +++ b/src/3rdparty/hwloc/include/hwloc/openfabrics-verbs.h @@ -19,10 +19,10 @@ #ifndef HWLOC_OPENFABRICS_VERBS_H #define HWLOC_OPENFABRICS_VERBS_H -#include -#include +#include "hwloc.h" +#include "hwloc/autogen/config.h" #ifdef HWLOC_LINUX_SYS -#include +#include "hwloc/linux.h" #endif #include diff --git a/src/3rdparty/hwloc/include/hwloc/plugins.h b/src/3rdparty/hwloc/include/hwloc/plugins.h index cb22000d..0f53ac4d 100644 --- a/src/3rdparty/hwloc/include/hwloc/plugins.h +++ b/src/3rdparty/hwloc/include/hwloc/plugins.h @@ -1,5 +1,5 @@ /* - * Copyright © 2013-2017 Inria. All rights reserved. + * Copyright © 2013-2019 Inria. All rights reserved. * Copyright © 2016 Cisco Systems, Inc. All rights reserved. * See COPYING in top-level directory. */ @@ -13,7 +13,8 @@ struct hwloc_backend; -#include +#include "hwloc.h" + #ifdef HWLOC_INSIDE_PLUGIN /* needed for hwloc_plugin_check_namespace() */ #include @@ -25,52 +26,36 @@ struct hwloc_backend; * @{ */ -/** \brief Discovery component type */ -typedef enum hwloc_disc_component_type_e { - /** \brief CPU-only discovery through the OS, or generic no-OS support. - * \hideinitializer */ - HWLOC_DISC_COMPONENT_TYPE_CPU = (1<<0), - - /** \brief xml or synthetic, - * platform-specific components such as bgq. - * Anything the discovers CPU and everything else. - * No misc backend is expected to complement a global component. - * \hideinitializer */ - HWLOC_DISC_COMPONENT_TYPE_GLOBAL = (1<<1), - - /** \brief OpenCL, Cuda, etc. - * \hideinitializer */ - HWLOC_DISC_COMPONENT_TYPE_MISC = (1<<2) -} hwloc_disc_component_type_t; - /** \brief Discovery component structure * * This is the major kind of components, taking care of the discovery. * They are registered by generic components, either statically-built or as plugins. */ struct hwloc_disc_component { - /** \brief Discovery component type */ - hwloc_disc_component_type_t type; - /** \brief Name. * If this component is built as a plugin, this name does not have to match the plugin filename. */ const char *name; - /** \brief Component types to exclude, as an OR'ed set of ::hwloc_disc_component_type_e. + /** \brief Discovery phases performed by this component. + * OR'ed set of ::hwloc_disc_phase_t + */ + unsigned phases; + + /** \brief Component phases to exclude, as an OR'ed set of ::hwloc_disc_phase_t. * - * For a GLOBAL component, this usually includes all other types (~0). + * For a GLOBAL component, this usually includes all other phases (\c ~UL). * * Other components only exclude types that may bring conflicting * topology information. MISC components should likely not be excluded * since they usually bring non-primary additional information. */ - unsigned excludes; + unsigned excluded_phases; /** \brief Instantiate callback to create a backend from the component. * Parameters data1, data2, data3 are NULL except for components * that have special enabling routines such as hwloc_topology_set_xml(). */ - struct hwloc_backend * (*instantiate)(struct hwloc_disc_component *component, const void *data1, const void *data2, const void *data3); + struct hwloc_backend * (*instantiate)(struct hwloc_topology *topology, struct hwloc_disc_component *component, unsigned excluded_phases, const void *data1, const void *data2, const void *data3); /** \brief Component priority. * Used to sort topology->components, higher priority first. @@ -107,6 +92,72 @@ struct hwloc_disc_component { * @{ */ +/** \brief Discovery phase */ +typedef enum hwloc_disc_phase_e { + /** \brief xml or synthetic, platform-specific components such as bgq. + * Discovers everything including CPU, memory, I/O and everything else. + * A component with a Global phase usually excludes all other phases. + * \hideinitializer */ + HWLOC_DISC_PHASE_GLOBAL = (1U<<0), + + /** \brief CPU discovery. + * \hideinitializer */ + HWLOC_DISC_PHASE_CPU = (1U<<1), + + /** \brief Attach memory to existing CPU objects. + * \hideinitializer */ + HWLOC_DISC_PHASE_MEMORY = (1U<<2), + + /** \brief Attach PCI devices and bridges to existing CPU objects. + * \hideinitializer */ + HWLOC_DISC_PHASE_PCI = (1U<<3), + + /** \brief I/O discovery that requires PCI devices (OS devices such as OpenCL, CUDA, etc.). + * \hideinitializer */ + HWLOC_DISC_PHASE_IO = (1U<<4), + + /** \brief Misc objects that gets added below anything else. + * \hideinitializer */ + HWLOC_DISC_PHASE_MISC = (1U<<5), + + /** \brief Annotating existing objects, adding distances, etc. + * \hideinitializer */ + HWLOC_DISC_PHASE_ANNOTATE = (1U<<6), + + /** \brief Final tweaks to a ready-to-use topology. + * This phase runs once the topology is loaded, before it is returned to the topology. + * Hence it may only use the main hwloc API for modifying the topology, + * for instance by restricting it, adding info attributes, etc. + * \hideinitializer */ + HWLOC_DISC_PHASE_TWEAK = (1U<<7) +} hwloc_disc_phase_t; + +/** \brief Discovery status flags */ +enum hwloc_disc_status_flag_e { + /** \brief The sets of allowed resources were already retrieved \hideinitializer */ + HWLOC_DISC_STATUS_FLAG_GOT_ALLOWED_RESOURCES = (1UL<<1) +}; + +/** \brief Discovery status structure + * + * Used by the core and backends to inform about what has been/is being done + * during the discovery process. + */ +struct hwloc_disc_status { + /** \brief The current discovery phase that is performed. + * Must match one of the phases in the component phases field. + */ + hwloc_disc_phase_t phase; + + /** \brief Dynamically excluded phases. + * If a component decides during discovery that some phases are no longer needed. + */ + unsigned excluded_phases; + + /** \brief OR'ed set of hwloc_disc_status_flag_e */ + unsigned long flags; +}; + /** \brief Discovery backend structure * * A backend is the instantiation of a discovery component. @@ -116,6 +167,14 @@ struct hwloc_disc_component { * hwloc_backend_alloc() initializes all fields to default values * that the component may change (except "component" and "next") * before enabling the backend with hwloc_backend_enable(). + * + * Most backends assume that the topology is_thissystem flag is + * set because they talk to the underlying operating system. + * However they may still be used in topologies without the + * is_thissystem flag for debugging reasons. + * In practice, they are usually auto-disabled in such cases + * (excluded by xml or synthetic backends, or by environment + * variables when changing the Linux fsroot or the x86 cpuid path). */ struct hwloc_backend { /** \private Reserved for the core, set by hwloc_backend_alloc() */ @@ -127,12 +186,20 @@ struct hwloc_backend { /** \private Reserved for the core. Used internally to list backends topology->backends. */ struct hwloc_backend * next; + /** \brief Discovery phases performed by this component, possibly without some of them if excluded by other components. + * OR'ed set of ::hwloc_disc_phase_t + */ + unsigned phases; + /** \brief Backend flags, currently always 0. */ unsigned long flags; /** \brief Backend-specific 'is_thissystem' property. - * Set to 0 or 1 if the backend should enforce the thissystem flag when it gets enabled. - * Set to -1 if the backend doesn't care (default). */ + * Set to 0 if the backend disables the thissystem flag for this topology + * (e.g. loading from xml or synthetic string, + * or using a different fsroot on Linux, or a x86 CPUID dump). + * Set to -1 if the backend doesn't care (default). + */ int is_thissystem; /** \brief Backend private data, or NULL if none. */ @@ -147,20 +214,22 @@ struct hwloc_backend { * or because of an actual discovery/gathering failure. * May be NULL. */ - int (*discover)(struct hwloc_backend *backend); + int (*discover)(struct hwloc_backend *backend, struct hwloc_disc_status *status); - /** \brief Callback used by the PCI backend to retrieve the locality of a PCI object from the OS/cpu backend. - * May be NULL. */ + /** \brief Callback to retrieve the locality of a PCI object. + * Called by the PCI core when attaching PCI hierarchy to CPU objects. + * May be NULL. + */ int (*get_pci_busid_cpuset)(struct hwloc_backend *backend, struct hwloc_pcidev_attr_s *busid, hwloc_bitmap_t cpuset); }; /** \brief Allocate a backend structure, set good default values, initialize backend->component and topology, etc. * The caller will then modify whatever needed, and call hwloc_backend_enable(). */ -HWLOC_DECLSPEC struct hwloc_backend * hwloc_backend_alloc(struct hwloc_disc_component *component); +HWLOC_DECLSPEC struct hwloc_backend * hwloc_backend_alloc(struct hwloc_topology *topology, struct hwloc_disc_component *component); /** \brief Enable a previously allocated and setup backend. */ -HWLOC_DECLSPEC int hwloc_backend_enable(struct hwloc_topology *topology, struct hwloc_backend *backend); +HWLOC_DECLSPEC int hwloc_backend_enable(struct hwloc_backend *backend); /** @} */ @@ -480,7 +549,9 @@ HWLOC_DECLSPEC hwloc_obj_type_t hwloc_pcidisc_check_bridge_type(unsigned device_ * * Returns -1 and destroys /p obj if bridge fields are invalid. */ -HWLOC_DECLSPEC int hwloc_pcidisc_setup_bridge_attr(hwloc_obj_t obj, const unsigned char *config); +HWLOC_DECLSPEC int hwloc_pcidisc_find_bridge_buses(unsigned domain, unsigned bus, unsigned dev, unsigned func, + unsigned *secondary_busp, unsigned *subordinate_busp, + const unsigned char *config); /** \brief Insert a PCI object in the given PCI tree by looking at PCI bus IDs. * @@ -490,10 +561,7 @@ HWLOC_DECLSPEC void hwloc_pcidisc_tree_insert_by_busid(struct hwloc_obj **treep, /** \brief Add some hostbridges on top of the given tree of PCI objects and attach them to the topology. * - * For now, they will be attached to the root object. The core will move them to their actual PCI - * locality using hwloc_pci_belowroot_apply_locality() at the end of the discovery. - * - * In the meantime, other backends lookup PCI objects or localities (for instance to attach OS devices) + * Other backends may lookup PCI objects or localities (for instance to attach OS devices) * by using hwloc_pcidisc_find_by_busid() or hwloc_pcidisc_find_busid_parent(). */ HWLOC_DECLSPEC int hwloc_pcidisc_tree_attach(struct hwloc_topology *topology, struct hwloc_obj *tree); @@ -507,32 +575,14 @@ HWLOC_DECLSPEC int hwloc_pcidisc_tree_attach(struct hwloc_topology *topology, st * @{ */ -/** \brief Find the PCI object that matches the bus ID. - * - * To be used after a PCI backend added PCI devices with hwloc_pcidisc_tree_attach() - * and before the core moves them to their actual location with hwloc_pci_belowroot_apply_locality(). - * - * If no exactly matching object is found, return the container bridge if any, or NULL. - * - * On failure, it may be possible to find the PCI locality (instead of the PCI device) - * by calling hwloc_pcidisc_find_busid_parent(). - * - * \note This is semantically identical to hwloc_get_pcidev_by_busid() which only works - * after the topology is fully loaded. - */ -HWLOC_DECLSPEC struct hwloc_obj * hwloc_pcidisc_find_by_busid(struct hwloc_topology *topology, unsigned domain, unsigned bus, unsigned dev, unsigned func); - /** \brief Find the normal parent of a PCI bus ID. * * Look at PCI affinity to find out where the given PCI bus ID should be attached. * - * This function should be used to attach an I/O device directly under a normal - * (non-I/O) object, instead of below a PCI object. - * It is usually used by backends when hwloc_pcidisc_find_by_busid() failed - * to find the hwloc object corresponding to this bus ID, for instance because - * PCI discovery is not supported on this platform. + * This function should be used to attach an I/O device under the corresponding + * PCI object (if any), or under a normal (non-I/O) object with same locality. */ -HWLOC_DECLSPEC struct hwloc_obj * hwloc_pcidisc_find_busid_parent(struct hwloc_topology *topology, unsigned domain, unsigned bus, unsigned dev, unsigned func); +HWLOC_DECLSPEC struct hwloc_obj * hwloc_pci_find_parent_by_busid(struct hwloc_topology *topology, unsigned domain, unsigned bus, unsigned dev, unsigned func); /** @} */ diff --git a/src/3rdparty/hwloc/include/hwloc/rename.h b/src/3rdparty/hwloc/include/hwloc/rename.h index 7cef1b2e..a23738d0 100644 --- a/src/3rdparty/hwloc/include/hwloc/rename.h +++ b/src/3rdparty/hwloc/include/hwloc/rename.h @@ -1,13 +1,13 @@ /* * Copyright © 2009-2011 Cisco Systems, Inc. All rights reserved. - * Copyright © 2010-2018 Inria. All rights reserved. + * Copyright © 2010-2019 Inria. All rights reserved. * See COPYING in top-level directory. */ #ifndef HWLOC_RENAME_H #define HWLOC_RENAME_H -#include +#include "hwloc/autogen/config.h" #ifdef __cplusplus @@ -49,7 +49,9 @@ extern "C" { #define HWLOC_OBJ_MACHINE HWLOC_NAME_CAPS(OBJ_MACHINE) #define HWLOC_OBJ_NUMANODE HWLOC_NAME_CAPS(OBJ_NUMANODE) +#define HWLOC_OBJ_MEMCACHE HWLOC_NAME_CAPS(OBJ_MEMCACHE) #define HWLOC_OBJ_PACKAGE HWLOC_NAME_CAPS(OBJ_PACKAGE) +#define HWLOC_OBJ_DIE HWLOC_NAME_CAPS(OBJ_DIE) #define HWLOC_OBJ_CORE HWLOC_NAME_CAPS(OBJ_CORE) #define HWLOC_OBJ_PU HWLOC_NAME_CAPS(OBJ_PU) #define HWLOC_OBJ_L1CACHE HWLOC_NAME_CAPS(OBJ_L1CACHE) @@ -116,7 +118,7 @@ extern "C" { #define hwloc_topology_flags_e HWLOC_NAME(topology_flags_e) -#define HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM HWLOC_NAME_CAPS(TOPOLOGY_FLAG_WHOLE_SYSTEM) +#define HWLOC_TOPOLOGY_FLAG_INCLUDE_DISALLOWED HWLOC_NAME_CAPS(TOPOLOGY_FLAG_WITH_DISALLOWED) #define HWLOC_TOPOLOGY_FLAG_IS_THISSYSTEM HWLOC_NAME_CAPS(TOPOLOGY_FLAG_IS_THISSYSTEM) #define HWLOC_TOPOLOGY_FLAG_THISSYSTEM_ALLOWED_RESOURCES HWLOC_NAME_CAPS(TOPOLOGY_FLAG_THISSYSTEM_ALLOWED_RESOURCES) @@ -124,6 +126,9 @@ extern "C" { #define hwloc_topology_set_synthetic HWLOC_NAME(topology_set_synthetic) #define hwloc_topology_set_xml HWLOC_NAME(topology_set_xml) #define hwloc_topology_set_xmlbuffer HWLOC_NAME(topology_set_xmlbuffer) +#define hwloc_topology_components_flag_e HWLOC_NAME(hwloc_topology_components_flag_e) +#define HWLOC_TOPOLOGY_COMPONENTS_FLAG_BLACKLIST HWLOC_NAME_CAPS(TOPOLOGY_COMPONENTS_FLAG_BLACKLIST) +#define hwloc_topology_set_components HWLOC_NAME(topology_set_components) #define hwloc_topology_set_flags HWLOC_NAME(topology_set_flags) #define hwloc_topology_is_thissystem HWLOC_NAME(topology_is_thissystem) @@ -151,10 +156,18 @@ extern "C" { #define hwloc_restrict_flags_e HWLOC_NAME(restrict_flags_e) #define HWLOC_RESTRICT_FLAG_REMOVE_CPULESS HWLOC_NAME_CAPS(RESTRICT_FLAG_REMOVE_CPULESS) +#define HWLOC_RESTRICT_FLAG_BYNODESET HWLOC_NAME_CAPS(RESTRICT_FLAG_BYNODESET) +#define HWLOC_RESTRICT_FLAG_REMOVE_MEMLESS HWLOC_NAME_CAPS(RESTRICT_FLAG_REMOVE_MEMLESS) #define HWLOC_RESTRICT_FLAG_ADAPT_MISC HWLOC_NAME_CAPS(RESTRICT_FLAG_ADAPT_MISC) #define HWLOC_RESTRICT_FLAG_ADAPT_IO HWLOC_NAME_CAPS(RESTRICT_FLAG_ADAPT_IO) #define hwloc_topology_restrict HWLOC_NAME(topology_restrict) +#define hwloc_allow_flags_e HWLOC_NAME(allow_flags_e) +#define HWLOC_ALLOW_FLAG_ALL HWLOC_NAME_CAPS(ALLOW_FLAG_ALL) +#define HWLOC_ALLOW_FLAG_LOCAL_RESTRICTIONS HWLOC_NAME_CAPS(ALLOW_FLAG_LOCAL_RESTRICTIONS) +#define HWLOC_ALLOW_FLAG_CUSTOM HWLOC_NAME_CAPS(ALLOW_FLAG_CUSTOM) +#define hwloc_topology_allow HWLOC_NAME(topology_allow) + #define hwloc_topology_insert_misc_object HWLOC_NAME(topology_insert_misc_object) #define hwloc_topology_alloc_group_object HWLOC_NAME(topology_alloc_group_object) #define hwloc_topology_insert_group_object HWLOC_NAME(topology_insert_group_object) @@ -172,6 +185,7 @@ extern "C" { #define HWLOC_TYPE_DEPTH_OS_DEVICE HWLOC_NAME_CAPS(TYPE_DEPTH_OS_DEVICE) #define HWLOC_TYPE_DEPTH_MISC HWLOC_NAME_CAPS(TYPE_DEPTH_MISC) #define HWLOC_TYPE_DEPTH_NUMANODE HWLOC_NAME_CAPS(TYPE_DEPTH_NUMANODE) +#define HWLOC_TYPE_DEPTH_MEMCACHE HWLOC_NAME_CAPS(TYPE_DEPTH_MEMCACHE) #define hwloc_get_depth_type HWLOC_NAME(get_depth_type) #define hwloc_get_nbobjs_by_depth HWLOC_NAME(get_nbobjs_by_depth) @@ -266,10 +280,12 @@ extern "C" { #define hwloc_bitmap_zero HWLOC_NAME(bitmap_zero) #define hwloc_bitmap_fill HWLOC_NAME(bitmap_fill) #define hwloc_bitmap_from_ulong HWLOC_NAME(bitmap_from_ulong) - +#define hwloc_bitmap_from_ulongs HWLOC_NAME(bitmap_from_ulongs) #define hwloc_bitmap_from_ith_ulong HWLOC_NAME(bitmap_from_ith_ulong) #define hwloc_bitmap_to_ulong HWLOC_NAME(bitmap_to_ulong) #define hwloc_bitmap_to_ith_ulong HWLOC_NAME(bitmap_to_ith_ulong) +#define hwloc_bitmap_to_ulongs HWLOC_NAME(bitmap_to_ulongs) +#define hwloc_bitmap_nr_ulongs HWLOC_NAME(bitmap_nr_ulongs) #define hwloc_bitmap_only HWLOC_NAME(bitmap_only) #define hwloc_bitmap_allbut HWLOC_NAME(bitmap_allbut) #define hwloc_bitmap_set HWLOC_NAME(bitmap_set) @@ -380,10 +396,13 @@ extern "C" { #define HWLOC_DISTANCES_KIND_FROM_USER HWLOC_NAME_CAPS(DISTANCES_KIND_FROM_USER) #define HWLOC_DISTANCES_KIND_MEANS_LATENCY HWLOC_NAME_CAPS(DISTANCES_KIND_MEANS_LATENCY) #define HWLOC_DISTANCES_KIND_MEANS_BANDWIDTH HWLOC_NAME_CAPS(DISTANCES_KIND_MEANS_BANDWIDTH) +#define HWLOC_DISTANCES_KIND_HETEROGENEOUS_TYPES HWLOC_NAME_CAPS(DISTANCES_KIND_HETEROGENEOUS_TYPES) #define hwloc_distances_get HWLOC_NAME(distances_get) #define hwloc_distances_get_by_depth HWLOC_NAME(distances_get_by_depth) #define hwloc_distances_get_by_type HWLOC_NAME(distances_get_by_type) +#define hwloc_distances_get_by_name HWLOC_NAME(distances_get_by_name) +#define hwloc_distances_get_name HWLOC_NAME(distances_get_name) #define hwloc_distances_release HWLOC_NAME(distances_release) #define hwloc_distances_obj_index HWLOC_NAME(distances_obj_index) #define hwloc_distances_obj_pair_values HWLOC_NAME(distances_pair_values) @@ -396,6 +415,7 @@ extern "C" { #define hwloc_distances_remove HWLOC_NAME(distances_remove) #define hwloc_distances_remove_by_depth HWLOC_NAME(distances_remove_by_depth) #define hwloc_distances_remove_by_type HWLOC_NAME(distances_remove_by_type) +#define hwloc_distances_release_remove HWLOC_NAME(distances_release_remove) /* diff.h */ @@ -469,6 +489,8 @@ extern "C" { /* opencl.h */ +#define hwloc_cl_device_topology_amd HWLOC_NAME(cl_device_topology_amd) +#define hwloc_opencl_get_device_pci_busid HWLOC_NAME(opencl_get_device_pci_ids) #define hwloc_opencl_get_device_cpuset HWLOC_NAME(opencl_get_device_cpuset) #define hwloc_opencl_get_device_osdev HWLOC_NAME(opencl_get_device_osdev) #define hwloc_opencl_get_device_osdev_by_index HWLOC_NAME(opencl_get_device_osdev_by_index) @@ -502,13 +524,22 @@ extern "C" { /* hwloc/plugins.h */ -#define hwloc_disc_component_type_e HWLOC_NAME(disc_component_type_e) -#define HWLOC_DISC_COMPONENT_TYPE_CPU HWLOC_NAME_CAPS(DISC_COMPONENT_TYPE_CPU) -#define HWLOC_DISC_COMPONENT_TYPE_GLOBAL HWLOC_NAME_CAPS(DISC_COMPONENT_TYPE_GLOBAL) -#define HWLOC_DISC_COMPONENT_TYPE_MISC HWLOC_NAME_CAPS(DISC_COMPONENT_TYPE_MISC) -#define hwloc_disc_component_type_t HWLOC_NAME(disc_component_type_t) +#define hwloc_disc_phase_e HWLOC_NAME(disc_phase_e) +#define HWLOC_DISC_PHASE_GLOBAL HWLOC_NAME_CAPS(DISC_PHASE_GLOBAL) +#define HWLOC_DISC_PHASE_CPU HWLOC_NAME_CAPS(DISC_PHASE_CPU) +#define HWLOC_DISC_PHASE_MEMORY HWLOC_NAME_CAPS(DISC_PHASE_MEMORY) +#define HWLOC_DISC_PHASE_PCI HWLOC_NAME_CAPS(DISC_PHASE_PCI) +#define HWLOC_DISC_PHASE_IO HWLOC_NAME_CAPS(DISC_PHASE_IO) +#define HWLOC_DISC_PHASE_MISC HWLOC_NAME_CAPS(DISC_PHASE_MISC) +#define HWLOC_DISC_PHASE_ANNOTATE HWLOC_NAME_CAPS(DISC_PHASE_ANNOTATE) +#define HWLOC_DISC_PHASE_TWEAK HWLOC_NAME_CAPS(DISC_PHASE_TWEAK) +#define hwloc_disc_phase_t HWLOC_NAME(disc_phase_t) #define hwloc_disc_component HWLOC_NAME(disc_component) +#define hwloc_disc_status_flag_e HWLOC_NAME(disc_status_flag_e) +#define HWLOC_DISC_STATUS_FLAG_GOT_ALLOWED_RESOURCES HWLOC_NAME_CAPS(DISC_STATUS_FLAG_GOT_ALLOWED_RESOURCES) +#define hwloc_disc_status HWLOC_NAME(disc_status) + #define hwloc_backend HWLOC_NAME(backend) #define hwloc_backend_alloc HWLOC_NAME(backend_alloc) @@ -540,12 +571,11 @@ extern "C" { #define hwloc_pcidisc_find_cap HWLOC_NAME(pcidisc_find_cap) #define hwloc_pcidisc_find_linkspeed HWLOC_NAME(pcidisc_find_linkspeed) #define hwloc_pcidisc_check_bridge_type HWLOC_NAME(pcidisc_check_bridge_type) -#define hwloc_pcidisc_setup_bridge_attr HWLOC_NAME(pcidisc_setup_bridge_attr) +#define hwloc_pcidisc_find_bridge_buses HWLOC_NAME(pcidisc_find_bridge_buses) #define hwloc_pcidisc_tree_insert_by_busid HWLOC_NAME(pcidisc_tree_insert_by_busid) #define hwloc_pcidisc_tree_attach HWLOC_NAME(pcidisc_tree_attach) -#define hwloc_pcidisc_find_by_busid HWLOC_NAME(pcidisc_find_by_busid) -#define hwloc_pcidisc_find_busid_parent HWLOC_NAME(pcidisc_find_busid_parent) +#define hwloc_pci_find_parent_by_busid HWLOC_NAME(pcidisc_find_busid_parent) /* hwloc/deprecated.h */ @@ -571,8 +601,9 @@ extern "C" { /* private/misc.h */ +#ifndef HWLOC_HAVE_CORRECT_SNPRINTF #define hwloc_snprintf HWLOC_NAME(snprintf) -#define hwloc_namecoloncmp HWLOC_NAME(namecoloncmp) +#endif #define hwloc_ffsl_manual HWLOC_NAME(ffsl_manual) #define hwloc_ffs32 HWLOC_NAME(ffs32) #define hwloc_ffsl_from_ffs32 HWLOC_NAME(ffsl_from_ffs32) @@ -631,8 +662,9 @@ extern "C" { #define hwloc_backends_is_thissystem HWLOC_NAME(backends_is_thissystem) #define hwloc_backends_find_callbacks HWLOC_NAME(backends_find_callbacks) -#define hwloc_backends_init HWLOC_NAME(backends_init) +#define hwloc_topology_components_init HWLOC_NAME(topology_components_init) #define hwloc_backends_disable_all HWLOC_NAME(backends_disable_all) +#define hwloc_topology_components_fini HWLOC_NAME(topology_components_fini) #define hwloc_components_init HWLOC_NAME(components_init) #define hwloc_components_fini HWLOC_NAME(components_fini) @@ -656,7 +688,6 @@ extern "C" { #define hwloc_cuda_component HWLOC_NAME(cuda_component) #define hwloc_gl_component HWLOC_NAME(gl_component) -#define hwloc_linuxio_component HWLOC_NAME(linuxio_component) #define hwloc_nvml_component HWLOC_NAME(nvml_component) #define hwloc_opencl_component HWLOC_NAME(opencl_component) #define hwloc_pci_component HWLOC_NAME(pci_component) @@ -669,6 +700,9 @@ extern "C" { #define hwloc_special_level_s HWLOC_NAME(special_level_s) #define hwloc_pci_forced_locality_s HWLOC_NAME(pci_forced_locality_s) +#define hwloc_pci_locality_s HWLOC_NAME(pci_locality_s) + +#define hwloc_topology_forced_component_s HWLOC_NAME(topology_forced_component) #define hwloc_alloc_root_sets HWLOC_NAME(alloc_root_sets) #define hwloc_setup_pu_level HWLOC_NAME(setup_pu_level) @@ -687,8 +721,8 @@ extern "C" { #define hwloc_pci_discovery_init HWLOC_NAME(pci_discovery_init) #define hwloc_pci_discovery_prepare HWLOC_NAME(pci_discovery_prepare) #define hwloc_pci_discovery_exit HWLOC_NAME(pci_discovery_exit) +#define hwloc_pci_find_by_busid HWLOC_NAME(pcidisc_find_by_busid) #define hwloc_find_insert_io_parent_by_complete_cpuset HWLOC_NAME(hwloc_find_insert_io_parent_by_complete_cpuset) -#define hwloc_pci_belowroot_apply_locality HWLOC_NAME(pci_belowroot_apply_locality) #define hwloc__add_info HWLOC_NAME(_add_info) #define hwloc__add_info_nodup HWLOC_NAME(_add_info_nodup) diff --git a/src/3rdparty/hwloc/include/hwloc/shmem.h b/src/3rdparty/hwloc/include/hwloc/shmem.h index 22249463..86f57b4f 100644 --- a/src/3rdparty/hwloc/include/hwloc/shmem.h +++ b/src/3rdparty/hwloc/include/hwloc/shmem.h @@ -10,7 +10,7 @@ #ifndef HWLOC_SHMEM_H #define HWLOC_SHMEM_H -#include +#include "hwloc.h" #ifdef __cplusplus extern "C" { diff --git a/src/3rdparty/hwloc/include/private/components.h b/src/3rdparty/hwloc/include/private/components.h index 8525bbe4..e28c00b1 100644 --- a/src/3rdparty/hwloc/include/private/components.h +++ b/src/3rdparty/hwloc/include/private/components.h @@ -1,5 +1,5 @@ /* - * Copyright © 2012-2015 Inria. All rights reserved. + * Copyright © 2012-2019 Inria. All rights reserved. * See COPYING in top-level directory. */ @@ -16,13 +16,13 @@ #ifndef PRIVATE_COMPONENTS_H #define PRIVATE_COMPONENTS_H 1 -#include +#include "hwloc/plugins.h" struct hwloc_topology; extern int hwloc_disc_component_force_enable(struct hwloc_topology *topology, int envvar_forced, /* 1 if forced through envvar, 0 if forced through API */ - int type, const char *name, + const char *name, const void *data1, const void *data2, const void *data3); extern void hwloc_disc_components_enable_others(struct hwloc_topology *topology); @@ -30,10 +30,12 @@ extern void hwloc_disc_components_enable_others(struct hwloc_topology *topology) extern void hwloc_backends_is_thissystem(struct hwloc_topology *topology); extern void hwloc_backends_find_callbacks(struct hwloc_topology *topology); -/* Initialize the list of backends used by a topology */ -extern void hwloc_backends_init(struct hwloc_topology *topology); +/* Initialize the lists of components and backends used by a topology */ +extern void hwloc_topology_components_init(struct hwloc_topology *topology); /* Disable and destroy all backends used by a topology */ extern void hwloc_backends_disable_all(struct hwloc_topology *topology); +/* Cleanup the lists of components used by a topology */ +extern void hwloc_topology_components_fini(struct hwloc_topology *topology); /* Used by the core to setup/destroy the list of components */ extern void hwloc_components_init(void); /* increases components refcount, should be called exactly once per topology (during init) */ diff --git a/src/3rdparty/hwloc/include/private/debug.h b/src/3rdparty/hwloc/include/private/debug.h index 74b697db..637e0141 100644 --- a/src/3rdparty/hwloc/include/private/debug.h +++ b/src/3rdparty/hwloc/include/private/debug.h @@ -11,8 +11,8 @@ #ifndef HWLOC_DEBUG_H #define HWLOC_DEBUG_H -#include -#include +#include "private/autogen/config.h" +#include "private/misc.h" #ifdef HWLOC_DEBUG #include diff --git a/src/3rdparty/hwloc/include/private/internal-components.h b/src/3rdparty/hwloc/include/private/internal-components.h index b138a0eb..d3c89783 100644 --- a/src/3rdparty/hwloc/include/private/internal-components.h +++ b/src/3rdparty/hwloc/include/private/internal-components.h @@ -1,5 +1,5 @@ /* - * Copyright © 2018 Inria. All rights reserved. + * Copyright © 2018-2019 Inria. All rights reserved. * * See COPYING in top-level directory. */ @@ -29,7 +29,6 @@ HWLOC_DECLSPEC extern const struct hwloc_component hwloc_x86_component; /* I/O discovery */ HWLOC_DECLSPEC extern const struct hwloc_component hwloc_cuda_component; HWLOC_DECLSPEC extern const struct hwloc_component hwloc_gl_component; -HWLOC_DECLSPEC extern const struct hwloc_component hwloc_linuxio_component; HWLOC_DECLSPEC extern const struct hwloc_component hwloc_nvml_component; HWLOC_DECLSPEC extern const struct hwloc_component hwloc_opencl_component; HWLOC_DECLSPEC extern const struct hwloc_component hwloc_pci_component; diff --git a/src/3rdparty/hwloc/include/private/misc.h b/src/3rdparty/hwloc/include/private/misc.h index 66608bc7..6c02d793 100644 --- a/src/3rdparty/hwloc/include/private/misc.h +++ b/src/3rdparty/hwloc/include/private/misc.h @@ -1,6 +1,6 @@ /* * Copyright © 2009 CNRS - * Copyright © 2009-2018 Inria. All rights reserved. + * Copyright © 2009-2019 Inria. All rights reserved. * Copyright © 2009-2012 Université Bordeaux * Copyright © 2011 Cisco Systems, Inc. All rights reserved. * See COPYING in top-level directory. @@ -11,9 +11,9 @@ #ifndef HWLOC_PRIVATE_MISC_H #define HWLOC_PRIVATE_MISC_H -#include -#include -#include +#include "hwloc/autogen/config.h" +#include "private/autogen/config.h" +#include "hwloc.h" #ifdef HWLOC_HAVE_DECL_STRNCASECMP #ifdef HAVE_STRINGS_H @@ -439,14 +439,14 @@ hwloc_linux_pci_link_speed_from_string(const char *string) static __hwloc_inline int hwloc__obj_type_is_normal (hwloc_obj_type_t type) { /* type contiguity is asserted in topology_check() */ - return type <= HWLOC_OBJ_GROUP; + return type <= HWLOC_OBJ_GROUP || type == HWLOC_OBJ_DIE; } -/* Any object attached to memory children, currently only NUMA nodes */ +/* Any object attached to memory children, currently NUMA nodes or Memory-side caches */ static __hwloc_inline int hwloc__obj_type_is_memory (hwloc_obj_type_t type) { /* type contiguity is asserted in topology_check() */ - return type == HWLOC_OBJ_NUMANODE; + return type == HWLOC_OBJ_NUMANODE || type == HWLOC_OBJ_MEMCACHE; } /* I/O or Misc object, without cpusets or nodesets. */ @@ -463,6 +463,7 @@ static __hwloc_inline int hwloc__obj_type_is_io (hwloc_obj_type_t type) return type >= HWLOC_OBJ_BRIDGE && type <= HWLOC_OBJ_OS_DEVICE; } +/* Any CPU caches (not Memory-side caches) */ static __hwloc_inline int hwloc__obj_type_is_cache(hwloc_obj_type_t type) { @@ -572,12 +573,4 @@ typedef SSIZE_T ssize_t; # endif #endif -#if defined HWLOC_WIN_SYS && !defined __MINGW32__ && !defined(__CYGWIN__) -/* MSVC doesn't support C99 variable-length array */ -#include -#define HWLOC_VLA(_type, _name, _nb) _type *_name = (_type*) _alloca((_nb)*sizeof(_type)) -#else -#define HWLOC_VLA(_type, _name, _nb) _type _name[_nb] -#endif - #endif /* HWLOC_PRIVATE_MISC_H */ diff --git a/src/3rdparty/hwloc/include/private/private.h b/src/3rdparty/hwloc/include/private/private.h index 8e3964ab..5f878937 100644 --- a/src/3rdparty/hwloc/include/private/private.h +++ b/src/3rdparty/hwloc/include/private/private.h @@ -22,11 +22,12 @@ #ifndef HWLOC_PRIVATE_H #define HWLOC_PRIVATE_H -#include -#include -#include -#include -#include +#include "private/autogen/config.h" +#include "hwloc.h" +#include "hwloc/bitmap.h" +#include "private/components.h" +#include "private/misc.h" + #include #ifdef HAVE_UNISTD_H #include @@ -39,7 +40,7 @@ #endif #include -#define HWLOC_TOPOLOGY_ABI 0x20000 /* version of the layout of struct topology */ +#define HWLOC_TOPOLOGY_ABI 0x20100 /* version of the layout of struct topology */ /***************************************************** * WARNING: @@ -67,12 +68,13 @@ struct hwloc_topology { void *adopted_shmem_addr; size_t adopted_shmem_length; -#define HWLOC_NR_SLEVELS 5 +#define HWLOC_NR_SLEVELS 6 #define HWLOC_SLEVEL_NUMANODE 0 #define HWLOC_SLEVEL_BRIDGE 1 #define HWLOC_SLEVEL_PCIDEV 2 #define HWLOC_SLEVEL_OSDEV 3 #define HWLOC_SLEVEL_MISC 4 +#define HWLOC_SLEVEL_MEMCACHE 5 /* order must match negative depth, it's asserted in setup_defaults() */ #define HWLOC_SLEVEL_FROM_DEPTH(x) (HWLOC_TYPE_DEPTH_NUMANODE-(x)) #define HWLOC_SLEVEL_TO_DEPTH(x) (HWLOC_TYPE_DEPTH_NUMANODE-(x)) @@ -86,6 +88,7 @@ struct hwloc_topology { hwloc_bitmap_t allowed_nodeset; struct hwloc_binding_hooks { + /* These are actually rather OS hooks since some of them are not about binding */ int (*set_thisproc_cpubind)(hwloc_topology_t topology, hwloc_const_cpuset_t set, int flags); int (*get_thisproc_cpubind)(hwloc_topology_t topology, hwloc_cpuset_t set, int flags); int (*set_thisthread_cpubind)(hwloc_topology_t topology, hwloc_const_cpuset_t set, int flags); @@ -127,20 +130,35 @@ struct hwloc_topology { int userdata_not_decoded; struct hwloc_internal_distances_s { - hwloc_obj_type_t type; + char *name; /* FIXME: needs an API to set it from user */ + + unsigned id; /* to match the container id field of public distances structure + * not exported to XML, regenerated during _add() + */ + + /* if all objects have the same type, different_types is NULL and unique_type is valid. + * otherwise unique_type is HWLOC_OBJ_TYPE_NONE and different_types contains individual objects types. + */ + hwloc_obj_type_t unique_type; + hwloc_obj_type_t *different_types; + /* add union hwloc_obj_attr_u if we ever support groups */ unsigned nbobjs; - uint64_t *indexes; /* array of OS or GP indexes before we can convert them into objs. */ + uint64_t *indexes; /* array of OS or GP indexes before we can convert them into objs. + * OS indexes for distances covering only PUs or only NUMAnodes. + */ +#define HWLOC_DIST_TYPE_USE_OS_INDEX(_type) ((_type) == HWLOC_OBJ_PU || (_type == HWLOC_OBJ_NUMANODE)) uint64_t *values; /* distance matrices, ordered according to the above indexes/objs array. * distance from i to j is stored in slot i*nbnodes+j. */ unsigned long kind; +#define HWLOC_INTERNAL_DIST_FLAG_OBJS_VALID (1U<<0) /* if the objs array is valid below */ + unsigned iflags; + /* objects are currently stored in physical_index order */ hwloc_obj_t *objs; /* array of objects */ - int objs_are_valid; /* set to 1 if the array objs is still valid, 0 if needs refresh */ - unsigned id; /* to match the container id field of public distances structure */ struct hwloc_internal_distances_s *prev, *next; } *first_dist, *last_dist; unsigned next_dist_id; @@ -153,8 +171,9 @@ struct hwloc_topology { /* list of enabled backends. */ struct hwloc_backend * backends; - struct hwloc_backend * get_pci_busid_cpuset_backend; - unsigned backend_excludes; + struct hwloc_backend * get_pci_busid_cpuset_backend; /* first backend that provides get_pci_busid_cpuset() callback */ + unsigned backend_phases; + unsigned backend_excluded_phases; /* memory allocator for topology objects */ struct hwloc_tma * tma; @@ -176,7 +195,6 @@ struct hwloc_topology { struct hwloc_numanode_attr_s machine_memory; /* pci stuff */ - int need_pci_belowroot_apply_locality; int pci_has_forced_locality; unsigned pci_forced_locality_nr; struct hwloc_pci_forced_locality_s { @@ -185,13 +203,32 @@ struct hwloc_topology { hwloc_bitmap_t cpuset; } * pci_forced_locality; + /* component blacklisting */ + unsigned nr_blacklisted_components; + struct hwloc_topology_forced_component_s { + struct hwloc_disc_component *component; + unsigned phases; + } *blacklisted_components; + + /* FIXME: keep until topo destroy and reuse for finding specific buses */ + struct hwloc_pci_locality_s { + unsigned domain; + unsigned bus_min; + unsigned bus_max; + hwloc_bitmap_t cpuset; + hwloc_obj_t parent; + struct hwloc_pci_locality_s *prev, *next; + } *first_pci_locality, *last_pci_locality; }; extern void hwloc_alloc_root_sets(hwloc_obj_t root); extern void hwloc_setup_pu_level(struct hwloc_topology *topology, unsigned nb_pus); extern int hwloc_get_sysctlbyname(const char *name, int64_t *n); extern int hwloc_get_sysctl(int name[], unsigned namelen, int *n); -extern int hwloc_fallback_nbprocessors(struct hwloc_topology *topology); + +/* returns the number of CPU from the OS (only valid if thissystem) */ +#define HWLOC_FALLBACK_NBPROCESSORS_INCLUDE_OFFLINE 1 /* by default we try to get only the online CPUs */ +extern int hwloc_fallback_nbprocessors(unsigned flags); extern int hwloc__object_cpusets_compare_first(hwloc_obj_t obj1, hwloc_obj_t obj2); extern void hwloc__reorder_children(hwloc_obj_t parent); @@ -208,19 +245,17 @@ extern void hwloc_pci_discovery_init(struct hwloc_topology *topology); extern void hwloc_pci_discovery_prepare(struct hwloc_topology *topology); extern void hwloc_pci_discovery_exit(struct hwloc_topology *topology); +/* Look for an object matching the given domain/bus/func, + * either exactly or return the smallest container bridge + */ +extern struct hwloc_obj * hwloc_pci_find_by_busid(struct hwloc_topology *topology, unsigned domain, unsigned bus, unsigned dev, unsigned func); + /* Look for an object matching complete cpuset exactly, or insert one. * Return NULL on failure. * Return a good fallback (object above) on failure to insert. */ extern hwloc_obj_t hwloc_find_insert_io_parent_by_complete_cpuset(struct hwloc_topology *topology, hwloc_cpuset_t cpuset); -/* Move PCI objects currently attached to the root object ot their actual location. - * Called by the core at the end of hwloc_topology_load(). - * Prior to this call, all PCI objects may be found below the root object. - * After this call and a reconnect of levels, all PCI objects are available through levels. - */ -extern int hwloc_pci_belowroot_apply_locality(struct hwloc_topology *topology); - extern int hwloc__add_info(struct hwloc_info_s **infosp, unsigned *countp, const char *name, const char *value); extern int hwloc__add_info_nodup(struct hwloc_info_s **infosp, unsigned *countp, const char *name, const char *value, int replace); extern int hwloc__move_infos(struct hwloc_info_s **dst_infosp, unsigned *dst_countp, struct hwloc_info_s **src_infosp, unsigned *src_countp); @@ -313,8 +348,8 @@ extern void hwloc_internal_distances_prepare(hwloc_topology_t topology); extern void hwloc_internal_distances_destroy(hwloc_topology_t topology); extern int hwloc_internal_distances_dup(hwloc_topology_t new, hwloc_topology_t old); extern void hwloc_internal_distances_refresh(hwloc_topology_t topology); -extern int hwloc_internal_distances_add(hwloc_topology_t topology, unsigned nbobjs, hwloc_obj_t *objs, uint64_t *values, unsigned long kind, unsigned long flags); -extern int hwloc_internal_distances_add_by_index(hwloc_topology_t topology, hwloc_obj_type_t type, unsigned nbobjs, uint64_t *indexes, uint64_t *values, unsigned long kind, unsigned long flags); +extern int hwloc_internal_distances_add(hwloc_topology_t topology, const char *name, unsigned nbobjs, hwloc_obj_t *objs, uint64_t *values, unsigned long kind, unsigned long flags); +extern int hwloc_internal_distances_add_by_index(hwloc_topology_t topology, const char *name, hwloc_obj_type_t unique_type, hwloc_obj_type_t *different_types, unsigned nbobjs, uint64_t *indexes, uint64_t *values, unsigned long kind, unsigned long flags); extern void hwloc_internal_distances_invalidate_cached_objs(hwloc_topology_t topology); /* encode src buffer into target buffer. @@ -330,13 +365,19 @@ extern int hwloc_encode_to_base64(const char *src, size_t srclength, char *targe */ extern int hwloc_decode_from_base64(char const *src, char *target, size_t targsize); -/* Check whether needle matches the beginning of haystack, at least n, and up - * to a colon or \0 */ -extern int hwloc_namecoloncmp(const char *haystack, const char *needle, size_t n); - /* On some systems, snprintf returns the size of written data, not the actually - * required size. hwloc_snprintf always report the actually required size. */ + * required size. Sometimes it returns -1 on truncation too. + * And sometimes it doesn't like NULL output buffers. + * http://www.gnu.org/software/gnulib/manual/html_node/snprintf.html + * + * hwloc_snprintf behaves properly, but it's a bit overkill on the vast majority + * of platforms, so don't enable it unless really needed. + */ +#ifdef HWLOC_HAVE_CORRECT_SNPRINTF +#define hwloc_snprintf snprintf +#else extern int hwloc_snprintf(char *str, size_t size, const char *format, ...) __hwloc_attribute_format(printf, 3, 4); +#endif /* Return the name of the currently running program, if supported. * If not NULL, must be freed by the caller. @@ -356,7 +397,7 @@ extern char * hwloc_progname(struct hwloc_topology *topology); #define HWLOC_GROUP_KIND_INTEL_MODULE 102 /* no subkind */ #define HWLOC_GROUP_KIND_INTEL_TILE 103 /* no subkind */ #define HWLOC_GROUP_KIND_INTEL_DIE 104 /* no subkind */ -#define HWLOC_GROUP_KIND_S390_BOOK 110 /* no subkind */ +#define HWLOC_GROUP_KIND_S390_BOOK 110 /* subkind 0 is book, subkind 1 is drawer (group of books) */ #define HWLOC_GROUP_KIND_AMD_COMPUTE_UNIT 120 /* no subkind */ /* then, OS-specific groups */ #define HWLOC_GROUP_KIND_SOLARIS_PG_HW_PERF 200 /* subkind is group width */ diff --git a/src/3rdparty/hwloc/include/private/xml.h b/src/3rdparty/hwloc/include/private/xml.h index 7c73384d..f59fca1f 100644 --- a/src/3rdparty/hwloc/include/private/xml.h +++ b/src/3rdparty/hwloc/include/private/xml.h @@ -1,12 +1,12 @@ /* - * Copyright © 2009-2019 Inria. All rights reserved. + * Copyright © 2009-2017 Inria. All rights reserved. * See COPYING in top-level directory. */ #ifndef PRIVATE_XML_H #define PRIVATE_XML_H 1 -#include +#include "hwloc.h" #include @@ -54,7 +54,6 @@ struct hwloc_xml_backend_data_s { unsigned nbnumanodes; hwloc_obj_t first_numanode, last_numanode; /* temporary cousin-list for handling v1distances */ struct hwloc__xml_imported_v1distances_s *first_v1dist, *last_v1dist; - int dont_merge_die_groups; }; /************** diff --git a/src/3rdparty/hwloc/src/base64.c b/src/3rdparty/hwloc/src/base64.c index 7b3e1210..4df67bf9 100644 --- a/src/3rdparty/hwloc/src/base64.c +++ b/src/3rdparty/hwloc/src/base64.c @@ -11,7 +11,7 @@ /* include hwloc's config before anything else * so that extensions and features are properly enabled */ -#include +#include "private/private.h" /* $OpenBSD: base64.c,v 1.5 2006/10/21 09:55:03 otto Exp $ */ diff --git a/src/3rdparty/hwloc/src/bind.c b/src/3rdparty/hwloc/src/bind.c index b3457bc7..0bd85e25 100644 --- a/src/3rdparty/hwloc/src/bind.c +++ b/src/3rdparty/hwloc/src/bind.c @@ -1,15 +1,16 @@ /* * Copyright © 2009 CNRS - * Copyright © 2009-2018 Inria. All rights reserved. + * Copyright © 2009-2019 Inria. All rights reserved. * Copyright © 2009-2010, 2012 Université Bordeaux * Copyright © 2011-2015 Cisco Systems, Inc. All rights reserved. * See COPYING in top-level directory. */ -#include -#include -#include -#include +#include "private/autogen/config.h" +#include "hwloc.h" +#include "private/private.h" +#include "hwloc/helper.h" + #ifdef HAVE_SYS_MMAN_H # include #endif @@ -885,6 +886,8 @@ hwloc_set_binding_hooks(struct hwloc_topology *topology) } else { /* not this system, use dummy binding hooks that do nothing (but don't return ENOSYS) */ hwloc_set_dummy_hooks(&topology->binding_hooks, &topology->support); + + /* Linux has some hooks that also work in this case, but they are not strictly needed yet. */ } /* if not is_thissystem, set_cpubind is fake diff --git a/src/3rdparty/hwloc/src/bitmap.c b/src/3rdparty/hwloc/src/bitmap.c index ea1264af..5fb9cd35 100644 --- a/src/3rdparty/hwloc/src/bitmap.c +++ b/src/3rdparty/hwloc/src/bitmap.c @@ -1,18 +1,18 @@ /* * Copyright © 2009 CNRS - * Copyright © 2009-2017 Inria. All rights reserved. + * Copyright © 2009-2018 Inria. All rights reserved. * Copyright © 2009-2011 Université Bordeaux * Copyright © 2009-2011 Cisco Systems, Inc. All rights reserved. * See COPYING in top-level directory. */ -#include -#include -#include -#include -#include -#include -#include +#include "private/autogen/config.h" +#include "hwloc/autogen/config.h" +#include "hwloc.h" +#include "private/misc.h" +#include "private/private.h" +#include "private/debug.h" +#include "hwloc/bitmap.h" #include #include @@ -766,6 +766,21 @@ int hwloc_bitmap_from_ith_ulong(struct hwloc_bitmap_s *set, unsigned i, unsigned return 0; } +int hwloc_bitmap_from_ulongs(struct hwloc_bitmap_s *set, unsigned nr, const unsigned long *masks) +{ + unsigned j; + + HWLOC__BITMAP_CHECK(set); + + if (hwloc_bitmap_reset_by_ulongs(set, nr) < 0) + return -1; + + for(j=0; julongs[j] = masks[j]; + set->infinite = 0; + return 0; +} + unsigned long hwloc_bitmap_to_ulong(const struct hwloc_bitmap_s *set) { HWLOC__BITMAP_CHECK(set); @@ -780,6 +795,30 @@ unsigned long hwloc_bitmap_to_ith_ulong(const struct hwloc_bitmap_s *set, unsign return HWLOC_SUBBITMAP_READULONG(set, i); } +int hwloc_bitmap_to_ulongs(const struct hwloc_bitmap_s *set, unsigned nr, unsigned long *masks) +{ + unsigned j; + + HWLOC__BITMAP_CHECK(set); + + for(j=0; jinfinite) + return -1; + + last = hwloc_bitmap_last(set); + return (last + HWLOC_BITS_PER_LONG-1)/HWLOC_BITS_PER_LONG; +} + int hwloc_bitmap_only(struct hwloc_bitmap_s * set, unsigned cpu) { unsigned index_ = HWLOC_SUBBITMAP_INDEX(cpu); diff --git a/src/3rdparty/hwloc/src/components.c b/src/3rdparty/hwloc/src/components.c index bd7c00e3..5c2879b6 100644 --- a/src/3rdparty/hwloc/src/components.c +++ b/src/3rdparty/hwloc/src/components.c @@ -1,18 +1,19 @@ /* - * Copyright © 2009-2017 Inria. All rights reserved. + * Copyright © 2009-2019 Inria. All rights reserved. * Copyright © 2012 Université Bordeaux * See COPYING in top-level directory. */ -#include -#include -#include -#include -#include +#include "private/autogen/config.h" +#include "hwloc.h" +#include "private/private.h" +#include "private/xml.h" +#include "private/misc.h" #define HWLOC_COMPONENT_STOP_NAME "stop" #define HWLOC_COMPONENT_EXCLUDE_CHAR '-' #define HWLOC_COMPONENT_SEPS "," +#define HWLOC_COMPONENT_PHASESEP_CHAR ':' /* list of all registered discovery components, sorted by priority, higher priority first. * noos is last because its priority is 0. @@ -232,17 +233,6 @@ hwloc_plugins_init(void) #endif /* HWLOC_HAVE_PLUGINS */ -static const char * -hwloc_disc_component_type_string(hwloc_disc_component_type_t type) -{ - switch (type) { - case HWLOC_DISC_COMPONENT_TYPE_CPU: return "cpu"; - case HWLOC_DISC_COMPONENT_TYPE_GLOBAL: return "global"; - case HWLOC_DISC_COMPONENT_TYPE_MISC: return "misc"; - default: return "**unknown**"; - } -} - static int hwloc_disc_component_register(struct hwloc_disc_component *component, const char *filename) @@ -256,21 +246,26 @@ hwloc_disc_component_register(struct hwloc_disc_component *component, return -1; } if (strchr(component->name, HWLOC_COMPONENT_EXCLUDE_CHAR) + || strchr(component->name, HWLOC_COMPONENT_PHASESEP_CHAR) || strcspn(component->name, HWLOC_COMPONENT_SEPS) != strlen(component->name)) { if (hwloc_components_verbose) fprintf(stderr, "Cannot register discovery component with name `%s' containing reserved characters `%c" HWLOC_COMPONENT_SEPS "'\n", component->name, HWLOC_COMPONENT_EXCLUDE_CHAR); return -1; } - /* check that the component type is valid */ - switch ((unsigned) component->type) { - case HWLOC_DISC_COMPONENT_TYPE_CPU: - case HWLOC_DISC_COMPONENT_TYPE_GLOBAL: - case HWLOC_DISC_COMPONENT_TYPE_MISC: - break; - default: - fprintf(stderr, "Cannot register discovery component `%s' with unknown type %u\n", - component->name, (unsigned) component->type); + + /* check that the component phases are valid */ + if (!component->phases + || (component->phases != HWLOC_DISC_PHASE_GLOBAL + && component->phases & ~(HWLOC_DISC_PHASE_CPU + |HWLOC_DISC_PHASE_MEMORY + |HWLOC_DISC_PHASE_PCI + |HWLOC_DISC_PHASE_IO + |HWLOC_DISC_PHASE_MISC + |HWLOC_DISC_PHASE_ANNOTATE + |HWLOC_DISC_PHASE_TWEAK))) { + fprintf(stderr, "Cannot register discovery component `%s' with invalid phases 0x%x\n", + component->name, component->phases); return -1; } @@ -295,8 +290,8 @@ hwloc_disc_component_register(struct hwloc_disc_component *component, prev = &((*prev)->next); } if (hwloc_components_verbose) - fprintf(stderr, "Registered %s discovery component `%s' with priority %u (%s%s)\n", - hwloc_disc_component_type_string(component->type), component->name, component->priority, + fprintf(stderr, "Registered discovery component `%s' phases 0x%x with priority %u (%s%s)\n", + component->name, component->phases, component->priority, filename ? "from plugin " : "statically build", filename ? filename : ""); prev = &hwloc_disc_components; @@ -310,7 +305,7 @@ hwloc_disc_component_register(struct hwloc_disc_component *component, return 0; } -#include +#include "static-components.h" static void (**hwloc_component_finalize_cbs)(unsigned long); static unsigned hwloc_component_finalize_cb_count; @@ -415,31 +410,152 @@ hwloc_components_init(void) } void -hwloc_backends_init(struct hwloc_topology *topology) +hwloc_topology_components_init(struct hwloc_topology *topology) { + topology->nr_blacklisted_components = 0; + topology->blacklisted_components = NULL; + topology->backends = NULL; - topology->backend_excludes = 0; + topology->backend_phases = 0; + topology->backend_excluded_phases = 0; } +/* look for name among components, ignoring things after `:' */ static struct hwloc_disc_component * -hwloc_disc_component_find(int type /* hwloc_disc_component_type_t or -1 if any */, - const char *name /* name of NULL if any */) +hwloc_disc_component_find(const char *name, const char **endp) { - struct hwloc_disc_component *comp = hwloc_disc_components; + struct hwloc_disc_component *comp; + size_t length; + const char *end = strchr(name, HWLOC_COMPONENT_PHASESEP_CHAR); + if (end) { + length = end-name; + if (endp) + *endp = end+1; + } else { + length = strlen(name); + if (endp) + *endp = NULL; + } + + comp = hwloc_disc_components; while (NULL != comp) { - if ((-1 == type || type == (int) comp->type) - && (NULL == name || !strcmp(name, comp->name))) + if (!strncmp(name, comp->name, length)) return comp; comp = comp->next; } return NULL; } +static unsigned +hwloc_phases_from_string(const char *s) +{ + if (!s) + return ~0U; + if (s[0]<'0' || s[0]>'9') { + if (!strcasecmp(s, "global")) + return HWLOC_DISC_PHASE_GLOBAL; + else if (!strcasecmp(s, "cpu")) + return HWLOC_DISC_PHASE_CPU; + if (!strcasecmp(s, "memory")) + return HWLOC_DISC_PHASE_MEMORY; + if (!strcasecmp(s, "pci")) + return HWLOC_DISC_PHASE_PCI; + if (!strcasecmp(s, "io")) + return HWLOC_DISC_PHASE_IO; + if (!strcasecmp(s, "misc")) + return HWLOC_DISC_PHASE_MISC; + if (!strcasecmp(s, "annotate")) + return HWLOC_DISC_PHASE_ANNOTATE; + if (!strcasecmp(s, "tweak")) + return HWLOC_DISC_PHASE_TWEAK; + return 0; + } + return (unsigned) strtoul(s, NULL, 0); +} + +static int +hwloc_disc_component_blacklist_one(struct hwloc_topology *topology, + const char *name) +{ + struct hwloc_topology_forced_component_s *blacklisted; + struct hwloc_disc_component *comp; + unsigned phases; + unsigned i; + + if (!strcmp(name, "linuxpci") || !strcmp(name, "linuxio")) { + /* replace linuxpci and linuxio with linux (with IO phases) + * for backward compatibility with pre-v2.0 and v2.0 respectively */ + if (hwloc_components_verbose) + fprintf(stderr, "Replacing deprecated component `%s' with `linux' IO phases in blacklisting\n", name); + comp = hwloc_disc_component_find("linux", NULL); + phases = HWLOC_DISC_PHASE_PCI | HWLOC_DISC_PHASE_IO | HWLOC_DISC_PHASE_MISC | HWLOC_DISC_PHASE_ANNOTATE; + + } else { + /* normal lookup */ + const char *end; + comp = hwloc_disc_component_find(name, &end); + phases = hwloc_phases_from_string(end); + } + if (!comp) { + errno = EINVAL; + return -1; + } + + if (hwloc_components_verbose) + fprintf(stderr, "Blacklisting component `%s` phases 0x%x\n", comp->name, phases); + + for(i=0; inr_blacklisted_components; i++) { + if (topology->blacklisted_components[i].component == comp) { + topology->blacklisted_components[i].phases |= phases; + return 0; + } + } + + blacklisted = realloc(topology->blacklisted_components, (topology->nr_blacklisted_components+1)*sizeof(*blacklisted)); + if (!blacklisted) + return -1; + + blacklisted[topology->nr_blacklisted_components].component = comp; + blacklisted[topology->nr_blacklisted_components].phases = phases; + topology->blacklisted_components = blacklisted; + topology->nr_blacklisted_components++; + return 0; +} + +int +hwloc_topology_set_components(struct hwloc_topology *topology, + unsigned long flags, + const char *name) +{ + if (topology->is_loaded) { + errno = EBUSY; + return -1; + } + + if (flags & ~HWLOC_TOPOLOGY_COMPONENTS_FLAG_BLACKLIST) { + errno = EINVAL; + return -1; + } + + /* this flag is strictly required for now */ + if (flags != HWLOC_TOPOLOGY_COMPONENTS_FLAG_BLACKLIST) { + errno = EINVAL; + return -1; + } + + if (!strncmp(name, "all", 3) && name[3] == HWLOC_COMPONENT_PHASESEP_CHAR) { + topology->backend_excluded_phases = hwloc_phases_from_string(name+4); + return 0; + } + + return hwloc_disc_component_blacklist_one(topology, name); +} + /* used by set_xml(), set_synthetic(), ... environment variables, ... to force the first backend */ int hwloc_disc_component_force_enable(struct hwloc_topology *topology, int envvar_forced, - int type, const char *name, + const char *name, const void *data1, const void *data2, const void *data3) { struct hwloc_disc_component *comp; @@ -450,18 +566,28 @@ hwloc_disc_component_force_enable(struct hwloc_topology *topology, return -1; } - comp = hwloc_disc_component_find(type, name); + comp = hwloc_disc_component_find(name, NULL); if (!comp) { errno = ENOSYS; return -1; } - backend = comp->instantiate(comp, data1, data2, data3); + backend = comp->instantiate(topology, comp, 0U /* force-enabled don't get any phase blacklisting */, + data1, data2, data3); if (backend) { + int err; backend->envvar_forced = envvar_forced; if (topology->backends) hwloc_backends_disable_all(topology); - return hwloc_backend_enable(topology, backend); + err = hwloc_backend_enable(backend); + + if (comp->phases == HWLOC_DISC_PHASE_GLOBAL) { + char *env = getenv("HWLOC_ANNOTATE_GLOBAL_COMPONENTS"); + if (env && atoi(env)) + topology->backend_excluded_phases &= ~HWLOC_DISC_PHASE_ANNOTATE; + } + + return err; } else return -1; } @@ -469,29 +595,32 @@ hwloc_disc_component_force_enable(struct hwloc_topology *topology, static int hwloc_disc_component_try_enable(struct hwloc_topology *topology, struct hwloc_disc_component *comp, - const char *comparg, - int envvar_forced) + int envvar_forced, + unsigned blacklisted_phases) { struct hwloc_backend *backend; - if (topology->backend_excludes & comp->type) { + if (!(comp->phases & ~(topology->backend_excluded_phases | blacklisted_phases))) { + /* all this backend phases are already excluded, exclude the backend entirely */ if (hwloc_components_verbose) /* do not warn if envvar_forced since system-wide HWLOC_COMPONENTS must be silently ignored after set_xml() etc. */ - fprintf(stderr, "Excluding %s discovery component `%s', conflicts with excludes 0x%x\n", - hwloc_disc_component_type_string(comp->type), comp->name, topology->backend_excludes); + fprintf(stderr, "Excluding discovery component `%s' phases 0x%x, conflicts with excludes 0x%x\n", + comp->name, comp->phases, topology->backend_excluded_phases); return -1; } - backend = comp->instantiate(comp, comparg, NULL, NULL); + backend = comp->instantiate(topology, comp, topology->backend_excluded_phases | blacklisted_phases, + NULL, NULL, NULL); if (!backend) { if (hwloc_components_verbose || envvar_forced) fprintf(stderr, "Failed to instantiate discovery component `%s'\n", comp->name); return -1; } + backend->phases &= ~blacklisted_phases; backend->envvar_forced = envvar_forced; - return hwloc_backend_enable(topology, backend); + return hwloc_backend_enable(backend); } void @@ -502,11 +631,12 @@ hwloc_disc_components_enable_others(struct hwloc_topology *topology) int tryall = 1; const char *_env; char *env; /* we'll to modify the env value, so duplicate it */ + unsigned i; _env = getenv("HWLOC_COMPONENTS"); env = _env ? strdup(_env) : NULL; - /* enable explicitly listed components */ + /* blacklist disabled components */ if (env) { char *curenv = env; size_t s; @@ -516,21 +646,41 @@ hwloc_disc_components_enable_others(struct hwloc_topology *topology) if (s) { char c; - /* replace linuxpci with linuxio for backward compatibility with pre-v2.0 */ - if (!strncmp(curenv, "linuxpci", 8) && s == 8) { - curenv[5] = 'i'; - curenv[6] = 'o'; - curenv[7] = *HWLOC_COMPONENT_SEPS; - } else if (curenv[0] == HWLOC_COMPONENT_EXCLUDE_CHAR && !strncmp(curenv+1, "linuxpci", 8) && s == 9) { - curenv[6] = 'i'; - curenv[7] = 'o'; - curenv[8] = *HWLOC_COMPONENT_SEPS; - /* skip this name, it's a negated one */ + if (curenv[0] != HWLOC_COMPONENT_EXCLUDE_CHAR) goto nextname; - } - if (curenv[0] == HWLOC_COMPONENT_EXCLUDE_CHAR) - goto nextname; + /* save the last char and replace with \0 */ + c = curenv[s]; + curenv[s] = '\0'; + + /* blacklist it, and just ignore failures to allocate */ + hwloc_disc_component_blacklist_one(topology, curenv+1); + + /* remove that blacklisted name from the string */ + for(i=0; inr_blacklisted_components; i++) + if (comp == topology->blacklisted_components[i].component) { + blacklisted_phases = topology->blacklisted_components[i].phases; + break; + } + if (comp->phases & ~blacklisted_phases) + hwloc_disc_component_try_enable(topology, comp, 1 /* envvar forced */, blacklisted_phases); } else { - fprintf(stderr, "Cannot find discovery component `%s'\n", curenv); + fprintf(stderr, "Cannot find discovery component `%s'\n", name); } /* restore chars (the second loop below needs env to be unmodified) */ curenv[s] = c; } -nextname: curenv += s; if (*curenv) /* Skip comma */ @@ -566,26 +729,24 @@ nextname: if (tryall) { comp = hwloc_disc_components; while (NULL != comp) { + unsigned blacklisted_phases = 0U; if (!comp->enabled_by_default) goto nextcomp; - /* check if this component was explicitly excluded in env */ - if (env) { - char *curenv = env; - while (*curenv) { - size_t s = strcspn(curenv, HWLOC_COMPONENT_SEPS); - if (curenv[0] == HWLOC_COMPONENT_EXCLUDE_CHAR && !strncmp(curenv+1, comp->name, s-1) && strlen(comp->name) == s-1) { - if (hwloc_components_verbose) - fprintf(stderr, "Excluding %s discovery component `%s' because of HWLOC_COMPONENTS environment variable\n", - hwloc_disc_component_type_string(comp->type), comp->name); - goto nextcomp; - } - curenv += s; - if (*curenv) - /* Skip comma */ - curenv++; + /* check if this component was blacklisted by the application */ + for(i=0; inr_blacklisted_components; i++) + if (comp == topology->blacklisted_components[i].component) { + blacklisted_phases = topology->blacklisted_components[i].phases; + break; } + + if (!(comp->phases & ~blacklisted_phases)) { + if (hwloc_components_verbose) + fprintf(stderr, "Excluding blacklisted discovery component `%s' phases 0x%x\n", + comp->name, comp->phases); + goto nextcomp; } - hwloc_disc_component_try_enable(topology, comp, NULL, 0 /* defaults, not envvar forced */); + + hwloc_disc_component_try_enable(topology, comp, 0 /* defaults, not envvar forced */, blacklisted_phases); nextcomp: comp = comp->next; } @@ -597,7 +758,7 @@ nextcomp: backend = topology->backends; fprintf(stderr, "Final list of enabled discovery components: "); while (backend != NULL) { - fprintf(stderr, "%s%s", first ? "" : ",", backend->component->name); + fprintf(stderr, "%s%s(0x%x)", first ? "" : ",", backend->component->name, backend->phases); backend = backend->next; first = 0; } @@ -638,7 +799,8 @@ hwloc_components_fini(void) } struct hwloc_backend * -hwloc_backend_alloc(struct hwloc_disc_component *component) +hwloc_backend_alloc(struct hwloc_topology *topology, + struct hwloc_disc_component *component) { struct hwloc_backend * backend = malloc(sizeof(*backend)); if (!backend) { @@ -646,6 +808,12 @@ hwloc_backend_alloc(struct hwloc_disc_component *component) return NULL; } backend->component = component; + backend->topology = topology; + /* filter-out component phases that are excluded */ + backend->phases = component->phases & ~topology->backend_excluded_phases; + if (backend->phases != component->phases && hwloc_components_verbose) + fprintf(stderr, "Trying discovery component `%s' with phases 0x%x instead of 0x%x\n", + component->name, backend->phases, component->phases); backend->flags = 0; backend->discover = NULL; backend->get_pci_busid_cpuset = NULL; @@ -665,14 +833,15 @@ hwloc_backend_disable(struct hwloc_backend *backend) } int -hwloc_backend_enable(struct hwloc_topology *topology, struct hwloc_backend *backend) +hwloc_backend_enable(struct hwloc_backend *backend) { + struct hwloc_topology *topology = backend->topology; struct hwloc_backend **pprev; /* check backend flags */ if (backend->flags) { - fprintf(stderr, "Cannot enable %s discovery component `%s' with unknown flags %lx\n", - hwloc_disc_component_type_string(backend->component->type), backend->component->name, backend->flags); + fprintf(stderr, "Cannot enable discovery component `%s' phases 0x%x with unknown flags %lx\n", + backend->component->name, backend->component->phases, backend->flags); return -1; } @@ -681,8 +850,8 @@ hwloc_backend_enable(struct hwloc_topology *topology, struct hwloc_backend *back while (NULL != *pprev) { if ((*pprev)->component == backend->component) { if (hwloc_components_verbose) - fprintf(stderr, "Cannot enable %s discovery component `%s' twice\n", - hwloc_disc_component_type_string(backend->component->type), backend->component->name); + fprintf(stderr, "Cannot enable discovery component `%s' phases 0x%x twice\n", + backend->component->name, backend->component->phases); hwloc_backend_disable(backend); errno = EBUSY; return -1; @@ -691,8 +860,8 @@ hwloc_backend_enable(struct hwloc_topology *topology, struct hwloc_backend *back } if (hwloc_components_verbose) - fprintf(stderr, "Enabling %s discovery component `%s'\n", - hwloc_disc_component_type_string(backend->component->type), backend->component->name); + fprintf(stderr, "Enabling discovery component `%s' with phases 0x%x (among 0x%x)\n", + backend->component->name, backend->phases, backend->component->phases); /* enqueue at the end */ pprev = &topology->backends; @@ -701,8 +870,8 @@ hwloc_backend_enable(struct hwloc_topology *topology, struct hwloc_backend *back backend->next = *pprev; *pprev = backend; - backend->topology = topology; - topology->backend_excludes |= backend->component->excludes; + topology->backend_phases |= backend->component->phases; + topology->backend_excluded_phases |= backend->component->excluded_phases; return 0; } @@ -712,7 +881,7 @@ hwloc_backends_is_thissystem(struct hwloc_topology *topology) struct hwloc_backend *backend; const char *local_env; - /* Apply is_thissystem topology flag before we enforce envvar backends. + /* * If the application changed the backend with set_foo(), * it may use set_flags() update the is_thissystem flag here. * If it changes the backend with environment variables below, @@ -775,11 +944,20 @@ hwloc_backends_disable_all(struct hwloc_topology *topology) while (NULL != (backend = topology->backends)) { struct hwloc_backend *next = backend->next; if (hwloc_components_verbose) - fprintf(stderr, "Disabling %s discovery component `%s'\n", - hwloc_disc_component_type_string(backend->component->type), backend->component->name); + fprintf(stderr, "Disabling discovery component `%s'\n", + backend->component->name); hwloc_backend_disable(backend); topology->backends = next; } topology->backends = NULL; - topology->backend_excludes = 0; + topology->backend_excluded_phases = 0; +} + +void +hwloc_topology_components_fini(struct hwloc_topology *topology) +{ + /* hwloc_backends_disable_all() must have been called earlier */ + assert(!topology->backends); + + free(topology->blacklisted_components); } diff --git a/src/3rdparty/hwloc/src/diff.c b/src/3rdparty/hwloc/src/diff.c index 00811a7b..7794358b 100644 --- a/src/3rdparty/hwloc/src/diff.c +++ b/src/3rdparty/hwloc/src/diff.c @@ -1,11 +1,11 @@ /* - * Copyright © 2013-2018 Inria. All rights reserved. + * Copyright © 2013-2019 Inria. All rights reserved. * See COPYING in top-level directory. */ -#include -#include -#include +#include "private/autogen/config.h" +#include "private/private.h" +#include "private/misc.h" int hwloc_topology_diff_destroy(hwloc_topology_diff_t diff) { @@ -351,7 +351,8 @@ int hwloc_topology_diff_build(hwloc_topology_t topo1, err = 1; break; } - if (dist1->type != dist2->type + if (dist1->unique_type != dist2->unique_type + || dist1->different_types || dist2->different_types /* too lazy to support this case */ || dist1->nbobjs != dist2->nbobjs || dist1->kind != dist2->kind || memcmp(dist1->values, dist2->values, dist1->nbobjs * dist1->nbobjs * sizeof(*dist1->values))) { @@ -463,6 +464,10 @@ int hwloc_topology_diff_apply(hwloc_topology_t topology, errno = EINVAL; return -1; } + if (topology->adopted_shmem_addr) { + errno = EPERM; + return -1; + } if (flags & ~HWLOC_TOPOLOGY_DIFF_APPLY_REVERSE) { errno = EINVAL; diff --git a/src/3rdparty/hwloc/src/distances.c b/src/3rdparty/hwloc/src/distances.c index f0b91f01..9e56a969 100644 --- a/src/3rdparty/hwloc/src/distances.c +++ b/src/3rdparty/hwloc/src/distances.c @@ -1,19 +1,22 @@ /* - * Copyright © 2010-2018 Inria. All rights reserved. + * Copyright © 2010-2019 Inria. All rights reserved. * Copyright © 2011-2012 Université Bordeaux * Copyright © 2011 Cisco Systems, Inc. All rights reserved. * See COPYING in top-level directory. */ -#include -#include -#include -#include -#include +#include "private/autogen/config.h" +#include "hwloc.h" +#include "private/private.h" +#include "private/debug.h" +#include "private/misc.h" #include #include +static struct hwloc_internal_distances_s * +hwloc__internal_distances_from_public(hwloc_topology_t topology, struct hwloc_distances_s *distances); + /****************************************************** * Global init, prepare, destroy, dup */ @@ -70,6 +73,8 @@ void hwloc_internal_distances_prepare(struct hwloc_topology *topology) static void hwloc_internal_distances_free(struct hwloc_internal_distances_s *dist) { + free(dist->name); + free(dist->different_types); free(dist->indexes); free(dist->objs); free(dist->values); @@ -96,15 +101,35 @@ static int hwloc_internal_distances_dup_one(struct hwloc_topology *new, struct h newdist = hwloc_tma_malloc(tma, sizeof(*newdist)); if (!newdist) return -1; + if (olddist->name) { + newdist->name = hwloc_tma_strdup(tma, olddist->name); + if (!newdist->name) { + assert(!tma || !tma->dontfree); /* this tma cannot fail to allocate */ + hwloc_internal_distances_free(newdist); + return -1; + } + } else { + newdist->name = NULL; + } - newdist->type = olddist->type; + if (olddist->different_types) { + newdist->different_types = hwloc_tma_malloc(tma, nbobjs * sizeof(*newdist->different_types)); + if (!newdist->different_types) { + assert(!tma || !tma->dontfree); /* this tma cannot fail to allocate */ + hwloc_internal_distances_free(newdist); + return -1; + } + memcpy(newdist->different_types, olddist->different_types, nbobjs * sizeof(*newdist->different_types)); + } else + newdist->different_types = NULL; + newdist->unique_type = olddist->unique_type; newdist->nbobjs = nbobjs; newdist->kind = olddist->kind; newdist->id = olddist->id; newdist->indexes = hwloc_tma_malloc(tma, nbobjs * sizeof(*newdist->indexes)); newdist->objs = hwloc_tma_calloc(tma, nbobjs * sizeof(*newdist->objs)); - newdist->objs_are_valid = 0; + newdist->iflags = olddist->iflags & ~HWLOC_INTERNAL_DIST_FLAG_OBJS_VALID; /* must be revalidated after dup() */ newdist->values = hwloc_tma_malloc(tma, nbobjs*nbobjs * sizeof(*newdist->values)); if (!newdist->indexes || !newdist->objs || !newdist->values) { assert(!tma || !tma->dontfree); /* this tma cannot fail to allocate */ @@ -150,6 +175,10 @@ int hwloc_distances_remove(hwloc_topology_t topology) errno = EINVAL; return -1; } + if (topology->adopted_shmem_addr) { + errno = EPERM; + return -1; + } hwloc_internal_distances_destroy(topology); return 0; } @@ -163,6 +192,10 @@ int hwloc_distances_remove_by_depth(hwloc_topology_t topology, int depth) errno = EINVAL; return -1; } + if (topology->adopted_shmem_addr) { + errno = EPERM; + return -1; + } /* switch back to types since we don't support groups for now */ type = hwloc_get_depth_type(topology, depth); @@ -174,7 +207,7 @@ int hwloc_distances_remove_by_depth(hwloc_topology_t topology, int depth) next = topology->first_dist; while ((dist = next) != NULL) { next = dist->next; - if (dist->type == type) { + if (dist->unique_type == type) { if (next) next->prev = dist->prev; else @@ -190,6 +223,27 @@ int hwloc_distances_remove_by_depth(hwloc_topology_t topology, int depth) return 0; } +int hwloc_distances_release_remove(hwloc_topology_t topology, + struct hwloc_distances_s *distances) +{ + struct hwloc_internal_distances_s *dist = hwloc__internal_distances_from_public(topology, distances); + if (!dist) { + errno = EINVAL; + return -1; + } + if (dist->prev) + dist->prev->next = dist->next; + else + topology->first_dist = dist->next; + if (dist->next) + dist->next->prev = dist->prev; + else + topology->last_dist = dist->prev; + hwloc_internal_distances_free(dist); + hwloc_distances_release(topology, distances); + return 0; +} + /****************************************************** * Add distances to the topology */ @@ -201,17 +255,34 @@ hwloc__groups_by_distances(struct hwloc_topology *topology, unsigned nbobjs, str * the caller gives us the distances and objs pointers, we'll free them later. */ static int -hwloc_internal_distances__add(hwloc_topology_t topology, - hwloc_obj_type_t type, unsigned nbobjs, hwloc_obj_t *objs, uint64_t *indexes, uint64_t *values, - unsigned long kind) +hwloc_internal_distances__add(hwloc_topology_t topology, const char *name, + hwloc_obj_type_t unique_type, hwloc_obj_type_t *different_types, + unsigned nbobjs, hwloc_obj_t *objs, uint64_t *indexes, uint64_t *values, + unsigned long kind, unsigned iflags) { - struct hwloc_internal_distances_s *dist = calloc(1, sizeof(*dist)); + struct hwloc_internal_distances_s *dist; + + if (different_types) { + kind |= HWLOC_DISTANCES_KIND_HETEROGENEOUS_TYPES; /* the user isn't forced to give it */ + } else if (kind & HWLOC_DISTANCES_KIND_HETEROGENEOUS_TYPES) { + errno = EINVAL; + goto err; + } + + dist = calloc(1, sizeof(*dist)); if (!dist) goto err; - dist->type = type; + if (name) + dist->name = strdup(name); /* ignore failure */ + + dist->unique_type = unique_type; + dist->different_types = different_types; dist->nbobjs = nbobjs; dist->kind = kind; + dist->iflags = iflags; + + assert(!!(iflags & HWLOC_INTERNAL_DIST_FLAG_OBJS_VALID) == !!objs); if (!objs) { assert(indexes); @@ -220,18 +291,16 @@ hwloc_internal_distances__add(hwloc_topology_t topology, dist->objs = calloc(nbobjs, sizeof(hwloc_obj_t)); if (!dist->objs) goto err_with_dist; - dist->objs_are_valid = 0; } else { unsigned i; assert(!indexes); /* we only have objs, generate the indexes arrays so that we can refresh objs later */ dist->objs = objs; - dist->objs_are_valid = 1; dist->indexes = malloc(nbobjs * sizeof(*dist->indexes)); if (!dist->indexes) goto err_with_dist; - if (dist->type == HWLOC_OBJ_PU || dist->type == HWLOC_OBJ_NUMANODE) { + if (HWLOC_DIST_TYPE_USE_OS_INDEX(dist->unique_type)) { for(i=0; iindexes[i] = objs[i]->os_index; } else { @@ -256,16 +325,19 @@ hwloc_internal_distances__add(hwloc_topology_t topology, err_with_dist: free(dist); err: + free(different_types); free(objs); free(indexes); free(values); return -1; } -int hwloc_internal_distances_add_by_index(hwloc_topology_t topology, - hwloc_obj_type_t type, unsigned nbobjs, uint64_t *indexes, uint64_t *values, +int hwloc_internal_distances_add_by_index(hwloc_topology_t topology, const char *name, + hwloc_obj_type_t unique_type, hwloc_obj_type_t *different_types, unsigned nbobjs, uint64_t *indexes, uint64_t *values, unsigned long kind, unsigned long flags) { + unsigned iflags = 0; /* objs not valid */ + if (nbobjs < 2) { errno = EINVAL; goto err; @@ -279,24 +351,71 @@ int hwloc_internal_distances_add_by_index(hwloc_topology_t topology, goto err; } - return hwloc_internal_distances__add(topology, type, nbobjs, NULL, indexes, values, kind); + return hwloc_internal_distances__add(topology, name, unique_type, different_types, nbobjs, NULL, indexes, values, kind, iflags); err: free(indexes); free(values); + free(different_types); return -1; } -int hwloc_internal_distances_add(hwloc_topology_t topology, +static void +hwloc_internal_distances_restrict(hwloc_obj_t *objs, + uint64_t *indexes, + uint64_t *values, + unsigned nbobjs, unsigned disappeared); + +int hwloc_internal_distances_add(hwloc_topology_t topology, const char *name, unsigned nbobjs, hwloc_obj_t *objs, uint64_t *values, unsigned long kind, unsigned long flags) { + hwloc_obj_type_t unique_type, *different_types; + unsigned i, disappeared = 0; + unsigned iflags = HWLOC_INTERNAL_DIST_FLAG_OBJS_VALID; + if (nbobjs < 2) { errno = EINVAL; goto err; } - if (topology->grouping && (flags & HWLOC_DISTANCES_ADD_FLAG_GROUP)) { + /* is there any NULL object? (useful in case of problem during insert in backends) */ + for(i=0; itype; + for(i=1; itype != unique_type) { + unique_type = HWLOC_OBJ_TYPE_NONE; + break; + } + if (unique_type == HWLOC_OBJ_TYPE_NONE) { + /* heterogeneous types */ + different_types = malloc(nbobjs * sizeof(*different_types)); + if (!different_types) + goto err; + for(i=0; itype; + + } else { + /* homogeneous types */ + different_types = NULL; + } + + if (topology->grouping && (flags & HWLOC_DISTANCES_ADD_FLAG_GROUP) && !different_types) { float full_accuracy = 0.f; float *accuracies; unsigned nbaccuracies; @@ -310,8 +429,8 @@ int hwloc_internal_distances_add(hwloc_topology_t topology, } if (topology->grouping_verbose) { - unsigned i, j; - int gp = (objs[0]->type != HWLOC_OBJ_NUMANODE && objs[0]->type != HWLOC_OBJ_PU); + unsigned j; + int gp = !HWLOC_DIST_TYPE_USE_OS_INDEX(unique_type); fprintf(stderr, "Trying to group objects using distance matrix:\n"); fprintf(stderr, "%s", gp ? "gp_index" : "os_index"); for(j=0; jtype, nbobjs, objs, NULL, values, kind); + return hwloc_internal_distances__add(topology, name, unique_type, different_types, nbobjs, objs, NULL, values, kind, iflags); err: free(objs); @@ -348,7 +467,6 @@ int hwloc_distances_add(hwloc_topology_t topology, unsigned nbobjs, hwloc_obj_t *objs, hwloc_uint64_t *values, unsigned long kind, unsigned long flags) { - hwloc_obj_type_t type; unsigned i; uint64_t *_values; hwloc_obj_t *_objs; @@ -358,6 +476,10 @@ int hwloc_distances_add(hwloc_topology_t topology, errno = EINVAL; return -1; } + if (topology->adopted_shmem_addr) { + errno = EPERM; + return -1; + } if ((kind & ~HWLOC_DISTANCES_KIND_ALL) || hwloc_weight_long(kind & HWLOC_DISTANCES_KIND_FROM_ALL) != 1 || hwloc_weight_long(kind & HWLOC_DISTANCES_KIND_MEANS_ALL) != 1 @@ -368,15 +490,8 @@ int hwloc_distances_add(hwloc_topology_t topology, /* no strict need to check for duplicates, things shouldn't break */ - type = objs[0]->type; - if (type == HWLOC_OBJ_GROUP) { - /* not supported yet, would require we save the subkind together with the type. */ - errno = EINVAL; - return -1; - } - for(i=1; itype != type) { + if (!objs[i]) { errno = EINVAL; return -1; } @@ -389,7 +504,7 @@ int hwloc_distances_add(hwloc_topology_t topology, memcpy(_objs, objs, nbobjs*sizeof(hwloc_obj_t)); memcpy(_values, values, nbobjs*nbobjs*sizeof(*_values)); - err = hwloc_internal_distances_add(topology, nbobjs, _objs, _values, kind, flags); + err = hwloc_internal_distances_add(topology, NULL, nbobjs, _objs, _values, kind, flags); if (err < 0) goto out; /* _objs and _values freed in hwloc_internal_distances_add() */ @@ -409,9 +524,9 @@ int hwloc_distances_add(hwloc_topology_t topology, * Refresh objects in distances */ -static hwloc_obj_t hwloc_find_obj_by_type_and_gp_index(hwloc_topology_t topology, hwloc_obj_type_t type, uint64_t gp_index) +static hwloc_obj_t hwloc_find_obj_by_depth_and_gp_index(hwloc_topology_t topology, unsigned depth, uint64_t gp_index) { - hwloc_obj_t obj = hwloc_get_obj_by_type(topology, type, 0); + hwloc_obj_t obj = hwloc_get_obj_by_depth(topology, depth, 0); while (obj) { if (obj->gp_index == gp_index) return obj; @@ -420,12 +535,31 @@ static hwloc_obj_t hwloc_find_obj_by_type_and_gp_index(hwloc_topology_t topology return NULL; } +static hwloc_obj_t hwloc_find_obj_by_type_and_gp_index(hwloc_topology_t topology, hwloc_obj_type_t type, uint64_t gp_index) +{ + int depth = hwloc_get_type_depth(topology, type); + if (depth == HWLOC_TYPE_DEPTH_UNKNOWN) + return NULL; + if (depth == HWLOC_TYPE_DEPTH_MULTIPLE) { + int topodepth = hwloc_topology_get_depth(topology); + for(depth=0; depthnbobjs; unsigned i, newi; unsigned j, newj; @@ -433,7 +567,7 @@ hwloc_internal_distances_restrict(struct hwloc_internal_distances_s *dist, if (objs[i]) { for(j=0, newj=0; jvalues[newi*(nbobjs-disappeared)+newj] = dist->values[i*nbobjs+j]; + values[newi*(nbobjs-disappeared)+newj] = values[i*nbobjs+j]; newj++; } newi++; @@ -442,25 +576,25 @@ hwloc_internal_distances_restrict(struct hwloc_internal_distances_s *dist, for(i=0, newi=0; iindexes[newi] = dist->indexes[i]; + if (indexes) + indexes[newi] = indexes[i]; newi++; } - - dist->nbobjs -= disappeared; } static int hwloc_internal_distances_refresh_one(hwloc_topology_t topology, struct hwloc_internal_distances_s *dist) { - hwloc_obj_type_t type = dist->type; + hwloc_obj_type_t unique_type = dist->unique_type; + hwloc_obj_type_t *different_types = dist->different_types; unsigned nbobjs = dist->nbobjs; hwloc_obj_t *objs = dist->objs; uint64_t *indexes = dist->indexes; unsigned disappeared = 0; unsigned i; - if (dist->objs_are_valid) + if (dist->iflags & HWLOC_INTERNAL_DIST_FLAG_OBJS_VALID) return 0; for(i=0; iindexes, dist->values, nbobjs, disappeared); + dist->nbobjs -= disappeared; + } - dist->objs_are_valid = 1; + dist->iflags |= HWLOC_INTERNAL_DIST_FLAG_OBJS_VALID; return 0; } @@ -520,32 +660,64 @@ hwloc_internal_distances_invalidate_cached_objs(hwloc_topology_t topology) { struct hwloc_internal_distances_s *dist; for(dist = topology->first_dist; dist; dist = dist->next) - dist->objs_are_valid = 0; + dist->iflags &= ~HWLOC_INTERNAL_DIST_FLAG_OBJS_VALID; } /****************************************************** * User API for getting distances */ +/* what we actually allocate for user queries, even if we only + * return the distances part of it. + */ +struct hwloc_distances_container_s { + unsigned id; + struct hwloc_distances_s distances; +}; + +#define HWLOC_DISTANCES_CONTAINER_OFFSET ((char*)&((struct hwloc_distances_container_s*)NULL)->distances - (char*)NULL) +#define HWLOC_DISTANCES_CONTAINER(_d) (struct hwloc_distances_container_s *) ( ((char*)_d) - HWLOC_DISTANCES_CONTAINER_OFFSET ) + +static struct hwloc_internal_distances_s * +hwloc__internal_distances_from_public(hwloc_topology_t topology, struct hwloc_distances_s *distances) +{ + struct hwloc_distances_container_s *cont = HWLOC_DISTANCES_CONTAINER(distances); + struct hwloc_internal_distances_s *dist; + for(dist = topology->first_dist; dist; dist = dist->next) + if (dist->id == cont->id) + return dist; + return NULL; +} + void hwloc_distances_release(hwloc_topology_t topology __hwloc_attribute_unused, struct hwloc_distances_s *distances) { + struct hwloc_distances_container_s *cont = HWLOC_DISTANCES_CONTAINER(distances); free(distances->values); free(distances->objs); - free(distances); + free(cont); +} + +const char * +hwloc_distances_get_name(hwloc_topology_t topology, struct hwloc_distances_s *distances) +{ + struct hwloc_internal_distances_s *dist = hwloc__internal_distances_from_public(topology, distances); + return dist ? dist->name : NULL; } static struct hwloc_distances_s * hwloc_distances_get_one(hwloc_topology_t topology __hwloc_attribute_unused, struct hwloc_internal_distances_s *dist) { + struct hwloc_distances_container_s *cont; struct hwloc_distances_s *distances; unsigned nbobjs; - distances = malloc(sizeof(*distances)); - if (!distances) + cont = malloc(sizeof(*cont)); + if (!cont) return NULL; + distances = &cont->distances; nbobjs = distances->nbobjs = dist->nbobjs; @@ -560,18 +732,20 @@ hwloc_distances_get_one(hwloc_topology_t topology __hwloc_attribute_unused, memcpy(distances->values, dist->values, nbobjs*nbobjs*sizeof(*distances->values)); distances->kind = dist->kind; + + cont->id = dist->id; return distances; out_with_objs: free(distances->objs); out: - free(distances); + free(cont); return NULL; } static int hwloc__distances_get(hwloc_topology_t topology, - hwloc_obj_type_t type, + const char *name, hwloc_obj_type_t type, unsigned *nrp, struct hwloc_distances_s **distancesp, unsigned long kind, unsigned long flags __hwloc_attribute_unused) { @@ -602,7 +776,10 @@ hwloc__distances_get(hwloc_topology_t topology, unsigned long kind_from = kind & HWLOC_DISTANCES_KIND_FROM_ALL; unsigned long kind_means = kind & HWLOC_DISTANCES_KIND_MEANS_ALL; - if (type != HWLOC_OBJ_TYPE_NONE && type != dist->type) + if (name && (!dist->name || strcmp(name, dist->name))) + continue; + + if (type != HWLOC_OBJ_TYPE_NONE && type != dist->unique_type) continue; if (kind_from && !(kind_from & dist->kind)) @@ -640,7 +817,7 @@ hwloc_distances_get(hwloc_topology_t topology, return -1; } - return hwloc__distances_get(topology, HWLOC_OBJ_TYPE_NONE, nrp, distancesp, kind, flags); + return hwloc__distances_get(topology, NULL, HWLOC_OBJ_TYPE_NONE, nrp, distancesp, kind, flags); } int @@ -655,14 +832,40 @@ hwloc_distances_get_by_depth(hwloc_topology_t topology, int depth, return -1; } - /* switch back to types since we don't support groups for now */ + /* FIXME: passing the depth of a group level may return group distances at a different depth */ type = hwloc_get_depth_type(topology, depth); if (type == (hwloc_obj_type_t)-1) { errno = EINVAL; return -1; } - return hwloc__distances_get(topology, type, nrp, distancesp, kind, flags); + return hwloc__distances_get(topology, NULL, type, nrp, distancesp, kind, flags); +} + +int +hwloc_distances_get_by_name(hwloc_topology_t topology, const char *name, + unsigned *nrp, struct hwloc_distances_s **distancesp, + unsigned long flags) +{ + if (flags || !topology->is_loaded) { + errno = EINVAL; + return -1; + } + + return hwloc__distances_get(topology, name, HWLOC_OBJ_TYPE_NONE, nrp, distancesp, HWLOC_DISTANCES_KIND_ALL, flags); +} + +int +hwloc_distances_get_by_type(hwloc_topology_t topology, hwloc_obj_type_t type, + unsigned *nrp, struct hwloc_distances_s **distancesp, + unsigned long kind, unsigned long flags) +{ + if (flags || !topology->is_loaded) { + errno = EINVAL; + return -1; + } + + return hwloc__distances_get(topology, NULL, type, nrp, distancesp, kind, flags); } /****************************************************** @@ -823,10 +1026,14 @@ hwloc__groups_by_distances(struct hwloc_topology *topology, float *accuracies, int needcheck) { - HWLOC_VLA(unsigned, groupids, nbobjs); + unsigned *groupids; unsigned nbgroups = 0; unsigned i,j; int verbose = topology->grouping_verbose; + hwloc_obj_t *groupobjs; + unsigned * groupsizes; + uint64_t *groupvalues; + unsigned failed = 0; if (nbobjs <= 2) return; @@ -836,6 +1043,10 @@ hwloc__groups_by_distances(struct hwloc_topology *topology, /* TODO hwloc__find_groups_by_max_distance() for bandwidth */ return; + groupids = malloc(nbobjs * sizeof(*groupids)); + if (!groupids) + return; + for(i=0; i -#include -#include +#include "private/autogen/config.h" +#include "private/private.h" +#include "private/misc.h" #include #ifdef HAVE_SYS_UTSNAME_H @@ -28,6 +28,7 @@ extern char *program_invocation_name; extern char *__progname; #endif +#ifndef HWLOC_HAVE_CORRECT_SNPRINTF int hwloc_snprintf(char *str, size_t size, const char *format, ...) { int ret; @@ -77,21 +78,7 @@ int hwloc_snprintf(char *str, size_t size, const char *format, ...) return ret; } - -int hwloc_namecoloncmp(const char *haystack, const char *needle, size_t n) -{ - size_t i = 0; - while (*haystack && *haystack != ':') { - int ha = *haystack++; - int low_h = tolower(ha); - int ne = *needle++; - int low_n = tolower(ne); - if (low_h != low_n) - return 1; - i++; - } - return i < n; -} +#endif void hwloc_add_uname_info(struct hwloc_topology *topology __hwloc_attribute_unused, void *cached_uname __hwloc_attribute_unused) diff --git a/src/3rdparty/hwloc/src/pci-common.c b/src/3rdparty/hwloc/src/pci-common.c index 00f08a9e..deca5cce 100644 --- a/src/3rdparty/hwloc/src/pci-common.c +++ b/src/3rdparty/hwloc/src/pci-common.c @@ -1,14 +1,14 @@ /* - * Copyright © 2009-2018 Inria. All rights reserved. + * Copyright © 2009-2019 Inria. All rights reserved. * See COPYING in top-level directory. */ -#include -#include -#include -#include -#include -#include +#include "private/autogen/config.h" +#include "hwloc.h" +#include "hwloc/plugins.h" +#include "private/private.h" +#include "private/debug.h" +#include "private/misc.h" #include #ifdef HAVE_UNISTD_H @@ -23,6 +23,11 @@ #define close _close #endif + +/************************************** + * Init/Exit and Forced PCI localities + */ + static void hwloc_pci_forced_locality_parse_one(struct hwloc_topology *topology, const char *string /* must contain a ' ' */, @@ -109,11 +114,11 @@ hwloc_pci_forced_locality_parse(struct hwloc_topology *topology, const char *_en void hwloc_pci_discovery_init(struct hwloc_topology *topology) { - topology->need_pci_belowroot_apply_locality = 0; - topology->pci_has_forced_locality = 0; topology->pci_forced_locality_nr = 0; topology->pci_forced_locality = NULL; + + topology->first_pci_locality = topology->last_pci_locality = NULL; } void @@ -135,7 +140,7 @@ hwloc_pci_discovery_prepare(struct hwloc_topology *topology) if (!err) { if (st.st_size <= 64*1024) { /* random limit large enough to store multiple cpusets for thousands of PUs */ buffer = malloc(st.st_size+1); - if (read(fd, buffer, st.st_size) == st.st_size) { + if (buffer && read(fd, buffer, st.st_size) == st.st_size) { buffer[st.st_size] = '\0'; hwloc_pci_forced_locality_parse(topology, buffer); } @@ -152,16 +157,31 @@ hwloc_pci_discovery_prepare(struct hwloc_topology *topology) } void -hwloc_pci_discovery_exit(struct hwloc_topology *topology __hwloc_attribute_unused) +hwloc_pci_discovery_exit(struct hwloc_topology *topology) { + struct hwloc_pci_locality_s *cur; unsigned i; + for(i=0; ipci_forced_locality_nr; i++) hwloc_bitmap_free(topology->pci_forced_locality[i].cpuset); free(topology->pci_forced_locality); + cur = topology->first_pci_locality; + while (cur) { + struct hwloc_pci_locality_s *next = cur->next; + hwloc_bitmap_free(cur->cpuset); + free(cur); + cur = next; + } + hwloc_pci_discovery_init(topology); } + +/****************************** + * Inserting in Tree by Bus ID + */ + #ifdef HWLOC_DEBUG static void hwloc_pci_traverse_print_cb(void * cbdata __hwloc_attribute_unused, @@ -324,32 +344,16 @@ hwloc_pcidisc_tree_insert_by_busid(struct hwloc_obj **treep, hwloc_pci_add_object(NULL /* no parent on top of tree */, treep, obj); } -int -hwloc_pcidisc_tree_attach(struct hwloc_topology *topology, struct hwloc_obj *old_tree) + +/********************** + * Attaching PCI Trees + */ + +static struct hwloc_obj * +hwloc_pcidisc_add_hostbridges(struct hwloc_topology *topology, + struct hwloc_obj *old_tree) { - struct hwloc_obj **next_hb_p; - enum hwloc_type_filter_e bfilter; - - if (!old_tree) - /* found nothing, exit */ - return 0; - -#ifdef HWLOC_DEBUG - hwloc_debug("%s", "\nPCI hierarchy:\n"); - hwloc_pci_traverse(NULL, old_tree, hwloc_pci_traverse_print_cb); - hwloc_debug("%s", "\n"); -#endif - - next_hb_p = &hwloc_get_root_obj(topology)->io_first_child; - while (*next_hb_p) - next_hb_p = &((*next_hb_p)->next_sibling); - - bfilter = topology->type_filter[HWLOC_OBJ_BRIDGE]; - if (bfilter == HWLOC_TYPE_FILTER_KEEP_NONE) { - *next_hb_p = old_tree; - topology->modified = 1; - goto done; - } + struct hwloc_obj * new = NULL, **newp = &new; /* * tree points to all objects connected to any upstream bus in the machine. @@ -358,15 +362,29 @@ hwloc_pcidisc_tree_attach(struct hwloc_topology *topology, struct hwloc_obj *old */ while (old_tree) { /* start a new host bridge */ - struct hwloc_obj *hostbridge = hwloc_alloc_setup_object(topology, HWLOC_OBJ_BRIDGE, HWLOC_UNKNOWN_INDEX); - struct hwloc_obj **dstnextp = &hostbridge->io_first_child; - struct hwloc_obj **srcnextp = &old_tree; - struct hwloc_obj *child = *srcnextp; - unsigned short current_domain = child->attr->pcidev.domain; - unsigned char current_bus = child->attr->pcidev.bus; - unsigned char current_subordinate = current_bus; + struct hwloc_obj *hostbridge; + struct hwloc_obj **dstnextp; + struct hwloc_obj **srcnextp; + struct hwloc_obj *child; + unsigned short current_domain; + unsigned char current_bus; + unsigned char current_subordinate; - hwloc_debug("Starting new PCI hostbridge %04x:%02x\n", current_domain, current_bus); + hostbridge = hwloc_alloc_setup_object(topology, HWLOC_OBJ_BRIDGE, HWLOC_UNKNOWN_INDEX); + if (!hostbridge) { + /* just queue remaining things without hostbridges and return */ + *newp = old_tree; + return new; + } + dstnextp = &hostbridge->io_first_child; + + srcnextp = &old_tree; + child = *srcnextp; + current_domain = child->attr->pcidev.domain; + current_bus = child->attr->pcidev.bus; + current_subordinate = current_bus; + + hwloc_debug("Adding new PCI hostbridge %04x:%02x\n", current_domain, current_bus); next_child: /* remove next child from tree */ @@ -395,19 +413,14 @@ hwloc_pcidisc_tree_attach(struct hwloc_topology *topology, struct hwloc_obj *old hostbridge->attr->bridge.downstream.pci.domain = current_domain; hostbridge->attr->bridge.downstream.pci.secondary_bus = current_bus; hostbridge->attr->bridge.downstream.pci.subordinate_bus = current_subordinate; - hwloc_debug("New PCI hostbridge %04x:[%02x-%02x]\n", + hwloc_debug(" new PCI hostbridge covers %04x:[%02x-%02x]\n", current_domain, current_bus, current_subordinate); - *next_hb_p = hostbridge; - next_hb_p = &hostbridge->next_sibling; - topology->modified = 1; /* needed in case somebody reconnects levels before the core calls hwloc_pci_belowroot_apply_locality() - * or if hwloc_pci_belowroot_apply_locality() keeps hostbridges below root. - */ + *newp = hostbridge; + newp = &hostbridge->next_sibling; } - done: - topology->need_pci_belowroot_apply_locality = 1; - return 0; + return new; } static struct hwloc_obj * @@ -458,6 +471,9 @@ hwloc__pci_find_busid_parent(struct hwloc_topology *topology, struct hwloc_pcide unsigned i; int err; + hwloc_debug("Looking for parent of PCI busid %04x:%02x:%02x.%01x\n", + busid->domain, busid->bus, busid->dev, busid->func); + /* try to match a forced locality */ if (topology->pci_has_forced_locality) { for(i=0; ipci_forced_locality_nr; i++) { @@ -489,7 +505,7 @@ hwloc__pci_find_busid_parent(struct hwloc_topology *topology, struct hwloc_pcide } if (*env) { /* force the cpuset */ - hwloc_debug("Overriding localcpus using %s in the environment\n", envname); + hwloc_debug("Overriding PCI locality using %s in the environment\n", envname); hwloc_bitmap_sscanf(cpuset, env); forced = 1; } @@ -499,7 +515,7 @@ hwloc__pci_find_busid_parent(struct hwloc_topology *topology, struct hwloc_pcide } if (!forced) { - /* get the cpuset by asking the OS backend. */ + /* get the cpuset by asking the backend that provides the relevant hook, if any. */ struct hwloc_backend *backend = topology->get_pci_busid_cpuset_backend; if (backend) err = backend->get_pci_busid_cpuset(backend, busid, cpuset); @@ -510,7 +526,7 @@ hwloc__pci_find_busid_parent(struct hwloc_topology *topology, struct hwloc_pcide hwloc_bitmap_copy(cpuset, hwloc_topology_get_topology_cpuset(topology)); } - hwloc_debug_bitmap("Attaching PCI tree to cpuset %s\n", cpuset); + hwloc_debug_bitmap(" will attach PCI bus to cpuset %s\n", cpuset); parent = hwloc_find_insert_io_parent_by_complete_cpuset(topology, cpuset); if (parent) { @@ -526,11 +542,129 @@ hwloc__pci_find_busid_parent(struct hwloc_topology *topology, struct hwloc_pcide return parent; } +int +hwloc_pcidisc_tree_attach(struct hwloc_topology *topology, struct hwloc_obj *tree) +{ + enum hwloc_type_filter_e bfilter; + + if (!tree) + /* found nothing, exit */ + return 0; + +#ifdef HWLOC_DEBUG + hwloc_debug("%s", "\nPCI hierarchy:\n"); + hwloc_pci_traverse(NULL, tree, hwloc_pci_traverse_print_cb); + hwloc_debug("%s", "\n"); +#endif + + bfilter = topology->type_filter[HWLOC_OBJ_BRIDGE]; + if (bfilter != HWLOC_TYPE_FILTER_KEEP_NONE) { + tree = hwloc_pcidisc_add_hostbridges(topology, tree); + } + + while (tree) { + struct hwloc_obj *obj, *pciobj; + struct hwloc_obj *parent; + struct hwloc_pci_locality_s *loc; + unsigned domain, bus_min, bus_max; + + obj = tree; + + /* hostbridges don't have a PCI busid for looking up locality, use their first child */ + if (obj->type == HWLOC_OBJ_BRIDGE && obj->attr->bridge.upstream_type == HWLOC_OBJ_BRIDGE_HOST) + pciobj = obj->io_first_child; + else + pciobj = obj; + /* now we have a pci device or a pci bridge */ + assert(pciobj->type == HWLOC_OBJ_PCI_DEVICE + || (pciobj->type == HWLOC_OBJ_BRIDGE && pciobj->attr->bridge.upstream_type == HWLOC_OBJ_BRIDGE_PCI)); + + if (obj->type == HWLOC_OBJ_BRIDGE) { + domain = obj->attr->bridge.downstream.pci.domain; + bus_min = obj->attr->bridge.downstream.pci.secondary_bus; + bus_max = obj->attr->bridge.downstream.pci.subordinate_bus; + } else { + domain = pciobj->attr->pcidev.domain; + bus_min = pciobj->attr->pcidev.bus; + bus_max = pciobj->attr->pcidev.bus; + } + + /* find where to attach that PCI bus */ + parent = hwloc__pci_find_busid_parent(topology, &pciobj->attr->pcidev); + + /* reuse the previous locality if possible */ + if (topology->last_pci_locality + && parent == topology->last_pci_locality->parent + && domain == topology->last_pci_locality->domain + && (bus_min == topology->last_pci_locality->bus_max + || bus_min == topology->last_pci_locality->bus_max+1)) { + hwloc_debug(" Reusing PCI locality up to bus %04x:%02x\n", + domain, bus_max); + topology->last_pci_locality->bus_max = bus_max; + goto done; + } + + loc = malloc(sizeof(*loc)); + if (!loc) { + /* fallback to attaching to root */ + parent = hwloc_get_root_obj(topology); + goto done; + } + + loc->domain = domain; + loc->bus_min = bus_min; + loc->bus_max = bus_max; + loc->parent = parent; + loc->cpuset = hwloc_bitmap_dup(parent->cpuset); + if (!loc->cpuset) { + /* fallback to attaching to root */ + free(loc); + parent = hwloc_get_root_obj(topology); + goto done; + } + + hwloc_debug("Adding PCI locality %s P#%u for bus %04x:[%02x:%02x]\n", + hwloc_obj_type_string(parent->type), parent->os_index, loc->domain, loc->bus_min, loc->bus_max); + if (topology->last_pci_locality) { + loc->prev = topology->last_pci_locality; + loc->next = NULL; + topology->last_pci_locality->next = loc; + topology->last_pci_locality = loc; + } else { + loc->prev = NULL; + loc->next = NULL; + topology->first_pci_locality = loc; + topology->last_pci_locality = loc; + } + + done: + /* dequeue this object */ + tree = obj->next_sibling; + obj->next_sibling = NULL; + hwloc_insert_object_by_parent(topology, parent, obj); + } + + return 0; +} + + +/********************************* + * Finding PCI objects or parents + */ + struct hwloc_obj * -hwloc_pcidisc_find_busid_parent(struct hwloc_topology *topology, - unsigned domain, unsigned bus, unsigned dev, unsigned func) +hwloc_pci_find_parent_by_busid(struct hwloc_topology *topology, + unsigned domain, unsigned bus, unsigned dev, unsigned func) { struct hwloc_pcidev_attr_s busid; + hwloc_obj_t parent; + + /* try to find that exact busid */ + parent = hwloc_pci_find_by_busid(topology, domain, bus, dev, func); + if (parent) + return parent; + + /* try to find the locality of that bus instead */ busid.domain = domain; busid.bus = bus; busid.dev = dev; @@ -538,66 +672,10 @@ hwloc_pcidisc_find_busid_parent(struct hwloc_topology *topology, return hwloc__pci_find_busid_parent(topology, &busid); } -int -hwloc_pci_belowroot_apply_locality(struct hwloc_topology *topology) -{ - struct hwloc_obj *root = hwloc_get_root_obj(topology); - struct hwloc_obj **listp, *obj; - - if (!topology->need_pci_belowroot_apply_locality) - return 0; - topology->need_pci_belowroot_apply_locality = 0; - - /* root->io_first_child contains some PCI hierarchies, any maybe some non-PCI things. - * insert the PCI trees according to their PCI-locality. - */ - listp = &root->io_first_child; - while ((obj = *listp) != NULL) { - struct hwloc_pcidev_attr_s *busid; - struct hwloc_obj *parent; - - /* skip non-PCI objects */ - if (obj->type != HWLOC_OBJ_PCI_DEVICE - && !(obj->type == HWLOC_OBJ_BRIDGE && obj->attr->bridge.downstream_type == HWLOC_OBJ_BRIDGE_PCI) - && !(obj->type == HWLOC_OBJ_BRIDGE && obj->attr->bridge.upstream_type == HWLOC_OBJ_BRIDGE_PCI)) { - listp = &obj->next_sibling; - continue; - } - - if (obj->type == HWLOC_OBJ_PCI_DEVICE - || (obj->type == HWLOC_OBJ_BRIDGE - && obj->attr->bridge.upstream_type == HWLOC_OBJ_BRIDGE_PCI)) - busid = &obj->attr->pcidev; - else { - /* hostbridges don't have a PCI busid for looking up locality, use their first child if PCI */ - hwloc_obj_t child = obj->io_first_child; - if (child && (child->type == HWLOC_OBJ_PCI_DEVICE - || (child->type == HWLOC_OBJ_BRIDGE - && child->attr->bridge.upstream_type == HWLOC_OBJ_BRIDGE_PCI))) - busid = &obj->io_first_child->attr->pcidev; - else - continue; - } - - /* attach the object (and children) where it belongs */ - parent = hwloc__pci_find_busid_parent(topology, busid); - if (parent == root) { - /* keep this object here */ - listp = &obj->next_sibling; - } else { - /* dequeue this object */ - *listp = obj->next_sibling; - obj->next_sibling = NULL; - hwloc_insert_object_by_parent(topology, parent, obj); - } - } - - return 0; -} - +/* return the smallest object that contains the desired busid */ static struct hwloc_obj * -hwloc__pci_belowroot_find_by_busid(hwloc_obj_t parent, - unsigned domain, unsigned bus, unsigned dev, unsigned func) +hwloc__pci_find_by_busid(hwloc_obj_t parent, + unsigned domain, unsigned bus, unsigned dev, unsigned func) { hwloc_obj_t child; @@ -622,7 +700,7 @@ hwloc__pci_belowroot_find_by_busid(hwloc_obj_t parent, && child->attr->bridge.downstream.pci.secondary_bus <= bus && child->attr->bridge.downstream.pci.subordinate_bus >= bus) /* not the right bus id, but it's included in the bus below that bridge */ - return hwloc__pci_belowroot_find_by_busid(child, domain, bus, dev, func); + return hwloc__pci_find_by_busid(child, domain, bus, dev, func); } else if (child->type == HWLOC_OBJ_BRIDGE && child->attr->bridge.upstream_type != HWLOC_OBJ_BRIDGE_PCI @@ -632,7 +710,7 @@ hwloc__pci_belowroot_find_by_busid(hwloc_obj_t parent, && child->attr->bridge.downstream.pci.secondary_bus <= bus && child->attr->bridge.downstream.pci.subordinate_bus >= bus) { /* contains our bus, recurse */ - return hwloc__pci_belowroot_find_by_busid(child, domain, bus, dev, func); + return hwloc__pci_find_by_busid(child, domain, bus, dev, func); } } /* didn't find anything, return parent */ @@ -640,17 +718,54 @@ hwloc__pci_belowroot_find_by_busid(hwloc_obj_t parent, } struct hwloc_obj * -hwloc_pcidisc_find_by_busid(struct hwloc_topology *topology, - unsigned domain, unsigned bus, unsigned dev, unsigned func) +hwloc_pci_find_by_busid(struct hwloc_topology *topology, + unsigned domain, unsigned bus, unsigned dev, unsigned func) { + struct hwloc_pci_locality_s *loc; hwloc_obj_t root = hwloc_get_root_obj(topology); - hwloc_obj_t parent = hwloc__pci_belowroot_find_by_busid(root, domain, bus, dev, func); - if (parent == root) + hwloc_obj_t parent = NULL; + + hwloc_debug("pcidisc looking for bus id %04x:%02x:%02x.%01x\n", domain, bus, dev, func); + loc = topology->first_pci_locality; + while (loc) { + if (loc->domain == domain && loc->bus_min <= bus && loc->bus_max >= bus) { + parent = loc->parent; + assert(parent); + hwloc_debug(" found pci locality for %04x:[%02x:%02x]\n", + loc->domain, loc->bus_min, loc->bus_max); + break; + } + loc = loc->next; + } + /* if we failed to insert localities, look at root too */ + if (!parent) + parent = root; + + hwloc_debug(" looking for bus %04x:%02x:%02x.%01x below %s P#%u\n", + domain, bus, dev, func, + hwloc_obj_type_string(parent->type), parent->os_index); + parent = hwloc__pci_find_by_busid(parent, domain, bus, dev, func); + if (parent == root) { + hwloc_debug(" found nothing better than root object, ignoring\n"); return NULL; - else + } else { + if (parent->type == HWLOC_OBJ_PCI_DEVICE + || (parent->type == HWLOC_OBJ_BRIDGE && parent->attr->bridge.upstream_type == HWLOC_OBJ_BRIDGE_PCI)) + hwloc_debug(" found busid %04x:%02x:%02x.%01x\n", + parent->attr->pcidev.domain, parent->attr->pcidev.bus, + parent->attr->pcidev.dev, parent->attr->pcidev.func); + else + hwloc_debug(" found parent %s P#%u\n", + hwloc_obj_type_string(parent->type), parent->os_index); return parent; + } } + +/******************************* + * Parsing the PCI Config Space + */ + #define HWLOC_PCI_STATUS 0x06 #define HWLOC_PCI_STATUS_CAP_LIST 0x10 #define HWLOC_PCI_CAPABILITY_LIST 0x34 @@ -703,13 +818,14 @@ hwloc_pcidisc_find_linkspeed(const unsigned char *config, * PCIe Gen2 = 5 GT/s signal-rate per lane with 8/10 encoding = 0.5 GB/s data-rate per lane * PCIe Gen3 = 8 GT/s signal-rate per lane with 128/130 encoding = 1 GB/s data-rate per lane * PCIe Gen4 = 16 GT/s signal-rate per lane with 128/130 encoding = 2 GB/s data-rate per lane + * PCIe Gen5 = 32 GT/s signal-rate per lane with 128/130 encoding = 4 GB/s data-rate per lane */ /* lanespeed in Gbit/s */ if (speed <= 2) lanespeed = 2.5f * speed * 0.8f; else - lanespeed = 8.0f * (1<<(speed-3)) * 128/130; /* assume Gen5 will be 32 GT/s and so on */ + lanespeed = 8.0f * (1<<(speed-3)) * 128/130; /* assume Gen6 will be 64 GT/s and so on */ /* linkspeed in GB/s */ *linkspeed = lanespeed * width / 8; @@ -738,30 +854,27 @@ hwloc_pcidisc_check_bridge_type(unsigned device_class, const unsigned char *conf #define HWLOC_PCI_SUBORDINATE_BUS 0x1a int -hwloc_pcidisc_setup_bridge_attr(hwloc_obj_t obj, +hwloc_pcidisc_find_bridge_buses(unsigned domain, unsigned bus, unsigned dev, unsigned func, + unsigned *secondary_busp, unsigned *subordinate_busp, const unsigned char *config) { - struct hwloc_bridge_attr_s *battr = &obj->attr->bridge; - struct hwloc_pcidev_attr_s *pattr = &battr->upstream.pci; + unsigned secondary_bus, subordinate_bus; - if (config[HWLOC_PCI_PRIMARY_BUS] != pattr->bus) { + if (config[HWLOC_PCI_PRIMARY_BUS] != bus) { /* Sometimes the config space contains 00 instead of the actual primary bus number. * Always trust the bus ID because it was built by the system which has more information * to workaround such problems (e.g. ACPI information about PCI parent/children). */ hwloc_debug(" %04x:%02x:%02x.%01x bridge with (ignored) invalid PCI_PRIMARY_BUS %02x\n", - pattr->domain, pattr->bus, pattr->dev, pattr->func, config[HWLOC_PCI_PRIMARY_BUS]); + domain, bus, dev, func, config[HWLOC_PCI_PRIMARY_BUS]); } - battr->upstream_type = HWLOC_OBJ_BRIDGE_PCI; - battr->downstream_type = HWLOC_OBJ_BRIDGE_PCI; - battr->downstream.pci.domain = pattr->domain; - battr->downstream.pci.secondary_bus = config[HWLOC_PCI_SECONDARY_BUS]; - battr->downstream.pci.subordinate_bus = config[HWLOC_PCI_SUBORDINATE_BUS]; + secondary_bus = config[HWLOC_PCI_SECONDARY_BUS]; + subordinate_bus = config[HWLOC_PCI_SUBORDINATE_BUS]; - if (battr->downstream.pci.secondary_bus <= pattr->bus - || battr->downstream.pci.subordinate_bus <= pattr->bus - || battr->downstream.pci.secondary_bus > battr->downstream.pci.subordinate_bus) { + if (secondary_bus <= bus + || subordinate_bus <= bus + || secondary_bus > subordinate_bus) { /* This should catch most cases of invalid bridge information * (e.g. 00 for secondary and subordinate). * Ideally we would also check that [secondary-subordinate] is included @@ -769,15 +882,21 @@ hwloc_pcidisc_setup_bridge_attr(hwloc_obj_t obj, * because objects may be discovered out of order (especially in the fsroot case). */ hwloc_debug(" %04x:%02x:%02x.%01x bridge has invalid secondary-subordinate buses [%02x-%02x]\n", - pattr->domain, pattr->bus, pattr->dev, pattr->func, - battr->downstream.pci.secondary_bus, battr->downstream.pci.subordinate_bus); - hwloc_free_unlinked_object(obj); + domain, bus, dev, func, + secondary_bus, subordinate_bus); return -1; } + *secondary_busp = secondary_bus; + *subordinate_busp = subordinate_bus; return 0; } + +/**************** + * Class Strings + */ + const char * hwloc_pci_class_string(unsigned short class_id) { diff --git a/src/3rdparty/hwloc/src/shmem.c b/src/3rdparty/hwloc/src/shmem.c index 6c507f52..94d55eef 100644 --- a/src/3rdparty/hwloc/src/shmem.c +++ b/src/3rdparty/hwloc/src/shmem.c @@ -1,12 +1,12 @@ /* - * Copyright © 2017-2018 Inria. All rights reserved. + * Copyright © 2017-2019 Inria. All rights reserved. * See COPYING in top-level directory. */ -#include -#include -#include -#include +#include "private/autogen/config.h" +#include "hwloc.h" +#include "hwloc/shmem.h" +#include "private/private.h" #ifndef HWLOC_WIN_SYS @@ -214,6 +214,8 @@ hwloc_shmem_topology_adopt(hwloc_topology_t *topologyp, new->support.discovery = malloc(sizeof(*new->support.discovery)); new->support.cpubind = malloc(sizeof(*new->support.cpubind)); new->support.membind = malloc(sizeof(*new->support.membind)); + if (!new->support.discovery || !new->support.cpubind || !new->support.membind) + goto out_with_support; memcpy(new->support.discovery, old->support.discovery, sizeof(*new->support.discovery)); memcpy(new->support.cpubind, old->support.cpubind, sizeof(*new->support.cpubind)); memcpy(new->support.membind, old->support.membind, sizeof(*new->support.membind)); @@ -230,6 +232,11 @@ hwloc_shmem_topology_adopt(hwloc_topology_t *topologyp, *topologyp = new; return 0; + out_with_support: + free(new->support.discovery); + free(new->support.cpubind); + free(new->support.membind); + free(new); out_with_components: hwloc_components_fini(); out_with_mmap: diff --git a/src/3rdparty/hwloc/src/topology-noos.c b/src/3rdparty/hwloc/src/topology-noos.c index 77871eb1..174b6fd8 100644 --- a/src/3rdparty/hwloc/src/topology-noos.c +++ b/src/3rdparty/hwloc/src/topology-noos.c @@ -1,26 +1,34 @@ /* * Copyright © 2009 CNRS - * Copyright © 2009-2017 Inria. All rights reserved. + * Copyright © 2009-2019 Inria. All rights reserved. * Copyright © 2009-2012 Université Bordeaux * Copyright © 2009-2011 Cisco Systems, Inc. All rights reserved. * See COPYING in top-level directory. */ -#include -#include -#include +#include "private/autogen/config.h" +#include "hwloc.h" +#include "private/private.h" static int -hwloc_look_noos(struct hwloc_backend *backend) +hwloc_look_noos(struct hwloc_backend *backend, struct hwloc_disc_status *dstatus) { + /* + * This backend uses the underlying OS. + * However we don't enforce topology->is_thissystem so that + * we may still force use this backend when debugging with !thissystem. + */ + struct hwloc_topology *topology = backend->topology; int nbprocs; + assert(dstatus->phase == HWLOC_DISC_PHASE_CPU); + if (topology->levels[0][0]->cpuset) /* somebody discovered things */ return -1; - nbprocs = hwloc_fallback_nbprocessors(topology); + nbprocs = hwloc_fallback_nbprocessors(0); if (nbprocs >= 1) topology->support.discovery->pu = 1; else @@ -33,13 +41,15 @@ hwloc_look_noos(struct hwloc_backend *backend) } static struct hwloc_backend * -hwloc_noos_component_instantiate(struct hwloc_disc_component *component, +hwloc_noos_component_instantiate(struct hwloc_topology *topology, + struct hwloc_disc_component *component, + unsigned excluded_phases __hwloc_attribute_unused, const void *_data1 __hwloc_attribute_unused, const void *_data2 __hwloc_attribute_unused, const void *_data3 __hwloc_attribute_unused) { struct hwloc_backend *backend; - backend = hwloc_backend_alloc(component); + backend = hwloc_backend_alloc(topology, component); if (!backend) return NULL; backend->discover = hwloc_look_noos; @@ -47,9 +57,9 @@ hwloc_noos_component_instantiate(struct hwloc_disc_component *component, } static struct hwloc_disc_component hwloc_noos_disc_component = { - HWLOC_DISC_COMPONENT_TYPE_CPU, "no_os", - HWLOC_DISC_COMPONENT_TYPE_GLOBAL, + HWLOC_DISC_PHASE_CPU, + HWLOC_DISC_PHASE_GLOBAL, hwloc_noos_component_instantiate, 40, /* lower than native OS component, higher than globals */ 1, diff --git a/src/3rdparty/hwloc/src/topology-synthetic.c b/src/3rdparty/hwloc/src/topology-synthetic.c index 1fe334d1..686efce1 100644 --- a/src/3rdparty/hwloc/src/topology-synthetic.c +++ b/src/3rdparty/hwloc/src/topology-synthetic.c @@ -6,11 +6,11 @@ * See COPYING in top-level directory. */ -#include -#include -#include -#include -#include +#include "private/autogen/config.h" +#include "hwloc.h" +#include "private/private.h" +#include "private/misc.h" +#include "private/debug.h" #include #include @@ -122,6 +122,7 @@ hwloc_synthetic_process_indexes(struct hwloc_synthetic_backend_data_s *data, unsigned long nbs = 1; unsigned j, mul; const char *tmp; + struct hwloc_synthetic_intlv_loop_s *loops; tmp = attr; while (tmp) { @@ -132,9 +133,10 @@ hwloc_synthetic_process_indexes(struct hwloc_synthetic_backend_data_s *data, tmp++; } - { /* nr_loops colon-separated fields, but we may need one more at the end */ - HWLOC_VLA(struct hwloc_synthetic_intlv_loop_s, loops, nr_loops+1); + loops = malloc((nr_loops+1) * sizeof(*loops)); + if (!loops) + goto out_with_array; if (*attr >= '0' && *attr <= '9') { /* interleaving as x*y:z*t:... */ @@ -148,11 +150,13 @@ hwloc_synthetic_process_indexes(struct hwloc_synthetic_backend_data_s *data, if (tmp2 == tmp || *tmp2 != '*') { if (verbose) fprintf(stderr, "Failed to read synthetic index interleaving loop '%s' without number before '*'\n", tmp); + free(loops); goto out_with_array; } if (!step) { if (verbose) fprintf(stderr, "Invalid interleaving loop with step 0 at '%s'\n", tmp); + free(loops); goto out_with_array; } tmp2++; @@ -160,11 +164,13 @@ hwloc_synthetic_process_indexes(struct hwloc_synthetic_backend_data_s *data, if (tmp3 == tmp2 || (*tmp3 && *tmp3 != ':' && *tmp3 != ')' && *tmp3 != ' ')) { if (verbose) fprintf(stderr, "Failed to read synthetic index interleaving loop '%s' without number between '*' and ':'\n", tmp); + free(loops); goto out_with_array; } if (!nb) { if (verbose) fprintf(stderr, "Invalid interleaving loop with number 0 at '%s'\n", tmp2); + free(loops); goto out_with_array; } loops[cur_loop].step = step; @@ -192,11 +198,13 @@ hwloc_synthetic_process_indexes(struct hwloc_synthetic_backend_data_s *data, if (err < 0) { if (verbose) fprintf(stderr, "Failed to read synthetic index interleaving loop type '%s'\n", tmp); + free(loops); goto out_with_array; } if (type == HWLOC_OBJ_MISC || type == HWLOC_OBJ_BRIDGE || type == HWLOC_OBJ_PCI_DEVICE || type == HWLOC_OBJ_OS_DEVICE) { if (verbose) fprintf(stderr, "Misc object type disallowed in synthetic index interleaving loop type '%s'\n", tmp); + free(loops); goto out_with_array; } for(i=0; ; i++) { @@ -217,6 +225,7 @@ hwloc_synthetic_process_indexes(struct hwloc_synthetic_backend_data_s *data, if (verbose) fprintf(stderr, "Failed to find level for synthetic index interleaving loop type '%s'\n", tmp); + free(loops); goto out_with_array; } tmp = strchr(tmp, ':'); @@ -235,6 +244,7 @@ hwloc_synthetic_process_indexes(struct hwloc_synthetic_backend_data_s *data, if (loops[i].level_depth == mydepth && i != cur_loop) { if (verbose) fprintf(stderr, "Invalid duplicate interleaving loop type in synthetic index '%s'\n", attr); + free(loops); goto out_with_array; } if (loops[i].level_depth < mydepth @@ -264,6 +274,7 @@ hwloc_synthetic_process_indexes(struct hwloc_synthetic_backend_data_s *data, } else { if (verbose) fprintf(stderr, "Invalid index interleaving total width %lu instead of %lu\n", nbs, total); + free(loops); goto out_with_array; } } @@ -278,6 +289,8 @@ hwloc_synthetic_process_indexes(struct hwloc_synthetic_backend_data_s *data, mul *= nb; } + free(loops); + /* check that we have the right values (cannot pass total, cannot give duplicate 0) */ for(j=0; j= total) { @@ -293,7 +306,6 @@ hwloc_synthetic_process_indexes(struct hwloc_synthetic_backend_data_s *data, } indexes->array = array; - } } return; @@ -527,7 +539,8 @@ hwloc_backend_synthetic_init(struct hwloc_synthetic_backend_data_s *data, if (*pos < '0' || *pos > '9') { if (hwloc_type_sscanf(pos, &type, &attrs, sizeof(attrs)) < 0) { - if (!strncmp(pos, "Die", 3) || !strncmp(pos, "Tile", 4) || !strncmp(pos, "Module", 6)) { + if (!strncmp(pos, "Tile", 4) || !strncmp(pos, "Module", 6)) { + /* possible future types */ type = HWLOC_OBJ_GROUP; } else { /* FIXME: allow generic "Cache" string? would require to deal with possibly duplicate cache levels */ @@ -645,6 +658,12 @@ hwloc_backend_synthetic_init(struct hwloc_synthetic_backend_data_s *data, errno = EINVAL; return -1; } + if (type_count[HWLOC_OBJ_DIE] > 1) { + if (verbose) + fprintf(stderr, "Synthetic string cannot have several die levels\n"); + errno = EINVAL; + return -1; + } if (type_count[HWLOC_OBJ_NUMANODE] > 1) { if (verbose) fprintf(stderr, "Synthetic string cannot have several NUMA node levels\n"); @@ -829,6 +848,7 @@ hwloc_synthetic_set_attr(struct hwloc_synthetic_attr_s *sattr, obj->attr->numanode.page_types[0].count = sattr->memorysize / 4096; break; case HWLOC_OBJ_PACKAGE: + case HWLOC_OBJ_DIE: break; case HWLOC_OBJ_L1CACHE: case HWLOC_OBJ_L2CACHE: @@ -953,13 +973,19 @@ hwloc__look_synthetic(struct hwloc_topology *topology, } static int -hwloc_look_synthetic(struct hwloc_backend *backend) +hwloc_look_synthetic(struct hwloc_backend *backend, struct hwloc_disc_status *dstatus) { + /* + * This backend enforces !topology->is_thissystem by default. + */ + struct hwloc_topology *topology = backend->topology; struct hwloc_synthetic_backend_data_s *data = backend->private_data; hwloc_bitmap_t cpuset = hwloc_bitmap_alloc(); unsigned i; + assert(dstatus->phase == HWLOC_DISC_PHASE_GLOBAL); + assert(!topology->levels[0][0]->cpuset); hwloc_alloc_root_sets(topology->levels[0][0]); @@ -1001,7 +1027,9 @@ hwloc_synthetic_backend_disable(struct hwloc_backend *backend) } static struct hwloc_backend * -hwloc_synthetic_component_instantiate(struct hwloc_disc_component *component, +hwloc_synthetic_component_instantiate(struct hwloc_topology *topology, + struct hwloc_disc_component *component, + unsigned excluded_phases __hwloc_attribute_unused, const void *_data1, const void *_data2 __hwloc_attribute_unused, const void *_data3 __hwloc_attribute_unused) @@ -1021,7 +1049,7 @@ hwloc_synthetic_component_instantiate(struct hwloc_disc_component *component, } } - backend = hwloc_backend_alloc(component); + backend = hwloc_backend_alloc(topology, component); if (!backend) goto out; @@ -1051,8 +1079,8 @@ hwloc_synthetic_component_instantiate(struct hwloc_disc_component *component, } static struct hwloc_disc_component hwloc_synthetic_disc_component = { - HWLOC_DISC_COMPONENT_TYPE_GLOBAL, "synthetic", + HWLOC_DISC_PHASE_GLOBAL, ~0, hwloc_synthetic_component_instantiate, 30, @@ -1267,6 +1295,12 @@ hwloc__export_synthetic_obj(struct hwloc_topology * topology, unsigned long flag /* if exporting to v1 or without extended-types, use all-v1-compatible Socket name */ res = hwloc_snprintf(tmp, tmplen, "Socket%s", aritys); + } else if (obj->type == HWLOC_OBJ_DIE + && (flags & (HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_NO_EXTENDED_TYPES + |HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_V1))) { + /* if exporting to v1 or without extended-types, use all-v1-compatible Group name */ + res = hwloc_snprintf(tmp, tmplen, "Group%s", aritys); + } else if (obj->type == HWLOC_OBJ_GROUP /* don't export group depth */ || flags & HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_NO_EXTENDED_TYPES) { res = hwloc_snprintf(tmp, tmplen, "%s%s", hwloc_obj_type_string(obj->type), aritys); @@ -1323,16 +1357,26 @@ hwloc__export_synthetic_memory_children(struct hwloc_topology * topology, unsign } while (mchild) { - /* v2: export all NUMA children */ - - assert(mchild->type == HWLOC_OBJ_NUMANODE); /* only NUMA node memory children for now */ + /* FIXME: really recurse to export memcaches and numanode, + * but it requires clever parsing of [ memcache [numa] [numa] ] during import, + * better attaching of things to describe the hierarchy. + */ + hwloc_obj_t numanode = mchild; + /* only export the first NUMA node leaf of each memory child + * FIXME: This assumes mscache aren't shared between nodes, that's true in current platforms + */ + while (numanode && numanode->type != HWLOC_OBJ_NUMANODE) { + assert(numanode->arity == 1); + numanode = numanode->memory_first_child; + } + assert(numanode); /* there's always a numanode at the bottom of the memory tree */ if (needprefix) hwloc__export_synthetic_add_char(&ret, &tmp, &tmplen, ' '); hwloc__export_synthetic_add_char(&ret, &tmp, &tmplen, '['); - res = hwloc__export_synthetic_obj(topology, flags, mchild, (unsigned)-1, tmp, tmplen); + res = hwloc__export_synthetic_obj(topology, flags, numanode, (unsigned)-1, tmp, tmplen); if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0) return -1; @@ -1366,9 +1410,8 @@ hwloc_check_memory_symmetric(struct hwloc_topology * topology) assert(node); first_parent = node->parent; - assert(hwloc__obj_type_is_normal(first_parent->type)); /* only depth-1 memory children for now */ - /* check whether all object on parent's level have same number of NUMA children */ + /* check whether all object on parent's level have same number of NUMA bits */ for(i=0; idepth); i++) { hwloc_obj_t parent, mchild; @@ -1379,10 +1422,9 @@ hwloc_check_memory_symmetric(struct hwloc_topology * topology) if (parent->memory_arity != first_parent->memory_arity) goto out_with_bitmap; - /* clear these NUMA children from remaining_nodes */ + /* clear children NUMA bits from remaining_nodes */ mchild = parent->memory_first_child; while (mchild) { - assert(mchild->type == HWLOC_OBJ_NUMANODE); /* only NUMA node memory children for now */ hwloc_bitmap_clr(remaining_nodes, mchild->os_index); /* cannot use parent->nodeset, some normal children may have other NUMA nodes */ mchild = mchild->next_sibling; } diff --git a/src/3rdparty/hwloc/src/topology-windows.c b/src/3rdparty/hwloc/src/topology-windows.c index d03645c0..22521aa3 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-2018 Inria. All rights reserved. + * Copyright © 2009-2019 Inria. All rights reserved. * Copyright © 2009-2012 Université Bordeaux * Copyright © 2011 Cisco Systems, Inc. All rights reserved. * See COPYING in top-level directory. @@ -9,10 +9,10 @@ /* To try to get all declarations duplicated below. */ #define _WIN32_WINNT 0x0601 -#include -#include -#include -#include +#include "private/autogen/config.h" +#include "hwloc.h" +#include "private/private.h" +#include "private/debug.h" #include @@ -731,8 +731,14 @@ hwloc_win_get_area_memlocation(hwloc_topology_t topology __hwloc_attribute_unuse */ static int -hwloc_look_windows(struct hwloc_backend *backend) +hwloc_look_windows(struct hwloc_backend *backend, struct hwloc_disc_status *dstatus) { + /* + * This backend uses the underlying OS. + * However we don't enforce topology->is_thissystem so that + * we may still force use this backend when debugging with !thissystem. + */ + struct hwloc_topology *topology = backend->topology; hwloc_bitmap_t groups_pu_set = NULL; SYSTEM_INFO SystemInfo; @@ -740,6 +746,8 @@ hwloc_look_windows(struct hwloc_backend *backend) int gotnuma = 0; int gotnumamemory = 0; + assert(dstatus->phase == HWLOC_DISC_PHASE_CPU); + if (topology->levels[0][0]->cpuset) /* somebody discovered things */ return -1; @@ -1136,13 +1144,15 @@ static void hwloc_windows_component_finalize(unsigned long flags __hwloc_attribu } static struct hwloc_backend * -hwloc_windows_component_instantiate(struct hwloc_disc_component *component, +hwloc_windows_component_instantiate(struct hwloc_topology *topology, + struct hwloc_disc_component *component, + unsigned excluded_phases __hwloc_attribute_unused, const void *_data1 __hwloc_attribute_unused, const void *_data2 __hwloc_attribute_unused, const void *_data3 __hwloc_attribute_unused) { struct hwloc_backend *backend; - backend = hwloc_backend_alloc(component); + backend = hwloc_backend_alloc(topology, component); if (!backend) return NULL; backend->discover = hwloc_look_windows; @@ -1150,9 +1160,9 @@ hwloc_windows_component_instantiate(struct hwloc_disc_component *component, } static struct hwloc_disc_component hwloc_windows_disc_component = { - HWLOC_DISC_COMPONENT_TYPE_CPU, "windows", - HWLOC_DISC_COMPONENT_TYPE_GLOBAL, + HWLOC_DISC_PHASE_CPU, + HWLOC_DISC_PHASE_GLOBAL, hwloc_windows_component_instantiate, 50, 1, @@ -1168,10 +1178,12 @@ const struct hwloc_component hwloc_windows_component = { }; int -hwloc_fallback_nbprocessors(struct hwloc_topology *topology __hwloc_attribute_unused) { +hwloc_fallback_nbprocessors(unsigned flags __hwloc_attribute_unused) { int n; SYSTEM_INFO sysinfo; + /* TODO handle flags & HWLOC_FALLBACK_NBPROCESSORS_INCLUDE_OFFLINE */ + /* by default, ignore groups (return only the number in the current group) */ GetSystemInfo(&sysinfo); n = sysinfo.dwNumberOfProcessors; /* FIXME could be non-contigous, rather return a mask from dwActiveProcessorMask? */ diff --git a/src/3rdparty/hwloc/src/topology-x86.c b/src/3rdparty/hwloc/src/topology-x86.c index 4aefdcf1..1060157d 100644 --- a/src/3rdparty/hwloc/src/topology-x86.c +++ b/src/3rdparty/hwloc/src/topology-x86.c @@ -14,13 +14,12 @@ * on various architectures, without having to use this x86-specific code. */ -#include -#include -#include -#include -#include - -#include +#include "private/autogen/config.h" +#include "hwloc.h" +#include "private/private.h" +#include "private/debug.h" +#include "private/misc.h" +#include "private/cpuid-x86.h" #include #ifdef HAVE_DIRENT_H @@ -70,6 +69,8 @@ cpuiddump_read(const char *dirpath, unsigned idx) { struct cpuiddump *cpuiddump; struct cpuiddump_entry *cur; + size_t filenamelen; + char *filename; FILE *file; char line[128]; unsigned nr; @@ -80,16 +81,16 @@ cpuiddump_read(const char *dirpath, unsigned idx) goto out; } - { - size_t filenamelen = strlen(dirpath) + 15; - HWLOC_VLA(char, filename, filenamelen); + filenamelen = strlen(dirpath) + 15; + filename = malloc(filenamelen); + if (!filename) + goto out_with_dump; snprintf(filename, filenamelen, "%s/pu%u", dirpath, idx); file = fopen(filename, "r"); if (!file) { fprintf(stderr, "Could not read dumped cpuid file %s, ignoring cpuiddump.\n", filename); - goto out_with_dump; + goto out_with_filename; } - } nr = 0; while (fgets(line, sizeof(line), file)) @@ -117,10 +118,13 @@ cpuiddump_read(const char *dirpath, unsigned idx) cpuiddump->nr = nr; fclose(file); + free(filename); return cpuiddump; out_with_file: fclose(file); + out_with_filename: + free(filename); out_with_dump: free(cpuiddump); out: @@ -170,6 +174,11 @@ static void cpuid_or_from_dump(unsigned *eax, unsigned *ebx, unsigned *ecx, unsi * Core detection routines and structures */ +enum hwloc_x86_disc_flags { + HWLOC_X86_DISC_FLAG_FULL = (1<<0), /* discover everything instead of only annotating */ + HWLOC_X86_DISC_FLAG_TOPOEXT_NUMANODES = (1<<1) /* use AMD topoext numanode information */ +}; + #define has_topoext(features) ((features)[6] & (1 << 22)) #define has_x2apic(features) ((features)[4] & (1 << 21)) @@ -190,12 +199,15 @@ struct cacheinfo { struct procinfo { unsigned present; unsigned apicid; - unsigned packageid; - unsigned dieid; - unsigned nodeid; - unsigned unitid; - unsigned threadid; - unsigned coreid; +#define PKG 0 +#define CORE 1 +#define NODE 2 +#define UNIT 3 +#define TILE 4 +#define MODULE 5 +#define DIE 6 +#define HWLOC_X86_PROCINFO_ID_NR 7 + unsigned ids[HWLOC_X86_PROCINFO_ID_NR]; unsigned *otherids; unsigned levels; unsigned numcaches; @@ -215,7 +227,8 @@ enum cpuid_type { unknown }; -static void fill_amd_cache(struct procinfo *infos, unsigned level, hwloc_obj_cache_type_t type, unsigned nbthreads_sharing, unsigned cpuid) +/* AMD legacy cache information from specific CPUID 0x80000005-6 leaves */ +static void setup__amd_cache_legacy(struct procinfo *infos, unsigned level, hwloc_obj_cache_type_t type, unsigned nbthreads_sharing, unsigned cpuid) { struct cacheinfo *cache, *tmpcaches; unsigned cachenum; @@ -262,7 +275,249 @@ static void fill_amd_cache(struct procinfo *infos, unsigned level, hwloc_obj_cac hwloc_debug("cache L%u t%u linesize %u ways %d size %luKB\n", cache->level, cache->nbthreads_sharing, cache->linesize, cache->ways, cache->size >> 10); } -static void look_exttopoenum(struct procinfo *infos, unsigned leaf, struct cpuiddump *src_cpuiddump) +/* AMD legacy cache information from CPUID 0x80000005-6 leaves */ +static void read_amd_caches_legacy(struct procinfo *infos, struct cpuiddump *src_cpuiddump, unsigned legacy_max_log_proc) +{ + unsigned eax, ebx, ecx, edx; + + eax = 0x80000005; + cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump); + setup__amd_cache_legacy(infos, 1, HWLOC_OBJ_CACHE_DATA, 1, ecx); /* private L1d */ + setup__amd_cache_legacy(infos, 1, HWLOC_OBJ_CACHE_INSTRUCTION, 1, edx); /* private L1i */ + + eax = 0x80000006; + cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump); + if (ecx & 0xf000) + /* This is actually supported on Intel but LinePerTag isn't returned in bits 8-11. + * Could be useful if some Intels (at least before Core micro-architecture) + * support this leaf without leaf 0x4. + */ + setup__amd_cache_legacy(infos, 2, HWLOC_OBJ_CACHE_UNIFIED, 1, ecx); /* private L2u */ + if (edx & 0xf000) + setup__amd_cache_legacy(infos, 3, HWLOC_OBJ_CACHE_UNIFIED, legacy_max_log_proc, edx); /* package-wide L3u */ +} + +/* AMD caches from CPUID 0x8000001d leaf (topoext) */ +static void read_amd_caches_topoext(struct procinfo *infos, struct cpuiddump *src_cpuiddump) +{ + unsigned eax, ebx, ecx, edx; + unsigned cachenum; + struct cacheinfo *cache; + + /* the code below doesn't want any other cache yet */ + assert(!infos->numcaches); + + for (cachenum = 0; ; cachenum++) { + eax = 0x8000001d; + ecx = cachenum; + cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump); + if ((eax & 0x1f) == 0) + break; + infos->numcaches++; + } + + cache = infos->cache = malloc(infos->numcaches * sizeof(*infos->cache)); + if (cache) { + for (cachenum = 0; ; cachenum++) { + unsigned long linesize, linepart, ways, sets; + eax = 0x8000001d; + ecx = cachenum; + cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump); + + if ((eax & 0x1f) == 0) + break; + switch (eax & 0x1f) { + case 1: cache->type = HWLOC_OBJ_CACHE_DATA; break; + case 2: cache->type = HWLOC_OBJ_CACHE_INSTRUCTION; break; + default: cache->type = HWLOC_OBJ_CACHE_UNIFIED; break; + } + + cache->level = (eax >> 5) & 0x7; + /* Note: actually number of cores */ + cache->nbthreads_sharing = ((eax >> 14) & 0xfff) + 1; + + cache->linesize = linesize = (ebx & 0xfff) + 1; + cache->linepart = linepart = ((ebx >> 12) & 0x3ff) + 1; + ways = ((ebx >> 22) & 0x3ff) + 1; + + if (eax & (1 << 9)) + /* Fully associative */ + cache->ways = -1; + else + cache->ways = ways; + cache->sets = sets = ecx + 1; + cache->size = linesize * linepart * ways * sets; + cache->inclusive = edx & 0x2; + + hwloc_debug("cache %u L%u%c t%u linesize %lu linepart %lu ways %lu sets %lu, size %luKB\n", + cachenum, cache->level, + cache->type == HWLOC_OBJ_CACHE_DATA ? 'd' : cache->type == HWLOC_OBJ_CACHE_INSTRUCTION ? 'i' : 'u', + cache->nbthreads_sharing, linesize, linepart, ways, sets, cache->size >> 10); + + cache++; + } + } else { + infos->numcaches = 0; + } +} + +/* Intel cache info from CPUID 0x04 leaf */ +static void read_intel_caches(struct hwloc_x86_backend_data_s *data, struct procinfo *infos, struct cpuiddump *src_cpuiddump) +{ + unsigned level; + struct cacheinfo *tmpcaches; + unsigned eax, ebx, ecx, edx; + unsigned oldnumcaches = infos->numcaches; /* in case we got caches above */ + unsigned cachenum; + struct cacheinfo *cache; + + for (cachenum = 0; ; cachenum++) { + eax = 0x04; + ecx = cachenum; + cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump); + + hwloc_debug("cache %u type %u\n", cachenum, eax & 0x1f); + if ((eax & 0x1f) == 0) + break; + level = (eax >> 5) & 0x7; + if (data->is_knl && level == 3) + /* KNL reports wrong L3 information (size always 0, cpuset always the entire machine, ignore it */ + break; + infos->numcaches++; + } + + tmpcaches = realloc(infos->cache, infos->numcaches * sizeof(*infos->cache)); + if (!tmpcaches) { + infos->numcaches = oldnumcaches; + } else { + infos->cache = tmpcaches; + cache = &infos->cache[oldnumcaches]; + + for (cachenum = 0; ; cachenum++) { + unsigned long linesize, linepart, ways, sets; + eax = 0x04; + ecx = cachenum; + cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump); + + if ((eax & 0x1f) == 0) + break; + level = (eax >> 5) & 0x7; + if (data->is_knl && level == 3) + /* KNL reports wrong L3 information (size always 0, cpuset always the entire machine, ignore it */ + break; + switch (eax & 0x1f) { + case 1: cache->type = HWLOC_OBJ_CACHE_DATA; break; + case 2: cache->type = HWLOC_OBJ_CACHE_INSTRUCTION; break; + default: cache->type = HWLOC_OBJ_CACHE_UNIFIED; break; + } + + cache->level = level; + cache->nbthreads_sharing = ((eax >> 14) & 0xfff) + 1; + + cache->linesize = linesize = (ebx & 0xfff) + 1; + cache->linepart = linepart = ((ebx >> 12) & 0x3ff) + 1; + ways = ((ebx >> 22) & 0x3ff) + 1; + if (eax & (1 << 9)) + /* Fully associative */ + cache->ways = -1; + else + cache->ways = ways; + cache->sets = sets = ecx + 1; + cache->size = linesize * linepart * ways * sets; + cache->inclusive = edx & 0x2; + + hwloc_debug("cache %u L%u%c t%u linesize %lu linepart %lu ways %lu sets %lu, size %luKB\n", + cachenum, cache->level, + cache->type == HWLOC_OBJ_CACHE_DATA ? 'd' : cache->type == HWLOC_OBJ_CACHE_INSTRUCTION ? 'i' : 'u', + cache->nbthreads_sharing, linesize, linepart, ways, sets, cache->size >> 10); + cache++; + } + } +} + +/* AMD core/thread info from CPUID 0x80000008 leaf */ +static void read_amd_cores_legacy(struct procinfo *infos, struct cpuiddump *src_cpuiddump) +{ + unsigned eax, ebx, ecx, edx; + unsigned max_nbcores; + unsigned max_nbthreads; + unsigned coreidsize; + unsigned logprocid; + unsigned threadid __hwloc_attribute_unused; + + eax = 0x80000008; + cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump); + + coreidsize = (ecx >> 12) & 0xf; + hwloc_debug("core ID size: %u\n", coreidsize); + if (!coreidsize) { + max_nbcores = (ecx & 0xff) + 1; + } else + max_nbcores = 1 << coreidsize; + hwloc_debug("Thus max # of cores: %u\n", max_nbcores); + + /* No multithreaded AMD for this old CPUID leaf */ + max_nbthreads = 1 ; + hwloc_debug("and max # of threads: %u\n", max_nbthreads); + + /* legacy_max_log_proc is deprecated, it can be smaller than max_nbcores, + * which is the maximum number of cores that the processor could theoretically support + * (see "Multiple Core Calculation" in the AMD CPUID specification). + * Recompute packageid/coreid accordingly. + */ + infos->ids[PKG] = infos->apicid / max_nbcores; + logprocid = infos->apicid % max_nbcores; + infos->ids[CORE] = logprocid / max_nbthreads; + threadid = logprocid % max_nbthreads; + hwloc_debug("this is thread %u of core %u\n", threadid, infos->ids[CORE]); +} + +/* AMD unit/node from CPUID 0x8000001e leaf (topoext) */ +static void read_amd_cores_topoext(struct procinfo *infos, unsigned long flags, struct cpuiddump *src_cpuiddump) +{ + unsigned apic_id, nodes_per_proc = 0; + unsigned eax, ebx, ecx, edx; + + eax = 0x8000001e; + cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump); + infos->apicid = apic_id = eax; + + if (flags & HWLOC_X86_DISC_FLAG_TOPOEXT_NUMANODES) { + if (infos->cpufamilynumber == 0x16) { + /* ecx is reserved */ + infos->ids[NODE] = 0; + nodes_per_proc = 1; + } else { + /* AMD other families or Hygon family 18h */ + infos->ids[NODE] = ecx & 0xff; + nodes_per_proc = ((ecx >> 8) & 7) + 1; + } + if ((infos->cpufamilynumber == 0x15 && nodes_per_proc > 2) + || ((infos->cpufamilynumber == 0x17 || infos->cpufamilynumber == 0x18) && nodes_per_proc > 4)) { + hwloc_debug("warning: undefined nodes_per_proc value %u, assuming it means %u\n", nodes_per_proc, nodes_per_proc); + } + } + + if (infos->cpufamilynumber <= 0x16) { /* topoext appeared in 0x15 and compute-units were only used in 0x15 and 0x16 */ + unsigned cores_per_unit; + /* coreid was obtained from read_amd_cores_legacy() earlier */ + infos->ids[UNIT] = ebx & 0xff; + cores_per_unit = ((ebx >> 8) & 0xff) + 1; + hwloc_debug("topoext %08x, %u nodes, node %u, %u cores in unit %u\n", apic_id, nodes_per_proc, infos->ids[NODE], cores_per_unit, infos->ids[UNIT]); + /* coreid and unitid are package-wide (core 0-15 and unit 0-7 on 16-core 2-NUMAnode processor). + * The Linux kernel reduces theses to NUMA-node-wide (by applying %core_per_node and %unit_per node respectively). + * It's not clear if we should do this as well. + */ + } else { + unsigned threads_per_core; + infos->ids[CORE] = ebx & 0xff; + threads_per_core = ((ebx >> 8) & 0xff) + 1; + hwloc_debug("topoext %08x, %u nodes, node %u, %u threads in core %u\n", apic_id, nodes_per_proc, infos->ids[NODE], threads_per_core, infos->ids[CORE]); + } +} + +/* Intel core/thread or even die/module/tile from CPUID 0x0b or 0x1f leaves (v1 and v2 extended topology enumeration) */ +static void read_intel_cores_exttopoenum(struct procinfo *infos, unsigned leaf, struct cpuiddump *src_cpuiddump) { unsigned level, apic_nextshift, apic_number, apic_type, apic_id = 0, apic_shift = 0, id; unsigned threadid __hwloc_attribute_unused = 0; /* shut-up compiler */ @@ -302,11 +557,19 @@ static void look_exttopoenum(struct procinfo *infos, unsigned leaf, struct cpuid /* apic_number is the actual number of threads per core */ break; case 2: - infos->coreid = id; - /* apic_number is the actual number of threads per module */ + infos->ids[CORE] = id; + /* apic_number is the actual number of threads per die */ + break; + case 3: + infos->ids[MODULE] = id; + /* apic_number is the actual number of threads per tile */ + break; + case 4: + infos->ids[TILE] = id; + /* apic_number is the actual number of threads per die */ break; case 5: - infos->dieid = id; + infos->ids[DIE] = id; /* apic_number is the actual number of threads per package */ break; default: @@ -317,16 +580,16 @@ static void look_exttopoenum(struct procinfo *infos, unsigned leaf, struct cpuid apic_shift = apic_nextshift; } infos->apicid = apic_id; - infos->packageid = apic_id >> apic_shift; - hwloc_debug("x2APIC remainder: %u\n", infos->packageid); - hwloc_debug("this is thread %u of core %u\n", threadid, infos->coreid); + infos->ids[PKG] = apic_id >> apic_shift; + hwloc_debug("x2APIC remainder: %u\n", infos->ids[PKG]); + hwloc_debug("this is thread %u of core %u\n", threadid, infos->ids[CORE]); } } } /* Fetch information from the processor itself thanks to cpuid and store it in * infos for summarize to analyze them globally */ -static void look_proc(struct hwloc_backend *backend, struct procinfo *infos, unsigned highest_cpuid, unsigned highest_ext_cpuid, unsigned *features, enum cpuid_type cpuid_type, struct cpuiddump *src_cpuiddump) +static void look_proc(struct hwloc_backend *backend, struct procinfo *infos, unsigned long flags, unsigned highest_cpuid, unsigned highest_ext_cpuid, unsigned *features, enum cpuid_type cpuid_type, struct cpuiddump *src_cpuiddump) { struct hwloc_x86_backend_data_s *data = backend->private_data; unsigned eax, ebx, ecx = 0, edx; @@ -348,9 +611,9 @@ static void look_proc(struct hwloc_backend *backend, struct procinfo *infos, uns else legacy_max_log_proc = 1; hwloc_debug("APIC ID 0x%02x legacy_max_log_proc %u\n", infos->apicid, legacy_max_log_proc); - infos->packageid = infos->apicid / legacy_max_log_proc; + infos->ids[PKG] = infos->apicid / legacy_max_log_proc; legacy_log_proc_id = infos->apicid % legacy_max_log_proc; - hwloc_debug("phys %u legacy thread %u\n", infos->packageid, legacy_log_proc_id); + hwloc_debug("phys %u legacy thread %u\n", infos->ids[PKG], legacy_log_proc_id); /* Get cpu model/family/stepping numbers from same cpuid */ _model = (eax>>4) & 0xf; @@ -397,258 +660,88 @@ static void look_proc(struct hwloc_backend *backend, struct procinfo *infos, uns /* infos was calloc'ed, already ends with \0 */ } - /* Get core/thread information from cpuid 0x80000008 - * (not supported on Intel) - */ - if (cpuid_type != intel && cpuid_type != zhaoxin && highest_ext_cpuid >= 0x80000008) { - unsigned max_nbcores; - unsigned max_nbthreads; - unsigned coreidsize; - unsigned logprocid; - eax = 0x80000008; - cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump); - coreidsize = (ecx >> 12) & 0xf; - hwloc_debug("core ID size: %u\n", coreidsize); - if (!coreidsize) { - max_nbcores = (ecx & 0xff) + 1; - } else - max_nbcores = 1 << coreidsize; - hwloc_debug("Thus max # of cores: %u\n", max_nbcores); - /* Still no multithreaded AMD */ - max_nbthreads = 1 ; - hwloc_debug("and max # of threads: %u\n", max_nbthreads); - /* legacy_max_log_proc is deprecated, it can be smaller than max_nbcores, - * which is the maximum number of cores that the processor could theoretically support - * (see "Multiple Core Calculation" in the AMD CPUID specification). - * Recompute packageid/threadid/coreid accordingly. - */ - infos->packageid = infos->apicid / max_nbcores; - logprocid = infos->apicid % max_nbcores; - infos->threadid = logprocid % max_nbthreads; - infos->coreid = logprocid / max_nbthreads; - hwloc_debug("this is thread %u of core %u\n", infos->threadid, infos->coreid); - } - - infos->numcaches = 0; - infos->cache = NULL; - - /* Get apicid, nodeid, unitid from cpuid 0x8000001e - * and cache information from cpuid 0x8000001d - * (AMD topology extension) - */ - if (cpuid_type != intel && cpuid_type != zhaoxin && has_topoext(features)) { - unsigned apic_id, node_id, nodes_per_proc; - - /* the code below doesn't want any other cache yet */ - assert(!infos->numcaches); - - eax = 0x8000001e; - cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump); - infos->apicid = apic_id = eax; - - if (infos->cpufamilynumber == 0x16) { - /* ecx is reserved */ - node_id = 0; - nodes_per_proc = 1; - } else { - /* AMD other families or Hygon family 18h */ - node_id = ecx & 0xff; - nodes_per_proc = ((ecx >> 8) & 7) + 1; - } - infos->nodeid = node_id; - if ((infos->cpufamilynumber == 0x15 && nodes_per_proc > 2) - || ((infos->cpufamilynumber == 0x17 || infos->cpufamilynumber == 0x18) && nodes_per_proc > 4)) { - hwloc_debug("warning: undefined nodes_per_proc value %u, assuming it means %u\n", nodes_per_proc, nodes_per_proc); - } - - if (infos->cpufamilynumber <= 0x16) { /* topoext appeared in 0x15 and compute-units were only used in 0x15 and 0x16 */ - unsigned unit_id, cores_per_unit; - infos->unitid = unit_id = ebx & 0xff; - cores_per_unit = ((ebx >> 8) & 0xff) + 1; - hwloc_debug("topoext %08x, %u nodes, node %u, %u cores in unit %u\n", apic_id, nodes_per_proc, node_id, cores_per_unit, unit_id); - /* coreid and unitid are package-wide (core 0-15 and unit 0-7 on 16-core 2-NUMAnode processor). - * The Linux kernel reduces theses to NUMA-node-wide (by applying %core_per_node and %unit_per node respectively). - * It's not clear if we should do this as well. - */ - } else { - unsigned core_id, threads_per_core; - infos->coreid = core_id = ebx & 0xff; - threads_per_core = ((ebx >> 8) & 0xff) + 1; - hwloc_debug("topoext %08x, %u nodes, node %u, %u threads in core %u\n", apic_id, nodes_per_proc, node_id, threads_per_core, core_id); - } - - for (cachenum = 0; ; cachenum++) { - eax = 0x8000001d; - ecx = cachenum; - cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump); - if ((eax & 0x1f) == 0) - break; - infos->numcaches++; - } - - cache = infos->cache = malloc(infos->numcaches * sizeof(*infos->cache)); - if (cache) { - for (cachenum = 0; ; cachenum++) { - unsigned long linesize, linepart, ways, sets; - eax = 0x8000001d; - ecx = cachenum; - cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump); - - if ((eax & 0x1f) == 0) - break; - switch (eax & 0x1f) { - case 1: cache->type = HWLOC_OBJ_CACHE_DATA; break; - case 2: cache->type = HWLOC_OBJ_CACHE_INSTRUCTION; break; - default: cache->type = HWLOC_OBJ_CACHE_UNIFIED; break; - } - - cache->level = (eax >> 5) & 0x7; - /* Note: actually number of cores */ - cache->nbthreads_sharing = ((eax >> 14) & 0xfff) + 1; - - cache->linesize = linesize = (ebx & 0xfff) + 1; - cache->linepart = linepart = ((ebx >> 12) & 0x3ff) + 1; - ways = ((ebx >> 22) & 0x3ff) + 1; - - if (eax & (1 << 9)) - /* Fully associative */ - cache->ways = -1; - else - cache->ways = ways; - cache->sets = sets = ecx + 1; - cache->size = linesize * linepart * ways * sets; - cache->inclusive = edx & 0x2; - - hwloc_debug("cache %u L%u%c t%u linesize %lu linepart %lu ways %lu sets %lu, size %luKB\n", - cachenum, cache->level, - cache->type == HWLOC_OBJ_CACHE_DATA ? 'd' : cache->type == HWLOC_OBJ_CACHE_INSTRUCTION ? 'i' : 'u', - cache->nbthreads_sharing, linesize, linepart, ways, sets, cache->size >> 10); - - cache++; - } - } else { - infos->numcaches = 0; - } - } else { - /* If there's no topoext, - * get cache information from cpuid 0x80000005 and 0x80000006 - * (not supported on Intel) - */ - if (cpuid_type != intel && cpuid_type != zhaoxin && highest_ext_cpuid >= 0x80000005) { - eax = 0x80000005; - cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump); - fill_amd_cache(infos, 1, HWLOC_OBJ_CACHE_DATA, 1, ecx); /* private L1d */ - fill_amd_cache(infos, 1, HWLOC_OBJ_CACHE_INSTRUCTION, 1, edx); /* private L1i */ - } - if (cpuid_type != intel && cpuid_type != zhaoxin && highest_ext_cpuid >= 0x80000006) { - eax = 0x80000006; - cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump); - if (ecx & 0xf000) - /* This is actually supported on Intel but LinePerTag isn't returned in bits 8-11. - * Could be useful if some Intels (at least before Core micro-architecture) - * support this leaf without leaf 0x4. - */ - fill_amd_cache(infos, 2, HWLOC_OBJ_CACHE_UNIFIED, 1, ecx); /* private L2u */ - if (edx & 0xf000) - fill_amd_cache(infos, 3, HWLOC_OBJ_CACHE_UNIFIED, legacy_max_log_proc, edx); /* package-wide L3u */ - } - } - - /* Get thread/core + cache information from cpuid 0x04 - * (not supported on AMD) - */ if ((cpuid_type != amd && cpuid_type != hygon) && highest_cpuid >= 0x04) { - unsigned max_nbcores; - unsigned max_nbthreads; - unsigned level; - struct cacheinfo *tmpcaches; - unsigned oldnumcaches = infos->numcaches; /* in case we got caches above */ - - for (cachenum = 0; ; cachenum++) { - eax = 0x04; - ecx = cachenum; - cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump); - - hwloc_debug("cache %u type %u\n", cachenum, eax & 0x1f); - if ((eax & 0x1f) == 0) - break; - level = (eax >> 5) & 0x7; - if (data->is_knl && level == 3) - /* KNL reports wrong L3 information (size always 0, cpuset always the entire machine, ignore it */ - break; - infos->numcaches++; - - if (!cachenum) { - /* by the way, get thread/core information from the first cache */ - max_nbcores = ((eax >> 26) & 0x3f) + 1; - max_nbthreads = legacy_max_log_proc / max_nbcores; - hwloc_debug("thus %u threads\n", max_nbthreads); - infos->threadid = legacy_log_proc_id % max_nbthreads; - infos->coreid = legacy_log_proc_id / max_nbthreads; - hwloc_debug("this is thread %u of core %u\n", infos->threadid, infos->coreid); - } + /* Get core/thread information from first cache reported by cpuid 0x04 + * (not supported on AMD) + */ + eax = 0x04; + ecx = 0; + cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump); + if ((eax & 0x1f) != 0) { + /* cache looks valid */ + unsigned max_nbcores; + unsigned max_nbthreads; + unsigned threadid __hwloc_attribute_unused; + max_nbcores = ((eax >> 26) & 0x3f) + 1; + max_nbthreads = legacy_max_log_proc / max_nbcores; + hwloc_debug("thus %u threads\n", max_nbthreads); + threadid = legacy_log_proc_id % max_nbthreads; + infos->ids[CORE] = legacy_log_proc_id / max_nbthreads; + hwloc_debug("this is thread %u of core %u\n", threadid, infos->ids[CORE]); } + } - tmpcaches = realloc(infos->cache, infos->numcaches * sizeof(*infos->cache)); - if (!tmpcaches) { - infos->numcaches = oldnumcaches; - } else { - infos->cache = tmpcaches; - cache = &infos->cache[oldnumcaches]; + /********************************************************************************* + * Get the hierarchy of thread, core, die, package, etc. from CPU-specific leaves + */ - for (cachenum = 0; ; cachenum++) { - unsigned long linesize, linepart, ways, sets; - eax = 0x04; - ecx = cachenum; - cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump); + if (cpuid_type != intel && cpuid_type != zhaoxin && highest_ext_cpuid >= 0x80000008 && !has_x2apic(features)) { + /* Get core/thread information from cpuid 0x80000008 + * (not supported on Intel) + * We could ignore this codepath when x2apic is supported, but we may need + * nodeids if HWLOC_X86_TOPOEXT_NUMANODES is set. + */ + read_amd_cores_legacy(infos, src_cpuiddump); + } - if ((eax & 0x1f) == 0) - break; - level = (eax >> 5) & 0x7; - if (data->is_knl && level == 3) - /* KNL reports wrong L3 information (size always 0, cpuset always the entire machine, ignore it */ - break; - switch (eax & 0x1f) { - case 1: cache->type = HWLOC_OBJ_CACHE_DATA; break; - case 2: cache->type = HWLOC_OBJ_CACHE_INSTRUCTION; break; - default: cache->type = HWLOC_OBJ_CACHE_UNIFIED; break; - } - - cache->level = level; - cache->nbthreads_sharing = ((eax >> 14) & 0xfff) + 1; - - cache->linesize = linesize = (ebx & 0xfff) + 1; - cache->linepart = linepart = ((ebx >> 12) & 0x3ff) + 1; - ways = ((ebx >> 22) & 0x3ff) + 1; - if (eax & (1 << 9)) - /* Fully associative */ - cache->ways = -1; - else - cache->ways = ways; - cache->sets = sets = ecx + 1; - cache->size = linesize * linepart * ways * sets; - cache->inclusive = edx & 0x2; - - hwloc_debug("cache %u L%u%c t%u linesize %lu linepart %lu ways %lu sets %lu, size %luKB\n", - cachenum, cache->level, - cache->type == HWLOC_OBJ_CACHE_DATA ? 'd' : cache->type == HWLOC_OBJ_CACHE_INSTRUCTION ? 'i' : 'u', - cache->nbthreads_sharing, linesize, linepart, ways, sets, cache->size >> 10); - cache++; - } - } + if (cpuid_type != intel && cpuid_type != zhaoxin && has_topoext(features)) { + /* Get apicid, nodeid, unitid/coreid from cpuid 0x8000001e (AMD topology extension). + * Requires read_amd_cores_legacy() for coreid on family 0x15-16. + * + * Only needed when x2apic supported if NUMA nodes are needed. + */ + read_amd_cores_topoext(infos, flags, src_cpuiddump); } if ((cpuid_type == intel) && highest_cpuid >= 0x1f) { /* Get package/die/module/tile/core/thread information from cpuid 0x1f * (Intel v2 Extended Topology Enumeration) */ - look_exttopoenum(infos, 0x1f, src_cpuiddump); + read_intel_cores_exttopoenum(infos, 0x1f, src_cpuiddump); - } else if ((cpuid_type == intel || cpuid_type == zhaoxin) && highest_cpuid >= 0x0b && has_x2apic(features)) { + } else if ((cpuid_type == intel || cpuid_type == amd || cpuid_type == zhaoxin) + && highest_cpuid >= 0x0b && has_x2apic(features)) { /* Get package/core/thread information from cpuid 0x0b * (Intel v1 Extended Topology Enumeration) */ - look_exttopoenum(infos, 0x0b, src_cpuiddump); + read_intel_cores_exttopoenum(infos, 0x0b, src_cpuiddump); + } + + /************************************** + * Get caches from CPU-specific leaves + */ + + infos->numcaches = 0; + infos->cache = NULL; + + if (cpuid_type != intel && cpuid_type != zhaoxin && has_topoext(features)) { + /* Get cache information from cpuid 0x8000001d (AMD topology extension) */ + read_amd_caches_topoext(infos, src_cpuiddump); + + } else if (cpuid_type != intel && cpuid_type != zhaoxin && highest_ext_cpuid >= 0x80000006) { + /* If there's no topoext, + * get cache information from cpuid 0x80000005 and 0x80000006. + * (not supported on Intel) + * It looks like we cannot have 0x80000005 without 0x80000006. + */ + read_amd_caches_legacy(infos, src_cpuiddump, legacy_max_log_proc); + } + + if ((cpuid_type != amd && cpuid_type != hygon) && highest_cpuid >= 0x04) { + /* Get cache information from cpuid 0x04 + * (not supported on AMD) + */ + read_intel_caches(data, infos, src_cpuiddump); } /* Now that we have all info, compute cacheids and apply quirks */ @@ -736,8 +829,55 @@ hwloc_x86_add_cpuinfos(hwloc_obj_t obj, struct procinfo *info, int replace) hwloc__add_info_nodup(&obj->infos, &obj->infos_count, "CPUStepping", number, replace); } +static void +hwloc_x86_add_groups(hwloc_topology_t topology, + struct procinfo *infos, + unsigned nbprocs, + hwloc_bitmap_t remaining_cpuset, + unsigned type, + const char *subtype, + unsigned kind, + int dont_merge) +{ + hwloc_bitmap_t obj_cpuset; + hwloc_obj_t obj; + unsigned i, j; + + while ((i = hwloc_bitmap_first(remaining_cpuset)) != (unsigned) -1) { + unsigned packageid = infos[i].ids[PKG]; + unsigned id = infos[i].ids[type]; + + if (id == (unsigned)-1) { + hwloc_bitmap_clr(remaining_cpuset, i); + continue; + } + + obj_cpuset = hwloc_bitmap_alloc(); + for (j = i; j < nbprocs; j++) { + if (infos[j].ids[type] == (unsigned) -1) { + hwloc_bitmap_clr(remaining_cpuset, j); + continue; + } + + if (infos[j].ids[PKG] == packageid && infos[j].ids[type] == id) { + hwloc_bitmap_set(obj_cpuset, j); + hwloc_bitmap_clr(remaining_cpuset, j); + } + } + + obj = hwloc_alloc_setup_object(topology, HWLOC_OBJ_GROUP, id); + obj->cpuset = obj_cpuset; + obj->subtype = strdup(subtype); + obj->attr->group.kind = kind; + obj->attr->group.dont_merge = dont_merge; + hwloc_debug_2args_bitmap("os %s %u has cpuset %s\n", + subtype, id, obj_cpuset); + hwloc_insert_object_by_cpuset(topology, obj); + } +} + /* Analyse information stored in infos, and build/annotate topology levels accordingly */ -static void summarize(struct hwloc_backend *backend, struct procinfo *infos, int fulldiscovery) +static void summarize(struct hwloc_backend *backend, struct procinfo *infos, unsigned long flags) { struct hwloc_topology *topology = backend->topology; struct hwloc_x86_backend_data_s *data = backend->private_data; @@ -747,6 +887,7 @@ static void summarize(struct hwloc_backend *backend, struct procinfo *infos, int int one = -1; hwloc_bitmap_t remaining_cpuset; int gotnuma = 0; + int fulldiscovery = (flags & HWLOC_X86_DISC_FLAG_FULL); for (i = 0; i < nbprocs; i++) if (infos[i].present) { @@ -773,11 +914,11 @@ static void summarize(struct hwloc_backend *backend, struct procinfo *infos, int hwloc_bitmap_copy(remaining_cpuset, complete_cpuset); while ((i = hwloc_bitmap_first(remaining_cpuset)) != (unsigned) -1) { if (fulldiscovery) { - unsigned packageid = infos[i].packageid; + unsigned packageid = infos[i].ids[PKG]; hwloc_bitmap_t package_cpuset = hwloc_bitmap_alloc(); for (j = i; j < nbprocs; j++) { - if (infos[j].packageid == packageid) { + if (infos[j].ids[PKG] == packageid) { hwloc_bitmap_set(package_cpuset, j); hwloc_bitmap_clr(remaining_cpuset, j); } @@ -811,7 +952,7 @@ static void summarize(struct hwloc_backend *backend, struct procinfo *infos, int } /* Look for Numa nodes inside packages (cannot be filtered-out) */ - if (fulldiscovery && getenv("HWLOC_X86_TOPOEXT_NUMANODES")) { + if (fulldiscovery && (flags & HWLOC_X86_DISC_FLAG_TOPOEXT_NUMANODES)) { hwloc_bitmap_t node_cpuset; hwloc_obj_t node; @@ -819,8 +960,8 @@ static void summarize(struct hwloc_backend *backend, struct procinfo *infos, int hwloc_bitmap_copy(remaining_cpuset, complete_cpuset); while ((i = hwloc_bitmap_first(remaining_cpuset)) != (unsigned) -1) { - unsigned packageid = infos[i].packageid; - unsigned nodeid = infos[i].nodeid; + unsigned packageid = infos[i].ids[PKG]; + unsigned nodeid = infos[i].ids[NODE]; if (nodeid == (unsigned)-1) { hwloc_bitmap_clr(remaining_cpuset, i); @@ -829,12 +970,12 @@ static void summarize(struct hwloc_backend *backend, struct procinfo *infos, int node_cpuset = hwloc_bitmap_alloc(); for (j = i; j < nbprocs; j++) { - if (infos[j].nodeid == (unsigned) -1) { + if (infos[j].ids[NODE] == (unsigned) -1) { hwloc_bitmap_clr(remaining_cpuset, j); continue; } - if (infos[j].packageid == packageid && infos[j].nodeid == nodeid) { + if (infos[j].ids[PKG] == packageid && infos[j].ids[NODE] == nodeid) { hwloc_bitmap_set(node_cpuset, j); hwloc_bitmap_clr(remaining_cpuset, j); } @@ -852,77 +993,21 @@ static void summarize(struct hwloc_backend *backend, struct procinfo *infos, int if (hwloc_filter_check_keep_object_type(topology, HWLOC_OBJ_GROUP)) { if (fulldiscovery) { - char *env; - int dont_merge; - hwloc_bitmap_t unit_cpuset, die_cpuset; - hwloc_obj_t unit, die; - - /* Look for Compute units inside packages */ + /* Look for AMD Compute units inside packages */ hwloc_bitmap_copy(remaining_cpuset, complete_cpuset); - while ((i = hwloc_bitmap_first(remaining_cpuset)) != (unsigned) -1) { - unsigned packageid = infos[i].packageid; - unsigned unitid = infos[i].unitid; - - if (unitid == (unsigned)-1) { - hwloc_bitmap_clr(remaining_cpuset, i); - continue; - } - - unit_cpuset = hwloc_bitmap_alloc(); - for (j = i; j < nbprocs; j++) { - if (infos[j].unitid == (unsigned) -1) { - hwloc_bitmap_clr(remaining_cpuset, j); - continue; - } - - if (infos[j].packageid == packageid && infos[j].unitid == unitid) { - hwloc_bitmap_set(unit_cpuset, j); - hwloc_bitmap_clr(remaining_cpuset, j); - } - } - unit = hwloc_alloc_setup_object(topology, HWLOC_OBJ_GROUP, unitid); - unit->cpuset = unit_cpuset; - unit->subtype = strdup("ComputeUnit"); - unit->attr->group.kind = HWLOC_GROUP_KIND_AMD_COMPUTE_UNIT; - hwloc_debug_1arg_bitmap("os unit %u has cpuset %s\n", - unitid, unit_cpuset); - hwloc_insert_object_by_cpuset(topology, unit); - } - - /* Look for Dies inside packages */ - env = getenv("HWLOC_DONT_MERGE_DIE_GROUPS"); - dont_merge = env && atoi(env); + hwloc_x86_add_groups(topology, infos, nbprocs, remaining_cpuset, + UNIT, "Compute Unit", + HWLOC_GROUP_KIND_AMD_COMPUTE_UNIT, 0); + /* Look for Intel Modules inside packages */ hwloc_bitmap_copy(remaining_cpuset, complete_cpuset); - while ((i = hwloc_bitmap_first(remaining_cpuset)) != (unsigned) -1) { - unsigned packageid = infos[i].packageid; - unsigned dieid = infos[i].dieid; - - if (dieid == (unsigned)-1) { - hwloc_bitmap_clr(remaining_cpuset, i); - continue; - } - - die_cpuset = hwloc_bitmap_alloc(); - for (j = i; j < nbprocs; j++) { - if (infos[j].dieid == (unsigned) -1) { - hwloc_bitmap_clr(remaining_cpuset, j); - continue; - } - - if (infos[j].packageid == packageid && infos[j].dieid == dieid) { - hwloc_bitmap_set(die_cpuset, j); - hwloc_bitmap_clr(remaining_cpuset, j); - } - } - die = hwloc_alloc_setup_object(topology, HWLOC_OBJ_GROUP, dieid); - die->cpuset = die_cpuset; - die->subtype = strdup("Die"); - die->attr->group.kind = HWLOC_GROUP_KIND_INTEL_DIE; - die->attr->group.dont_merge = dont_merge; - hwloc_debug_1arg_bitmap("os die %u has cpuset %s\n", - dieid, die_cpuset); - hwloc_insert_object_by_cpuset(topology, die); - } + hwloc_x86_add_groups(topology, infos, nbprocs, remaining_cpuset, + MODULE, "Module", + HWLOC_GROUP_KIND_INTEL_MODULE, 0); + /* Look for Intel Tiles inside packages */ + hwloc_bitmap_copy(remaining_cpuset, complete_cpuset); + hwloc_x86_add_groups(topology, infos, nbprocs, remaining_cpuset, + TILE, "Tile", + HWLOC_GROUP_KIND_INTEL_TILE, 0); /* Look for unknown objects */ if (infos[one].otherids) { @@ -956,6 +1041,43 @@ static void summarize(struct hwloc_backend *backend, struct procinfo *infos, int } } + if (hwloc_filter_check_keep_object_type(topology, HWLOC_OBJ_DIE)) { + /* Look for Intel Dies inside packages */ + if (fulldiscovery) { + hwloc_bitmap_t die_cpuset; + hwloc_obj_t die; + + hwloc_bitmap_copy(remaining_cpuset, complete_cpuset); + while ((i = hwloc_bitmap_first(remaining_cpuset)) != (unsigned) -1) { + unsigned packageid = infos[i].ids[PKG]; + unsigned dieid = infos[i].ids[DIE]; + + if (dieid == (unsigned) -1) { + hwloc_bitmap_clr(remaining_cpuset, i); + continue; + } + + die_cpuset = hwloc_bitmap_alloc(); + for (j = i; j < nbprocs; j++) { + if (infos[j].ids[DIE] == (unsigned) -1) { + hwloc_bitmap_clr(remaining_cpuset, j); + continue; + } + + if (infos[j].ids[PKG] == packageid && infos[j].ids[DIE] == dieid) { + hwloc_bitmap_set(die_cpuset, j); + hwloc_bitmap_clr(remaining_cpuset, j); + } + } + die = hwloc_alloc_setup_object(topology, HWLOC_OBJ_DIE, dieid); + die->cpuset = die_cpuset; + hwloc_debug_1arg_bitmap("os die %u has cpuset %s\n", + dieid, die_cpuset); + hwloc_insert_object_by_cpuset(topology, die); + } + } + } + if (hwloc_filter_check_keep_object_type(topology, HWLOC_OBJ_CORE)) { /* Look for cores */ if (fulldiscovery) { @@ -964,9 +1086,9 @@ static void summarize(struct hwloc_backend *backend, struct procinfo *infos, int hwloc_bitmap_copy(remaining_cpuset, complete_cpuset); while ((i = hwloc_bitmap_first(remaining_cpuset)) != (unsigned) -1) { - unsigned packageid = infos[i].packageid; - unsigned nodeid = infos[i].nodeid; - unsigned coreid = infos[i].coreid; + unsigned packageid = infos[i].ids[PKG]; + unsigned nodeid = infos[i].ids[NODE]; + unsigned coreid = infos[i].ids[CORE]; if (coreid == (unsigned) -1) { hwloc_bitmap_clr(remaining_cpuset, i); @@ -975,12 +1097,12 @@ static void summarize(struct hwloc_backend *backend, struct procinfo *infos, int core_cpuset = hwloc_bitmap_alloc(); for (j = i; j < nbprocs; j++) { - if (infos[j].coreid == (unsigned) -1) { + if (infos[j].ids[CORE] == (unsigned) -1) { hwloc_bitmap_clr(remaining_cpuset, j); continue; } - if (infos[j].packageid == packageid && infos[j].nodeid == nodeid && infos[j].coreid == coreid) { + if (infos[j].ids[PKG] == packageid && infos[j].ids[NODE] == nodeid && infos[j].ids[CORE] == coreid) { hwloc_bitmap_set(core_cpuset, j); hwloc_bitmap_clr(remaining_cpuset, j); } @@ -1056,7 +1178,7 @@ static void summarize(struct hwloc_backend *backend, struct procinfo *infos, int } else { /* Add the missing cache */ hwloc_bitmap_t cache_cpuset; - unsigned packageid = infos[i].packageid; + unsigned packageid = infos[i].ids[PKG]; unsigned cacheid = infos[i].cache[l].cacheid; /* Now look for others sharing it */ cache_cpuset = hwloc_bitmap_alloc(); @@ -1071,7 +1193,7 @@ static void summarize(struct hwloc_backend *backend, struct procinfo *infos, int hwloc_bitmap_clr(remaining_cpuset, j); continue; } - if (infos[j].packageid == packageid && infos[j].cache[l2].cacheid == cacheid) { + if (infos[j].ids[PKG] == packageid && infos[j].cache[l2].cacheid == cacheid) { hwloc_bitmap_set(cache_cpuset, j); hwloc_bitmap_clr(remaining_cpuset, j); } @@ -1103,7 +1225,7 @@ static void summarize(struct hwloc_backend *backend, struct procinfo *infos, int } static int -look_procs(struct hwloc_backend *backend, struct procinfo *infos, int fulldiscovery, +look_procs(struct hwloc_backend *backend, struct procinfo *infos, unsigned long flags, unsigned highest_cpuid, unsigned highest_ext_cpuid, unsigned *features, enum cpuid_type cpuid_type, int (*get_cpubind)(hwloc_topology_t topology, hwloc_cpuset_t set, int flags), int (*set_cpubind)(hwloc_topology_t topology, hwloc_const_cpuset_t set, int flags)) @@ -1139,7 +1261,7 @@ look_procs(struct hwloc_backend *backend, struct procinfo *infos, int fulldiscov } } - look_proc(backend, &infos[i], highest_cpuid, highest_ext_cpuid, features, cpuid_type, src_cpuiddump); + look_proc(backend, &infos[i], flags, highest_cpuid, highest_ext_cpuid, features, cpuid_type, src_cpuiddump); if (data->src_cpuiddump_path) { cpuiddump_free(src_cpuiddump); @@ -1152,10 +1274,10 @@ look_procs(struct hwloc_backend *backend, struct procinfo *infos, int fulldiscov hwloc_bitmap_free(orig_cpuset); } - if (!data->apicid_unique) - fulldiscovery = 0; - else - summarize(backend, infos, fulldiscovery); + if (data->apicid_unique) + summarize(backend, infos, flags); + /* if !data->apicid_unique, do nothing and return success, so that the caller does nothing either */ + return 0; } @@ -1223,7 +1345,7 @@ static int fake_set_cpubind(hwloc_topology_t topology __hwloc_attribute_unused, } static -int hwloc_look_x86(struct hwloc_backend *backend, int fulldiscovery) +int hwloc_look_x86(struct hwloc_backend *backend, unsigned long flags) { struct hwloc_x86_backend_data_s *data = backend->private_data; unsigned nbprocs = data->nbprocs; @@ -1245,13 +1367,18 @@ int hwloc_look_x86(struct hwloc_backend *backend, int fulldiscovery) int ret = -1; if (data->src_cpuiddump_path) { - /* just read cpuid from the dump */ + /* Just read cpuid from the dump (implies !topology->is_thissystem by default) */ src_cpuiddump = cpuiddump_read(data->src_cpuiddump_path, 0); if (!src_cpuiddump) goto out; } else { - /* otherwise check if binding works */ + /* Using real hardware. + * However we don't enforce topology->is_thissystem so that + * we may still force use this backend when debugging with !thissystem. + */ + + /* check if binding works */ memset(&hooks, 0, sizeof(hooks)); support.membind = &memsupport; hwloc_set_native_binding_hooks(&hooks, &support); @@ -1281,12 +1408,13 @@ int hwloc_look_x86(struct hwloc_backend *backend, int fulldiscovery) if (NULL == infos) goto out; for (i = 0; i < nbprocs; i++) { - infos[i].nodeid = (unsigned) -1; - infos[i].packageid = (unsigned) -1; - infos[i].dieid = (unsigned) -1; - infos[i].unitid = (unsigned) -1; - infos[i].coreid = (unsigned) -1; - infos[i].threadid = (unsigned) -1; + infos[i].ids[PKG] = (unsigned) -1; + infos[i].ids[CORE] = (unsigned) -1; + infos[i].ids[NODE] = (unsigned) -1; + infos[i].ids[UNIT] = (unsigned) -1; + infos[i].ids[TILE] = (unsigned) -1; + infos[i].ids[MODULE] = (unsigned) -1; + infos[i].ids[DIE] = (unsigned) -1; } eax = 0x00; @@ -1334,7 +1462,7 @@ int hwloc_look_x86(struct hwloc_backend *backend, int fulldiscovery) hwloc_x86_os_state_save(&os_state, src_cpuiddump); - ret = look_procs(backend, infos, fulldiscovery, + ret = look_procs(backend, infos, flags, highest_cpuid, highest_ext_cpuid, features, cpuid_type, get_cpubind, set_cpubind); if (!ret) @@ -1343,8 +1471,8 @@ int hwloc_look_x86(struct hwloc_backend *backend, int fulldiscovery) if (nbprocs == 1) { /* only one processor, no need to bind */ - look_proc(backend, &infos[0], highest_cpuid, highest_ext_cpuid, features, cpuid_type, src_cpuiddump); - summarize(backend, infos, fulldiscovery); + look_proc(backend, &infos[0], flags, highest_cpuid, highest_ext_cpuid, features, cpuid_type, src_cpuiddump); + summarize(backend, infos, flags); ret = 0; } @@ -1367,13 +1495,20 @@ out: } static int -hwloc_x86_discover(struct hwloc_backend *backend) +hwloc_x86_discover(struct hwloc_backend *backend, struct hwloc_disc_status *dstatus) { struct hwloc_x86_backend_data_s *data = backend->private_data; struct hwloc_topology *topology = backend->topology; + unsigned long flags = 0; int alreadypus = 0; int ret; + assert(dstatus->phase == HWLOC_DISC_PHASE_CPU); + + if (getenv("HWLOC_X86_TOPOEXT_NUMANODES")) { + flags |= HWLOC_X86_DISC_FLAG_TOPOEXT_NUMANODES; + } + #if HAVE_DECL_RUNNING_ON_VALGRIND if (RUNNING_ON_VALGRIND && !data->src_cpuiddump_path) { fprintf(stderr, "hwloc x86 backend cannot work under Valgrind, disabling.\n" @@ -1387,7 +1522,7 @@ hwloc_x86_discover(struct hwloc_backend *backend) assert(data->nbprocs > 0); /* enforced by hwloc_x86_component_instantiate() */ topology->support.discovery->pu = 1; } else { - int nbprocs = hwloc_fallback_nbprocessors(topology); + int nbprocs = hwloc_fallback_nbprocessors(HWLOC_FALLBACK_NBPROCESSORS_INCLUDE_OFFLINE); if (nbprocs >= 1) topology->support.discovery->pu = 1; else @@ -1405,7 +1540,7 @@ hwloc_x86_discover(struct hwloc_backend *backend) /* several object types were added, we can't easily complete, just do partial discovery */ hwloc_topology_reconnect(topology, 0); - ret = hwloc_look_x86(backend, 0); + ret = hwloc_look_x86(backend, flags); if (ret) hwloc_obj_add_info(topology->levels[0][0], "Backend", "x86"); return 0; @@ -1415,7 +1550,7 @@ hwloc_x86_discover(struct hwloc_backend *backend) } fulldiscovery: - if (hwloc_look_x86(backend, 1) < 0) { + if (hwloc_look_x86(backend, flags | HWLOC_X86_DISC_FLAG_FULL) < 0) { /* if failed, create PUs */ if (!alreadypus) hwloc_setup_pu_level(topology, data->nbprocs); @@ -1446,6 +1581,7 @@ hwloc_x86_check_cpuiddump_input(const char *src_cpuiddump_path, hwloc_bitmap_t s #if !(defined HWLOC_WIN_SYS && !defined __MINGW32__ && !defined __CYGWIN__) /* needs a lot of work */ struct dirent *dirent; DIR *dir; + char *path; FILE *file; char line [32]; @@ -1453,23 +1589,26 @@ hwloc_x86_check_cpuiddump_input(const char *src_cpuiddump_path, hwloc_bitmap_t s if (!dir) return -1; - char path[strlen(src_cpuiddump_path) + strlen("/hwloc-cpuid-info") + 1]; + path = malloc(strlen(src_cpuiddump_path) + strlen("/hwloc-cpuid-info") + 1); + if (!path) + goto out_with_dir; sprintf(path, "%s/hwloc-cpuid-info", src_cpuiddump_path); file = fopen(path, "r"); if (!file) { fprintf(stderr, "Couldn't open dumped cpuid summary %s\n", path); - goto out_with_dir; + goto out_with_path; } if (!fgets(line, sizeof(line), file)) { fprintf(stderr, "Found read dumped cpuid summary in %s\n", path); fclose(file); - goto out_with_dir; + goto out_with_path; } fclose(file); if (strcmp(line, "Architecture: x86\n")) { fprintf(stderr, "Found non-x86 dumped cpuid summary in %s: %s\n", path, line); - goto out_with_dir; + goto out_with_path; } + free(path); while ((dirent = readdir(dir)) != NULL) { if (!strncmp(dirent->d_name, "pu", 2)) { @@ -1497,7 +1636,9 @@ hwloc_x86_check_cpuiddump_input(const char *src_cpuiddump_path, hwloc_bitmap_t s return 0; -out_with_dir: + out_with_path: + free(path); + out_with_dir: closedir(dir); #endif /* HWLOC_WIN_SYS & !__MINGW32__ needs a lot of work */ return -1; @@ -1513,7 +1654,9 @@ hwloc_x86_backend_disable(struct hwloc_backend *backend) } static struct hwloc_backend * -hwloc_x86_component_instantiate(struct hwloc_disc_component *component, +hwloc_x86_component_instantiate(struct hwloc_topology *topology, + struct hwloc_disc_component *component, + unsigned excluded_phases __hwloc_attribute_unused, const void *_data1 __hwloc_attribute_unused, const void *_data2 __hwloc_attribute_unused, const void *_data3 __hwloc_attribute_unused) @@ -1522,7 +1665,7 @@ hwloc_x86_component_instantiate(struct hwloc_disc_component *component, struct hwloc_x86_backend_data_s *data; const char *src_cpuiddump_path; - backend = hwloc_backend_alloc(component); + backend = hwloc_backend_alloc(topology, component); if (!backend) goto out; @@ -1565,9 +1708,9 @@ hwloc_x86_component_instantiate(struct hwloc_disc_component *component, } static struct hwloc_disc_component hwloc_x86_disc_component = { - HWLOC_DISC_COMPONENT_TYPE_CPU, "x86", - HWLOC_DISC_COMPONENT_TYPE_GLOBAL, + HWLOC_DISC_PHASE_CPU, + HWLOC_DISC_PHASE_GLOBAL, hwloc_x86_component_instantiate, 45, /* between native and no_os */ 1, diff --git a/src/3rdparty/hwloc/src/topology-xml-nolibxml.c b/src/3rdparty/hwloc/src/topology-xml-nolibxml.c index 5a0d02da..d0e9ec16 100644 --- a/src/3rdparty/hwloc/src/topology-xml-nolibxml.c +++ b/src/3rdparty/hwloc/src/topology-xml-nolibxml.c @@ -1,18 +1,18 @@ /* * Copyright © 2009 CNRS - * Copyright © 2009-2018 Inria. All rights reserved. + * Copyright © 2009-2019 Inria. All rights reserved. * Copyright © 2009-2011 Université Bordeaux * Copyright © 2009-2011 Cisco Systems, Inc. All rights reserved. * See COPYING in top-level directory. */ -#include -#include -#include -#include -#include -#include -#include +#include "private/autogen/config.h" +#include "hwloc.h" +#include "hwloc/plugins.h" +#include "private/private.h" +#include "private/misc.h" +#include "private/xml.h" +#include "private/debug.h" #include #include @@ -27,9 +27,8 @@ *******************/ struct hwloc__nolibxml_backend_data_s { - size_t buflen; /* size of both buffer and copy buffers, set during backend_init() */ + size_t buflen; /* size of both buffer, set during backend_init() */ char *buffer; /* allocated and filled during backend_init() */ - char *copy; /* allocated during backend_init(), used later during actual parsing */ }; typedef struct hwloc__nolibxml_import_state_data_s { @@ -260,14 +259,11 @@ hwloc_nolibxml_look_init(struct hwloc_xml_backend_data_s *bdata, struct hwloc__nolibxml_backend_data_s *nbdata = bdata->data; unsigned major, minor; char *end; - char *buffer; + char *buffer = nbdata->buffer; + char *tagname; HWLOC_BUILD_ASSERT(sizeof(*nstate) <= sizeof(state->data)); - /* use a copy in the temporary buffer, we may modify during parsing */ - buffer = nbdata->copy; - memcpy(buffer, nbdata->buffer, nbdata->buflen); - /* skip headers */ while (!strncmp(buffer, "version_major = major; bdata->version_minor = minor; end = strchr(buffer, '>') + 1; + tagname = "topology"; } else if (!strncmp(buffer, "", 10)) { bdata->version_major = 1; bdata->version_minor = 0; end = buffer + 10; + tagname = "topology"; } else if (!strncmp(buffer, "", 6)) { bdata->version_major = 0; bdata->version_minor = 9; end = buffer + 6; + tagname = "root"; } else goto failed; @@ -301,7 +300,7 @@ hwloc_nolibxml_look_init(struct hwloc_xml_backend_data_s *bdata, state->parent = NULL; nstate->closed = 0; nstate->tagbuffer = end; - nstate->tagname = (char *) "topology"; + nstate->tagname = tagname; nstate->attrbuffer = NULL; return 0; /* success */ @@ -320,10 +319,6 @@ hwloc_nolibxml_free_buffers(struct hwloc_xml_backend_data_s *bdata) free(nbdata->buffer); nbdata->buffer = NULL; } - if (nbdata->copy) { - free(nbdata->copy); - nbdata->copy = NULL; - } } static void @@ -429,19 +424,11 @@ hwloc_nolibxml_backend_init(struct hwloc_xml_backend_data_s *bdata, goto out_with_nbdata; } - /* allocate a temporary copy buffer that we may modify during parsing */ - nbdata->copy = malloc(nbdata->buflen+1); - if (!nbdata->copy) - goto out_with_buffer; - nbdata->copy[nbdata->buflen] = '\0'; - bdata->look_init = hwloc_nolibxml_look_init; bdata->look_done = hwloc_nolibxml_look_done; bdata->backend_exit = hwloc_nolibxml_backend_exit; return 0; -out_with_buffer: - free(nbdata->buffer); out_with_nbdata: free(nbdata); out: @@ -666,7 +653,7 @@ hwloc__nolibxml_export_end_object(hwloc__xml_export_state_t state, const char *n } static void -hwloc__nolibxml_export_add_content(hwloc__xml_export_state_t state, const char *buffer, size_t length) +hwloc__nolibxml_export_add_content(hwloc__xml_export_state_t state, const char *buffer, size_t length __hwloc_attribute_unused) { hwloc__nolibxml_export_state_data_t ndata = (void *) state->data; int res; @@ -678,7 +665,7 @@ hwloc__nolibxml_export_add_content(hwloc__xml_export_state_t state, const char * } ndata->has_content = 1; - res = hwloc_snprintf(ndata->buffer, ndata->remaining, buffer, length); + res = hwloc_snprintf(ndata->buffer, ndata->remaining, "%s", buffer); hwloc__nolibxml_export_update_buffer(ndata, res); } @@ -799,6 +786,7 @@ hwloc___nolibxml_prepare_export_diff(hwloc_topology_diff_t diff, const char *ref state.new_prop = hwloc__nolibxml_export_new_prop; state.add_content = hwloc__nolibxml_export_add_content; state.end_object = hwloc__nolibxml_export_end_object; + state.global = NULL; ndata->indent = 0; ndata->written = 0; diff --git a/src/3rdparty/hwloc/src/topology-xml.c b/src/3rdparty/hwloc/src/topology-xml.c index e7c5ef62..f6bb210c 100644 --- a/src/3rdparty/hwloc/src/topology-xml.c +++ b/src/3rdparty/hwloc/src/topology-xml.c @@ -6,12 +6,12 @@ * See COPYING in top-level directory. */ -#include -#include -#include -#include -#include -#include +#include "private/autogen/config.h" +#include "hwloc.h" +#include "private/xml.h" +#include "private/private.h" +#include "private/misc.h" +#include "private/debug.h" #include @@ -158,7 +158,7 @@ hwloc__xml_import_object_attr(struct hwloc_topology *topology, else if (!strcmp(name, "cache_size")) { unsigned long long lvalue = strtoull(value, NULL, 10); - if (hwloc__obj_type_is_cache(obj->type) || obj->type == _HWLOC_OBJ_CACHE_OLD) + if (hwloc__obj_type_is_cache(obj->type) || obj->type == _HWLOC_OBJ_CACHE_OLD || obj->type == HWLOC_OBJ_MEMCACHE) obj->attr->cache.size = lvalue; else if (hwloc__xml_verbose()) fprintf(stderr, "%s: ignoring cache_size attribute for non-cache object type\n", @@ -167,7 +167,7 @@ hwloc__xml_import_object_attr(struct hwloc_topology *topology, else if (!strcmp(name, "cache_linesize")) { unsigned long lvalue = strtoul(value, NULL, 10); - if (hwloc__obj_type_is_cache(obj->type) || obj->type == _HWLOC_OBJ_CACHE_OLD) + if (hwloc__obj_type_is_cache(obj->type) || obj->type == _HWLOC_OBJ_CACHE_OLD || obj->type == HWLOC_OBJ_MEMCACHE) obj->attr->cache.linesize = lvalue; else if (hwloc__xml_verbose()) fprintf(stderr, "%s: ignoring cache_linesize attribute for non-cache object type\n", @@ -176,7 +176,7 @@ hwloc__xml_import_object_attr(struct hwloc_topology *topology, else if (!strcmp(name, "cache_associativity")) { int lvalue = atoi(value); - if (hwloc__obj_type_is_cache(obj->type) || obj->type == _HWLOC_OBJ_CACHE_OLD) + if (hwloc__obj_type_is_cache(obj->type) || obj->type == _HWLOC_OBJ_CACHE_OLD || obj->type == HWLOC_OBJ_MEMCACHE) obj->attr->cache.associativity = lvalue; else if (hwloc__xml_verbose()) fprintf(stderr, "%s: ignoring cache_associativity attribute for non-cache object type\n", @@ -185,7 +185,7 @@ hwloc__xml_import_object_attr(struct hwloc_topology *topology, else if (!strcmp(name, "cache_type")) { unsigned long lvalue = strtoul(value, NULL, 10); - if (hwloc__obj_type_is_cache(obj->type) || obj->type == _HWLOC_OBJ_CACHE_OLD) { + if (hwloc__obj_type_is_cache(obj->type) || obj->type == _HWLOC_OBJ_CACHE_OLD || obj->type == HWLOC_OBJ_MEMCACHE) { if (lvalue == HWLOC_OBJ_CACHE_UNIFIED || lvalue == HWLOC_OBJ_CACHE_DATA || lvalue == HWLOC_OBJ_CACHE_INSTRUCTION) @@ -211,7 +211,7 @@ hwloc__xml_import_object_attr(struct hwloc_topology *topology, else if (!strcmp(name, "depth")) { unsigned long lvalue = strtoul(value, NULL, 10); - if (hwloc__obj_type_is_cache(obj->type) || obj->type == _HWLOC_OBJ_CACHE_OLD) { + if (hwloc__obj_type_is_cache(obj->type) || obj->type == _HWLOC_OBJ_CACHE_OLD || obj->type == HWLOC_OBJ_MEMCACHE) { obj->attr->cache.depth = lvalue; } else if (obj->type == HWLOC_OBJ_GROUP || obj->type == HWLOC_OBJ_BRIDGE) { /* will be overwritten by the core */ @@ -805,21 +805,13 @@ hwloc__xml_import_object(hwloc_topology_t topology, state->global->msgprefix); goto error_with_object; } - } else if (!strcasecmp(attrvalue, "Die")) { - /* deal with possible future type */ - obj->type = HWLOC_OBJ_GROUP; - obj->subtype = strdup("Die"); - obj->attr->group.kind = HWLOC_GROUP_KIND_INTEL_DIE; - obj->attr->group.dont_merge = data->dont_merge_die_groups; } else if (!strcasecmp(attrvalue, "Tile")) { /* deal with possible future type */ obj->type = HWLOC_OBJ_GROUP; - obj->subtype = strdup("Tile"); obj->attr->group.kind = HWLOC_GROUP_KIND_INTEL_TILE; } else if (!strcasecmp(attrvalue, "Module")) { /* deal with possible future type */ obj->type = HWLOC_OBJ_GROUP; - obj->subtype = strdup("Module"); obj->attr->group.kind = HWLOC_GROUP_KIND_INTEL_MODULE; } else if (!strcasecmp(attrvalue, "MemCache")) { /* ignore possible future type */ @@ -1053,6 +1045,13 @@ hwloc__xml_import_object(hwloc_topology_t topology, /* end of 1.x specific checks */ } + /* 2.0 backward compatibility */ + if (obj->type == HWLOC_OBJ_GROUP) { + if (obj->attr->group.kind == HWLOC_GROUP_KIND_INTEL_DIE + || (obj->subtype && !strcmp(obj->subtype, "Die"))) + obj->type = HWLOC_OBJ_DIE; + } + /* check that cache attributes are coherent with the actual type */ if (hwloc__obj_type_is_cache(obj->type) && obj->type != hwloc_cache_type_by_depth_type(obj->attr->cache.depth, obj->attr->cache.type)) { @@ -1212,19 +1211,24 @@ hwloc__xml_import_object(hwloc_topology_t topology, static int hwloc__xml_v2import_distances(hwloc_topology_t topology, - hwloc__xml_import_state_t state) + hwloc__xml_import_state_t state, + int heterotypes) { - hwloc_obj_type_t type = HWLOC_OBJ_TYPE_NONE; + hwloc_obj_type_t unique_type = HWLOC_OBJ_TYPE_NONE; + hwloc_obj_type_t *different_types = NULL; unsigned nbobjs = 0; - int indexing = 0; + int indexing = heterotypes; int os_indexing = 0; - int gp_indexing = 0; + int gp_indexing = heterotypes; + char *name = NULL; unsigned long kind = 0; unsigned nr_indexes, nr_u64values; uint64_t *indexes; uint64_t *u64values; int ret; +#define _TAG_NAME (heterotypes ? "distances2hetero" : "distances2") + /* process attributes */ while (1) { char *attrname, *attrvalue; @@ -1233,8 +1237,12 @@ hwloc__xml_v2import_distances(hwloc_topology_t topology, if (!strcmp(attrname, "nbobjs")) nbobjs = strtoul(attrvalue, NULL, 10); else if (!strcmp(attrname, "type")) { - if (hwloc_type_sscanf(attrvalue, &type, NULL, 0) < 0) + if (hwloc_type_sscanf(attrvalue, &unique_type, NULL, 0) < 0) { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: unrecognized %s type %s\n", + state->global->msgprefix, _TAG_NAME, attrvalue); goto out; + } } else if (!strcmp(attrname, "indexing")) { indexing = 1; @@ -1246,27 +1254,32 @@ hwloc__xml_v2import_distances(hwloc_topology_t topology, else if (!strcmp(attrname, "kind")) { kind = strtoul(attrvalue, NULL, 10); } + else if (!strcmp(attrname, "name")) { + name = attrvalue; + } else { if (hwloc__xml_verbose()) - fprintf(stderr, "%s: ignoring unknown distance attribute %s\n", - state->global->msgprefix, attrname); + fprintf(stderr, "%s: ignoring unknown %s attribute %s\n", + state->global->msgprefix, _TAG_NAME, attrname); } } /* abort if missing attribute */ - if (!nbobjs || type == HWLOC_OBJ_TYPE_NONE || !indexing || !kind) { + if (!nbobjs || (!heterotypes && unique_type == HWLOC_OBJ_TYPE_NONE) || !indexing || !kind) { if (hwloc__xml_verbose()) - fprintf(stderr, "%s: distance2 missing some attributes\n", - state->global->msgprefix); + fprintf(stderr, "%s: %s missing some attributes\n", + state->global->msgprefix, _TAG_NAME); goto out; } indexes = malloc(nbobjs*sizeof(*indexes)); u64values = malloc(nbobjs*nbobjs*sizeof(*u64values)); - if (!indexes || !u64values) { + if (heterotypes) + different_types = malloc(nbobjs*sizeof(*different_types)); + if (!indexes || !u64values || (heterotypes && !different_types)) { if (hwloc__xml_verbose()) - fprintf(stderr, "%s: failed to allocate distances arrays for %u objects\n", - state->global->msgprefix, nbobjs); + fprintf(stderr, "%s: failed to allocate %s arrays for %u objects\n", + state->global->msgprefix, _TAG_NAME, nbobjs); goto out_with_arrays; } @@ -1290,16 +1303,16 @@ hwloc__xml_v2import_distances(hwloc_topology_t topology, is_u64values = 1; if (!is_index && !is_u64values) { if (hwloc__xml_verbose()) - fprintf(stderr, "%s: distance2 with unrecognized child %s\n", - state->global->msgprefix, tag); + fprintf(stderr, "%s: %s with unrecognized child %s\n", + state->global->msgprefix, _TAG_NAME, tag); goto out_with_arrays; } if (state->global->next_attr(&childstate, &attrname, &attrvalue) < 0 || strcmp(attrname, "length")) { if (hwloc__xml_verbose()) - fprintf(stderr, "%s: distance2 child must have length attribute\n", - state->global->msgprefix); + fprintf(stderr, "%s: %s child must have length attribute\n", + state->global->msgprefix, _TAG_NAME); goto out_with_arrays; } length = atoi(attrvalue); @@ -1307,24 +1320,43 @@ hwloc__xml_v2import_distances(hwloc_topology_t topology, ret = state->global->get_content(&childstate, &buffer, length); if (ret < 0) { if (hwloc__xml_verbose()) - fprintf(stderr, "%s: distance2 child needs content of length %d\n", - state->global->msgprefix, length); + fprintf(stderr, "%s: %s child needs content of length %d\n", + state->global->msgprefix, _TAG_NAME, length); goto out_with_arrays; } if (is_index) { /* get indexes */ - char *tmp; + char *tmp, *tmp2; if (nr_indexes >= nbobjs) { if (hwloc__xml_verbose()) - fprintf(stderr, "%s: distance2 with more than %u indexes\n", - state->global->msgprefix, nbobjs); + fprintf(stderr, "%s: %s with more than %u indexes\n", + state->global->msgprefix, _TAG_NAME, nbobjs); goto out_with_arrays; } tmp = buffer; while (1) { char *next; - unsigned long long u = strtoull(tmp, &next, 0); + unsigned long long u; + if (heterotypes) { + hwloc_obj_type_t t = HWLOC_OBJ_TYPE_NONE; + if (hwloc_type_sscanf(tmp, &t, NULL, 0) < 0) { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: %s with unrecognized heterogeneous type %s\n", + state->global->msgprefix, _TAG_NAME, tmp); + goto out_with_arrays; + } + tmp2 = strchr(tmp, ':'); + if (!tmp2) { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: %s with missing colon after heterogeneous type %s\n", + state->global->msgprefix, _TAG_NAME, tmp); + goto out_with_arrays; + } + tmp = tmp2+1; + different_types[nr_indexes] = t; + } + u = strtoull(tmp, &next, 0); if (next == tmp) break; indexes[nr_indexes++] = u; @@ -1340,8 +1372,8 @@ hwloc__xml_v2import_distances(hwloc_topology_t topology, char *tmp; if (nr_u64values >= nbobjs*nbobjs) { if (hwloc__xml_verbose()) - fprintf(stderr, "%s: distance2 with more than %u u64values\n", - state->global->msgprefix, nbobjs*nbobjs); + fprintf(stderr, "%s: %s with more than %u u64values\n", + state->global->msgprefix, _TAG_NAME, nbobjs*nbobjs); goto out_with_arrays; } tmp = buffer; @@ -1364,8 +1396,8 @@ hwloc__xml_v2import_distances(hwloc_topology_t topology, ret = state->global->close_tag(&childstate); if (ret < 0) { if (hwloc__xml_verbose()) - fprintf(stderr, "%s: distance2 with more than %u indexes\n", - state->global->msgprefix, nbobjs); + fprintf(stderr, "%s: %s with more than %u indexes\n", + state->global->msgprefix, _TAG_NAME, nbobjs); goto out_with_arrays; } @@ -1374,56 +1406,60 @@ hwloc__xml_v2import_distances(hwloc_topology_t topology, if (nr_indexes != nbobjs) { if (hwloc__xml_verbose()) - fprintf(stderr, "%s: distance2 with less than %u indexes\n", - state->global->msgprefix, nbobjs); + fprintf(stderr, "%s: %s with less than %u indexes\n", + state->global->msgprefix, _TAG_NAME, nbobjs); goto out_with_arrays; } if (nr_u64values != nbobjs*nbobjs) { if (hwloc__xml_verbose()) - fprintf(stderr, "%s: distance2 with less than %u u64values\n", - state->global->msgprefix, nbobjs*nbobjs); + fprintf(stderr, "%s: %s with less than %u u64values\n", + state->global->msgprefix, _TAG_NAME, nbobjs*nbobjs); goto out_with_arrays; } if (nbobjs < 2) { /* distances with a single object are useless, even if the XML isn't invalid */ if (hwloc__xml_verbose()) - fprintf(stderr, "%s: ignoring distances2 with only %u objects\n", - state->global->msgprefix, nbobjs); + fprintf(stderr, "%s: ignoring %s with only %u objects\n", + state->global->msgprefix, _TAG_NAME, nbobjs); goto out_ignore; } - if (type == HWLOC_OBJ_PU || type == HWLOC_OBJ_NUMANODE) { + if (unique_type == HWLOC_OBJ_PU || unique_type == HWLOC_OBJ_NUMANODE) { if (!os_indexing) { if (hwloc__xml_verbose()) - fprintf(stderr, "%s: ignoring PU or NUMA distances2 without os_indexing\n", - state->global->msgprefix); + fprintf(stderr, "%s: ignoring PU or NUMA %s without os_indexing\n", + state->global->msgprefix, _TAG_NAME); goto out_ignore; } } else { if (!gp_indexing) { if (hwloc__xml_verbose()) - fprintf(stderr, "%s: ignoring !PU or !NUMA distances2 without gp_indexing\n", - state->global->msgprefix); + fprintf(stderr, "%s: ignoring !PU or !NUMA %s without gp_indexing\n", + state->global->msgprefix, _TAG_NAME); goto out_ignore; } } - hwloc_internal_distances_add_by_index(topology, type, nbobjs, indexes, u64values, kind, 0); + hwloc_internal_distances_add_by_index(topology, name, unique_type, different_types, nbobjs, indexes, u64values, kind, 0); /* prevent freeing below */ indexes = NULL; u64values = NULL; + different_types = NULL; out_ignore: + free(different_types); free(indexes); free(u64values); return state->global->close_tag(state); out_with_arrays: + free(different_types); free(indexes); free(u64values); out: return -1; +#undef _TAG_NAME } static int @@ -1625,8 +1661,12 @@ hwloc_convert_from_v1dist_floats(hwloc_topology_t topology, unsigned nbobjs, flo /* this canNOT be the first XML call */ static int -hwloc_look_xml(struct hwloc_backend *backend) +hwloc_look_xml(struct hwloc_backend *backend, struct hwloc_disc_status *dstatus) { + /* + * This backend enforces !topology->is_thissystem by default. + */ + struct hwloc_topology *topology = backend->topology; struct hwloc_xml_backend_data_s *data = backend->private_data; struct hwloc__xml_import_state_s state, childstate; @@ -1634,9 +1674,10 @@ hwloc_look_xml(struct hwloc_backend *backend) char *tag; int gotignored = 0; hwloc_localeswitch_declare; - char *env; int ret; + assert(dstatus->phase == HWLOC_DISC_PHASE_GLOBAL); + state.global = data; assert(!root->cpuset); @@ -1647,9 +1688,6 @@ hwloc_look_xml(struct hwloc_backend *backend) data->first_numanode = data->last_numanode = NULL; data->first_v1dist = data->last_v1dist = NULL; - env = getenv("HWLOC_DONT_MERGE_DIE_GROUPS"); - data->dont_merge_die_groups = env && atoi(env); - ret = data->look_init(data, &state); if (ret < 0) goto failed; @@ -1684,15 +1722,20 @@ hwloc_look_xml(struct hwloc_backend *backend) goto failed; if (!ret) break; - if (strcmp(tag, "distances2")) { + if (!strcmp(tag, "distances2")) { + ret = hwloc__xml_v2import_distances(topology, &childstate, 0); + if (ret < 0) + goto failed; + } else if (!strcmp(tag, "distances2hetero")) { + ret = hwloc__xml_v2import_distances(topology, &childstate, 1); + if (ret < 0) + goto failed; + } else { if (hwloc__xml_verbose()) fprintf(stderr, "%s: ignoring unknown tag `%s' after root object, expected `distances2'\n", data->msgprefix, tag); goto done; } - ret = hwloc__xml_v2import_distances(topology, &childstate); - if (ret < 0) - goto failed; state.global->close_child(&childstate); } } @@ -1742,8 +1785,8 @@ done: inext_cousin) objs[i] = node; -hwloc_convert_from_v1dist_floats(topology, nbobjs, v1dist->floats, values); - hwloc_internal_distances_add(topology, nbobjs, objs, values, v1dist->kind, 0); + hwloc_convert_from_v1dist_floats(topology, nbobjs, v1dist->floats, values); + hwloc_internal_distances_add(topology, NULL, nbobjs, objs, values, v1dist->kind, 0); } else { free(objs); free(values); @@ -1791,9 +1834,11 @@ hwloc_convert_from_v1dist_floats(topology, nbobjs, v1dist->floats, values); /* we could add "BackendSource=XML" to notify that XML was used between the actual backend and here */ topology->support.discovery->pu = 1; + topology->support.discovery->disallowed_pu = 1; if (data->nbnumanodes) { topology->support.discovery->numa = 1; topology->support.discovery->numa_memory = 1; // FIXME + topology->support.discovery->disallowed_numa = 1; } if (data->look_done) @@ -1936,6 +1981,9 @@ hwloc__xml_export_safestrdup(const char *old) char *new = malloc(strlen(old)+1); char *dst = new; const char *src = old; + if (!new) + return NULL; + while (*src) { if (HWLOC_XML_CHAR_VALID(*src)) *(dst++) = *src; @@ -1955,6 +2003,8 @@ hwloc__xml_export_object_contents (hwloc__xml_export_state_t state, hwloc_topolo if (v1export && obj->type == HWLOC_OBJ_PACKAGE) state->new_prop(state, "type", "Socket"); + else if (v1export && obj->type == HWLOC_OBJ_DIE) + state->new_prop(state, "type", "Group"); else if (v1export && hwloc__obj_type_is_cache(obj->type)) state->new_prop(state, "type", "Cache"); else @@ -1966,8 +2016,23 @@ hwloc__xml_export_object_contents (hwloc__xml_export_state_t state, hwloc_topolo } if (obj->cpuset) { - if (v1export && obj->type == HWLOC_OBJ_NUMANODE && obj->sibling_rank > 0) { - /* v1 non-first NUMA nodes have empty cpusets */ + int empty_cpusets = 0; + + if (v1export && obj->type == HWLOC_OBJ_NUMANODE) { + /* walk up this memory hierarchy to find-out if we are the first numa node. + * v1 non-first NUMA nodes have empty cpusets. + */ + hwloc_obj_t parent = obj; + while (!hwloc_obj_type_is_normal(parent->type)) { + if (parent->sibling_rank > 0) { + empty_cpusets = 1; + break; + } + parent = parent->parent; + } + } + + if (empty_cpusets) { state->new_prop(state, "cpuset", "0x0"); state->new_prop(state, "online_cpuset", "0x0"); state->new_prop(state, "complete_cpuset", "0x0"); @@ -2024,13 +2089,17 @@ hwloc__xml_export_object_contents (hwloc__xml_export_state_t state, hwloc_topolo if (obj->name) { char *name = hwloc__xml_export_safestrdup(obj->name); - state->new_prop(state, "name", name); - free(name); + if (name) { + state->new_prop(state, "name", name); + free(name); + } } if (!v1export && obj->subtype) { char *subtype = hwloc__xml_export_safestrdup(obj->subtype); - state->new_prop(state, "subtype", subtype); - free(subtype); + if (subtype) { + state->new_prop(state, "subtype", subtype); + free(subtype); + } } switch (obj->type) { @@ -2057,6 +2126,7 @@ hwloc__xml_export_object_contents (hwloc__xml_export_state_t state, hwloc_topolo case HWLOC_OBJ_L1ICACHE: case HWLOC_OBJ_L2ICACHE: case HWLOC_OBJ_L3ICACHE: + case HWLOC_OBJ_MEMCACHE: sprintf(tmp, "%llu", (unsigned long long) obj->attr->cache.size); state->new_prop(state, "cache_size", tmp); sprintf(tmp, "%u", obj->attr->cache.depth); @@ -2125,23 +2195,34 @@ hwloc__xml_export_object_contents (hwloc__xml_export_state_t state, hwloc_topolo for(i=0; iinfos_count; i++) { char *name = hwloc__xml_export_safestrdup(obj->infos[i].name); char *value = hwloc__xml_export_safestrdup(obj->infos[i].value); - struct hwloc__xml_export_state_s childstate; - state->new_child(state, &childstate, "info"); - childstate.new_prop(&childstate, "name", name); - childstate.new_prop(&childstate, "value", value); - childstate.end_object(&childstate, "info"); + if (name && value) { + struct hwloc__xml_export_state_s childstate; + state->new_child(state, &childstate, "info"); + childstate.new_prop(&childstate, "name", name); + childstate.new_prop(&childstate, "value", value); + childstate.end_object(&childstate, "info"); + } free(name); free(value); } if (v1export && obj->subtype) { char *subtype = hwloc__xml_export_safestrdup(obj->subtype); + if (subtype) { + struct hwloc__xml_export_state_s childstate; + int is_coproctype = (obj->type == HWLOC_OBJ_OS_DEVICE && obj->attr->osdev.type == HWLOC_OBJ_OSDEV_COPROC); + state->new_child(state, &childstate, "info"); + childstate.new_prop(&childstate, "name", is_coproctype ? "CoProcType" : "Type"); + childstate.new_prop(&childstate, "value", subtype); + childstate.end_object(&childstate, "info"); + free(subtype); + } + } + if (v1export && obj->type == HWLOC_OBJ_DIE) { struct hwloc__xml_export_state_s childstate; - int is_coproctype = (obj->type == HWLOC_OBJ_OS_DEVICE && obj->attr->osdev.type == HWLOC_OBJ_OSDEV_COPROC); state->new_child(state, &childstate, "info"); - childstate.new_prop(&childstate, "name", is_coproctype ? "CoProcType" : "Type"); - childstate.new_prop(&childstate, "value", subtype); + childstate.new_prop(&childstate, "name", "Type"); + childstate.new_prop(&childstate, "value", "Die"); childstate.end_object(&childstate, "info"); - free(subtype); } if (v1export && !obj->parent) { @@ -2152,19 +2233,27 @@ hwloc__xml_export_object_contents (hwloc__xml_export_state_t state, hwloc_topolo for(dist = topology->first_dist; dist; dist = dist->next) { struct hwloc__xml_export_state_s childstate; unsigned nbobjs = dist->nbobjs; + unsigned *logical_to_v2array; int depth; - if (nbobjs != (unsigned) hwloc_get_nbobjs_by_type(topology, dist->type)) + if (nbobjs != (unsigned) hwloc_get_nbobjs_by_type(topology, dist->unique_type)) continue; if (!(dist->kind & HWLOC_DISTANCES_KIND_MEANS_LATENCY)) continue; - { - HWLOC_VLA(unsigned, logical_to_v2array, nbobjs); + if (dist->kind & HWLOC_DISTANCES_KIND_HETEROGENEOUS_TYPES) + continue; + + logical_to_v2array = malloc(nbobjs * sizeof(*logical_to_v2array)); + if (!logical_to_v2array) { + fprintf(stderr, "xml/export/v1: failed to allocated logical_to_v2array\n"); + continue; + } + for(i=0; iobjs[i]->logical_index] = i; /* compute the relative depth */ - if (dist->type == HWLOC_OBJ_NUMANODE) { + if (dist->unique_type == HWLOC_OBJ_NUMANODE) { /* for NUMA nodes, use the highest normal-parent depth + 1 */ depth = -1; for(i=0; itype) + parent_with_memory; + depth = hwloc_get_type_depth(topology, dist->unique_type) + parent_with_memory; } state->new_child(state, &childstate, "distances"); @@ -2210,7 +2299,7 @@ hwloc__xml_export_object_contents (hwloc__xml_export_state_t state, hwloc_topolo } } childstate.end_object(&childstate, "distances"); - } + free(logical_to_v2array); } } @@ -2243,13 +2332,90 @@ hwloc__xml_v2export_object (hwloc__xml_export_state_t parentstate, hwloc_topolog static void hwloc__xml_v1export_object (hwloc__xml_export_state_t parentstate, hwloc_topology_t topology, hwloc_obj_t obj, unsigned long flags); +static hwloc_obj_t +hwloc__xml_v1export_object_next_numanode(hwloc_obj_t obj, hwloc_obj_t cur) +{ + hwloc_obj_t parent; + + if (!cur) { + /* first numa node is on the very bottom left */ + cur = obj->memory_first_child; + goto find_first; + } + + /* walk-up until there's a next sibling */ + parent = cur; + while (1) { + if (parent->next_sibling) { + /* found a next sibling, we'll walk down-left from there */ + cur = parent->next_sibling; + break; + } + parent = parent->parent; + if (parent == obj) + return NULL; + } + + find_first: + while (cur->type != HWLOC_OBJ_NUMANODE) + cur = cur->memory_first_child; + assert(cur); + return cur; +} + +static unsigned +hwloc__xml_v1export_object_list_numanodes(hwloc_obj_t obj, hwloc_obj_t *first_p, hwloc_obj_t **nodes_p) +{ + hwloc_obj_t *nodes, cur; + int nr; + + if (!obj->memory_first_child) { + *first_p = NULL; + *nodes_p = NULL; + return 0; + } + /* we're sure there's at least one numa node */ + + nr = hwloc_bitmap_weight(obj->nodeset); + assert(nr > 0); + /* these are local nodes, but some of them may be attached above instead of here */ + + nodes = calloc(nr, sizeof(*nodes)); + if (!nodes) { + /* only return the first node */ + cur = hwloc__xml_v1export_object_next_numanode(obj, NULL); + assert(cur); + *first_p = cur; + *nodes_p = NULL; + return 1; + } + + nr = 0; + cur = NULL; + while (1) { + cur = hwloc__xml_v1export_object_next_numanode(obj, cur); + if (!cur) + break; + nodes[nr++] = cur; + } + + *first_p = nodes[0]; + *nodes_p = nodes; + return nr; +} + static void hwloc__xml_v1export_object_with_memory(hwloc__xml_export_state_t parentstate, hwloc_topology_t topology, hwloc_obj_t obj, unsigned long flags) { struct hwloc__xml_export_state_s gstate, mstate, ostate, *state = parentstate; hwloc_obj_t child; + unsigned nr_numanodes; + hwloc_obj_t *numanodes, first_numanode; + unsigned i; - if (obj->parent->arity > 1 && obj->memory_arity > 1 && parentstate->global->v1_memory_group) { + nr_numanodes = hwloc__xml_v1export_object_list_numanodes(obj, &first_numanode, &numanodes); + + if (obj->parent->arity > 1 && nr_numanodes > 1 && parentstate->global->v1_memory_group) { /* child has sibling, we must add a Group around those memory children */ hwloc_obj_t group = parentstate->global->v1_memory_group; parentstate->new_child(parentstate, &gstate, "object"); @@ -2266,10 +2432,8 @@ hwloc__xml_v1export_object_with_memory(hwloc__xml_export_state_t parentstate, hw } /* export first memory child */ - child = obj->memory_first_child; - assert(child->type == HWLOC_OBJ_NUMANODE); state->new_child(state, &mstate, "object"); - hwloc__xml_export_object_contents (&mstate, topology, child, flags); + hwloc__xml_export_object_contents (&mstate, topology, first_numanode, flags); /* then the actual object */ mstate.new_child(&mstate, &ostate, "object"); @@ -2288,9 +2452,10 @@ hwloc__xml_v1export_object_with_memory(hwloc__xml_export_state_t parentstate, hw mstate.end_object(&mstate, "object"); /* now other memory children */ - for_each_memory_child(child, obj) - if (child->sibling_rank > 0) - hwloc__xml_v1export_object (state, topology, child, flags); + for(i=1; inew_child(state, &_childstate, tagname); \ + for(_j=0; \ + _i+_j<(nr) && _jtype), (unsigned long long) (objs)[_i+_j]->gp_index); \ + _i += _j; \ + sprintf(_tmp2, "%lu", (unsigned long) _len); \ + _childstate.new_prop(&_childstate, "length", _tmp2); \ + _childstate.add_content(&_childstate, _tmp, _len); \ + _childstate.end_object(&_childstate, tagname); \ + } \ +} while (0) + +static void +hwloc___xml_v2export_distances(hwloc__xml_export_state_t parentstate, struct hwloc_internal_distances_s *dist) +{ + char tmp[255]; + unsigned nbobjs = dist->nbobjs; + struct hwloc__xml_export_state_s state; + + if (dist->different_types) { + parentstate->new_child(parentstate, &state, "distances2hetero"); + } else { + parentstate->new_child(parentstate, &state, "distances2"); + state.new_prop(&state, "type", hwloc_obj_type_string(dist->unique_type)); + } + + sprintf(tmp, "%u", nbobjs); + state.new_prop(&state, "nbobjs", tmp); + sprintf(tmp, "%lu", dist->kind); + state.new_prop(&state, "kind", tmp); + if (dist->name) + state.new_prop(&state, "name", dist->name); + + if (!dist->different_types) { + state.new_prop(&state, "indexing", + HWLOC_DIST_TYPE_USE_OS_INDEX(dist->unique_type) ? "os" : "gp"); + } + + /* TODO don't hardwire 10 below. either snprintf the max to guess it, or just append until the end of the buffer */ + if (dist->different_types) { + EXPORT_TYPE_GPINDEX_ARRAY(&state, nbobjs, dist->objs, "indexes", 10); + } else { + EXPORT_ARRAY(&state, unsigned long long, nbobjs, dist->indexes, "indexes", "%llu", 10); + } + EXPORT_ARRAY(&state, unsigned long long, nbobjs*nbobjs, dist->values, "u64values", "%llu", 10); + state.end_object(&state, dist->different_types ? "distances2hetero" : "distances2"); +} + static void hwloc__xml_v2export_distances(hwloc__xml_export_state_t parentstate, hwloc_topology_t topology) { struct hwloc_internal_distances_s *dist; - for(dist = topology->first_dist; dist; dist = dist->next) { - char tmp[255]; - unsigned nbobjs = dist->nbobjs; - struct hwloc__xml_export_state_s state; - - parentstate->new_child(parentstate, &state, "distances2"); - - state.new_prop(&state, "type", hwloc_obj_type_string(dist->type)); - sprintf(tmp, "%u", nbobjs); - state.new_prop(&state, "nbobjs", tmp); - sprintf(tmp, "%lu", dist->kind); - state.new_prop(&state, "kind", tmp); - - state.new_prop(&state, "indexing", - (dist->type == HWLOC_OBJ_NUMANODE || dist->type == HWLOC_OBJ_PU) ? "os" : "gp"); - /* TODO don't hardwire 10 below. either snprintf the max to guess it, or just append until the end of the buffer */ - EXPORT_ARRAY(&state, unsigned long long, nbobjs, dist->indexes, "indexes", "%llu", 10); - EXPORT_ARRAY(&state, unsigned long long, nbobjs*nbobjs, dist->values, "u64values", "%llu", 10); - state.end_object(&state, "distances2"); - } + for(dist = topology->first_dist; dist; dist = dist->next) + if (!dist->different_types) + hwloc___xml_v2export_distances(parentstate, dist); + /* export homogeneous distances first in case the importer doesn't support heterogeneous and stops there */ + for(dist = topology->first_dist; dist; dist = dist->next) + if (dist->different_types) + hwloc___xml_v2export_distances(parentstate, dist); } void @@ -2378,18 +2587,22 @@ hwloc__xml_export_topology(hwloc__xml_export_state_t state, hwloc_topology_t top hwloc_obj_t root = hwloc_get_root_obj(topology); if (flags & HWLOC_TOPOLOGY_EXPORT_XML_FLAG_V1) { - if (root->memory_first_child) { + hwloc_obj_t *numanodes, first_numanode; + unsigned nr_numanodes; + + nr_numanodes = hwloc__xml_v1export_object_list_numanodes(root, &first_numanode, &numanodes); + + if (nr_numanodes) { /* we don't use hwloc__xml_v1export_object_with_memory() because we want/can keep root above the numa node */ struct hwloc__xml_export_state_s rstate, mstate; hwloc_obj_t child; + unsigned i; /* export the root */ state->new_child(state, &rstate, "object"); hwloc__xml_export_object_contents (&rstate, topology, root, flags); /* export first memory child */ - child = root->memory_first_child; - assert(child->type == HWLOC_OBJ_NUMANODE); rstate.new_child(&rstate, &mstate, "object"); - hwloc__xml_export_object_contents (&mstate, topology, child, flags); + hwloc__xml_export_object_contents (&mstate, topology, first_numanode, flags); /* then its normal/io/misc children */ for_each_child(child, root) hwloc__xml_v1export_object (&mstate, topology, child, flags); @@ -2400,15 +2613,16 @@ hwloc__xml_export_topology(hwloc__xml_export_state_t state, hwloc_topology_t top /* close first memory child */ mstate.end_object(&mstate, "object"); /* now other memory children */ - for_each_memory_child(child, root) - if (child->sibling_rank > 0) - hwloc__xml_v1export_object (&rstate, topology, child, flags); + for(i=1; i +#include "private/autogen/config.h" #define _ATFILE_SOURCE #include @@ -25,10 +25,10 @@ #include #include -#include -#include -#include -#include +#include "hwloc.h" +#include "private/private.h" +#include "private/debug.h" +#include "private/misc.h" #ifdef HAVE_MACH_MACH_INIT_H #include @@ -136,14 +136,28 @@ int hwloc_get_sysctl(int name[], unsigned namelen, int *ret) } #endif -/* Return the OS-provided number of processors. Unlike other methods such as - reading sysfs on Linux, this method is not virtualizable; thus it's only - used as a fall-back method, allowing virtual backends (FSROOT, etc) to - have the desired effect. */ +/* Return the OS-provided number of processors. + * Assumes topology->is_thissystem is true. + */ #ifndef HWLOC_WIN_SYS /* The windows implementation is in topology-windows.c */ int -hwloc_fallback_nbprocessors(struct hwloc_topology *topology __hwloc_attribute_unused) { +hwloc_fallback_nbprocessors(unsigned flags) { int n; + + if (flags & HWLOC_FALLBACK_NBPROCESSORS_INCLUDE_OFFLINE) { + /* try to get all CPUs for Linux and Solaris that can handle offline CPUs */ +#if HAVE_DECL__SC_NPROCESSORS_CONF + n = sysconf(_SC_NPROCESSORS_CONF); +#elif HAVE_DECL__SC_NPROC_CONF + n = sysconf(_SC_NPROC_CONF); +#else + n = -1; +#endif + if (n != -1) + return n; + } + + /* try getting only online CPUs, or whatever we can get */ #if HAVE_DECL__SC_NPROCESSORS_ONLN n = sysconf(_SC_NPROCESSORS_ONLN); #elif HAVE_DECL__SC_NPROC_ONLN @@ -762,9 +776,7 @@ hwloc__duplicate_object(struct hwloc_topology *newtopology, /* place us for real */ assert(newobj->logical_index < level_width); level[newobj->logical_index] = newobj; - /* link to already-inserted cousins - * (hwloc_pci_belowroot_apply_locality() can cause out-of-order logical indexes) - */ + /* link to already-inserted cousins */ if (newobj->logical_index > 0 && level[newobj->logical_index-1]) { newobj->prev_cousin = level[newobj->logical_index-1]; level[newobj->logical_index-1]->next_cousin = newobj; @@ -991,31 +1003,35 @@ hwloc_topology_dup(hwloc_topology_t *newp, /***** Make sure you update obj_type_priority[] below as well. *****/ static const unsigned obj_type_order[] = { /* first entry is HWLOC_OBJ_MACHINE */ 0, - /* next entry is HWLOC_OBJ_PACKAGE */ 3, - /* next entry is HWLOC_OBJ_CORE */ 12, - /* next entry is HWLOC_OBJ_PU */ 16, - /* next entry is HWLOC_OBJ_L1CACHE */ 10, - /* next entry is HWLOC_OBJ_L2CACHE */ 8, - /* next entry is HWLOC_OBJ_L3CACHE */ 6, - /* next entry is HWLOC_OBJ_L4CACHE */ 5, - /* next entry is HWLOC_OBJ_L5CACHE */ 4, - /* next entry is HWLOC_OBJ_L1ICACHE */ 11, - /* next entry is HWLOC_OBJ_L2ICACHE */ 9, - /* next entry is HWLOC_OBJ_L3ICACHE */ 7, + /* next entry is HWLOC_OBJ_PACKAGE */ 4, + /* next entry is HWLOC_OBJ_CORE */ 14, + /* next entry is HWLOC_OBJ_PU */ 18, + /* next entry is HWLOC_OBJ_L1CACHE */ 12, + /* next entry is HWLOC_OBJ_L2CACHE */ 10, + /* next entry is HWLOC_OBJ_L3CACHE */ 8, + /* next entry is HWLOC_OBJ_L4CACHE */ 7, + /* next entry is HWLOC_OBJ_L5CACHE */ 6, + /* next entry is HWLOC_OBJ_L1ICACHE */ 13, + /* next entry is HWLOC_OBJ_L2ICACHE */ 11, + /* next entry is HWLOC_OBJ_L3ICACHE */ 9, /* next entry is HWLOC_OBJ_GROUP */ 1, - /* next entry is HWLOC_OBJ_NUMANODE */ 2, - /* next entry is HWLOC_OBJ_BRIDGE */ 13, - /* next entry is HWLOC_OBJ_PCI_DEVICE */ 14, - /* next entry is HWLOC_OBJ_OS_DEVICE */ 15, - /* next entry is HWLOC_OBJ_MISC */ 17 + /* next entry is HWLOC_OBJ_NUMANODE */ 3, + /* next entry is HWLOC_OBJ_BRIDGE */ 15, + /* next entry is HWLOC_OBJ_PCI_DEVICE */ 16, + /* next entry is HWLOC_OBJ_OS_DEVICE */ 17, + /* next entry is HWLOC_OBJ_MISC */ 19, + /* next entry is HWLOC_OBJ_MEMCACHE */ 2, + /* next entry is HWLOC_OBJ_DIE */ 5 }; #ifndef NDEBUG /* only used in debug check assert if !NDEBUG */ static const hwloc_obj_type_t obj_order_type[] = { HWLOC_OBJ_MACHINE, HWLOC_OBJ_GROUP, + HWLOC_OBJ_MEMCACHE, HWLOC_OBJ_NUMANODE, HWLOC_OBJ_PACKAGE, + HWLOC_OBJ_DIE, HWLOC_OBJ_L5CACHE, HWLOC_OBJ_L4CACHE, HWLOC_OBJ_L3CACHE, @@ -1040,6 +1056,7 @@ static const hwloc_obj_type_t obj_order_type[] = { * Always keep Machine/NUMANode/PU/PCIDev/OSDev * then Core * then Package + * then Die * then Cache, * then Instruction Caches * then always drop Group/Misc/Bridge. @@ -1065,7 +1082,9 @@ static const int obj_type_priority[] = { /* next entry is HWLOC_OBJ_BRIDGE */ 0, /* next entry is HWLOC_OBJ_PCI_DEVICE */ 100, /* next entry is HWLOC_OBJ_OS_DEVICE */ 100, - /* next entry is HWLOC_OBJ_MISC */ 0 + /* next entry is HWLOC_OBJ_MISC */ 0, + /* next entry is HWLOC_OBJ_MEMCACHE */ 19, + /* next entry is HWLOC_OBJ_DIE */ 30 }; int hwloc_compare_types (hwloc_obj_type_t type1, hwloc_obj_type_t type2) @@ -1118,12 +1137,10 @@ hwloc_type_cmp(hwloc_obj_t obj1, hwloc_obj_t obj2) /* * How to compare objects based on cpusets. */ - static int hwloc_obj_cmp_sets(hwloc_obj_t obj1, hwloc_obj_t obj2) { hwloc_bitmap_t set1, set2; - int res = HWLOC_OBJ_DIFFERENT; assert(!hwloc__obj_type_is_special(obj1->type)); assert(!hwloc__obj_type_is_special(obj2->type)); @@ -1136,45 +1153,10 @@ hwloc_obj_cmp_sets(hwloc_obj_t obj1, hwloc_obj_t obj2) set1 = obj1->cpuset; set2 = obj2->cpuset; } - if (set1 && set2 && !hwloc_bitmap_iszero(set1) && !hwloc_bitmap_iszero(set2)) { - res = hwloc_bitmap_compare_inclusion(set1, set2); - if (res == HWLOC_OBJ_INTERSECTS) - return HWLOC_OBJ_INTERSECTS; - } + if (set1 && set2 && !hwloc_bitmap_iszero(set1) && !hwloc_bitmap_iszero(set2)) + return hwloc_bitmap_compare_inclusion(set1, set2); - /* then compare nodesets, and combine the results */ - if (obj1->complete_nodeset && obj2->complete_nodeset) { - set1 = obj1->complete_nodeset; - set2 = obj2->complete_nodeset; - } else { - set1 = obj1->nodeset; - set2 = obj2->nodeset; - } - if (set1 && set2 && !hwloc_bitmap_iszero(set1) && !hwloc_bitmap_iszero(set2)) { - int noderes = hwloc_bitmap_compare_inclusion(set1, set2); - /* deal with conflicting cpusets/nodesets inclusions */ - if (noderes == HWLOC_OBJ_INCLUDED) { - if (res == HWLOC_OBJ_CONTAINS) - /* contradicting order for cpusets and nodesets */ - return HWLOC_OBJ_INTERSECTS; - res = HWLOC_OBJ_INCLUDED; - - } else if (noderes == HWLOC_OBJ_CONTAINS) { - if (res == HWLOC_OBJ_INCLUDED) - /* contradicting order for cpusets and nodesets */ - return HWLOC_OBJ_INTERSECTS; - res = HWLOC_OBJ_CONTAINS; - - } else if (noderes == HWLOC_OBJ_INTERSECTS) { - return HWLOC_OBJ_INTERSECTS; - - } else { - /* nodesets are different, keep the cpuset order */ - - } - } - - return res; + return HWLOC_OBJ_DIFFERENT; } /* Compare object cpusets based on complete_cpuset if defined (always correctly ordered), @@ -1189,10 +1171,6 @@ hwloc__object_cpusets_compare_first(hwloc_obj_t obj1, hwloc_obj_t obj2) return hwloc_bitmap_compare_first(obj1->complete_cpuset, obj2->complete_cpuset); else if (obj1->cpuset && obj2->cpuset) return hwloc_bitmap_compare_first(obj1->cpuset, obj2->cpuset); - else if (obj1->complete_nodeset && obj2->complete_nodeset) - return hwloc_bitmap_compare_first(obj1->complete_nodeset, obj2->complete_nodeset); - else if (obj1->nodeset && obj2->nodeset) - return hwloc_bitmap_compare_first(obj1->nodeset, obj2->nodeset); return 0; } @@ -1346,7 +1324,11 @@ hwloc__insert_try_merge_group(hwloc_obj_t old, hwloc_obj_t new) } } -/* Try to insert OBJ in CUR, recurse if needed. +/* + * The main insertion routine, only used for CPU-side object (normal types) + * uisng cpuset only (or complete_cpuset). + * + * Try to insert OBJ in CUR, recurse if needed. * Returns the object if it was inserted, * the remaining object it was merged, * NULL if failed to insert. @@ -1546,17 +1528,116 @@ hwloc__find_insert_memory_parent(struct hwloc_topology *topology, hwloc_obj_t ob return group; } -/*attach the given memory object below the given normal parent. */ +/* only works for MEMCACHE and NUMAnode with a single bit in nodeset */ +static hwloc_obj_t +hwloc___attach_memory_object_by_nodeset(struct hwloc_topology *topology, hwloc_obj_t parent, + hwloc_obj_t obj, + hwloc_report_error_t report_error) +{ + hwloc_obj_t *curp = &parent->memory_first_child; + unsigned first = hwloc_bitmap_first(obj->nodeset); + + while (*curp) { + hwloc_obj_t cur = *curp; + unsigned curfirst = hwloc_bitmap_first(cur->nodeset); + + if (first < curfirst) { + /* insert before cur */ + obj->next_sibling = cur; + *curp = obj; + obj->memory_first_child = NULL; + obj->parent = parent; + topology->modified = 1; + return obj; + } + + if (first == curfirst) { + /* identical nodeset */ + if (obj->type == HWLOC_OBJ_NUMANODE) { + if (cur->type == HWLOC_OBJ_NUMANODE) { + /* identical NUMA nodes? ignore the new one */ + if (report_error) { + char curstr[512]; + char objstr[512]; + char msg[1100]; + hwloc__report_error_format_obj(curstr, sizeof(curstr), cur); + hwloc__report_error_format_obj(objstr, sizeof(objstr), obj); + snprintf(msg, sizeof(msg), "%s and %s have identical nodesets!", objstr, curstr); + report_error(msg, __LINE__); + } + return NULL; + } + assert(cur->type == HWLOC_OBJ_MEMCACHE); + /* insert the new NUMA node below that existing memcache */ + return hwloc___attach_memory_object_by_nodeset(topology, cur, obj, report_error); + + } else { + assert(obj->type == HWLOC_OBJ_MEMCACHE); + if (cur->type == HWLOC_OBJ_MEMCACHE) { + if (cur->attr->cache.depth == obj->attr->cache.depth) + /* memcache with same nodeset and depth, ignore the new one */ + return NULL; + if (cur->attr->cache.depth > obj->attr->cache.depth) + /* memcache with higher cache depth is actually *higher* in the hierarchy + * (depth starts from the NUMA node). + * insert the new memcache below the existing one + */ + return hwloc___attach_memory_object_by_nodeset(topology, cur, obj, report_error); + } + /* insert the memcache above the existing memcache or numa node */ + obj->next_sibling = cur->next_sibling; + cur->next_sibling = NULL; + obj->memory_first_child = cur; + cur->parent = obj; + *curp = obj; + obj->parent = parent; + topology->modified = 1; + return obj; + } + } + + curp = &cur->next_sibling; + } + + /* append to the end of the list */ + obj->next_sibling = NULL; + *curp = obj; + obj->memory_first_child = NULL; + obj->parent = parent; + topology->modified = 1; + return obj; +} + +/* Attach the given memory object below the given normal parent. + * + * Only the nodeset is used to find the location inside memory children below parent. + * + * Nodeset inclusion inside the given memory hierarchy is guaranteed by this function, + * but nodesets are not propagated to CPU-side parent yet. It will be done by + * propagate_nodeset() later. + */ struct hwloc_obj * hwloc__attach_memory_object(struct hwloc_topology *topology, hwloc_obj_t parent, hwloc_obj_t obj, - hwloc_report_error_t report_error __hwloc_attribute_unused) + hwloc_report_error_t report_error) { - hwloc_obj_t *cur_children; + hwloc_obj_t result; assert(parent); assert(hwloc__obj_type_is_normal(parent->type)); + /* Check the nodeset */ + if (!obj->nodeset || hwloc_bitmap_iszero(obj->nodeset)) + return NULL; + /* Initialize or check the complete nodeset */ + if (!obj->complete_nodeset) { + obj->complete_nodeset = hwloc_bitmap_dup(obj->nodeset); + } else if (!hwloc_bitmap_isincluded(obj->nodeset, obj->complete_nodeset)) { + return NULL; + } + /* Neither ACPI nor Linux support multinode mscache */ + assert(hwloc_bitmap_weight(obj->nodeset) == 1); + #if 0 /* TODO: enable this instead of hack in fixup_sets once NUMA nodes are inserted late */ /* copy the parent cpuset in case it's larger than expected. @@ -1565,35 +1646,22 @@ hwloc__attach_memory_object(struct hwloc_topology *topology, hwloc_obj_t parent, * However, the user decided the ignore Groups, so hierarchy/locality loss is expected. */ hwloc_bitmap_copy(obj->cpuset, parent->cpuset); + hwloc_bitmap_copy(obj->complete_cpuset, parent->complete_cpuset); #endif - /* only NUMA nodes are memory for now, just append to the end of the list */ - assert(obj->type == HWLOC_OBJ_NUMANODE); - assert(obj->nodeset); - cur_children = &parent->memory_first_child; - while (*cur_children) { - /* TODO check that things are inserted in order. - * it's OK for KNL, the only user so far - */ - cur_children = &(*cur_children)->next_sibling; - } - *cur_children = obj; - obj->next_sibling = NULL; - - /* Initialize the complete nodeset if needed */ - if (!obj->complete_nodeset) { - obj->complete_nodeset = hwloc_bitmap_dup(obj->nodeset); - } - - /* Add the bit to the top sets, and to the parent CPU-side object */ - if (obj->type == HWLOC_OBJ_NUMANODE) { - if (hwloc_bitmap_isset(obj->nodeset, obj->os_index)) + result = hwloc___attach_memory_object_by_nodeset(topology, parent, obj, report_error); + if (result == obj) { + /* Add the bit to the top sets, and to the parent CPU-side object */ + if (obj->type == HWLOC_OBJ_NUMANODE) { hwloc_bitmap_set(topology->levels[0][0]->nodeset, obj->os_index); - hwloc_bitmap_set(topology->levels[0][0]->complete_nodeset, obj->os_index); + hwloc_bitmap_set(topology->levels[0][0]->complete_nodeset, obj->os_index); + } } - - topology->modified = 1; - return obj; + if (result != obj) { + /* either failed to insert, or got merged, free the original object */ + hwloc_free_unlinked_object(obj); + } + return result; } /* insertion routine that lets you change the error reporting callback */ @@ -1699,11 +1767,18 @@ hwloc_alloc_setup_object(hwloc_topology_t topology, hwloc_obj_type_t type, unsigned os_index) { struct hwloc_obj *obj = hwloc_tma_malloc(topology->tma, sizeof(*obj)); + if (!obj) + return NULL; memset(obj, 0, sizeof(*obj)); obj->type = type; obj->os_index = os_index; obj->gp_index = topology->next_gp_index++; obj->attr = hwloc_tma_malloc(topology->tma, sizeof(*obj->attr)); + if (!obj->attr) { + assert(!topology->tma || !topology->tma->dontfree); /* this tma cannot fail to allocate */ + free(obj); + return NULL; + } memset(obj->attr, 0, sizeof(*obj->attr)); /* do not allocate the cpuset here, let the caller do it */ return obj; @@ -1717,6 +1792,10 @@ hwloc_topology_alloc_group_object(struct hwloc_topology *topology) errno = EINVAL; return NULL; } + if (topology->adopted_shmem_addr) { + errno = EPERM; + return NULL; + } return hwloc_alloc_setup_object(topology, HWLOC_OBJ_GROUP, HWLOC_UNKNOWN_INDEX); } @@ -1736,6 +1815,10 @@ hwloc_topology_insert_group_object(struct hwloc_topology *topology, hwloc_obj_t errno = EINVAL; return NULL; } + if (topology->adopted_shmem_addr) { + errno = EPERM; + return NULL; + } if (topology->type_filter[HWLOC_OBJ_GROUP] == HWLOC_TYPE_FILTER_KEEP_NONE) { hwloc_free_unlinked_object(obj); @@ -1754,12 +1837,30 @@ hwloc_topology_insert_group_object(struct hwloc_topology *topology, hwloc_obj_t hwloc_bitmap_and(obj->complete_nodeset, obj->complete_nodeset, root->complete_nodeset); if ((!obj->cpuset || hwloc_bitmap_iszero(obj->cpuset)) - && (!obj->complete_cpuset || hwloc_bitmap_iszero(obj->complete_cpuset)) - && (!obj->nodeset || hwloc_bitmap_iszero(obj->nodeset)) - && (!obj->complete_nodeset || hwloc_bitmap_iszero(obj->complete_nodeset))) { - hwloc_free_unlinked_object(obj); - errno = EINVAL; - return NULL; + && (!obj->complete_cpuset || hwloc_bitmap_iszero(obj->complete_cpuset))) { + /* we'll insert by cpuset, so build cpuset from the nodeset */ + hwloc_const_bitmap_t nodeset = obj->nodeset ? obj->nodeset : obj->complete_nodeset; + hwloc_obj_t numa; + + if ((!obj->nodeset || hwloc_bitmap_iszero(obj->nodeset)) + && (!obj->complete_nodeset || hwloc_bitmap_iszero(obj->complete_nodeset))) { + hwloc_free_unlinked_object(obj); + errno = EINVAL; + return NULL; + } + + if (!obj->cpuset) { + obj->cpuset = hwloc_bitmap_alloc(); + if (!obj->cpuset) { + hwloc_free_unlinked_object(obj); + return NULL; + } + } + + numa = NULL; + while ((numa = hwloc_get_next_obj_by_type(topology, HWLOC_OBJ_NUMANODE, numa)) != NULL) + if (hwloc_bitmap_isset(nodeset, numa->os_index)) + hwloc_bitmap_or(obj->cpuset, obj->cpuset, numa->cpuset); } cmp = hwloc_obj_cmp_sets(obj, root); @@ -1806,6 +1907,10 @@ hwloc_topology_insert_misc_object(struct hwloc_topology *topology, hwloc_obj_t p errno = EINVAL; return NULL; } + if (topology->adopted_shmem_addr) { + errno = EPERM; + return NULL; + } obj = hwloc_alloc_setup_object(topology, HWLOC_OBJ_MISC, HWLOC_UNKNOWN_INDEX); if (name) @@ -1963,6 +2068,7 @@ fixup_sets(hwloc_obj_t obj) in_memory_list = 0; /* iterate over normal children first, we'll come back for memory children later */ + /* FIXME: if memory objects are inserted late, we should update their cpuset and complete_cpuset at insertion instead of here */ iterate: while (child) { /* our cpuset must be included in our parent's one */ @@ -1980,6 +2086,12 @@ fixup_sets(hwloc_obj_t obj) child->complete_nodeset = hwloc_bitmap_dup(child->nodeset); } + if (hwloc_obj_type_is_memory(child->type)) { + /* update memory children cpusets in case some CPU-side parent was removed */ + hwloc_bitmap_copy(child->cpuset, obj->cpuset); + hwloc_bitmap_copy(child->complete_cpuset, obj->complete_cpuset); + } + fixup_sets(child); child = child->next_sibling; } @@ -2025,9 +2137,8 @@ hwloc_obj_add_children_sets(hwloc_obj_t obj) /* CPU objects are inserted by cpusets, we know their cpusets are properly included. * We just need fixup_sets() to make sure they aren't too wide. * - * Memory objects are inserted by cpusets to find their CPU parent, - * but nodesets are only used inside the memory hierarchy below that parent. - * Thus we need to propagate nodesets to CPU-side parents and children. + * Within each memory hierarchy, nodeset are consistent as well. + * However they must be propagated to their CPU-side parents. * * A memory object nodeset consists of NUMA nodes below it. * A normal object nodeset consists in NUMA nodes attached to any @@ -2060,27 +2171,12 @@ propagate_nodeset(hwloc_obj_t obj) /* now add our local nodeset */ for_each_memory_child(child, obj) { - /* FIXME rather recurse in the memory hierarchy */ - - /* first, update children complete_nodeset if needed */ - if (!child->complete_nodeset) - child->complete_nodeset = hwloc_bitmap_dup(child->nodeset); - else - hwloc_bitmap_or(child->complete_nodeset, child->complete_nodeset, child->nodeset); - /* add memory children nodesets to ours */ hwloc_bitmap_or(obj->nodeset, obj->nodeset, child->nodeset); hwloc_bitmap_or(obj->complete_nodeset, obj->complete_nodeset, child->complete_nodeset); - - /* by the way, copy our cpusets to memory children */ - if (child->cpuset) - hwloc_bitmap_copy(child->cpuset, obj->cpuset); - else - child->cpuset = hwloc_bitmap_dup(obj->cpuset); - if (child->complete_cpuset) - hwloc_bitmap_copy(child->complete_cpuset, obj->complete_cpuset); - else - child->complete_cpuset = hwloc_bitmap_dup(obj->complete_cpuset); + /* no need to recurse because hwloc__attach_memory_object() + * makes sure nodesets are consistent within each memory hierarchy. + */ } /* Propagate our nodeset to CPU children. */ @@ -2219,6 +2315,7 @@ hwloc_reset_normal_type_depths(hwloc_topology_t topology) for (i=HWLOC_OBJ_TYPE_MIN; i<=HWLOC_OBJ_GROUP; i++) topology->type_depth[i] = HWLOC_TYPE_DEPTH_UNKNOWN; /* type contiguity is asserted in topology_check() */ + topology->type_depth[HWLOC_OBJ_DIE] = HWLOC_TYPE_DEPTH_UNKNOWN; } static int @@ -2245,6 +2342,8 @@ hwloc_compare_levels_structure(hwloc_topology_t topology, unsigned i) return -1; for(j=0; jlevel_nbobjects[i]; j++) { + if (topology->levels[i-1][j] != topology->levels[i][j]->parent) + return -1; if (topology->levels[i-1][j]->arity != 1) return -1; if (checkmemory && topology->levels[i-1][j]->memory_arity) @@ -2434,6 +2533,7 @@ hwloc_propagate_symmetric_subtree(hwloc_topology_t topology, hwloc_obj_t root) { hwloc_obj_t child; unsigned arity = root->arity; + hwloc_obj_t *array; int ok; /* assume we're not symmetric by default */ @@ -2465,8 +2565,9 @@ hwloc_propagate_symmetric_subtree(hwloc_topology_t topology, hwloc_obj_t root) /* now check that children subtrees are identical. * just walk down the first child in each tree and compare their depth and arities */ -{ - HWLOC_VLA(hwloc_obj_t, array, arity); + array = malloc(arity * sizeof(*array)); + if (!array) + return; memcpy(array, root->children, arity * sizeof(*array)); while (1) { unsigned i; @@ -2474,8 +2575,9 @@ hwloc_propagate_symmetric_subtree(hwloc_topology_t topology, hwloc_obj_t root) for(i=1; idepth != array[0]->depth || array[i]->arity != array[0]->arity) { - return; - } + free(array); + return; + } if (!array[0]->arity) /* no more children level, we're ok */ break; @@ -2483,7 +2585,7 @@ hwloc_propagate_symmetric_subtree(hwloc_topology_t topology, hwloc_obj_t root) for(i=0; ifirst_child; } -} + free(array); /* everything went fine, we're symmetric */ good: @@ -2601,57 +2703,23 @@ hwloc_connect_children(hwloc_obj_t parent) } /* - * Check whether there is an object below ROOT that has the same type as OBJ + * Check whether there is an object strictly below ROOT that has the same type as OBJ */ static int find_same_type(hwloc_obj_t root, hwloc_obj_t obj) { hwloc_obj_t child; - if (hwloc_type_cmp(root, obj) == HWLOC_OBJ_EQUAL) - return 1; - - for_each_child (child, root) + for_each_child (child, root) { + if (hwloc_type_cmp(child, obj) == HWLOC_OBJ_EQUAL) + return 1; if (find_same_type(child, obj)) return 1; + } return 0; } -/* traverse the array of current object and compare them with top_obj. - * if equal, take the object and put its children into the remaining objs. - * if not equal, put the object into the remaining objs. - */ -static unsigned -hwloc_level_take_objects(hwloc_obj_t top_obj, - hwloc_obj_t *current_objs, unsigned n_current_objs, - hwloc_obj_t *taken_objs, unsigned n_taken_objs __hwloc_attribute_unused, - hwloc_obj_t *remaining_objs, unsigned n_remaining_objs __hwloc_attribute_unused) -{ - unsigned taken_i = 0; - unsigned new_i = 0; - unsigned i, j; - - for (i = 0; i < n_current_objs; i++) - if (hwloc_type_cmp(top_obj, current_objs[i]) == HWLOC_OBJ_EQUAL) { - /* Take it, add main children. */ - taken_objs[taken_i++] = current_objs[i]; - for (j = 0; j < current_objs[i]->arity; j++) - remaining_objs[new_i++] = current_objs[i]->children[j]; - } else { - /* Leave it. */ - remaining_objs[new_i++] = current_objs[i]; - } - -#ifdef HWLOC_DEBUG - /* Make sure we didn't mess up. */ - assert(taken_i == n_taken_objs); - assert(new_i == n_current_objs - n_taken_objs + n_remaining_objs); -#endif - - return new_i; -} - static int hwloc_build_level_from_list(struct hwloc_special_level_s *slevel) { @@ -2670,6 +2738,9 @@ hwloc_build_level_from_list(struct hwloc_special_level_s *slevel) if (nb) { /* allocate and fill level */ slevel->objs = malloc(nb * sizeof(struct hwloc_obj *)); + if (!slevel->objs) + return -1; + obj = slevel->first; i = 0; while (obj) { @@ -2709,7 +2780,17 @@ hwloc_list_special_objects(hwloc_topology_t topology, hwloc_obj_t obj) /* Insert the main NUMA node list */ hwloc_append_special_object(&topology->slevels[HWLOC_SLEVEL_NUMANODE], obj); - /* Recurse */ + /* Recurse, NUMA nodes only have Misc children */ + for_each_misc_child(child, obj) + hwloc_list_special_objects(topology, child); + + } else if (obj->type == HWLOC_OBJ_MEMCACHE) { + obj->next_cousin = NULL; + obj->depth = HWLOC_TYPE_DEPTH_MEMCACHE; + /* Insert the main MemCache list */ + hwloc_append_special_object(&topology->slevels[HWLOC_SLEVEL_MEMCACHE], obj); + + /* Recurse, MemCaches have NUMA nodes or Misc children */ for_each_memory_child(child, obj) hwloc_list_special_objects(topology, child); for_each_misc_child(child, obj) @@ -2742,6 +2823,7 @@ hwloc_list_special_objects(hwloc_topology_t topology, hwloc_obj_t obj) /* Insert in the main osdev list */ hwloc_append_special_object(&topology->slevels[HWLOC_SLEVEL_OSDEV], obj); } + /* Recurse, I/O only have I/O and Misc children */ for_each_io_child(child, obj) hwloc_list_special_objects(topology, child); @@ -2762,7 +2844,7 @@ hwloc_list_special_objects(hwloc_topology_t topology, hwloc_obj_t obj) } /* Build I/O levels */ -static void +static int hwloc_connect_io_misc_levels(hwloc_topology_t topology) { unsigned i; @@ -2773,8 +2855,12 @@ hwloc_connect_io_misc_levels(hwloc_topology_t topology) hwloc_list_special_objects(topology, topology->levels[0][0]); - for(i=0; islevels[i]); + for(i=0; islevels[i]) < 0) + return -1; + } + + return 0; } /* @@ -2849,32 +2935,48 @@ hwloc_connect_levels(hwloc_topology_t topology) /* Now peek all objects of the same type, build a level with that and * replace them with their children. */ - /* First count them. */ - n_taken_objs = 0; - n_new_objs = 0; - for (i = 0; i < n_objs; i++) - if (hwloc_type_cmp(top_obj, objs[i]) == HWLOC_OBJ_EQUAL) { - n_taken_objs++; - n_new_objs += objs[i]->arity; - } - - /* New level. */ - taken_objs = malloc((n_taken_objs + 1) * sizeof(taken_objs[0])); - /* New list of pending objects. */ - if (n_objs - n_taken_objs + n_new_objs) { - new_objs = malloc((n_objs - n_taken_objs + n_new_objs) * sizeof(new_objs[0])); - } else { -#ifdef HWLOC_DEBUG - assert(!n_new_objs); - assert(n_objs == n_taken_objs); -#endif - new_objs = NULL; + /* allocate enough to take all current objects and an ending NULL */ + taken_objs = malloc((n_objs+1) * sizeof(taken_objs[0])); + if (!taken_objs) { + free(objs); + errno = ENOMEM; + return -1; } - n_new_objs = hwloc_level_take_objects(top_obj, - objs, n_objs, - taken_objs, n_taken_objs, - new_objs, n_new_objs); + /* allocate enough to keep all current objects or their children */ + n_new_objs = 0; + for (i = 0; i < n_objs; i++) { + if (objs[i]->arity) + n_new_objs += objs[i]->arity; + else + n_new_objs++; + } + new_objs = malloc(n_new_objs * sizeof(new_objs[0])); + if (!new_objs) { + free(objs); + free(taken_objs); + errno = ENOMEM; + return -1; + } + + /* now actually take these objects */ + n_new_objs = 0; + n_taken_objs = 0; + for (i = 0; i < n_objs; i++) + if (hwloc_type_cmp(top_obj, objs[i]) == HWLOC_OBJ_EQUAL) { + /* Take it, add main children. */ + taken_objs[n_taken_objs++] = objs[i]; + memcpy(&new_objs[n_new_objs], objs[i]->children, objs[i]->arity * sizeof(new_objs[0])); + n_new_objs += objs[i]->arity; + } else { + /* Leave it. */ + new_objs[n_new_objs++] = objs[i]; + } + + if (!n_new_objs) { + free(new_objs); + new_objs = NULL; + } /* Ok, put numbers in the level and link cousins. */ for (i = 0; i < n_taken_objs; i++) { @@ -2964,13 +3066,69 @@ hwloc_topology_reconnect(struct hwloc_topology *topology, unsigned long flags) if (hwloc_connect_levels(topology) < 0) return -1; - hwloc_connect_io_misc_levels(topology); + if (hwloc_connect_io_misc_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 + * + * Only needed for OSDev for now. + */ +static hwloc_obj_t +hwloc_debug_insert_osdev_sorted(hwloc_obj_t queue, hwloc_obj_t obj) +{ + hwloc_obj_t *pcur = &queue; + while (*pcur && strcmp((*pcur)->name, obj->name) < 0) + pcur = &((*pcur)->next_sibling); + obj->next_sibling = *pcur; + *pcur = obj; + return queue; +} + +static void +hwloc_debug_sort_children(hwloc_obj_t root) +{ + hwloc_obj_t child; + + if (root->io_first_child) { + hwloc_obj_t osdevqueue, *pchild; + + pchild = &root->io_first_child; + osdevqueue = NULL; + while ((child = *pchild) != NULL) { + if (child->type != HWLOC_OBJ_OS_DEVICE) { + /* keep non-osdev untouched */ + pchild = &child->next_sibling; + continue; + } + + /* dequeue this child */ + *pchild = child->next_sibling; + child->next_sibling = NULL; + + /* insert in osdev queue in order */ + osdevqueue = hwloc_debug_insert_osdev_sorted(osdevqueue, child); + } + + /* requeue the now-sorted osdev queue */ + *pchild = osdevqueue; + } + + /* Recurse */ + for_each_child(child, root) + hwloc_debug_sort_children(child); + for_each_memory_child(child, root) + hwloc_debug_sort_children(child); + for_each_io_child(child, root) + hwloc_debug_sort_children(child); + /* no I/O under Misc */ +} + void hwloc_alloc_root_sets(hwloc_obj_t root) { /* @@ -2992,11 +3150,32 @@ void hwloc_alloc_root_sets(hwloc_obj_t root) root->complete_nodeset = hwloc_bitmap_alloc(); } -/* Main discovery loop */ -static int -hwloc_discover(struct hwloc_topology *topology) +static void +hwloc_discover_by_phase(struct hwloc_topology *topology, + struct hwloc_disc_status *dstatus, + const char *phasename __hwloc_attribute_unused) { struct hwloc_backend *backend; + hwloc_debug("%s phase discovery...\n", phasename); + for(backend = topology->backends; backend; backend = backend->next) { + if (dstatus->phase & dstatus->excluded_phases) + break; + if (!(backend->phases & dstatus->phase)) + continue; + if (!backend->discover) + continue; + hwloc_debug("%s phase discovery in component %s...\n", phasename, backend->component->name); + backend->discover(backend, dstatus); + hwloc_debug_print_objects(0, topology->levels[0][0]); + } +} + +/* Main discovery loop */ +static int +hwloc_discover(struct hwloc_topology *topology, + struct hwloc_disc_status *dstatus) +{ + const char *env; topology->modified = 0; /* no need to reconnect yet */ @@ -3038,38 +3217,70 @@ hwloc_discover(struct hwloc_topology *topology) * automatically propagated to the whole tree after detection. */ - /* - * Discover CPUs first - */ - backend = topology->backends; - while (NULL != backend) { - if (backend->component->type != HWLOC_DISC_COMPONENT_TYPE_CPU - && backend->component->type != HWLOC_DISC_COMPONENT_TYPE_GLOBAL) - /* not yet */ - goto next_cpubackend; - if (!backend->discover) - goto next_cpubackend; - backend->discover(backend); - hwloc_debug_print_objects(0, topology->levels[0][0]); + if (topology->backend_phases & HWLOC_DISC_PHASE_GLOBAL) { + /* usually, GLOBAL is alone. + * but HWLOC_ANNOTATE_GLOBAL_COMPONENTS=1 allows optional ANNOTATE steps. + */ + struct hwloc_backend *global_backend = topology->backends; + assert(global_backend); + assert(global_backend->phases == HWLOC_DISC_PHASE_GLOBAL); -next_cpubackend: - backend = backend->next; + /* + * Perform the single-component-based GLOBAL discovery + */ + hwloc_debug("GLOBAL phase discovery...\n"); + hwloc_debug("GLOBAL phase discovery with component %s...\n", global_backend->component->name); + dstatus->phase = HWLOC_DISC_PHASE_GLOBAL; + global_backend->discover(global_backend, dstatus); + hwloc_debug_print_objects(0, topology->levels[0][0]); + } + /* Don't explicitly ignore other phases, in case there's ever + * a need to bring them back. + * The component with usually exclude them by default anyway. + * Except if annotating global components is explicitly requested. + */ + + if (topology->backend_phases & HWLOC_DISC_PHASE_CPU) { + /* + * Discover CPUs first + */ + dstatus->phase = HWLOC_DISC_PHASE_CPU; + hwloc_discover_by_phase(topology, dstatus, "CPU"); + } + + if (!(topology->backend_phases & (HWLOC_DISC_PHASE_GLOBAL|HWLOC_DISC_PHASE_CPU))) { + hwloc_debug("No GLOBAL or CPU component phase found\n"); + /* we'll fail below */ } /* One backend should have called hwloc_alloc_root_sets() * and set bits during PU and NUMA insert. */ if (!topology->levels[0][0]->cpuset || hwloc_bitmap_iszero(topology->levels[0][0]->cpuset)) { - hwloc_debug("%s", "No PU added by any CPU and global backend\n"); + hwloc_debug("%s", "No PU added by any CPU or GLOBAL component phase\n"); errno = EINVAL; return -1; } - if (topology->binding_hooks.get_allowed_resources && topology->is_thissystem) { - const char *env = getenv("HWLOC_THISSYSTEM_ALLOWED_RESOURCES"); - if ((env && atoi(env)) - || (topology->flags & HWLOC_TOPOLOGY_FLAG_THISSYSTEM_ALLOWED_RESOURCES)) - topology->binding_hooks.get_allowed_resources(topology); + /* + * Memory-specific discovery + */ + if (topology->backend_phases & HWLOC_DISC_PHASE_MEMORY) { + dstatus->phase = HWLOC_DISC_PHASE_MEMORY; + hwloc_discover_by_phase(topology, dstatus, "MEMORY"); + } + + if (/* check if getting the sets of locally allowed resources is possible */ + topology->binding_hooks.get_allowed_resources + && topology->is_thissystem + /* check whether it has been done already */ + && !(dstatus->flags & HWLOC_DISC_STATUS_FLAG_GOT_ALLOWED_RESOURCES) + /* check whether it was explicitly requested */ + && ((topology->flags & HWLOC_TOPOLOGY_FLAG_THISSYSTEM_ALLOWED_RESOURCES) != 0 + || ((env = getenv("HWLOC_THISSYSTEM_ALLOWED_RESOURCES")) != NULL && atoi(env)))) { + /* OK, get the sets of locally allowed resources */ + topology->binding_hooks.get_allowed_resources(topology); + dstatus->flags |= HWLOC_DISC_STATUS_FLAG_GOT_ALLOWED_RESOURCES; } /* If there's no NUMA node, add one with all the memory. @@ -3113,7 +3324,7 @@ next_cpubackend: hwloc_debug_print_objects(0, topology->levels[0][0]); - if (!(topology->flags & HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM)) { + if (!(topology->flags & HWLOC_TOPOLOGY_FLAG_INCLUDE_DISALLOWED)) { hwloc_debug("%s", "\nRemoving unauthorized sets from all sets\n"); remove_unused_sets(topology, topology->levels[0][0]); hwloc_debug_print_objects(0, topology->levels[0][0]); @@ -3149,28 +3360,27 @@ next_cpubackend: hwloc_debug_print_objects(0, topology->levels[0][0]); /* - * Additional discovery with other backends + * Additional discovery */ - - backend = topology->backends; - while (NULL != backend) { - if (backend->component->type == HWLOC_DISC_COMPONENT_TYPE_CPU - || backend->component->type == HWLOC_DISC_COMPONENT_TYPE_GLOBAL) - /* already done above */ - goto next_noncpubackend; - if (!backend->discover) - goto next_noncpubackend; - backend->discover(backend); - hwloc_debug_print_objects(0, topology->levels[0][0]); - -next_noncpubackend: - backend = backend->next; + if (topology->backend_phases & HWLOC_DISC_PHASE_PCI) { + dstatus->phase = HWLOC_DISC_PHASE_PCI; + hwloc_discover_by_phase(topology, dstatus, "PCI"); + } + if (topology->backend_phases & HWLOC_DISC_PHASE_IO) { + dstatus->phase = HWLOC_DISC_PHASE_IO; + hwloc_discover_by_phase(topology, dstatus, "IO"); + } + if (topology->backend_phases & HWLOC_DISC_PHASE_MISC) { + dstatus->phase = HWLOC_DISC_PHASE_MISC; + hwloc_discover_by_phase(topology, dstatus, "MISC"); + } + if (topology->backend_phases & HWLOC_DISC_PHASE_ANNOTATE) { + dstatus->phase = HWLOC_DISC_PHASE_ANNOTATE; + hwloc_discover_by_phase(topology, dstatus, "ANNOTATE"); } - hwloc_pci_belowroot_apply_locality(topology); - - hwloc_debug("%s", "\nNow reconnecting\n"); - hwloc_debug_print_objects(0, topology->levels[0][0]); + if (getenv("HWLOC_DEBUG_SORT_CHILDREN")) + hwloc_debug_sort_children(topology->levels[0][0]); /* Remove some stuff */ @@ -3217,7 +3427,8 @@ next_noncpubackend: /* add some identification attributes if not loading from XML */ if (topology->backends - && strcmp(topology->backends->component->name, "xml")) { + && strcmp(topology->backends->component->name, "xml") + && !getenv("HWLOC_DONT_ADD_VERSION_INFO")) { char *value; /* add a hwlocVersion */ hwloc_obj_add_info(topology->levels[0][0], "hwlocVersion", HWLOC_VERSION); @@ -3269,6 +3480,7 @@ hwloc_topology_setup_defaults(struct hwloc_topology *topology) HWLOC_BUILD_ASSERT(HWLOC_SLEVEL_BRIDGE == HWLOC_SLEVEL_FROM_DEPTH(HWLOC_TYPE_DEPTH_BRIDGE)); HWLOC_BUILD_ASSERT(HWLOC_SLEVEL_PCIDEV == HWLOC_SLEVEL_FROM_DEPTH(HWLOC_TYPE_DEPTH_PCI_DEVICE)); HWLOC_BUILD_ASSERT(HWLOC_SLEVEL_OSDEV == HWLOC_SLEVEL_FROM_DEPTH(HWLOC_TYPE_DEPTH_OS_DEVICE)); + HWLOC_BUILD_ASSERT(HWLOC_SLEVEL_MEMCACHE == HWLOC_SLEVEL_FROM_DEPTH(HWLOC_TYPE_DEPTH_MEMCACHE)); /* sane values to type_depth */ hwloc_reset_normal_type_depths(topology); @@ -3277,6 +3489,7 @@ hwloc_topology_setup_defaults(struct hwloc_topology *topology) topology->type_depth[HWLOC_OBJ_BRIDGE] = HWLOC_TYPE_DEPTH_BRIDGE; topology->type_depth[HWLOC_OBJ_PCI_DEVICE] = HWLOC_TYPE_DEPTH_PCI_DEVICE; topology->type_depth[HWLOC_OBJ_OS_DEVICE] = HWLOC_TYPE_DEPTH_OS_DEVICE; + topology->type_depth[HWLOC_OBJ_MEMCACHE] = HWLOC_TYPE_DEPTH_MEMCACHE; /* Create the actual machine object, but don't touch its attributes yet * since the OS backend may still change the object into something else @@ -3303,7 +3516,7 @@ hwloc__topology_init (struct hwloc_topology **topologyp, topology->tma = tma; hwloc_components_init(); /* uses malloc without tma, but won't need it since dup() caller already took a reference */ - hwloc_backends_init(topology); + hwloc_topology_components_init(topology); hwloc_pci_discovery_init(topology); /* make sure both dup() and load() get sane variables */ /* Setup topology context */ @@ -3320,7 +3533,7 @@ hwloc__topology_init (struct hwloc_topology **topologyp, topology->support.cpubind = hwloc_tma_malloc(tma, sizeof(*topology->support.cpubind)); topology->support.membind = hwloc_tma_malloc(tma, sizeof(*topology->support.membind)); - topology->nb_levels_allocated = nblevels; /* enough for default 9 levels = Mach+Pack+NUMA+L3+L2+L1d+L1i+Co+PU */ + topology->nb_levels_allocated = nblevels; /* enough for default 10 levels = Mach+Pack+Die+NUMA+L3+L2+L1d+L1i+Co+PU */ topology->levels = hwloc_tma_calloc(tma, topology->nb_levels_allocated * sizeof(*topology->levels)); topology->level_nbobjects = hwloc_tma_calloc(tma, topology->nb_levels_allocated * sizeof(*topology->level_nbobjects)); @@ -3343,7 +3556,7 @@ int hwloc_topology_init (struct hwloc_topology **topologyp) { return hwloc__topology_init(topologyp, - 16, /* 16 is enough for default 9 levels = Mach+Pack+NUMA+L3+L2+L1d+L1i+Co+PU */ + 16, /* 16 is enough for default 10 levels = Mach+Pack+Die+NUMA+L3+L2+L1d+L1i+Co+PU */ NULL); /* no TMA for normal topologies, too many allocations to fix */ } @@ -3376,7 +3589,7 @@ hwloc_topology_set_synthetic(struct hwloc_topology *topology, const char *descri return hwloc_disc_component_force_enable(topology, 0 /* api */, - -1, "synthetic", + "synthetic", description, NULL, NULL); } @@ -3391,7 +3604,7 @@ hwloc_topology_set_xml(struct hwloc_topology *topology, return hwloc_disc_component_force_enable(topology, 0 /* api */, - -1, "xml", + "xml", xmlpath, NULL, NULL); } @@ -3407,7 +3620,7 @@ hwloc_topology_set_xmlbuffer(struct hwloc_topology *topology, return hwloc_disc_component_force_enable(topology, 0 /* api */, - -1, "xml", NULL, + "xml", NULL, xmlbuffer, (void*) (uintptr_t) size); } @@ -3420,7 +3633,7 @@ hwloc_topology_set_flags (struct hwloc_topology *topology, unsigned long flags) return -1; } - if (flags & ~(HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM|HWLOC_TOPOLOGY_FLAG_IS_THISSYSTEM|HWLOC_TOPOLOGY_FLAG_THISSYSTEM_ALLOWED_RESOURCES)) { + if (flags & ~(HWLOC_TOPOLOGY_FLAG_INCLUDE_DISALLOWED|HWLOC_TOPOLOGY_FLAG_IS_THISSYSTEM|HWLOC_TOPOLOGY_FLAG_THISSYSTEM_ALLOWED_RESOURCES)) { errno = EINVAL; return -1; } @@ -3445,6 +3658,7 @@ hwloc__topology_filter_init(struct hwloc_topology *topology) topology->type_filter[HWLOC_OBJ_L1ICACHE] = HWLOC_TYPE_FILTER_KEEP_NONE; topology->type_filter[HWLOC_OBJ_L2ICACHE] = HWLOC_TYPE_FILTER_KEEP_NONE; topology->type_filter[HWLOC_OBJ_L3ICACHE] = HWLOC_TYPE_FILTER_KEEP_NONE; + topology->type_filter[HWLOC_OBJ_MEMCACHE] = HWLOC_TYPE_FILTER_KEEP_NONE; topology->type_filter[HWLOC_OBJ_GROUP] = HWLOC_TYPE_FILTER_KEEP_STRUCTURE; topology->type_filter[HWLOC_OBJ_MISC] = HWLOC_TYPE_FILTER_KEEP_NONE; topology->type_filter[HWLOC_OBJ_BRIDGE] = HWLOC_TYPE_FILTER_KEEP_NONE; @@ -3575,6 +3789,7 @@ hwloc_topology_destroy (struct hwloc_topology *topology) } hwloc_backends_disable_all(topology); + hwloc_topology_components_fini(topology); hwloc_components_fini(); hwloc_topology_clear(topology); @@ -3591,6 +3806,8 @@ hwloc_topology_destroy (struct hwloc_topology *topology) int hwloc_topology_load (struct hwloc_topology *topology) { + struct hwloc_disc_status dstatus; + const char *env; int err; if (topology->is_loaded) { @@ -3617,7 +3834,7 @@ hwloc_topology_load (struct hwloc_topology *topology) if (fsroot_path_env) hwloc_disc_component_force_enable(topology, 1 /* env force */, - HWLOC_DISC_COMPONENT_TYPE_CPU, "linux", + "linux", NULL /* backend will getenv again */, NULL, NULL); } if (!topology->backends) { @@ -3625,7 +3842,7 @@ hwloc_topology_load (struct hwloc_topology *topology) if (cpuid_path_env) hwloc_disc_component_force_enable(topology, 1 /* env force */, - HWLOC_DISC_COMPONENT_TYPE_CPU, "x86", + "x86", NULL /* backend will getenv again */, NULL, NULL); } if (!topology->backends) { @@ -3633,7 +3850,7 @@ hwloc_topology_load (struct hwloc_topology *topology) if (synthetic_env) hwloc_disc_component_force_enable(topology, 1 /* env force */, - -1, "synthetic", + "synthetic", synthetic_env, NULL, NULL); } if (!topology->backends) { @@ -3641,11 +3858,19 @@ hwloc_topology_load (struct hwloc_topology *topology) if (xmlpath_env) hwloc_disc_component_force_enable(topology, 1 /* env force */, - -1, "xml", + "xml", xmlpath_env, NULL, NULL); } } + dstatus.excluded_phases = 0; + dstatus.flags = 0; /* did nothing yet */ + + env = getenv("HWLOC_ALLOW"); + if (env && !strcmp(env, "all")) + /* don't retrieve the sets of allowed resources */ + dstatus.flags |= HWLOC_DISC_STATUS_FLAG_GOT_ALLOWED_RESOURCES; + /* instantiate all possible other backends now */ hwloc_disc_components_enable_others(topology); /* now that backends are enabled, update the thissystem flag and some callbacks */ @@ -3660,7 +3885,7 @@ hwloc_topology_load (struct hwloc_topology *topology) hwloc_pci_discovery_prepare(topology); /* actual topology discovery */ - err = hwloc_discover(topology); + err = hwloc_discover(topology, &dstatus); if (err < 0) goto out; @@ -3682,6 +3907,12 @@ hwloc_topology_load (struct hwloc_topology *topology) hwloc_internal_distances_refresh(topology); topology->is_loaded = 1; + + if (topology->backend_phases & HWLOC_DISC_PHASE_TWEAK) { + dstatus.phase = HWLOC_DISC_PHASE_TWEAK; + hwloc_discover_by_phase(topology, &dstatus, "TWEAK"); + } + return 0; out: @@ -3740,7 +3971,75 @@ restrict_object_by_cpuset(hwloc_topology_t topology, unsigned long flags, hwloc_ && hwloc_bitmap_iszero(obj->cpuset) && (obj->type != HWLOC_OBJ_NUMANODE || (flags & HWLOC_RESTRICT_FLAG_REMOVE_CPULESS))) { /* remove object */ - hwloc_debug("%s", "\nRemoving object during restrict"); + hwloc_debug("%s", "\nRemoving object during restrict by cpuset"); + hwloc_debug_print_object(0, obj); + + if (!(flags & HWLOC_RESTRICT_FLAG_ADAPT_IO)) { + hwloc_free_object_siblings_and_children(obj->io_first_child); + obj->io_first_child = NULL; + } + if (!(flags & HWLOC_RESTRICT_FLAG_ADAPT_MISC)) { + hwloc_free_object_siblings_and_children(obj->misc_first_child); + obj->misc_first_child = NULL; + } + assert(!obj->first_child); + assert(!obj->memory_first_child); + unlink_and_free_single_object(pobj); + topology->modified = 1; + } +} + +/* adjust object nodesets according the given droppednodeset, + * drop object whose nodeset becomes empty and that have no children, + * and propagate PU removal as cpuset changes in parents. + */ +static void +restrict_object_by_nodeset(hwloc_topology_t topology, unsigned long flags, hwloc_obj_t *pobj, + hwloc_bitmap_t droppedcpuset, hwloc_bitmap_t droppednodeset) +{ + hwloc_obj_t obj = *pobj, child, *pchild; + int modified = 0; + + if (hwloc_bitmap_intersects(obj->complete_nodeset, droppednodeset)) { + hwloc_bitmap_andnot(obj->nodeset, obj->nodeset, droppednodeset); + hwloc_bitmap_andnot(obj->complete_nodeset, obj->complete_nodeset, droppednodeset); + modified = 1; + } else { + if ((flags & HWLOC_RESTRICT_FLAG_REMOVE_MEMLESS) + && hwloc_bitmap_iszero(obj->complete_nodeset)) { + /* we're empty, there's a PU below us, it'll be removed this time */ + modified = 1; + } + /* cpuset cannot intersect unless nodeset intersects or is empty */ + if (droppedcpuset) + assert(!hwloc_bitmap_intersects(obj->complete_cpuset, droppedcpuset) + || hwloc_bitmap_iszero(obj->complete_nodeset)); + } + if (droppedcpuset) { + hwloc_bitmap_andnot(obj->cpuset, obj->cpuset, droppedcpuset); + hwloc_bitmap_andnot(obj->complete_cpuset, obj->complete_cpuset, droppedcpuset); + } + + if (modified) { + for_each_child_safe(child, obj, pchild) + restrict_object_by_nodeset(topology, flags, pchild, droppedcpuset, droppednodeset); + if (flags & HWLOC_RESTRICT_FLAG_REMOVE_MEMLESS) + /* cpuset may have changed above where some NUMA nodes were removed. + * if some hwloc_bitmap_first(child->complete_cpuset) changed, children might need to be reordered */ + hwloc__reorder_children(obj); + + for_each_memory_child_safe(child, obj, pchild) + restrict_object_by_nodeset(topology, flags, pchild, droppedcpuset, droppednodeset); + /* FIXME: we may have to reorder CPU-less groups of NUMA nodes if some of their nodes were removed */ + + /* Nothing to restrict under I/O or Misc */ + } + + if (!obj->first_child && !obj->memory_first_child /* arity not updated before connect_children() */ + && hwloc_bitmap_iszero(obj->nodeset) + && (obj->type != HWLOC_OBJ_PU || (flags & HWLOC_RESTRICT_FLAG_REMOVE_MEMLESS))) { + /* remove object */ + hwloc_debug("%s", "\nRemoving object during restrict by nodeset"); hwloc_debug_print_object(0, obj); if (!(flags & HWLOC_RESTRICT_FLAG_ADAPT_IO)) { @@ -3759,7 +4058,7 @@ restrict_object_by_cpuset(hwloc_topology_t topology, unsigned long flags, hwloc_ } int -hwloc_topology_restrict(struct hwloc_topology *topology, hwloc_const_cpuset_t cpuset, unsigned long flags) +hwloc_topology_restrict(struct hwloc_topology *topology, hwloc_const_bitmap_t set, unsigned long flags) { hwloc_bitmap_t droppedcpuset, droppednodeset; @@ -3767,15 +4066,35 @@ hwloc_topology_restrict(struct hwloc_topology *topology, hwloc_const_cpuset_t cp errno = EINVAL; return -1; } + if (topology->adopted_shmem_addr) { + errno = EPERM; + return -1; + } if (flags & ~(HWLOC_RESTRICT_FLAG_REMOVE_CPULESS - |HWLOC_RESTRICT_FLAG_ADAPT_MISC|HWLOC_RESTRICT_FLAG_ADAPT_IO)) { + |HWLOC_RESTRICT_FLAG_ADAPT_MISC|HWLOC_RESTRICT_FLAG_ADAPT_IO + |HWLOC_RESTRICT_FLAG_BYNODESET|HWLOC_RESTRICT_FLAG_REMOVE_MEMLESS)) { errno = EINVAL; return -1; } + if (flags & HWLOC_RESTRICT_FLAG_BYNODESET) { + /* cannot use CPULESS with BYNODESET */ + if (flags & HWLOC_RESTRICT_FLAG_REMOVE_CPULESS) { + errno = EINVAL; + return -1; + } + } else { + /* cannot use MEMLESS without BYNODESET */ + if (flags & HWLOC_RESTRICT_FLAG_REMOVE_MEMLESS) { + errno = EINVAL; + return -1; + } + } + /* make sure we'll keep something in the topology */ - if (!hwloc_bitmap_intersects(cpuset, topology->allowed_cpuset)) { + if (((flags & HWLOC_RESTRICT_FLAG_BYNODESET) && !hwloc_bitmap_intersects(set, topology->allowed_nodeset)) + || (!(flags & HWLOC_RESTRICT_FLAG_BYNODESET) && !hwloc_bitmap_intersects(set, topology->allowed_cpuset))) { errno = EINVAL; /* easy failure, just don't touch the topology */ return -1; } @@ -3788,39 +4107,76 @@ hwloc_topology_restrict(struct hwloc_topology *topology, hwloc_const_cpuset_t cp return -1; } - /* cpuset to clear */ - hwloc_bitmap_not(droppedcpuset, cpuset); - /* nodeset to clear */ - if (flags & HWLOC_RESTRICT_FLAG_REMOVE_CPULESS) { - hwloc_obj_t node = hwloc_get_obj_by_type(topology, HWLOC_OBJ_NUMANODE, 0); - do { - /* node will be removed if nodeset gets or was empty */ - if (hwloc_bitmap_iszero(node->cpuset) - || hwloc_bitmap_isincluded(node->cpuset, droppedcpuset)) - hwloc_bitmap_set(droppednodeset, node->os_index); - node = node->next_cousin; - } while (node); + if (flags & HWLOC_RESTRICT_FLAG_BYNODESET) { + /* nodeset to clear */ + hwloc_bitmap_not(droppednodeset, set); + /* cpuset to clear */ + if (flags & HWLOC_RESTRICT_FLAG_REMOVE_MEMLESS) { + hwloc_obj_t pu = hwloc_get_obj_by_type(topology, HWLOC_OBJ_PU, 0); + do { + /* PU will be removed if cpuset gets or was empty */ + if (hwloc_bitmap_iszero(pu->cpuset) + || hwloc_bitmap_isincluded(pu->nodeset, droppednodeset)) + hwloc_bitmap_set(droppedcpuset, pu->os_index); + pu = pu->next_cousin; + } while (pu); - /* check we're not removing all NUMA nodes */ - if (hwloc_bitmap_isincluded(topology->allowed_nodeset, droppednodeset)) { - errno = EINVAL; /* easy failure, just don't touch the topology */ + /* check we're not removing all PUs */ + if (hwloc_bitmap_isincluded(topology->allowed_cpuset, droppedcpuset)) { + errno = EINVAL; /* easy failure, just don't touch the topology */ + hwloc_bitmap_free(droppedcpuset); + hwloc_bitmap_free(droppednodeset); + return -1; + } + } + /* remove cpuset if empty */ + if (!(flags & HWLOC_RESTRICT_FLAG_REMOVE_MEMLESS) + || hwloc_bitmap_iszero(droppedcpuset)) { hwloc_bitmap_free(droppedcpuset); - hwloc_bitmap_free(droppednodeset); - return -1; + droppedcpuset = NULL; } - } - /* remove nodeset if empty */ - if (!(flags & HWLOC_RESTRICT_FLAG_REMOVE_CPULESS) - || hwloc_bitmap_iszero(droppednodeset)) { - hwloc_bitmap_free(droppednodeset); - droppednodeset = NULL; - } - /* now recurse to filter sets and drop things */ - restrict_object_by_cpuset(topology, flags, &topology->levels[0][0], droppedcpuset, droppednodeset); - hwloc_bitmap_andnot(topology->allowed_cpuset, topology->allowed_cpuset, droppedcpuset); - if (droppednodeset) + /* now recurse to filter sets and drop things */ + restrict_object_by_nodeset(topology, flags, &topology->levels[0][0], droppedcpuset, droppednodeset); hwloc_bitmap_andnot(topology->allowed_nodeset, topology->allowed_nodeset, droppednodeset); + if (droppedcpuset) + hwloc_bitmap_andnot(topology->allowed_cpuset, topology->allowed_cpuset, droppedcpuset); + + } else { + /* cpuset to clear */ + hwloc_bitmap_not(droppedcpuset, set); + /* nodeset to clear */ + if (flags & HWLOC_RESTRICT_FLAG_REMOVE_CPULESS) { + hwloc_obj_t node = hwloc_get_obj_by_type(topology, HWLOC_OBJ_NUMANODE, 0); + do { + /* node will be removed if nodeset gets or was empty */ + if (hwloc_bitmap_iszero(node->cpuset) + || hwloc_bitmap_isincluded(node->cpuset, droppedcpuset)) + hwloc_bitmap_set(droppednodeset, node->os_index); + node = node->next_cousin; + } while (node); + + /* check we're not removing all NUMA nodes */ + if (hwloc_bitmap_isincluded(topology->allowed_nodeset, droppednodeset)) { + errno = EINVAL; /* easy failure, just don't touch the topology */ + hwloc_bitmap_free(droppedcpuset); + hwloc_bitmap_free(droppednodeset); + return -1; + } + } + /* remove nodeset if empty */ + if (!(flags & HWLOC_RESTRICT_FLAG_REMOVE_CPULESS) + || hwloc_bitmap_iszero(droppednodeset)) { + hwloc_bitmap_free(droppednodeset); + droppednodeset = NULL; + } + + /* now recurse to filter sets and drop things */ + restrict_object_by_cpuset(topology, flags, &topology->levels[0][0], droppedcpuset, droppednodeset); + hwloc_bitmap_andnot(topology->allowed_cpuset, topology->allowed_cpuset, droppedcpuset); + if (droppednodeset) + hwloc_bitmap_andnot(topology->allowed_nodeset, topology->allowed_nodeset, droppednodeset); + } hwloc_bitmap_free(droppedcpuset); hwloc_bitmap_free(droppednodeset); @@ -3849,6 +4205,72 @@ hwloc_topology_restrict(struct hwloc_topology *topology, hwloc_const_cpuset_t cp return -1; } +int +hwloc_topology_allow(struct hwloc_topology *topology, + hwloc_const_cpuset_t cpuset, hwloc_const_nodeset_t nodeset, + unsigned long flags) +{ + if (!topology->is_loaded) + goto einval; + + if (topology->adopted_shmem_addr) { + errno = EPERM; + goto error; + } + + if (!(topology->flags & HWLOC_TOPOLOGY_FLAG_INCLUDE_DISALLOWED)) + goto einval; + + if (flags & ~(HWLOC_ALLOW_FLAG_ALL|HWLOC_ALLOW_FLAG_LOCAL_RESTRICTIONS|HWLOC_ALLOW_FLAG_CUSTOM)) + goto einval; + + switch (flags) { + case HWLOC_ALLOW_FLAG_ALL: { + if (cpuset || nodeset) + goto einval; + hwloc_bitmap_copy(topology->allowed_cpuset, hwloc_get_root_obj(topology)->complete_cpuset); + hwloc_bitmap_copy(topology->allowed_nodeset, hwloc_get_root_obj(topology)->complete_nodeset); + break; + } + case HWLOC_ALLOW_FLAG_LOCAL_RESTRICTIONS: { + if (cpuset || nodeset) + goto einval; + if (!topology->is_thissystem) + goto einval; + if (!topology->binding_hooks.get_allowed_resources) { + errno = ENOSYS; + goto error; + } + topology->binding_hooks.get_allowed_resources(topology); + break; + } + case HWLOC_ALLOW_FLAG_CUSTOM: { + if (cpuset) { + /* keep the intersection with the full topology cpuset, if not empty */ + if (!hwloc_bitmap_intersects(hwloc_get_root_obj(topology)->cpuset, cpuset)) + goto einval; + hwloc_bitmap_and(topology->allowed_cpuset, hwloc_get_root_obj(topology)->cpuset, cpuset); + } + if (nodeset) { + /* keep the intersection with the full topology nodeset, if not empty */ + if (!hwloc_bitmap_intersects(hwloc_get_root_obj(topology)->nodeset, nodeset)) + goto einval; + hwloc_bitmap_and(topology->allowed_nodeset, hwloc_get_root_obj(topology)->nodeset, nodeset); + } + break; + } + default: + goto einval; + } + + return 0; + + einval: + errno = EINVAL; + error: + return -1; +} + int hwloc_topology_is_thissystem(struct hwloc_topology *topology) { @@ -4005,7 +4427,7 @@ hwloc__check_children_cpusets(hwloc_topology_t topology __hwloc_attribute_unused assert(hwloc_bitmap_first(obj->cpuset) == (int) obj->os_index); assert(hwloc_bitmap_weight(obj->complete_cpuset) == 1); assert(hwloc_bitmap_first(obj->complete_cpuset) == (int) obj->os_index); - if (!(topology->flags & HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM)) { + if (!(topology->flags & HWLOC_TOPOLOGY_FLAG_INCLUDE_DISALLOWED)) { assert(hwloc_bitmap_isset(topology->allowed_cpuset, (int) obj->os_index)); } assert(!obj->arity); @@ -4166,6 +4588,8 @@ hwloc__check_object(hwloc_topology_t topology, hwloc_bitmap_t gp_indexes, hwloc_ assert(obj->cpuset); if (obj->type == HWLOC_OBJ_NUMANODE) assert(obj->depth == HWLOC_TYPE_DEPTH_NUMANODE); + else if (obj->type == HWLOC_OBJ_MEMCACHE) + assert(obj->depth == HWLOC_TYPE_DEPTH_MEMCACHE); else assert(obj->depth >= 0); } @@ -4219,7 +4643,7 @@ hwloc__check_nodesets(hwloc_topology_t topology, hwloc_obj_t obj, hwloc_bitmap_t assert(hwloc_bitmap_first(obj->nodeset) == (int) obj->os_index); assert(hwloc_bitmap_weight(obj->complete_nodeset) == 1); assert(hwloc_bitmap_first(obj->complete_nodeset) == (int) obj->os_index); - if (!(topology->flags & HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM)) { + if (!(topology->flags & HWLOC_TOPOLOGY_FLAG_INCLUDE_DISALLOWED)) { assert(hwloc_bitmap_isset(topology->allowed_nodeset, (int) obj->os_index)); } assert(!obj->arity); @@ -4362,7 +4786,9 @@ hwloc_topology_check(struct hwloc_topology *topology) HWLOC_BUILD_ASSERT(HWLOC_OBJ_BRIDGE + 1 == HWLOC_OBJ_PCI_DEVICE); HWLOC_BUILD_ASSERT(HWLOC_OBJ_PCI_DEVICE + 1 == HWLOC_OBJ_OS_DEVICE); HWLOC_BUILD_ASSERT(HWLOC_OBJ_OS_DEVICE + 1 == HWLOC_OBJ_MISC); - HWLOC_BUILD_ASSERT(HWLOC_OBJ_MISC + 1 == HWLOC_OBJ_TYPE_MAX); + HWLOC_BUILD_ASSERT(HWLOC_OBJ_MISC + 1 == HWLOC_OBJ_MEMCACHE); + HWLOC_BUILD_ASSERT(HWLOC_OBJ_MEMCACHE + 1 == HWLOC_OBJ_DIE); + HWLOC_BUILD_ASSERT(HWLOC_OBJ_DIE + 1 == HWLOC_OBJ_TYPE_MAX); /* make sure order and priority arrays have the right size */ HWLOC_BUILD_ASSERT(sizeof(obj_type_order)/sizeof(*obj_type_order) == HWLOC_OBJ_TYPE_MAX); @@ -4408,6 +4834,7 @@ hwloc_topology_check(struct hwloc_topology *topology) int d; type = hwloc_get_depth_type(topology, j); assert(type != HWLOC_OBJ_NUMANODE); + assert(type != HWLOC_OBJ_MEMCACHE); assert(type != HWLOC_OBJ_PCI_DEVICE); assert(type != HWLOC_OBJ_BRIDGE); assert(type != HWLOC_OBJ_OS_DEVICE); @@ -4423,6 +4850,9 @@ hwloc_topology_check(struct hwloc_topology *topology) if (type == HWLOC_OBJ_NUMANODE) { assert(d == HWLOC_TYPE_DEPTH_NUMANODE); assert(hwloc_get_depth_type(topology, d) == HWLOC_OBJ_NUMANODE); + } else if (type == HWLOC_OBJ_MEMCACHE) { + assert(d == HWLOC_TYPE_DEPTH_MEMCACHE); + assert(hwloc_get_depth_type(topology, d) == HWLOC_OBJ_MEMCACHE); } else if (type == HWLOC_OBJ_BRIDGE) { assert(d == HWLOC_TYPE_DEPTH_BRIDGE); assert(hwloc_get_depth_type(topology, d) == HWLOC_OBJ_BRIDGE); @@ -4449,7 +4879,7 @@ hwloc_topology_check(struct hwloc_topology *topology) assert(!obj->depth); /* check that allowed sets are larger than the main sets */ - if (topology->flags & HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM) { + if (topology->flags & HWLOC_TOPOLOGY_FLAG_INCLUDE_DISALLOWED) { assert(hwloc_bitmap_isincluded(topology->allowed_cpuset, obj->cpuset)); assert(hwloc_bitmap_isincluded(topology->allowed_nodeset, obj->nodeset)); } else { diff --git a/src/3rdparty/hwloc/src/traversal.c b/src/3rdparty/hwloc/src/traversal.c index 9c5e6268..0b744d78 100644 --- a/src/3rdparty/hwloc/src/traversal.c +++ b/src/3rdparty/hwloc/src/traversal.c @@ -1,16 +1,17 @@ /* * Copyright © 2009 CNRS - * Copyright © 2009-2018 Inria. All rights reserved. + * Copyright © 2009-2019 Inria. All rights reserved. * Copyright © 2009-2010 Université Bordeaux * Copyright © 2009-2011 Cisco Systems, Inc. All rights reserved. * See COPYING in top-level directory. */ -#include -#include -#include -#include -#include +#include "private/autogen/config.h" +#include "hwloc.h" +#include "private/private.h" +#include "private/misc.h" +#include "private/debug.h" + #ifdef HAVE_STRINGS_H #include #endif /* HAVE_STRINGS_H */ @@ -40,6 +41,8 @@ hwloc_get_depth_type (hwloc_topology_t topology, int depth) return HWLOC_OBJ_OS_DEVICE; case HWLOC_TYPE_DEPTH_MISC: return HWLOC_OBJ_MISC; + case HWLOC_TYPE_DEPTH_MEMCACHE: + return HWLOC_OBJ_MEMCACHE; default: return HWLOC_OBJ_TYPE_NONE; } @@ -237,8 +240,10 @@ hwloc_obj_type_string (hwloc_obj_type_t obj) case HWLOC_OBJ_MACHINE: return "Machine"; case HWLOC_OBJ_MISC: return "Misc"; case HWLOC_OBJ_GROUP: return "Group"; + case HWLOC_OBJ_MEMCACHE: return "MemCache"; case HWLOC_OBJ_NUMANODE: return "NUMANode"; case HWLOC_OBJ_PACKAGE: return "Package"; + case HWLOC_OBJ_DIE: return "Die"; case HWLOC_OBJ_L1CACHE: return "L1Cache"; case HWLOC_OBJ_L2CACHE: return "L2Cache"; case HWLOC_OBJ_L3CACHE: return "L3Cache"; @@ -256,6 +261,41 @@ hwloc_obj_type_string (hwloc_obj_type_t obj) } } +/* Check if string matches the given type at least on minmatch chars. + * On success, return the address of where matching stop, either pointing to \0 or to a suffix (digits, colon, etc) + * On error, return NULL; + */ +static __hwloc_inline const char * +hwloc__type_match(const char *string, + const char *type, /* type must be lowercase */ + size_t minmatch) +{ + const char *s, *t; + unsigned i; + for(i=0, s=string, t=type; ; i++, s++, t++) { + if (!*s) { + /* string ends before type */ + if (i= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') || *s == '-') + /* valid character that doesn't match */ + return NULL; + /* invalid character, we reached the end of the type namein string, stop matching here */ + if (i= '0' && string[1] <= '9') { + char *suffix; depthattr = strtol(string+1, &end, 10); - if (*end == 'i') { + if (*end == 'i' || *end == 'I') { if (depthattr >= 1 && depthattr <= 3) { type = HWLOC_OBJ_L1ICACHE + depthattr-1; cachetypeattr = HWLOC_OBJ_CACHE_INSTRUCTION; + suffix = end+1; } else return -1; } else { if (depthattr >= 1 && depthattr <= 5) { type = HWLOC_OBJ_L1CACHE + depthattr-1; - cachetypeattr = *end == 'd' ? HWLOC_OBJ_CACHE_DATA : HWLOC_OBJ_CACHE_UNIFIED; + if (*end == 'd' || *end == 'D') { + cachetypeattr = HWLOC_OBJ_CACHE_DATA; + suffix = end+1; + } else if (*end == 'u' || *end == 'U') { + cachetypeattr = HWLOC_OBJ_CACHE_UNIFIED; + suffix = end+1; + } else { + cachetypeattr = HWLOC_OBJ_CACHE_UNIFIED; + suffix = end; + } } else return -1; } + /* check whether the optional suffix matches "cache" */ + if (!hwloc__type_match(suffix, "cache", 0)) + return -1; - } else if (!hwloc_strncasecmp(string, "group", 2)) { - size_t length; + } else if ((end = (char *) hwloc__type_match(string, "group", 2)) != NULL) { type = HWLOC_OBJ_GROUP; - length = strcspn(string, "0123456789"); - if (length <= 5 && !hwloc_strncasecmp(string, "group", length) - && string[length] >= '0' && string[length] <= '9') { - depthattr = strtol(string+length, &end, 10); + if (*end >= '0' && *end <= '9') { + depthattr = strtol(end, &end, 10); } } else @@ -421,7 +477,9 @@ hwloc_obj_type_snprintf(char * __hwloc_restrict string, size_t size, hwloc_obj_t case HWLOC_OBJ_MISC: case HWLOC_OBJ_MACHINE: case HWLOC_OBJ_NUMANODE: + case HWLOC_OBJ_MEMCACHE: case HWLOC_OBJ_PACKAGE: + case HWLOC_OBJ_DIE: case HWLOC_OBJ_CORE: case HWLOC_OBJ_PU: return hwloc_snprintf(string, size, "%s", hwloc_obj_type_string(type)); @@ -523,6 +581,7 @@ hwloc_obj_attr_snprintf(char * __hwloc_restrict string, size_t size, hwloc_obj_t case HWLOC_OBJ_L1ICACHE: case HWLOC_OBJ_L2ICACHE: case HWLOC_OBJ_L3ICACHE: + case HWLOC_OBJ_MEMCACHE: if (verbose) { char assoc[32]; if (obj->attr->cache.associativity == -1) From 1fbbae1e4a186cbb82cdeaff6088bb03f450e8b8 Mon Sep 17 00:00:00 2001 From: SChernykh Date: Thu, 5 Dec 2019 19:39:47 +0100 Subject: [PATCH 04/31] Added 1GB hugepages support for Linux --- src/Summary.cpp | 9 +++-- src/backend/common/interfaces/IRxStorage.h | 6 ++-- src/backend/cpu/CpuConfig.cpp | 11 +++--- src/backend/cpu/CpuConfig.h | 2 ++ src/backend/cpu/interfaces/ICpuInfo.h | 1 + src/backend/cpu/platform/BasicCpuInfo.cpp | 14 +++++++- src/backend/cpu/platform/BasicCpuInfo.h | 2 ++ src/config.json | 1 + src/core/Miner.cpp | 2 +- src/crypto/common/VirtualMemory.h | 2 ++ src/crypto/common/VirtualMemory_unix.cpp | 42 ++++++++++++++++++---- src/crypto/common/VirtualMemory_win.cpp | 12 +++++++ src/crypto/randomx/allocator.cpp | 8 +++++ src/crypto/randomx/allocator.hpp | 5 +++ src/crypto/randomx/randomx.cpp | 6 +++- src/crypto/randomx/randomx.h | 1 + src/crypto/randomx/virtual_memory.cpp | 10 ++++++ src/crypto/randomx/virtual_memory.hpp | 1 + src/crypto/rx/Rx.cpp | 4 +-- src/crypto/rx/Rx.h | 2 +- src/crypto/rx/RxBasicStorage.cpp | 8 ++--- src/crypto/rx/RxBasicStorage.h | 2 +- src/crypto/rx/RxDataset.cpp | 21 +++++++---- src/crypto/rx/RxDataset.h | 8 +++-- src/crypto/rx/RxNUMAStorage.cpp | 12 +++---- src/crypto/rx/RxNUMAStorage.h | 2 +- src/crypto/rx/RxQueue.cpp | 6 ++-- src/crypto/rx/RxQueue.h | 6 ++-- 28 files changed, 156 insertions(+), 50 deletions(-) diff --git a/src/Summary.cpp b/src/Summary.cpp index 757297c5..db8ce9fa 100644 --- a/src/Summary.cpp +++ b/src/Summary.cpp @@ -63,6 +63,8 @@ static void print_memory(Config *config) { # ifdef _WIN32 Log::print(GREEN_BOLD(" * ") WHITE_BOLD("%-13s") "%s", "HUGE PAGES", config->cpu().isHugePages() ? (VirtualMemory::isHugepagesAvailable() ? GREEN_BOLD("permission granted") : RED_BOLD("unavailable")) : RED_BOLD("disabled")); + Log::print(GREEN_BOLD(" * ") WHITE_BOLD("%-13s") "%s", + "1GB PAGES", Cpu::info()->hasOneGbPages() ? GREEN_BOLD("available on Linux") : RED_BOLD("unavailable")); # endif } @@ -71,12 +73,13 @@ static void print_cpu(Config *) { const ICpuInfo *info = Cpu::info(); - Log::print(GREEN_BOLD(" * ") WHITE_BOLD("%-13s%s (%zu)") " %sx64 %sAES", + Log::print(GREEN_BOLD(" * ") WHITE_BOLD("%-13s%s (%zu)") " %sx64 %sAES %sPDPE1GB", "CPU", info->brand(), info->packages(), - info->isX64() ? GREEN_BOLD_S : RED_BOLD_S "-", - info->hasAES() ? GREEN_BOLD_S : RED_BOLD_S "-" + info->isX64() ? GREEN_BOLD_S : RED_BOLD_S "-", + info->hasAES() ? GREEN_BOLD_S : RED_BOLD_S "-", + info->hasOneGbPages() ? GREEN_BOLD_S : RED_BOLD_S "-" ); # if defined(XMRIG_FEATURE_LIBCPUID) || defined (XMRIG_FEATURE_HWLOC) Log::print(WHITE_BOLD(" %-13s") BLACK_BOLD("L2:") WHITE_BOLD("%.1f MB") BLACK_BOLD(" L3:") WHITE_BOLD("%.1f MB") diff --git a/src/backend/common/interfaces/IRxStorage.h b/src/backend/common/interfaces/IRxStorage.h index 9e0407b0..f64850f7 100644 --- a/src/backend/common/interfaces/IRxStorage.h +++ b/src/backend/common/interfaces/IRxStorage.h @@ -44,9 +44,9 @@ class IRxStorage public: virtual ~IRxStorage() = default; - virtual RxDataset *dataset(const Job &job, uint32_t nodeId) const = 0; - virtual std::pair hugePages() const = 0; - virtual void init(const RxSeed &seed, uint32_t threads, bool hugePages, RxConfig::Mode mode) = 0; + virtual RxDataset *dataset(const Job &job, uint32_t nodeId) const = 0; + virtual std::pair hugePages() const = 0; + virtual void init(const RxSeed &seed, uint32_t threads, bool hugePages, bool oneGbPages, RxConfig::Mode mode) = 0; }; diff --git a/src/backend/cpu/CpuConfig.cpp b/src/backend/cpu/CpuConfig.cpp index 7ebe904b..fb95f942 100644 --- a/src/backend/cpu/CpuConfig.cpp +++ b/src/backend/cpu/CpuConfig.cpp @@ -34,6 +34,7 @@ namespace xmrig { static const char *kEnabled = "enabled"; static const char *kHugePages = "huge-pages"; +static const char *kOneGbPages = "1gb-pages"; static const char *kHwAes = "hw-aes"; static const char *kMaxThreadsHint = "max-threads-hint"; static const char *kMemoryPool = "memory-pool"; @@ -68,6 +69,7 @@ rapidjson::Value xmrig::CpuConfig::toJSON(rapidjson::Document &doc) const obj.AddMember(StringRef(kEnabled), m_enabled, allocator); obj.AddMember(StringRef(kHugePages), m_hugePages, allocator); + obj.AddMember(StringRef(kOneGbPages), m_oneGbPages, allocator); obj.AddMember(StringRef(kHwAes), m_aes == AES_AUTO ? Value(kNullType) : Value(m_aes == AES_HW), allocator); obj.AddMember(StringRef(kPriority), priority() != -1 ? Value(priority()) : Value(kNullType), allocator); obj.AddMember(StringRef(kMemoryPool), m_memoryPool < 1 ? Value(m_memoryPool < 0) : Value(m_memoryPool), allocator); @@ -119,10 +121,11 @@ std::vector xmrig::CpuConfig::get(const Miner *miner, cons void xmrig::CpuConfig::read(const rapidjson::Value &value) { if (value.IsObject()) { - m_enabled = Json::getBool(value, kEnabled, m_enabled); - m_hugePages = Json::getBool(value, kHugePages, m_hugePages); - m_limit = Json::getUint(value, kMaxThreadsHint, m_limit); - m_yield = Json::getBool(value, kYield, m_yield); + m_enabled = Json::getBool(value, kEnabled, m_enabled); + m_hugePages = Json::getBool(value, kHugePages, m_hugePages); + m_oneGbPages = Json::getBool(value, kOneGbPages, m_oneGbPages); + m_limit = Json::getUint(value, kMaxThreadsHint, m_limit); + m_yield = Json::getBool(value, kYield, m_yield); setAesMode(Json::getValue(value, kHwAes)); setPriority(Json::getInt(value, kPriority, -1)); diff --git a/src/backend/cpu/CpuConfig.h b/src/backend/cpu/CpuConfig.h index 2306057f..74894dcd 100644 --- a/src/backend/cpu/CpuConfig.h +++ b/src/backend/cpu/CpuConfig.h @@ -54,6 +54,7 @@ public: inline bool isEnabled() const { return m_enabled; } inline bool isHugePages() const { return m_hugePages; } + inline bool isOneGbPages() const { return m_oneGbPages; } inline bool isShouldSave() const { return m_shouldSave; } inline bool isYield() const { return m_yield; } inline const Assembly &assembly() const { return m_assembly; } @@ -72,6 +73,7 @@ private: Assembly m_assembly; bool m_enabled = true; bool m_hugePages = true; + bool m_oneGbPages = false; bool m_shouldSave = false; bool m_yield = true; int m_memoryPool = 0; diff --git a/src/backend/cpu/interfaces/ICpuInfo.h b/src/backend/cpu/interfaces/ICpuInfo.h index 20e72391..2ffd00f2 100644 --- a/src/backend/cpu/interfaces/ICpuInfo.h +++ b/src/backend/cpu/interfaces/ICpuInfo.h @@ -48,6 +48,7 @@ public: virtual Assembly::Id assembly() const = 0; virtual bool hasAES() const = 0; virtual bool hasAVX2() const = 0; + virtual bool hasOneGbPages() const = 0; virtual const char *backend() const = 0; virtual const char *brand() const = 0; virtual CpuThreads threads(const Algorithm &algorithm, uint32_t limit) const = 0; diff --git a/src/backend/cpu/platform/BasicCpuInfo.cpp b/src/backend/cpu/platform/BasicCpuInfo.cpp index db3741ee..cdc810c3 100644 --- a/src/backend/cpu/platform/BasicCpuInfo.cpp +++ b/src/backend/cpu/platform/BasicCpuInfo.cpp @@ -45,6 +45,10 @@ # define bit_AVX2 (1 << 5) #endif +#ifndef bit_PDPE1GB +# define bit_PDPE1GB (1 << 26) +#endif + #include "backend/cpu/platform/BasicCpuInfo.h" #include "crypto/common/Assembly.h" @@ -53,6 +57,7 @@ #define VENDOR_ID (0) #define PROCESSOR_INFO (1) #define EXTENDED_FEATURES (7) +#define PROCESSOR_EXT_INFO (0x80000001) #define PROCESSOR_BRAND_STRING_1 (0x80000002) #define PROCESSOR_BRAND_STRING_2 (0x80000003) #define PROCESSOR_BRAND_STRING_3 (0x80000004) @@ -136,6 +141,12 @@ static inline bool has_avx2() } +static inline bool has_pdpe1gb() +{ + return has_feature(PROCESSOR_EXT_INFO, EDX_Reg, bit_PDPE1GB); +} + + } // namespace xmrig @@ -144,7 +155,8 @@ xmrig::BasicCpuInfo::BasicCpuInfo() : m_threads(std::thread::hardware_concurrency()), m_assembly(Assembly::NONE), m_aes(has_aes_ni()), - m_avx2(has_avx2()) + m_avx2(has_avx2()), + m_pdpe1gb(has_pdpe1gb()) { cpu_brand_string(m_brand); diff --git a/src/backend/cpu/platform/BasicCpuInfo.h b/src/backend/cpu/platform/BasicCpuInfo.h index 4c68c5f8..ecbd3e23 100644 --- a/src/backend/cpu/platform/BasicCpuInfo.h +++ b/src/backend/cpu/platform/BasicCpuInfo.h @@ -44,6 +44,7 @@ protected: inline Assembly::Id assembly() const override { return m_assembly; } inline bool hasAES() const override { return m_aes; } inline bool hasAVX2() const override { return m_avx2; } + inline bool hasOneGbPages() const override { return m_pdpe1gb; } inline const char *brand() const override { return m_brand; } inline size_t cores() const override { return 0; } inline size_t L2() const override { return 0; } @@ -60,6 +61,7 @@ private: Assembly m_assembly; bool m_aes; const bool m_avx2; + const bool m_pdpe1gb; }; diff --git a/src/config.json b/src/config.json index df366c0f..dd64f407 100644 --- a/src/config.json +++ b/src/config.json @@ -22,6 +22,7 @@ "cpu": { "enabled": true, "huge-pages": true, + "1gb-pages": false, "hw-aes": null, "priority": null, "memory-pool": false, diff --git a/src/core/Miner.cpp b/src/core/Miner.cpp index 5551268a..4e2f24ba 100644 --- a/src/core/Miner.cpp +++ b/src/core/Miner.cpp @@ -236,7 +236,7 @@ public: # ifdef XMRIG_ALGO_RANDOMX inline bool initRX() { - return Rx::init(job, controller->config()->rx(), controller->config()->cpu().isHugePages()); + return Rx::init(job, controller->config()->rx(), controller->config()->cpu().isHugePages(), controller->config()->cpu().isOneGbPages()); } # endif diff --git a/src/crypto/common/VirtualMemory.h b/src/crypto/common/VirtualMemory.h index 1c2e37d2..90ecdd69 100644 --- a/src/crypto/common/VirtualMemory.h +++ b/src/crypto/common/VirtualMemory.h @@ -61,6 +61,7 @@ public: static uint32_t bindToNUMANode(int64_t affinity); static void *allocateExecutableMemory(size_t size); static void *allocateLargePagesMemory(size_t size); + static void *allocateOneGbPagesMemory(size_t size); static void destroy(); static void flushInstructionCache(void *p, size_t size); static void freeLargePagesMemory(void *p, size_t size); @@ -81,6 +82,7 @@ private: static void osInit(bool hugePages); bool allocateLargePagesMemory(); + bool allocateOneGbPagesMemory(); void freeLargePagesMemory(); const size_t m_size; diff --git a/src/crypto/common/VirtualMemory_unix.cpp b/src/crypto/common/VirtualMemory_unix.cpp index ffa4b137..2e6eed30 100644 --- a/src/crypto/common/VirtualMemory_unix.cpp +++ b/src/crypto/common/VirtualMemory_unix.cpp @@ -58,24 +58,33 @@ void *xmrig::VirtualMemory::allocateExecutableMemory(size_t size) void *xmrig::VirtualMemory::allocateLargePagesMemory(size_t size) { - int flag_1gb = 0; - # if defined(__APPLE__) void *mem = mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, VM_FLAGS_SUPERPAGE_SIZE_2MB, 0); # elif defined(__FreeBSD__) void *mem = mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_ALIGNED_SUPER | MAP_PREFAULT_READ, -1, 0); # else + void *mem = mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB | MAP_POPULATE, 0, 0); +# endif + + return mem == MAP_FAILED ? nullptr : mem; +} + + +void *xmrig::VirtualMemory::allocateOneGbPagesMemory(size_t size) +{ +# if defined(__APPLE__) + void *mem = MAP_FAILED; +# elif defined(__FreeBSD__) + void *mem = MAP_FAILED; +# else # if defined(MAP_HUGE_1GB) - flag_1gb = (size > (1UL << 30)) ? MAP_HUGE_1GB : 0; + constexpr int flag_1gb = MAP_HUGE_1GB; # elif defined(MAP_HUGE_SHIFT) - flag_1gb = (size > (1UL << 30)) ? (30 << MAP_HUGE_SHIFT) : 0; + constexpr int flag_1gb = (30 << MAP_HUGE_SHIFT); # endif void *mem = mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB | MAP_POPULATE | flag_1gb, 0, 0); - if (mem == MAP_FAILED) { - mem = mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB | MAP_POPULATE, 0, 0); - } # endif return mem == MAP_FAILED ? nullptr : mem; @@ -132,6 +141,25 @@ bool xmrig::VirtualMemory::allocateLargePagesMemory() } +bool xmrig::VirtualMemory::allocateOneGbPagesMemory() +{ + m_scratchpad = static_cast(allocateOneGbPagesMemory(m_size)); + if (m_scratchpad) { + m_flags.set(FLAG_HUGEPAGES, true); + + madvise(m_scratchpad, m_size, MADV_RANDOM | MADV_WILLNEED); + + if (mlock(m_scratchpad, m_size) == 0) { + m_flags.set(FLAG_LOCK, true); + } + + return true; + } + + return false; +} + + void xmrig::VirtualMemory::freeLargePagesMemory() { if (m_flags.test(FLAG_LOCK)) { diff --git a/src/crypto/common/VirtualMemory_win.cpp b/src/crypto/common/VirtualMemory_win.cpp index bfd8de1d..70e48d3a 100644 --- a/src/crypto/common/VirtualMemory_win.cpp +++ b/src/crypto/common/VirtualMemory_win.cpp @@ -175,6 +175,12 @@ void *xmrig::VirtualMemory::allocateLargePagesMemory(size_t size) } +void *xmrig::VirtualMemory::allocateOneGbPagesMemory(size_t size) +{ + return nullptr; +} + + void xmrig::VirtualMemory::flushInstructionCache(void *p, size_t size) { ::FlushInstructionCache(GetCurrentProcess(), p, size); @@ -221,6 +227,12 @@ bool xmrig::VirtualMemory::allocateLargePagesMemory() return false; } +bool xmrig::VirtualMemory::allocateOneGbPagesMemory() +{ + m_scratchpad = nullptr; + return false; +} + void xmrig::VirtualMemory::freeLargePagesMemory() { diff --git a/src/crypto/randomx/allocator.cpp b/src/crypto/randomx/allocator.cpp index ff708a62..d46393f1 100644 --- a/src/crypto/randomx/allocator.cpp +++ b/src/crypto/randomx/allocator.cpp @@ -57,4 +57,12 @@ namespace randomx { freePagedMemory(ptr, count); }; + void* OneGbPageAllocator::allocMemory(size_t count) { + return allocOneGbPagesMemory(count); + } + + void OneGbPageAllocator::freeMemory(void* ptr, size_t count) { + freePagedMemory(ptr, count); + }; + } diff --git a/src/crypto/randomx/allocator.hpp b/src/crypto/randomx/allocator.hpp index d7aa3f95..74d8b068 100644 --- a/src/crypto/randomx/allocator.hpp +++ b/src/crypto/randomx/allocator.hpp @@ -43,4 +43,9 @@ namespace randomx { static void freeMemory(void*, size_t); }; + struct OneGbPageAllocator { + static void* allocMemory(size_t); + static void freeMemory(void*, size_t); + }; + } \ No newline at end of file diff --git a/src/crypto/randomx/randomx.cpp b/src/crypto/randomx/randomx.cpp index f9ce93f8..44d881eb 100644 --- a/src/crypto/randomx/randomx.cpp +++ b/src/crypto/randomx/randomx.cpp @@ -333,7 +333,11 @@ extern "C" { try { dataset = new randomx_dataset(); - if (flags & RANDOMX_FLAG_LARGE_PAGES) { + if (flags & RANDOMX_FLAG_1GB_PAGES) { + dataset->dealloc = &randomx::deallocDataset; + dataset->memory = (uint8_t*)randomx::OneGbPageAllocator::allocMemory(RANDOMX_DATASET_MAX_SIZE); + } + else if (flags & RANDOMX_FLAG_LARGE_PAGES) { dataset->dealloc = &randomx::deallocDataset; dataset->memory = (uint8_t*)randomx::LargePageAllocator::allocMemory(RANDOMX_DATASET_MAX_SIZE); } diff --git a/src/crypto/randomx/randomx.h b/src/crypto/randomx/randomx.h index ea3bb099..95bfdbf4 100644 --- a/src/crypto/randomx/randomx.h +++ b/src/crypto/randomx/randomx.h @@ -48,6 +48,7 @@ enum randomx_flags { RANDOMX_FLAG_HARD_AES = 2, RANDOMX_FLAG_FULL_MEM = 4, RANDOMX_FLAG_JIT = 8, + RANDOMX_FLAG_1GB_PAGES = 16, }; diff --git a/src/crypto/randomx/virtual_memory.cpp b/src/crypto/randomx/virtual_memory.cpp index 06165ffb..48a8a8d2 100644 --- a/src/crypto/randomx/virtual_memory.cpp +++ b/src/crypto/randomx/virtual_memory.cpp @@ -53,6 +53,16 @@ void* allocLargePagesMemory(std::size_t bytes) { } +void* allocOneGbPagesMemory(std::size_t bytes) { + void* mem = xmrig::VirtualMemory::allocateOneGbPagesMemory(bytes); + if (mem == nullptr) { + throw std::runtime_error("Failed to allocate 1GB pages memory"); + } + + return mem; +} + + void freePagedMemory(void* ptr, std::size_t bytes) { xmrig::VirtualMemory::freeLargePagesMemory(ptr, bytes); } diff --git a/src/crypto/randomx/virtual_memory.hpp b/src/crypto/randomx/virtual_memory.hpp index d3b31db1..8c5b4900 100644 --- a/src/crypto/randomx/virtual_memory.hpp +++ b/src/crypto/randomx/virtual_memory.hpp @@ -32,4 +32,5 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. void* allocExecutableMemory(std::size_t); void* allocLargePagesMemory(std::size_t); +void* allocOneGbPagesMemory(std::size_t); void freePagedMemory(void*, std::size_t); diff --git a/src/crypto/rx/Rx.cpp b/src/crypto/rx/Rx.cpp index b08f844e..a5767352 100644 --- a/src/crypto/rx/Rx.cpp +++ b/src/crypto/rx/Rx.cpp @@ -60,7 +60,7 @@ const char *xmrig::rx_tag() } -bool xmrig::Rx::init(const Job &job, const RxConfig &config, bool hugePages) +bool xmrig::Rx::init(const Job &job, const RxConfig &config, bool hugePages, bool oneGbPages) { if (job.algorithm().family() != Algorithm::RANDOM_X) { return true; @@ -70,7 +70,7 @@ bool xmrig::Rx::init(const Job &job, const RxConfig &config, bool hugePages) return true; } - d_ptr->queue.enqueue(job, config.nodeset(), config.threads(), hugePages, config.mode()); + d_ptr->queue.enqueue(job, config.nodeset(), config.threads(), hugePages, oneGbPages, config.mode()); return false; } diff --git a/src/crypto/rx/Rx.h b/src/crypto/rx/Rx.h index 4a81f5d5..74a7ef59 100644 --- a/src/crypto/rx/Rx.h +++ b/src/crypto/rx/Rx.h @@ -46,7 +46,7 @@ class RxDataset; class Rx { public: - static bool init(const Job &job, const RxConfig &config, bool hugePages); + static bool init(const Job &job, const RxConfig &config, bool hugePages, bool oneGbPages); static bool isReady(const Job &job); static RxDataset *dataset(const Job &job, uint32_t nodeId); static std::pair hugePages(); diff --git a/src/crypto/rx/RxBasicStorage.cpp b/src/crypto/rx/RxBasicStorage.cpp index bc1ceb2d..67ede47f 100644 --- a/src/crypto/rx/RxBasicStorage.cpp +++ b/src/crypto/rx/RxBasicStorage.cpp @@ -69,11 +69,11 @@ public: } - inline void createDataset(bool hugePages, RxConfig::Mode mode) + inline void createDataset(bool hugePages, bool oneGbPages, RxConfig::Mode mode) { const uint64_t ts = Chrono::steadyMSecs(); - m_dataset = new RxDataset(hugePages, true, mode); + m_dataset = new RxDataset(hugePages, oneGbPages, true, mode); printAllocStatus(ts); } @@ -157,12 +157,12 @@ std::pair xmrig::RxBasicStorage::hugePages() const } -void xmrig::RxBasicStorage::init(const RxSeed &seed, uint32_t threads, bool hugePages, RxConfig::Mode mode) +void xmrig::RxBasicStorage::init(const RxSeed &seed, uint32_t threads, bool hugePages, bool oneGbPages, RxConfig::Mode mode) { d_ptr->setSeed(seed); if (!d_ptr->dataset()) { - d_ptr->createDataset(hugePages, mode); + d_ptr->createDataset(hugePages, oneGbPages, mode); } d_ptr->initDataset(threads); diff --git a/src/crypto/rx/RxBasicStorage.h b/src/crypto/rx/RxBasicStorage.h index bd6575d2..edabff65 100644 --- a/src/crypto/rx/RxBasicStorage.h +++ b/src/crypto/rx/RxBasicStorage.h @@ -50,7 +50,7 @@ public: protected: RxDataset *dataset(const Job &job, uint32_t nodeId) const override; std::pair hugePages() const override; - void init(const RxSeed &seed, uint32_t threads, bool hugePages, RxConfig::Mode mode) override; + void init(const RxSeed &seed, uint32_t threads, bool hugePages, bool oneGbPages, RxConfig::Mode mode) override; private: RxBasicStoragePrivate *d_ptr; diff --git a/src/crypto/rx/RxDataset.cpp b/src/crypto/rx/RxDataset.cpp index ce41a58e..2b387e3c 100644 --- a/src/crypto/rx/RxDataset.cpp +++ b/src/crypto/rx/RxDataset.cpp @@ -29,7 +29,6 @@ #include "backend/common/Tags.h" #include "base/io/log/Log.h" #include "crypto/common/VirtualMemory.h" -#include "crypto/randomx/randomx.h" #include "crypto/rx/RxAlgo.h" #include "crypto/rx/RxCache.h" @@ -41,10 +40,10 @@ static_assert(RANDOMX_FLAG_LARGE_PAGES == 1, "RANDOMX_FLAG_LARGE_PAGES flag mismatch"); -xmrig::RxDataset::RxDataset(bool hugePages, bool cache, RxConfig::Mode mode) : +xmrig::RxDataset::RxDataset(bool hugePages, bool oneGbPages, bool cache, RxConfig::Mode mode) : m_mode(mode) { - allocate(hugePages); + allocate(hugePages, oneGbPages); if (cache) { m_cache = new RxCache(hugePages); @@ -123,11 +122,13 @@ size_t xmrig::RxDataset::size(bool cache) const std::pair xmrig::RxDataset::hugePages(bool cache) const { constexpr size_t twoMiB = 2U * 1024U * 1024U; + constexpr size_t oneGiB = 1024U * 1024U * 1024U; constexpr size_t cacheSize = VirtualMemory::align(RxCache::maxSize(), twoMiB) / twoMiB; - size_t total = VirtualMemory::align(maxSize(), twoMiB) / twoMiB; + size_t datasetPageSize = isOneGbPages() ? oneGiB : twoMiB; + size_t total = VirtualMemory::align(maxSize(), datasetPageSize) / datasetPageSize; uint32_t count = 0; - if (isHugePages()) { + if (isHugePages() || isOneGbPages()) { count += total; } @@ -159,7 +160,7 @@ void xmrig::RxDataset::setRaw(const void *raw) } -void xmrig::RxDataset::allocate(bool hugePages) +void xmrig::RxDataset::allocate(bool hugePages, bool oneGbPages) { if (m_mode == RxConfig::LightMode) { LOG_ERR(CLEAR "%s" RED_BOLD_S "fast RandomX mode disabled by config", rx_tag()); @@ -174,8 +175,14 @@ void xmrig::RxDataset::allocate(bool hugePages) } if (hugePages) { - m_flags = RANDOMX_FLAG_LARGE_PAGES; + m_flags = oneGbPages ? RANDOMX_FLAG_1GB_PAGES : RANDOMX_FLAG_LARGE_PAGES; m_dataset = randomx_alloc_dataset(static_cast(m_flags)); + + if (oneGbPages && !m_dataset) { + LOG_ERR(CLEAR "%s" RED_BOLD_S "Failed to allocate RandomX dataset using 1GB pages", rx_tag()); + m_flags = RANDOMX_FLAG_LARGE_PAGES; + m_dataset = randomx_alloc_dataset(static_cast(m_flags)); + } } if (!m_dataset) { diff --git a/src/crypto/rx/RxDataset.h b/src/crypto/rx/RxDataset.h index 30455956..0dba79bb 100644 --- a/src/crypto/rx/RxDataset.h +++ b/src/crypto/rx/RxDataset.h @@ -31,6 +31,7 @@ #include "base/tools/Object.h" #include "crypto/common/Algorithm.h" #include "crypto/randomx/configuration.h" +#include "crypto/randomx/randomx.h" #include "crypto/rx/RxConfig.h" @@ -50,11 +51,12 @@ class RxDataset public: XMRIG_DISABLE_COPY_MOVE_DEFAULT(RxDataset) - RxDataset(bool hugePages, bool cache, RxConfig::Mode mode); + RxDataset(bool hugePages, bool oneGbPages, bool cache, RxConfig::Mode mode); RxDataset(RxCache *cache); ~RxDataset(); - inline bool isHugePages() const { return m_flags & 1; } + inline bool isHugePages() const { return m_flags & RANDOMX_FLAG_LARGE_PAGES; } + inline bool isOneGbPages() const { return m_flags & RANDOMX_FLAG_1GB_PAGES; } inline randomx_dataset *get() const { return m_dataset; } inline RxCache *cache() const { return m_cache; } inline void setCache(RxCache *cache) { m_cache = cache; } @@ -68,7 +70,7 @@ public: static inline constexpr size_t maxSize() { return RANDOMX_DATASET_MAX_SIZE; } private: - void allocate(bool hugePages); + void allocate(bool hugePages, bool oneGbPages); const RxConfig::Mode m_mode = RxConfig::FastMode; int m_flags = 0; diff --git a/src/crypto/rx/RxNUMAStorage.cpp b/src/crypto/rx/RxNUMAStorage.cpp index 646d3d95..c9975068 100644 --- a/src/crypto/rx/RxNUMAStorage.cpp +++ b/src/crypto/rx/RxNUMAStorage.cpp @@ -120,12 +120,12 @@ public: } - inline void createDatasets(bool hugePages) + inline void createDatasets(bool hugePages, bool oneGbPages) { const uint64_t ts = Chrono::steadyMSecs(); for (uint32_t node : m_nodeset) { - m_threads.emplace_back(allocate, this, node, hugePages); + m_threads.emplace_back(allocate, this, node, hugePages, oneGbPages); } join(); @@ -188,7 +188,7 @@ public: private: - static void allocate(RxNUMAStoragePrivate *d_ptr, uint32_t nodeId, bool hugePages) + static void allocate(RxNUMAStoragePrivate *d_ptr, uint32_t nodeId, bool hugePages, bool oneGbPages) { const uint64_t ts = Chrono::steadyMSecs(); @@ -198,7 +198,7 @@ private: return; } - auto dataset = new RxDataset(hugePages, false, RxConfig::FastMode); + auto dataset = new RxDataset(hugePages, oneGbPages, false, RxConfig::FastMode); if (!dataset->get()) { printSkipped(nodeId, "failed to allocate dataset"); @@ -346,12 +346,12 @@ std::pair xmrig::RxNUMAStorage::hugePages() const } -void xmrig::RxNUMAStorage::init(const RxSeed &seed, uint32_t threads, bool hugePages, RxConfig::Mode) +void xmrig::RxNUMAStorage::init(const RxSeed &seed, uint32_t threads, bool hugePages, bool oneGbPages, RxConfig::Mode) { d_ptr->setSeed(seed); if (!d_ptr->isAllocated()) { - d_ptr->createDatasets(hugePages); + d_ptr->createDatasets(hugePages, oneGbPages); } d_ptr->initDatasets(threads); diff --git a/src/crypto/rx/RxNUMAStorage.h b/src/crypto/rx/RxNUMAStorage.h index bed0bc75..c3d77000 100644 --- a/src/crypto/rx/RxNUMAStorage.h +++ b/src/crypto/rx/RxNUMAStorage.h @@ -53,7 +53,7 @@ public: protected: RxDataset *dataset(const Job &job, uint32_t nodeId) const override; std::pair hugePages() const override; - void init(const RxSeed &seed, uint32_t threads, bool hugePages, RxConfig::Mode mode) override; + void init(const RxSeed &seed, uint32_t threads, bool hugePages, bool oneGbPages, RxConfig::Mode mode) override; private: RxNUMAStoragePrivate *d_ptr; diff --git a/src/crypto/rx/RxQueue.cpp b/src/crypto/rx/RxQueue.cpp index b420d0c2..286538f1 100644 --- a/src/crypto/rx/RxQueue.cpp +++ b/src/crypto/rx/RxQueue.cpp @@ -94,7 +94,7 @@ std::pair xmrig::RxQueue::hugePages() } -void xmrig::RxQueue::enqueue(const RxSeed &seed, const std::vector &nodeset, uint32_t threads, bool hugePages, RxConfig::Mode mode) +void xmrig::RxQueue::enqueue(const RxSeed &seed, const std::vector &nodeset, uint32_t threads, bool hugePages, bool oneGbPages, RxConfig::Mode mode) { std::unique_lock lock(m_mutex); @@ -114,7 +114,7 @@ void xmrig::RxQueue::enqueue(const RxSeed &seed, const std::vector &no return; } - m_queue.emplace_back(seed, nodeset, threads, hugePages, mode); + m_queue.emplace_back(seed, nodeset, threads, hugePages, oneGbPages, mode); m_seed = seed; m_state = STATE_PENDING; @@ -156,7 +156,7 @@ void xmrig::RxQueue::backgroundInit() Buffer::toHex(item.seed.data().data(), 8).data() ); - m_storage->init(item.seed, item.threads, item.hugePages, item.mode); + m_storage->init(item.seed, item.threads, item.hugePages, item.oneGbPages, item.mode); lock = std::unique_lock(m_mutex); diff --git a/src/crypto/rx/RxQueue.h b/src/crypto/rx/RxQueue.h index 2d77e2e3..6d8fa328 100644 --- a/src/crypto/rx/RxQueue.h +++ b/src/crypto/rx/RxQueue.h @@ -53,8 +53,9 @@ class RxDataset; class RxQueueItem { public: - RxQueueItem(const RxSeed &seed, const std::vector &nodeset, uint32_t threads, bool hugePages, RxConfig::Mode mode) : + RxQueueItem(const RxSeed &seed, const std::vector &nodeset, uint32_t threads, bool hugePages, bool oneGbPages, RxConfig::Mode mode) : hugePages(hugePages), + oneGbPages(oneGbPages), mode(mode), seed(seed), nodeset(nodeset), @@ -62,6 +63,7 @@ public: {} const bool hugePages; + const bool oneGbPages; const RxConfig::Mode mode; const RxSeed seed; const std::vector nodeset; @@ -80,7 +82,7 @@ public: bool isReady(const Job &job); RxDataset *dataset(const Job &job, uint32_t nodeId); std::pair hugePages(); - void enqueue(const RxSeed &seed, const std::vector &nodeset, uint32_t threads, bool hugePages, RxConfig::Mode mode); + void enqueue(const RxSeed &seed, const std::vector &nodeset, uint32_t threads, bool hugePages, bool oneGbPages, RxConfig::Mode mode); private: enum State { From b8762ed428cfac4903f880b260000b274d2c1bd4 Mon Sep 17 00:00:00 2001 From: XMRig Date: Fri, 6 Dec 2019 10:56:43 +0700 Subject: [PATCH 05/31] #1306 Added some network workarounds. --- src/base/net/stratum/Client.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/base/net/stratum/Client.cpp b/src/base/net/stratum/Client.cpp index 9729f3fb..f9c56e05 100644 --- a/src/base/net/stratum/Client.cpp +++ b/src/base/net/stratum/Client.cpp @@ -338,6 +338,10 @@ bool xmrig::Client::isCriticalError(const char *message) return true; } + if (strncasecmp(message, "Invalid job id", 14) == 0) { + return true; + } + return false; } @@ -558,7 +562,7 @@ void xmrig::Client::connect(sockaddr *addr) { setState(ConnectingState); - uv_connect_t *req = new uv_connect_t; + auto req = new uv_connect_t; req->data = m_storage.ptr(m_key); m_socket = new uv_tcp_t; @@ -799,7 +803,7 @@ void xmrig::Client::ping() void xmrig::Client::read(ssize_t nread) { - const size_t size = static_cast(nread); + const auto size = static_cast(nread); if (nread > 0 && size > m_recvBuf.available()) { nread = UV_ENOBUFS; @@ -859,7 +863,7 @@ void xmrig::Client::reconnect() void xmrig::Client::setState(SocketState state) { - LOG_DEBUG("[%s] state: \"%s\"", url(), states[state]); + LOG_DEBUG("[%s] state: \"%s\" -> \"%s\"", url(), states[m_state], states[state]); if (m_state == state) { return; @@ -956,6 +960,12 @@ void xmrig::Client::onConnect(uv_connect_t *req, int status) return; } + if (client->state() == ConnectedState) { + LOG_ERR("[%s] already connected"); + + return; + } + client->m_stream = static_cast(req->handle); client->m_stream->data = req->data; client->setState(ConnectedState); From f1827e925ec46db5e6f04e563659b8c6d4042b69 Mon Sep 17 00:00:00 2001 From: XMRig Date: Fri, 6 Dec 2019 11:56:13 +0700 Subject: [PATCH 06/31] Removed strdup from FileLog. --- src/base/io/log/backends/FileLog.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/base/io/log/backends/FileLog.cpp b/src/base/io/log/backends/FileLog.cpp index 1ff01637..c581b880 100644 --- a/src/base/io/log/backends/FileLog.cpp +++ b/src/base/io/log/backends/FileLog.cpp @@ -24,13 +24,14 @@ */ -#include -#include - - #include "base/io/log/backends/FileLog.h" +#include +#include +#include + + xmrig::FileLog::FileLog(const char *fileName) { uv_fs_t req; @@ -45,13 +46,12 @@ void xmrig::FileLog::print(int, const char *line, size_t, size_t size, bool colo return; } -# ifdef _WIN32 - uv_buf_t buf = uv_buf_init(strdup(line), static_cast(size)); -# else - uv_buf_t buf = uv_buf_init(strdup(line), size); -# endif + assert(strlen(line) == size); - uv_fs_t *req = new uv_fs_t; + uv_buf_t buf = uv_buf_init(new char[size], size); + memcpy(buf.base, line, size); + + auto req = new uv_fs_t; req->data = buf.base; uv_fs_write(uv_default_loop(), req, m_file, &buf, 1, -1, FileLog::onWrite); From 118b2e4a68070109e1c84e944753a5a4a81f1afc Mon Sep 17 00:00:00 2001 From: XMRig Date: Fri, 6 Dec 2019 18:39:14 +0700 Subject: [PATCH 07/31] Updated libuv version in build_deps.sh. --- scripts/build_deps.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build_deps.sh b/scripts/build_deps.sh index d8948323..7c4fc3da 100755 --- a/scripts/build_deps.sh +++ b/scripts/build_deps.sh @@ -1,6 +1,6 @@ #!/bin/bash -e -UV_VERSION="1.33.1" +UV_VERSION="1.34.0" OPENSSL_VERSION="1.1.1d" HWLOC_VERSION="2.1.0" From aa3dc7543430890de4d12c574a52f7b4737a0dd9 Mon Sep 17 00:00:00 2001 From: SChernykh Date: Fri, 6 Dec 2019 13:43:59 +0100 Subject: [PATCH 08/31] Fix ARM compilation --- src/backend/cpu/platform/BasicCpuInfo_arm.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/backend/cpu/platform/BasicCpuInfo_arm.cpp b/src/backend/cpu/platform/BasicCpuInfo_arm.cpp index e52bdf94..5dd54fe9 100644 --- a/src/backend/cpu/platform/BasicCpuInfo_arm.cpp +++ b/src/backend/cpu/platform/BasicCpuInfo_arm.cpp @@ -39,7 +39,8 @@ xmrig::BasicCpuInfo::BasicCpuInfo() : m_brand(), m_threads(std::thread::hardware_concurrency()), m_aes(false), - m_avx2(false) + m_avx2(false), + m_pdpe1gb(false) { # ifdef XMRIG_ARMv8 memcpy(m_brand, "ARMv8", 5); From e3422979d1c56a93b8d8b44df77db6ca1d7adbe6 Mon Sep 17 00:00:00 2001 From: SChernykh Date: Fri, 6 Dec 2019 13:55:33 +0100 Subject: [PATCH 09/31] Fixed compilation on systems without 1GB pages support --- src/crypto/common/VirtualMemory_unix.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/crypto/common/VirtualMemory_unix.cpp b/src/crypto/common/VirtualMemory_unix.cpp index 2e6eed30..a7790aaf 100644 --- a/src/crypto/common/VirtualMemory_unix.cpp +++ b/src/crypto/common/VirtualMemory_unix.cpp @@ -82,6 +82,8 @@ void *xmrig::VirtualMemory::allocateOneGbPagesMemory(size_t size) constexpr int flag_1gb = MAP_HUGE_1GB; # elif defined(MAP_HUGE_SHIFT) constexpr int flag_1gb = (30 << MAP_HUGE_SHIFT); +# else + constexpr int flag_1gb = 0; # endif void *mem = mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB | MAP_POPULATE | flag_1gb, 0, 0); From 3a75f39935815a5cefc1c376fb706cf039acc963 Mon Sep 17 00:00:00 2001 From: XMRig Date: Fri, 6 Dec 2019 22:17:04 +0700 Subject: [PATCH 10/31] #1386 Added priority for RandomX dataset initialization threads. --- src/backend/common/interfaces/IRxStorage.h | 6 +++--- src/core/Miner.cpp | 4 +++- src/crypto/rx/Rx.cpp | 4 ++-- src/crypto/rx/Rx.h | 2 +- src/crypto/rx/RxBasicStorage.cpp | 8 ++++---- src/crypto/rx/RxBasicStorage.h | 2 +- src/crypto/rx/RxDataset.cpp | 21 ++++++++++++++++++--- src/crypto/rx/RxDataset.h | 2 +- src/crypto/rx/RxNUMAStorage.cpp | 8 ++++---- src/crypto/rx/RxNUMAStorage.h | 2 +- src/crypto/rx/RxQueue.cpp | 6 +++--- src/crypto/rx/RxQueue.h | 6 ++++-- 12 files changed, 45 insertions(+), 26 deletions(-) diff --git a/src/backend/common/interfaces/IRxStorage.h b/src/backend/common/interfaces/IRxStorage.h index f64850f7..372eee6f 100644 --- a/src/backend/common/interfaces/IRxStorage.h +++ b/src/backend/common/interfaces/IRxStorage.h @@ -44,9 +44,9 @@ class IRxStorage public: virtual ~IRxStorage() = default; - virtual RxDataset *dataset(const Job &job, uint32_t nodeId) const = 0; - virtual std::pair hugePages() const = 0; - virtual void init(const RxSeed &seed, uint32_t threads, bool hugePages, bool oneGbPages, RxConfig::Mode mode) = 0; + virtual RxDataset *dataset(const Job &job, uint32_t nodeId) const = 0; + virtual std::pair hugePages() const = 0; + virtual void init(const RxSeed &seed, uint32_t threads, bool hugePages, bool oneGbPages, RxConfig::Mode mode, int priority) = 0; }; diff --git a/src/core/Miner.cpp b/src/core/Miner.cpp index 4e2f24ba..d9b734e5 100644 --- a/src/core/Miner.cpp +++ b/src/core/Miner.cpp @@ -236,7 +236,9 @@ public: # ifdef XMRIG_ALGO_RANDOMX inline bool initRX() { - return Rx::init(job, controller->config()->rx(), controller->config()->cpu().isHugePages(), controller->config()->cpu().isOneGbPages()); + const auto &cpu = controller->config()->cpu(); + + return Rx::init(job, controller->config()->rx(), cpu.isHugePages(), cpu.isOneGbPages(), cpu.priority()); } # endif diff --git a/src/crypto/rx/Rx.cpp b/src/crypto/rx/Rx.cpp index a5767352..7e99efa9 100644 --- a/src/crypto/rx/Rx.cpp +++ b/src/crypto/rx/Rx.cpp @@ -60,7 +60,7 @@ const char *xmrig::rx_tag() } -bool xmrig::Rx::init(const Job &job, const RxConfig &config, bool hugePages, bool oneGbPages) +bool xmrig::Rx::init(const Job &job, const RxConfig &config, bool hugePages, bool oneGbPages, int priority) { if (job.algorithm().family() != Algorithm::RANDOM_X) { return true; @@ -70,7 +70,7 @@ bool xmrig::Rx::init(const Job &job, const RxConfig &config, bool hugePages, boo return true; } - d_ptr->queue.enqueue(job, config.nodeset(), config.threads(), hugePages, oneGbPages, config.mode()); + d_ptr->queue.enqueue(job, config.nodeset(), config.threads(), hugePages, oneGbPages, config.mode(), priority); return false; } diff --git a/src/crypto/rx/Rx.h b/src/crypto/rx/Rx.h index 74a7ef59..6b1db813 100644 --- a/src/crypto/rx/Rx.h +++ b/src/crypto/rx/Rx.h @@ -46,7 +46,7 @@ class RxDataset; class Rx { public: - static bool init(const Job &job, const RxConfig &config, bool hugePages, bool oneGbPages); + static bool init(const Job &job, const RxConfig &config, bool hugePages, bool oneGbPages, int priority); static bool isReady(const Job &job); static RxDataset *dataset(const Job &job, uint32_t nodeId); static std::pair hugePages(); diff --git a/src/crypto/rx/RxBasicStorage.cpp b/src/crypto/rx/RxBasicStorage.cpp index 67ede47f..a0656e46 100644 --- a/src/crypto/rx/RxBasicStorage.cpp +++ b/src/crypto/rx/RxBasicStorage.cpp @@ -78,11 +78,11 @@ public: } - inline void initDataset(uint32_t threads) + inline void initDataset(uint32_t threads, int priority) { const uint64_t ts = Chrono::steadyMSecs(); - m_dataset->init(m_seed.data(), threads); + m_dataset->init(m_seed.data(), threads, priority); LOG_INFO("%s" GREEN_BOLD("dataset ready") BLACK_BOLD(" (%" PRIu64 " ms)"), rx_tag(), Chrono::steadyMSecs() - ts); @@ -157,7 +157,7 @@ std::pair xmrig::RxBasicStorage::hugePages() const } -void xmrig::RxBasicStorage::init(const RxSeed &seed, uint32_t threads, bool hugePages, bool oneGbPages, RxConfig::Mode mode) +void xmrig::RxBasicStorage::init(const RxSeed &seed, uint32_t threads, bool hugePages, bool oneGbPages, RxConfig::Mode mode, int priority) { d_ptr->setSeed(seed); @@ -165,5 +165,5 @@ void xmrig::RxBasicStorage::init(const RxSeed &seed, uint32_t threads, bool huge d_ptr->createDataset(hugePages, oneGbPages, mode); } - d_ptr->initDataset(threads); + d_ptr->initDataset(threads, priority); } diff --git a/src/crypto/rx/RxBasicStorage.h b/src/crypto/rx/RxBasicStorage.h index edabff65..bd8a5e00 100644 --- a/src/crypto/rx/RxBasicStorage.h +++ b/src/crypto/rx/RxBasicStorage.h @@ -50,7 +50,7 @@ public: protected: RxDataset *dataset(const Job &job, uint32_t nodeId) const override; std::pair hugePages() const override; - void init(const RxSeed &seed, uint32_t threads, bool hugePages, bool oneGbPages, RxConfig::Mode mode) override; + void init(const RxSeed &seed, uint32_t threads, bool hugePages, bool oneGbPages, RxConfig::Mode mode, int priority) override; private: RxBasicStoragePrivate *d_ptr; diff --git a/src/crypto/rx/RxDataset.cpp b/src/crypto/rx/RxDataset.cpp index 2b387e3c..5786926a 100644 --- a/src/crypto/rx/RxDataset.cpp +++ b/src/crypto/rx/RxDataset.cpp @@ -28,6 +28,7 @@ #include "crypto/rx/RxDataset.h" #include "backend/common/Tags.h" #include "base/io/log/Log.h" +#include "base/kernel/Platform.h" #include "crypto/common/VirtualMemory.h" #include "crypto/rx/RxAlgo.h" #include "crypto/rx/RxCache.h" @@ -40,6 +41,20 @@ static_assert(RANDOMX_FLAG_LARGE_PAGES == 1, "RANDOMX_FLAG_LARGE_PAGES flag mismatch"); +namespace xmrig { + + +static void init_dataset_wrapper(randomx_dataset *dataset, randomx_cache *cache, unsigned long startItem, unsigned long itemCount, int priority) +{ + Platform::setThreadPriority(priority); + + randomx_init_dataset(dataset, cache, startItem, itemCount); +} + + +} // namespace xmrig + + xmrig::RxDataset::RxDataset(bool hugePages, bool oneGbPages, bool cache, RxConfig::Mode mode) : m_mode(mode) { @@ -67,7 +82,7 @@ xmrig::RxDataset::~RxDataset() } -bool xmrig::RxDataset::init(const Buffer &seed, uint32_t numThreads) +bool xmrig::RxDataset::init(const Buffer &seed, uint32_t numThreads, int priority) { if (!m_cache) { return false; @@ -88,7 +103,7 @@ bool xmrig::RxDataset::init(const Buffer &seed, uint32_t numThreads) for (uint64_t i = 0; i < numThreads; ++i) { const uint32_t a = (datasetItemCount * i) / numThreads; const uint32_t b = (datasetItemCount * (i + 1)) / numThreads; - threads.emplace_back(randomx_init_dataset, m_dataset, m_cache->get(), a, b - a); + threads.emplace_back(init_dataset_wrapper, m_dataset, m_cache->get(), a, b - a, priority); } for (uint32_t i = 0; i < numThreads; ++i) { @@ -96,7 +111,7 @@ bool xmrig::RxDataset::init(const Buffer &seed, uint32_t numThreads) } } else { - randomx_init_dataset(m_dataset, m_cache->get(), 0, datasetItemCount); + init_dataset_wrapper(m_dataset, m_cache->get(), 0, datasetItemCount, priority); } return true; diff --git a/src/crypto/rx/RxDataset.h b/src/crypto/rx/RxDataset.h index 0dba79bb..ec9c7c8a 100644 --- a/src/crypto/rx/RxDataset.h +++ b/src/crypto/rx/RxDataset.h @@ -61,7 +61,7 @@ public: inline RxCache *cache() const { return m_cache; } inline void setCache(RxCache *cache) { m_cache = cache; } - bool init(const Buffer &seed, uint32_t numThreads); + bool init(const Buffer &seed, uint32_t numThreads, int priority); size_t size(bool cache = true) const; std::pair hugePages(bool cache = true) const; void *raw() const; diff --git a/src/crypto/rx/RxNUMAStorage.cpp b/src/crypto/rx/RxNUMAStorage.cpp index c9975068..a6d19148 100644 --- a/src/crypto/rx/RxNUMAStorage.cpp +++ b/src/crypto/rx/RxNUMAStorage.cpp @@ -148,13 +148,13 @@ public: } - inline void initDatasets(uint32_t threads) + inline void initDatasets(uint32_t threads, int priority) { uint64_t ts = Chrono::steadyMSecs(); auto id = m_nodeset.front(); auto primary = dataset(id); - primary->init(m_seed.data(), threads); + primary->init(m_seed.data(), threads, priority); printDatasetReady(id, ts); @@ -346,7 +346,7 @@ std::pair xmrig::RxNUMAStorage::hugePages() const } -void xmrig::RxNUMAStorage::init(const RxSeed &seed, uint32_t threads, bool hugePages, bool oneGbPages, RxConfig::Mode) +void xmrig::RxNUMAStorage::init(const RxSeed &seed, uint32_t threads, bool hugePages, bool oneGbPages, RxConfig::Mode, int priority) { d_ptr->setSeed(seed); @@ -354,5 +354,5 @@ void xmrig::RxNUMAStorage::init(const RxSeed &seed, uint32_t threads, bool hugeP d_ptr->createDatasets(hugePages, oneGbPages); } - d_ptr->initDatasets(threads); + d_ptr->initDatasets(threads, priority); } diff --git a/src/crypto/rx/RxNUMAStorage.h b/src/crypto/rx/RxNUMAStorage.h index c3d77000..e7ea842f 100644 --- a/src/crypto/rx/RxNUMAStorage.h +++ b/src/crypto/rx/RxNUMAStorage.h @@ -53,7 +53,7 @@ public: protected: RxDataset *dataset(const Job &job, uint32_t nodeId) const override; std::pair hugePages() const override; - void init(const RxSeed &seed, uint32_t threads, bool hugePages, bool oneGbPages, RxConfig::Mode mode) override; + void init(const RxSeed &seed, uint32_t threads, bool hugePages, bool oneGbPages, RxConfig::Mode mode, int priority) override; private: RxNUMAStoragePrivate *d_ptr; diff --git a/src/crypto/rx/RxQueue.cpp b/src/crypto/rx/RxQueue.cpp index 286538f1..8ca4a379 100644 --- a/src/crypto/rx/RxQueue.cpp +++ b/src/crypto/rx/RxQueue.cpp @@ -94,7 +94,7 @@ std::pair xmrig::RxQueue::hugePages() } -void xmrig::RxQueue::enqueue(const RxSeed &seed, const std::vector &nodeset, uint32_t threads, bool hugePages, bool oneGbPages, RxConfig::Mode mode) +void xmrig::RxQueue::enqueue(const RxSeed &seed, const std::vector &nodeset, uint32_t threads, bool hugePages, bool oneGbPages, RxConfig::Mode mode, int priority) { std::unique_lock lock(m_mutex); @@ -114,7 +114,7 @@ void xmrig::RxQueue::enqueue(const RxSeed &seed, const std::vector &no return; } - m_queue.emplace_back(seed, nodeset, threads, hugePages, oneGbPages, mode); + m_queue.emplace_back(seed, nodeset, threads, hugePages, oneGbPages, mode, priority); m_seed = seed; m_state = STATE_PENDING; @@ -156,7 +156,7 @@ void xmrig::RxQueue::backgroundInit() Buffer::toHex(item.seed.data().data(), 8).data() ); - m_storage->init(item.seed, item.threads, item.hugePages, item.oneGbPages, item.mode); + m_storage->init(item.seed, item.threads, item.hugePages, item.oneGbPages, item.mode, item.priority); lock = std::unique_lock(m_mutex); diff --git a/src/crypto/rx/RxQueue.h b/src/crypto/rx/RxQueue.h index 6d8fa328..8a362b8e 100644 --- a/src/crypto/rx/RxQueue.h +++ b/src/crypto/rx/RxQueue.h @@ -53,9 +53,10 @@ class RxDataset; class RxQueueItem { public: - RxQueueItem(const RxSeed &seed, const std::vector &nodeset, uint32_t threads, bool hugePages, bool oneGbPages, RxConfig::Mode mode) : + RxQueueItem(const RxSeed &seed, const std::vector &nodeset, uint32_t threads, bool hugePages, bool oneGbPages, RxConfig::Mode mode, int priority) : hugePages(hugePages), oneGbPages(oneGbPages), + priority(priority), mode(mode), seed(seed), nodeset(nodeset), @@ -64,6 +65,7 @@ public: const bool hugePages; const bool oneGbPages; + const int priority; const RxConfig::Mode mode; const RxSeed seed; const std::vector nodeset; @@ -82,7 +84,7 @@ public: bool isReady(const Job &job); RxDataset *dataset(const Job &job, uint32_t nodeId); std::pair hugePages(); - void enqueue(const RxSeed &seed, const std::vector &nodeset, uint32_t threads, bool hugePages, bool oneGbPages, RxConfig::Mode mode); + void enqueue(const RxSeed &seed, const std::vector &nodeset, uint32_t threads, bool hugePages, bool oneGbPages, RxConfig::Mode mode, int priority); private: enum State { From e9e747f0d19a39e6637393ce82aca83fa175ee4d Mon Sep 17 00:00:00 2001 From: XMRig Date: Sat, 7 Dec 2019 22:18:06 +0700 Subject: [PATCH 11/31] #1385 "max-threads-hint" option now also limit RandomX dataset initialization threads. --- src/backend/cpu/CpuConfig.h | 1 + src/backend/cpu/platform/BasicCpuInfo.cpp | 2 +- src/backend/cpu/platform/HwlocCpuInfo.cpp | 2 +- src/core/Miner.cpp | 7 +------ src/crypto/rx/Rx.cpp | 5 +++-- src/crypto/rx/Rx.h | 3 ++- src/crypto/rx/RxConfig.cpp | 13 +++++++++++-- src/crypto/rx/RxConfig.h | 2 +- 8 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/backend/cpu/CpuConfig.h b/src/backend/cpu/CpuConfig.h index 74894dcd..035a1673 100644 --- a/src/backend/cpu/CpuConfig.h +++ b/src/backend/cpu/CpuConfig.h @@ -61,6 +61,7 @@ public: inline const String &argon2Impl() const { return m_argon2Impl; } inline const Threads &threads() const { return m_threads; } inline int priority() const { return m_priority; } + inline uint32_t limit() const { return m_limit; } private: void generate(); diff --git a/src/backend/cpu/platform/BasicCpuInfo.cpp b/src/backend/cpu/platform/BasicCpuInfo.cpp index cdc810c3..e6145278 100644 --- a/src/backend/cpu/platform/BasicCpuInfo.cpp +++ b/src/backend/cpu/platform/BasicCpuInfo.cpp @@ -23,7 +23,7 @@ */ #include -#include +#include #include diff --git a/src/backend/cpu/platform/HwlocCpuInfo.cpp b/src/backend/cpu/platform/HwlocCpuInfo.cpp index 3983e8b0..a66bf9fd 100644 --- a/src/backend/cpu/platform/HwlocCpuInfo.cpp +++ b/src/backend/cpu/platform/HwlocCpuInfo.cpp @@ -262,7 +262,7 @@ xmrig::CpuThreads xmrig::HwlocCpuInfo::threads(const Algorithm &algorithm, uint3 void xmrig::HwlocCpuInfo::processTopLevelCache(hwloc_obj_t cache, const Algorithm &algorithm, CpuThreads &threads, size_t limit) const { - constexpr size_t oneMiB = 1024u * 1024u; + constexpr size_t oneMiB = 1024U * 1024U; size_t PUs = countByType(cache, HWLOC_OBJ_PU); if (PUs == 0) { diff --git a/src/core/Miner.cpp b/src/core/Miner.cpp index d9b734e5..ed988f26 100644 --- a/src/core/Miner.cpp +++ b/src/core/Miner.cpp @@ -234,12 +234,7 @@ public: # ifdef XMRIG_ALGO_RANDOMX - inline bool initRX() - { - const auto &cpu = controller->config()->cpu(); - - return Rx::init(job, controller->config()->rx(), cpu.isHugePages(), cpu.isOneGbPages(), cpu.priority()); - } + inline bool initRX() { return Rx::init(job, controller->config()->rx(), controller->config()->cpu()); } # endif diff --git a/src/crypto/rx/Rx.cpp b/src/crypto/rx/Rx.cpp index 7e99efa9..84c9b5fc 100644 --- a/src/crypto/rx/Rx.cpp +++ b/src/crypto/rx/Rx.cpp @@ -27,6 +27,7 @@ #include "crypto/rx/Rx.h" #include "backend/common/Tags.h" +#include "backend/cpu/CpuConfig.h" #include "base/io/log/Log.h" #include "crypto/rx/RxConfig.h" #include "crypto/rx/RxQueue.h" @@ -60,7 +61,7 @@ const char *xmrig::rx_tag() } -bool xmrig::Rx::init(const Job &job, const RxConfig &config, bool hugePages, bool oneGbPages, int priority) +bool xmrig::Rx::init(const Job &job, const RxConfig &config, const CpuConfig &cpu) { if (job.algorithm().family() != Algorithm::RANDOM_X) { return true; @@ -70,7 +71,7 @@ bool xmrig::Rx::init(const Job &job, const RxConfig &config, bool hugePages, boo return true; } - d_ptr->queue.enqueue(job, config.nodeset(), config.threads(), hugePages, oneGbPages, config.mode(), priority); + d_ptr->queue.enqueue(job, config.nodeset(), config.threads(cpu.limit()), cpu.isHugePages(), cpu.isOneGbPages(), config.mode(), cpu.priority()); return false; } diff --git a/src/crypto/rx/Rx.h b/src/crypto/rx/Rx.h index 6b1db813..abaca920 100644 --- a/src/crypto/rx/Rx.h +++ b/src/crypto/rx/Rx.h @@ -37,6 +37,7 @@ namespace xmrig class Algorithm; +class CpuConfig; class IRxListener; class Job; class RxConfig; @@ -46,7 +47,7 @@ class RxDataset; class Rx { public: - static bool init(const Job &job, const RxConfig &config, bool hugePages, bool oneGbPages, int priority); + static bool init(const Job &job, const RxConfig &config, const CpuConfig &cpu); static bool isReady(const Job &job); static RxDataset *dataset(const Job &job, uint32_t nodeId); static std::pair hugePages(); diff --git a/src/crypto/rx/RxConfig.cpp b/src/crypto/rx/RxConfig.cpp index ef823e60..7ae7d35d 100644 --- a/src/crypto/rx/RxConfig.cpp +++ b/src/crypto/rx/RxConfig.cpp @@ -30,6 +30,7 @@ #include #include +#include #ifdef _MSC_VER @@ -52,9 +53,17 @@ const char *xmrig::RxConfig::modeName() const } -uint32_t xmrig::RxConfig::threads() const +uint32_t xmrig::RxConfig::threads(uint32_t limit) const { - return m_threads < 1 ? static_cast(Cpu::info()->threads()) : static_cast(m_threads); + if (m_threads > 0) { + return m_threads; + } + + if (limit < 100) { + return std::max(static_cast(round(Cpu::info()->threads() * (limit / 100.0))), 1U); + } + + return Cpu::info()->threads(); } diff --git a/src/crypto/rx/RxConfig.h b/src/crypto/rx/RxConfig.h index 13ebb9d1..e3381fec 100644 --- a/src/crypto/rx/RxConfig.h +++ b/src/crypto/rx/RxConfig.h @@ -55,7 +55,7 @@ public: # endif const char *modeName() const; - uint32_t threads() const; + uint32_t threads(uint32_t limit = 100) const; inline Mode mode() const { return m_mode; } From 8ef3e2ec14a464c0905fa54b5b83f956da11e448 Mon Sep 17 00:00:00 2001 From: XMRig Date: Sun, 8 Dec 2019 10:20:23 +0700 Subject: [PATCH 12/31] Fixed build without hwloc. --- src/backend/cpu/platform/AdvancedCpuInfo.cpp | 46 +++++++++++++++++--- src/backend/cpu/platform/AdvancedCpuInfo.h | 6 ++- src/backend/cpu/platform/BasicCpuInfo.cpp | 4 +- 3 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src/backend/cpu/platform/AdvancedCpuInfo.cpp b/src/backend/cpu/platform/AdvancedCpuInfo.cpp index 5cae55e2..37a958db 100644 --- a/src/backend/cpu/platform/AdvancedCpuInfo.cpp +++ b/src/backend/cpu/platform/AdvancedCpuInfo.cpp @@ -22,6 +22,17 @@ * along with this program. If not, see . */ +#include "backend/cpu/platform/AdvancedCpuInfo.h" +#include "3rdparty/libcpuid/libcpuid.h" + + +#ifdef _MSC_VER +# include +#else +# include +#endif + + #include #include #include @@ -29,10 +40,6 @@ #include -#include "3rdparty/libcpuid/libcpuid.h" -#include "backend/cpu/platform/AdvancedCpuInfo.h" - - namespace xmrig { @@ -54,11 +61,38 @@ static inline void cpu_brand_string(char out[64], const char *in) { } +static inline void cpuid(uint32_t level, int32_t output[4]) +{ + memset(output, 0, sizeof(int32_t) * 4); + +# ifdef _MSC_VER + __cpuid(output, static_cast(level)); +# else + __cpuid_count(level, 0, output[0], output[1], output[2], output[3]); +# endif +} + + +static inline bool has_feature(uint32_t level, uint32_t reg, int32_t bit) +{ + int32_t cpu_info[4] = { 0 }; + cpuid(level, cpu_info); + + return (cpu_info[reg] & bit) != 0; +} + + +static inline bool has_pdpe1gb() +{ + return has_feature(0x80000001, 3, 1 << 26); +} + + } // namespace xmrig xmrig::AdvancedCpuInfo::AdvancedCpuInfo() : - m_brand() + m_pdpe1gb(has_pdpe1gb()) { struct cpu_raw_data_t raw = {}; struct cpu_id_t data = {}; @@ -74,7 +108,7 @@ xmrig::AdvancedCpuInfo::AdvancedCpuInfo() : m_cores = static_cast(data.num_cores) * m_packages; m_L3 = data.l3_cache > 0 ? static_cast(data.l3_cache) * m_packages : 0; - const size_t l2 = static_cast(data.l2_cache); + const auto l2 = static_cast(data.l2_cache); // Workaround for AMD CPUs https://github.com/anrieff/libcpuid/issues/97 if (data.vendor == VENDOR_AMD && data.ext_family >= 0x15 && data.ext_family < 0x17) { diff --git a/src/backend/cpu/platform/AdvancedCpuInfo.h b/src/backend/cpu/platform/AdvancedCpuInfo.h index e2909a91..f590ccfb 100644 --- a/src/backend/cpu/platform/AdvancedCpuInfo.h +++ b/src/backend/cpu/platform/AdvancedCpuInfo.h @@ -43,6 +43,7 @@ protected: inline Assembly::Id assembly() const override { return m_assembly; } inline bool hasAES() const override { return m_aes; } inline bool hasAVX2() const override { return m_avx2; } + inline bool hasOneGbPages() const override { return m_pdpe1gb; } inline const char *backend() const override { return m_backend; } inline const char *brand() const override { return m_brand; } inline size_t cores() const override { return m_cores; } @@ -57,8 +58,9 @@ private: bool m_aes = false; bool m_avx2 = false; bool m_L2_exclusive = false; - char m_backend[32]; - char m_brand[64 + 5]; + char m_backend[32]{}; + char m_brand[64 + 5]{}; + const bool m_pdpe1gb = false; size_t m_cores = 0; size_t m_L2 = 0; size_t m_L3 = 0; diff --git a/src/backend/cpu/platform/BasicCpuInfo.cpp b/src/backend/cpu/platform/BasicCpuInfo.cpp index e6145278..e2ad19ea 100644 --- a/src/backend/cpu/platform/BasicCpuInfo.cpp +++ b/src/backend/cpu/platform/BasicCpuInfo.cpp @@ -113,7 +113,7 @@ static void cpu_brand_string(char out[64 + 6]) { } -static bool has_feature(uint32_t level, uint32_t reg, int32_t bit) +static inline bool has_feature(uint32_t level, uint32_t reg, int32_t bit) { int32_t cpu_info[4] = { 0 }; cpuid(level, cpu_info); @@ -191,7 +191,7 @@ const char *xmrig::BasicCpuInfo::backend() const } -xmrig::CpuThreads xmrig::BasicCpuInfo::threads(const Algorithm &algorithm, uint32_t limit) const +xmrig::CpuThreads xmrig::BasicCpuInfo::threads(const Algorithm &algorithm, uint32_t) const { const size_t count = std::thread::hardware_concurrency(); From 91b50f1ac894bb66564e48cb28334588c616db51 Mon Sep 17 00:00:00 2001 From: XMRig Date: Sun, 8 Dec 2019 13:30:26 +0700 Subject: [PATCH 13/31] Added os.cmake --- CMakeLists.txt | 30 +++++++++++------------------- cmake/os.cmake | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 19 deletions(-) create mode 100644 cmake/os.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 849c1257..e507a03c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") include (CheckIncludeFile) include (cmake/cpu.cmake) +include (cmake/os.cmake) include (src/base/base.cmake) include (src/backend/backend.cmake) @@ -131,40 +132,31 @@ if (WITH_HWLOC) ) endif() -if (WIN32) - set(SOURCES_OS - "${SOURCES_OS}" +if (XMRIG_OS_WIN) + list(APPEND SOURCES_OS res/app.rc src/App_win.cpp src/crypto/common/VirtualMemory_win.cpp ) - add_definitions(/DWIN32) set(EXTRA_LIBS ws2_32 psapi iphlpapi userenv) -elseif (APPLE) - set(SOURCES_OS - "${SOURCES_OS}" +elseif (XMRIG_OS_APPLE) + list(APPEND SOURCES_OS src/App_unix.cpp src/crypto/common/VirtualMemory_unix.cpp ) else() - set(SOURCES_OS - "${SOURCES_OS}" + list(APPEND SOURCES_OS src/App_unix.cpp src/crypto/common/VirtualMemory_unix.cpp ) - if (CMAKE_SYSTEM_NAME STREQUAL FreeBSD) - set(EXTRA_LIBS kvm pthread) - else() + if (XMRIG_OS_ANDROID) + set(EXTRA_LIBS pthread rt dl log) + elseif (XMRIG_OS_LINUX) set(EXTRA_LIBS pthread rt dl) - endif() -endif() - -if (CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "Android") - EXECUTE_PROCESS(COMMAND uname -o COMMAND tr -d '\n' OUTPUT_VARIABLE OPERATING_SYSTEM) - if (OPERATING_SYSTEM MATCHES "Android") - set(EXTRA_LIBS ${EXTRA_LIBS} log) + elseif (XMRIG_OS_FREEBSD) + set(EXTRA_LIBS kvm pthread) endif() endif() diff --git a/cmake/os.cmake b/cmake/os.cmake new file mode 100644 index 00000000..0270cc93 --- /dev/null +++ b/cmake/os.cmake @@ -0,0 +1,45 @@ +if (WIN32) + set(XMRIG_OS_WIN ON) +elseif (APPLE) + set(XMRIG_OS_APPLE ON) + + if (IOS OR CMAKE_SYSTEM_NAME STREQUAL iOS) + set(XMRIG_OS_IOS ON) + else() + set(XMRIG_OS_MACOS ON) + endif() +else() + set(XMRIG_OS_UNIX ON) + + if (ANDROID OR CMAKE_SYSTEM_NAME MATCHES "Android") + set(XMRIG_OS_ANDROID ON) + elseif(CMAKE_SYSTEM_NAME MATCHES "Linux") + set(XMRIG_OS_LINUX ON) + elseif(CMAKE_SYSTEM_NAME STREQUAL FreeBSD) + set(XMRIG_OS_FREEBSD ON) + endif() +endif() + + +if (XMRIG_OS_WIN) + add_definitions(/DWIN32) + add_definitions(/DXMRIG_OS_WIN) +elseif(XMRIG_OS_APPLE) + add_definitions(/DXMRIG_OS_APPLE) + + if (XMRIG_OS_IOS) + add_definitions(/DXMRIG_OS_IOS) + else() + add_definitions(/DXMRIG_OS_MACOS) + endif() +elseif(XMRIG_OS_UNIX) + add_definitions(/DXMRIG_OS_UNIX) + + if (XMRIG_OS_ANDROID) + add_definitions(/DXMRIG_OS_ANDROID) + elseif (XMRIG_OS_LINUX) + add_definitions(/DXMRIG_OS_LINUX) + elseif (XMRIG_OS_FREEBSD) + add_definitions(/DXMRIG_OS_FREEBSD) + endif() +endif() From 86e25a13e3671a801ff6e5c9d8804d561d98be9e Mon Sep 17 00:00:00 2001 From: XMRig Date: Sun, 8 Dec 2019 14:21:28 +0700 Subject: [PATCH 14/31] New summary information about 1GB pages. --- src/Summary.cpp | 14 ++++---- src/crypto/common/VirtualMemory.h | 1 + src/crypto/common/VirtualMemory_unix.cpp | 45 ++++++++++++++++-------- src/crypto/common/VirtualMemory_win.cpp | 8 ++++- 4 files changed, 46 insertions(+), 22 deletions(-) diff --git a/src/Summary.cpp b/src/Summary.cpp index db8ce9fa..b925de30 100644 --- a/src/Summary.cpp +++ b/src/Summary.cpp @@ -60,26 +60,28 @@ inline static const char *asmName(Assembly::Id assembly) static void print_memory(Config *config) { -# ifdef _WIN32 +# ifdef XMRIG_OS_WIN Log::print(GREEN_BOLD(" * ") WHITE_BOLD("%-13s") "%s", "HUGE PAGES", config->cpu().isHugePages() ? (VirtualMemory::isHugepagesAvailable() ? GREEN_BOLD("permission granted") : RED_BOLD("unavailable")) : RED_BOLD("disabled")); + Log::print(GREEN_BOLD(" * ") WHITE_BOLD("%-13s") "%s", "1GB PAGES", YELLOW_BOLD("unavailable")); +# else + Log::print(GREEN_BOLD(" * ") WHITE_BOLD("%-13s") "%s", "HUGE PAGES", config->cpu().isHugePages() ? GREEN_BOLD("supported") : RED_BOLD("disabled")); Log::print(GREEN_BOLD(" * ") WHITE_BOLD("%-13s") "%s", - "1GB PAGES", Cpu::info()->hasOneGbPages() ? GREEN_BOLD("available on Linux") : RED_BOLD("unavailable")); + "1GB PAGES", config->cpu().isOneGbPages() ? (VirtualMemory::isOneGbPagesAvailable() ? GREEN_BOLD("supported") : YELLOW_BOLD("unavailable")) : YELLOW_BOLD("disabled")); # endif } static void print_cpu(Config *) { - const ICpuInfo *info = Cpu::info(); + const auto info = Cpu::info(); - Log::print(GREEN_BOLD(" * ") WHITE_BOLD("%-13s%s (%zu)") " %sx64 %sAES %sPDPE1GB", + Log::print(GREEN_BOLD(" * ") WHITE_BOLD("%-13s%s (%zu)") " %sx64 %sAES", "CPU", info->brand(), info->packages(), info->isX64() ? GREEN_BOLD_S : RED_BOLD_S "-", - info->hasAES() ? GREEN_BOLD_S : RED_BOLD_S "-", - info->hasOneGbPages() ? GREEN_BOLD_S : RED_BOLD_S "-" + info->hasAES() ? GREEN_BOLD_S : RED_BOLD_S "-" ); # if defined(XMRIG_FEATURE_LIBCPUID) || defined (XMRIG_FEATURE_HWLOC) Log::print(WHITE_BOLD(" %-13s") BLACK_BOLD("L2:") WHITE_BOLD("%.1f MB") BLACK_BOLD(" L3:") WHITE_BOLD("%.1f MB") diff --git a/src/crypto/common/VirtualMemory.h b/src/crypto/common/VirtualMemory.h index 90ecdd69..e5101f52 100644 --- a/src/crypto/common/VirtualMemory.h +++ b/src/crypto/common/VirtualMemory.h @@ -58,6 +58,7 @@ public: } static bool isHugepagesAvailable(); + static bool isOneGbPagesAvailable(); static uint32_t bindToNUMANode(int64_t affinity); static void *allocateExecutableMemory(size_t size); static void *allocateLargePagesMemory(size_t size); diff --git a/src/crypto/common/VirtualMemory_unix.cpp b/src/crypto/common/VirtualMemory_unix.cpp index a7790aaf..39135171 100644 --- a/src/crypto/common/VirtualMemory_unix.cpp +++ b/src/crypto/common/VirtualMemory_unix.cpp @@ -29,6 +29,7 @@ #include +#include "backend/cpu/Cpu.h" #include "crypto/common/portable/mm_malloc.h" #include "crypto/common/VirtualMemory.h" @@ -38,12 +39,27 @@ #endif +#if defined (XMRIG_OS_LINUX) && (defined(MAP_HUGE_1GB) || defined(MAP_HUGE_SHIFT)) +# define XMRIG_HAS_1GB_PAGES +#endif + + bool xmrig::VirtualMemory::isHugepagesAvailable() { return true; } +bool xmrig::VirtualMemory::isOneGbPagesAvailable() +{ +# ifdef XMRIG_HAS_1GB_PAGES + return Cpu::info()->hasOneGbPages(); +# else + return false; +# endif +} + + void *xmrig::VirtualMemory::allocateExecutableMemory(size_t size) { # if defined(__APPLE__) @@ -72,24 +88,23 @@ void *xmrig::VirtualMemory::allocateLargePagesMemory(size_t size) void *xmrig::VirtualMemory::allocateOneGbPagesMemory(size_t size) { -# if defined(__APPLE__) - void *mem = MAP_FAILED; -# elif defined(__FreeBSD__) - void *mem = MAP_FAILED; -# else +# ifdef XMRIG_HAS_1GB_PAGES + if (isOneGbPagesAvailable()) { +# if defined(MAP_HUGE_1GB) + constexpr int flag_1gb = MAP_HUGE_1GB; +# elif defined(MAP_HUGE_SHIFT) + constexpr int flag_1gb = (30 << MAP_HUGE_SHIFT); +# else + constexpr int flag_1gb = 0; +# endif -# if defined(MAP_HUGE_1GB) - constexpr int flag_1gb = MAP_HUGE_1GB; -# elif defined(MAP_HUGE_SHIFT) - constexpr int flag_1gb = (30 << MAP_HUGE_SHIFT); -# else - constexpr int flag_1gb = 0; + void *mem = mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB | MAP_POPULATE | flag_1gb, 0, 0); + + return mem == MAP_FAILED ? nullptr : mem; + } # endif - void *mem = mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB | MAP_POPULATE | flag_1gb, 0, 0); -# endif - - return mem == MAP_FAILED ? nullptr : mem; + return nullptr; } diff --git a/src/crypto/common/VirtualMemory_win.cpp b/src/crypto/common/VirtualMemory_win.cpp index 70e48d3a..370f7c07 100644 --- a/src/crypto/common/VirtualMemory_win.cpp +++ b/src/crypto/common/VirtualMemory_win.cpp @@ -156,6 +156,12 @@ bool xmrig::VirtualMemory::isHugepagesAvailable() } +bool xmrig::VirtualMemory::isOneGbPagesAvailable() +{ + return false; +} + + void *xmrig::VirtualMemory::allocateExecutableMemory(size_t size) { return VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); @@ -175,7 +181,7 @@ void *xmrig::VirtualMemory::allocateLargePagesMemory(size_t size) } -void *xmrig::VirtualMemory::allocateOneGbPagesMemory(size_t size) +void *xmrig::VirtualMemory::allocateOneGbPagesMemory(size_t) { return nullptr; } From 4dec0634723a3f3aacbeed8e670d59f4886521b5 Mon Sep 17 00:00:00 2001 From: XMRig Date: Sun, 8 Dec 2019 14:30:44 +0700 Subject: [PATCH 15/31] Fix summary. --- src/Summary.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Summary.cpp b/src/Summary.cpp index b925de30..020eca61 100644 --- a/src/Summary.cpp +++ b/src/Summary.cpp @@ -59,7 +59,8 @@ inline static const char *asmName(Assembly::Id assembly) #endif -static void print_memory(Config *config) { +static void print_memory(Config *config) +{ # ifdef XMRIG_OS_WIN Log::print(GREEN_BOLD(" * ") WHITE_BOLD("%-13s") "%s", "HUGE PAGES", config->cpu().isHugePages() ? (VirtualMemory::isHugepagesAvailable() ? GREEN_BOLD("permission granted") : RED_BOLD("unavailable")) : RED_BOLD("disabled")); @@ -67,7 +68,7 @@ static void print_memory(Config *config) { # else Log::print(GREEN_BOLD(" * ") WHITE_BOLD("%-13s") "%s", "HUGE PAGES", config->cpu().isHugePages() ? GREEN_BOLD("supported") : RED_BOLD("disabled")); Log::print(GREEN_BOLD(" * ") WHITE_BOLD("%-13s") "%s", - "1GB PAGES", config->cpu().isOneGbPages() ? (VirtualMemory::isOneGbPagesAvailable() ? GREEN_BOLD("supported") : YELLOW_BOLD("unavailable")) : YELLOW_BOLD("disabled")); + "1GB PAGES", (VirtualMemory::isOneGbPagesAvailable() ? (config->cpu().isOneGbPages() ? GREEN_BOLD("supported") : YELLOW_BOLD("disabled")) : YELLOW_BOLD("unavailable"))); # endif } From d0df8245990ea688b83942906a3e57051ef88758 Mon Sep 17 00:00:00 2001 From: SChernykh Date: Sun, 8 Dec 2019 16:14:02 +0100 Subject: [PATCH 16/31] Optimized dataset read for Ryzen CPUs Removed register dependency in dataset read, +0.8% speedup on average. --- src/backend/cpu/CpuWorker.cpp | 2 +- .../asm/program_read_dataset_ryzen.inc | 19 ++++++++++++++++++ src/crypto/randomx/jit_compiler_a64.cpp | 2 +- src/crypto/randomx/jit_compiler_a64.hpp | 2 +- src/crypto/randomx/jit_compiler_fallback.hpp | 2 +- src/crypto/randomx/jit_compiler_x86.cpp | 20 ++++++++++++++----- src/crypto/randomx/jit_compiler_x86.hpp | 2 +- src/crypto/randomx/jit_compiler_x86_static.S | 4 ++++ .../randomx/jit_compiler_x86_static.asm | 5 +++++ .../randomx/jit_compiler_x86_static.hpp | 1 + src/crypto/randomx/randomx.cpp | 19 +++++++++++++----- src/crypto/randomx/randomx.h | 6 +++++- src/crypto/randomx/virtual_machine.hpp | 4 ++++ src/crypto/randomx/vm_compiled.cpp | 2 +- src/crypto/rx/RxVm.cpp | 6 +++++- src/crypto/rx/RxVm.h | 3 ++- src/net/JobResults.cpp | 2 +- 17 files changed, 81 insertions(+), 20 deletions(-) create mode 100644 src/crypto/randomx/asm/program_read_dataset_ryzen.inc diff --git a/src/backend/cpu/CpuWorker.cpp b/src/backend/cpu/CpuWorker.cpp index 2e223a31..5d58106d 100644 --- a/src/backend/cpu/CpuWorker.cpp +++ b/src/backend/cpu/CpuWorker.cpp @@ -96,7 +96,7 @@ void xmrig::CpuWorker::allocateRandomX_VM() } if (!m_vm) { - m_vm = new RxVm(dataset, m_memory->scratchpad(), !m_hwAES); + m_vm = new RxVm(dataset, m_memory->scratchpad(), !m_hwAES, m_assembly); } } #endif diff --git a/src/crypto/randomx/asm/program_read_dataset_ryzen.inc b/src/crypto/randomx/asm/program_read_dataset_ryzen.inc new file mode 100644 index 00000000..37e2104b --- /dev/null +++ b/src/crypto/randomx/asm/program_read_dataset_ryzen.inc @@ -0,0 +1,19 @@ + mov rcx, rbp ;# ecx = ma + shr rcx, 32 + and ecx, RANDOMX_DATASET_BASE_MASK + xor rbp, rax ;# modify "mx" + mov rax, qword ptr [rdi+rcx] + mov edx, ebp ;# edx = mx + and edx, RANDOMX_DATASET_BASE_MASK + prefetchnta byte ptr [rdi+rdx] + ror rbp, 32 ;# swap "ma" and "mx" + add rcx, rdi ;# dataset cache line + xor r8, rax + xor r9, qword ptr [rcx+8] + xor r10, qword ptr [rcx+16] + xor r11, qword ptr [rcx+24] + xor r12, qword ptr [rcx+32] + xor r13, qword ptr [rcx+40] + xor r14, qword ptr [rcx+48] + xor r15, qword ptr [rcx+56] + \ No newline at end of file diff --git a/src/crypto/randomx/jit_compiler_a64.cpp b/src/crypto/randomx/jit_compiler_a64.cpp index bf790c2b..d291de4d 100644 --- a/src/crypto/randomx/jit_compiler_a64.cpp +++ b/src/crypto/randomx/jit_compiler_a64.cpp @@ -118,7 +118,7 @@ static void clear_code_cache(char* p1, char* p2) # endif } -void JitCompilerA64::generateProgram(Program& program, ProgramConfiguration& config) +void JitCompilerA64::generateProgram(Program& program, ProgramConfiguration& config, uint32_t) { uint32_t codePos = MainLoopBegin + 4; diff --git a/src/crypto/randomx/jit_compiler_a64.hpp b/src/crypto/randomx/jit_compiler_a64.hpp index e524feb8..05afdc70 100644 --- a/src/crypto/randomx/jit_compiler_a64.hpp +++ b/src/crypto/randomx/jit_compiler_a64.hpp @@ -49,7 +49,7 @@ namespace randomx { JitCompilerA64(); ~JitCompilerA64(); - void generateProgram(Program&, ProgramConfiguration&); + void generateProgram(Program&, ProgramConfiguration&, uint32_t); void generateProgramLight(Program&, ProgramConfiguration&, uint32_t); template diff --git a/src/crypto/randomx/jit_compiler_fallback.hpp b/src/crypto/randomx/jit_compiler_fallback.hpp index bc363858..063ae521 100644 --- a/src/crypto/randomx/jit_compiler_fallback.hpp +++ b/src/crypto/randomx/jit_compiler_fallback.hpp @@ -44,7 +44,7 @@ namespace randomx { JitCompilerFallback() { throw std::runtime_error("JIT compilation is not supported on this platform"); } - void generateProgram(Program&, ProgramConfiguration&) { + void generateProgram(Program&, ProgramConfiguration&, uint32_t) { } void generateProgramLight(Program&, ProgramConfiguration&, uint32_t) { diff --git a/src/crypto/randomx/jit_compiler_x86.cpp b/src/crypto/randomx/jit_compiler_x86.cpp index bfde7d00..082b9eb3 100644 --- a/src/crypto/randomx/jit_compiler_x86.cpp +++ b/src/crypto/randomx/jit_compiler_x86.cpp @@ -89,7 +89,6 @@ namespace randomx { const uint8_t* codeLoopBegin = (uint8_t*)&randomx_program_loop_begin; const uint8_t* codeLoopLoad = (uint8_t*)&randomx_program_loop_load; const uint8_t* codeProgamStart = (uint8_t*)&randomx_program_start; - const uint8_t* codeReadDataset = (uint8_t*)&randomx_program_read_dataset; const uint8_t* codeReadDatasetLightSshInit = (uint8_t*)&randomx_program_read_dataset_sshash_init; const uint8_t* codeReadDatasetLightSshFin = (uint8_t*)&randomx_program_read_dataset_sshash_fin; const uint8_t* codeDatasetInit = (uint8_t*)&randomx_dataset_init; @@ -105,7 +104,6 @@ namespace randomx { const int32_t prefetchScratchpadSize = codePrefetchScratchpadEnd - codePrefetchScratchpad; const int32_t prologueSize = codeLoopBegin - codePrologue; const int32_t loopLoadSize = codeProgamStart - codeLoopLoad; - const int32_t readDatasetSize = codeReadDatasetLightSshInit - codeReadDataset; const int32_t readDatasetLightInitSize = codeReadDatasetLightSshFin - codeReadDatasetLightSshInit; const int32_t readDatasetLightFinSize = codeLoopStore - codeReadDatasetLightSshFin; const int32_t loopStoreSize = codeLoopEnd - codeLoopStore; @@ -301,10 +299,22 @@ namespace randomx { freePagedMemory(allocatedCode, CodeSize); } - void JitCompilerX86::generateProgram(Program& prog, ProgramConfiguration& pcfg) { + void JitCompilerX86::generateProgram(Program& prog, ProgramConfiguration& pcfg, uint32_t flags) { generateProgramPrologue(prog, pcfg); - memcpy(code + codePos, RandomX_CurrentConfig.codeReadDatasetTweaked, readDatasetSize); - codePos += readDatasetSize; + + uint8_t* p; + uint32_t n; + if (flags & RANDOMX_FLAG_RYZEN) { + p = RandomX_CurrentConfig.codeReadDatasetRyzenTweaked; + n = RandomX_CurrentConfig.codeReadDatasetRyzenTweakedSize; + } + else { + p = RandomX_CurrentConfig.codeReadDatasetTweaked; + n = RandomX_CurrentConfig.codeReadDatasetTweakedSize; + } + memcpy(code + codePos, p, n); + codePos += n; + generateProgramEpilogue(prog, pcfg); } diff --git a/src/crypto/randomx/jit_compiler_x86.hpp b/src/crypto/randomx/jit_compiler_x86.hpp index f1864018..0d515b0e 100644 --- a/src/crypto/randomx/jit_compiler_x86.hpp +++ b/src/crypto/randomx/jit_compiler_x86.hpp @@ -49,7 +49,7 @@ namespace randomx { public: JitCompilerX86(); ~JitCompilerX86(); - void generateProgram(Program&, ProgramConfiguration&); + void generateProgram(Program&, ProgramConfiguration&, uint32_t); void generateProgramLight(Program&, ProgramConfiguration&, uint32_t); template void generateSuperscalarHash(SuperscalarProgram (&programs)[N], std::vector &); diff --git a/src/crypto/randomx/jit_compiler_x86_static.S b/src/crypto/randomx/jit_compiler_x86_static.S index c20cd743..8e1f9ef6 100644 --- a/src/crypto/randomx/jit_compiler_x86_static.S +++ b/src/crypto/randomx/jit_compiler_x86_static.S @@ -45,6 +45,7 @@ .global DECL(randomx_program_loop_load) .global DECL(randomx_program_start) .global DECL(randomx_program_read_dataset) +.global DECL(randomx_program_read_dataset_ryzen) .global DECL(randomx_program_read_dataset_sshash_init) .global DECL(randomx_program_read_dataset_sshash_fin) .global DECL(randomx_program_loop_store) @@ -110,6 +111,9 @@ DECL(randomx_program_start): DECL(randomx_program_read_dataset): #include "asm/program_read_dataset.inc" +DECL(randomx_program_read_dataset_ryzen): + #include "asm/program_read_dataset_ryzen.inc" + DECL(randomx_program_read_dataset_sshash_init): #include "asm/program_read_dataset_sshash_init.inc" diff --git a/src/crypto/randomx/jit_compiler_x86_static.asm b/src/crypto/randomx/jit_compiler_x86_static.asm index 73fa503a..7dd1232d 100644 --- a/src/crypto/randomx/jit_compiler_x86_static.asm +++ b/src/crypto/randomx/jit_compiler_x86_static.asm @@ -36,6 +36,7 @@ PUBLIC randomx_program_loop_begin PUBLIC randomx_program_loop_load PUBLIC randomx_program_start PUBLIC randomx_program_read_dataset +PUBLIC randomx_program_read_dataset_ryzen PUBLIC randomx_program_read_dataset_sshash_init PUBLIC randomx_program_read_dataset_sshash_fin PUBLIC randomx_dataset_init @@ -103,6 +104,10 @@ randomx_program_read_dataset PROC include asm/program_read_dataset.inc randomx_program_read_dataset ENDP +randomx_program_read_dataset_ryzen PROC + include asm/program_read_dataset_ryzen.inc +randomx_program_read_dataset_ryzen ENDP + randomx_program_read_dataset_sshash_init PROC include asm/program_read_dataset_sshash_init.inc randomx_program_read_dataset_sshash_init ENDP diff --git a/src/crypto/randomx/jit_compiler_x86_static.hpp b/src/crypto/randomx/jit_compiler_x86_static.hpp index 0a62c986..151c1c58 100644 --- a/src/crypto/randomx/jit_compiler_x86_static.hpp +++ b/src/crypto/randomx/jit_compiler_x86_static.hpp @@ -37,6 +37,7 @@ extern "C" { void randomx_program_loop_load(); void randomx_program_start(); void randomx_program_read_dataset(); + void randomx_program_read_dataset_ryzen(); void randomx_program_read_dataset_sshash_init(); void randomx_program_read_dataset_sshash_fin(); void randomx_program_loop_store(); diff --git a/src/crypto/randomx/randomx.cpp b/src/crypto/randomx/randomx.cpp index 44d881eb..08f4f241 100644 --- a/src/crypto/randomx/randomx.cpp +++ b/src/crypto/randomx/randomx.cpp @@ -157,8 +157,15 @@ RandomX_ConfigurationBase::RandomX_ConfigurationBase() } { const uint8_t* a = (const uint8_t*)&randomx_program_read_dataset; - const uint8_t* b = (const uint8_t*)&randomx_program_read_dataset_sshash_init; + const uint8_t* b = (const uint8_t*)&randomx_program_read_dataset_ryzen; memcpy(codeReadDatasetTweaked, a, b - a); + codeReadDatasetTweakedSize = b - a; + } + { + const uint8_t* a = (const uint8_t*)&randomx_program_read_dataset_ryzen; + const uint8_t* b = (const uint8_t*)&randomx_program_read_dataset_sshash_init; + memcpy(codeReadDatasetRyzenTweaked, a, b - a); + codeReadDatasetRyzenTweakedSize = b - a; } { const uint8_t* a = (const uint8_t*)&randomx_program_read_dataset_sshash_init; @@ -191,10 +198,11 @@ void RandomX_ConfigurationBase::Apply() #if defined(_M_X64) || defined(__x86_64__) *(uint32_t*)(codeShhPrefetchTweaked + 3) = ArgonMemory * 16 - 1; - const uint32_t DatasetBaseMask = DatasetBaseSize - RANDOMX_DATASET_ITEM_SIZE; - *(uint32_t*)(codeReadDatasetTweaked + 7) = DatasetBaseMask; - *(uint32_t*)(codeReadDatasetTweaked + 23) = DatasetBaseMask; - *(uint32_t*)(codeReadDatasetLightSshInitTweaked + 59) = DatasetBaseMask; + // Not needed right now because all variants use default dataset base size + //const uint32_t DatasetBaseMask = DatasetBaseSize - RANDOMX_DATASET_ITEM_SIZE; + //*(uint32_t*)(codeReadDatasetTweaked + 9) = DatasetBaseMask; + //*(uint32_t*)(codeReadDatasetTweaked + 24) = DatasetBaseMask; + //*(uint32_t*)(codeReadDatasetLightSshInitTweaked + 59) = DatasetBaseMask; *(uint32_t*)(codePrefetchScratchpadTweaked + 4) = ScratchpadL3Mask64_Calculated; *(uint32_t*)(codePrefetchScratchpadTweaked + 18) = ScratchpadL3Mask64_Calculated; @@ -435,6 +443,7 @@ extern "C" { } vm->setScratchpad(scratchpad); + vm->setFlags(flags); } catch (std::exception &ex) { delete vm; diff --git a/src/crypto/randomx/randomx.h b/src/crypto/randomx/randomx.h index 95bfdbf4..84ae7dfc 100644 --- a/src/crypto/randomx/randomx.h +++ b/src/crypto/randomx/randomx.h @@ -49,6 +49,7 @@ enum randomx_flags { RANDOMX_FLAG_FULL_MEM = 4, RANDOMX_FLAG_JIT = 8, RANDOMX_FLAG_1GB_PAGES = 16, + RANDOMX_FLAG_RYZEN = 64, }; @@ -118,7 +119,10 @@ struct RandomX_ConfigurationBase rx_vec_i128 fillAes4Rx4_Key[8]; uint8_t codeShhPrefetchTweaked[20]; - uint8_t codeReadDatasetTweaked[64]; + uint8_t codeReadDatasetTweaked[72]; + uint32_t codeReadDatasetTweakedSize; + uint8_t codeReadDatasetRyzenTweaked[72]; + uint32_t codeReadDatasetRyzenTweakedSize; uint8_t codeReadDatasetLightSshInitTweaked[68]; uint8_t codePrefetchScratchpadTweaked[32]; diff --git a/src/crypto/randomx/virtual_machine.hpp b/src/crypto/randomx/virtual_machine.hpp index d3718d04..3fdd86df 100644 --- a/src/crypto/randomx/virtual_machine.hpp +++ b/src/crypto/randomx/virtual_machine.hpp @@ -46,6 +46,9 @@ public: virtual void run(void* seed) = 0; void resetRoundingMode(); + void setFlags(uint32_t flags) { vm_flags = flags; } + uint32_t getFlags() const { return vm_flags; } + randomx::RegisterFile *getRegisterFile() { return ® } @@ -71,6 +74,7 @@ protected: randomx_dataset* datasetPtr; }; uint64_t datasetOffset; + uint32_t vm_flags; }; namespace randomx { diff --git a/src/crypto/randomx/vm_compiled.cpp b/src/crypto/randomx/vm_compiled.cpp index d2ee59e8..1ab76cd7 100644 --- a/src/crypto/randomx/vm_compiled.cpp +++ b/src/crypto/randomx/vm_compiled.cpp @@ -43,7 +43,7 @@ namespace randomx { void CompiledVm::run(void* seed) { VmBase::generateProgram(seed); randomx_vm::initialize(); - compiler.generateProgram(program, config); + compiler.generateProgram(program, config, getFlags()); mem.memory = datasetPtr->memory + datasetOffset; execute(); } diff --git a/src/crypto/rx/RxVm.cpp b/src/crypto/rx/RxVm.cpp index e8d615e8..8cdad371 100644 --- a/src/crypto/rx/RxVm.cpp +++ b/src/crypto/rx/RxVm.cpp @@ -31,7 +31,7 @@ #include "crypto/rx/RxVm.h" -xmrig::RxVm::RxVm(RxDataset *dataset, uint8_t *scratchpad, bool softAes) +xmrig::RxVm::RxVm(RxDataset *dataset, uint8_t *scratchpad, bool softAes, xmrig::Assembly assembly) { if (!softAes) { m_flags |= RANDOMX_FLAG_HARD_AES; @@ -45,6 +45,10 @@ xmrig::RxVm::RxVm(RxDataset *dataset, uint8_t *scratchpad, bool softAes) m_flags |= RANDOMX_FLAG_JIT; } + if (assembly == Assembly::RYZEN) { + m_flags |= RANDOMX_FLAG_RYZEN; + } + m_vm = randomx_create_vm(static_cast(m_flags), dataset->cache() ? dataset->cache()->get() : nullptr, dataset->get(), scratchpad); } diff --git a/src/crypto/rx/RxVm.h b/src/crypto/rx/RxVm.h index 30a31c2e..7cddf93b 100644 --- a/src/crypto/rx/RxVm.h +++ b/src/crypto/rx/RxVm.h @@ -29,6 +29,7 @@ #include "base/tools/Object.h" +#include "backend/cpu/Cpu.h" #include @@ -49,7 +50,7 @@ class RxVm public: XMRIG_DISABLE_COPY_MOVE_DEFAULT(RxVm); - RxVm(RxDataset *dataset, uint8_t *scratchpad, bool softAes); + RxVm(RxDataset *dataset, uint8_t *scratchpad, bool softAes, xmrig::Assembly assembly); ~RxVm(); inline randomx_vm *get() const { return m_vm; } diff --git a/src/net/JobResults.cpp b/src/net/JobResults.cpp index e8b4adce..26f16952 100644 --- a/src/net/JobResults.cpp +++ b/src/net/JobResults.cpp @@ -117,7 +117,7 @@ static void getResults(JobBundle &bundle, std::vector &results, uint3 return; } - auto vm = new RxVm(dataset, memory->scratchpad(), !hwAES); + auto vm = new RxVm(dataset, memory->scratchpad(), !hwAES, Assembly::NONE); for (uint32_t nonce : bundle.nonces) { *bundle.job.nonce() = nonce; From ffec42140860714a19a2fcb0623a067c453eacc3 Mon Sep 17 00:00:00 2001 From: SChernykh Date: Sun, 8 Dec 2019 16:20:46 +0100 Subject: [PATCH 17/31] Fixed indentation --- src/crypto/randomx/jit_compiler_x86.cpp | 8 ++++---- src/crypto/randomx/jit_compiler_x86_static.hpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/crypto/randomx/jit_compiler_x86.cpp b/src/crypto/randomx/jit_compiler_x86.cpp index 082b9eb3..2528c2cf 100644 --- a/src/crypto/randomx/jit_compiler_x86.cpp +++ b/src/crypto/randomx/jit_compiler_x86.cpp @@ -305,12 +305,12 @@ namespace randomx { uint8_t* p; uint32_t n; if (flags & RANDOMX_FLAG_RYZEN) { - p = RandomX_CurrentConfig.codeReadDatasetRyzenTweaked; - n = RandomX_CurrentConfig.codeReadDatasetRyzenTweakedSize; + p = RandomX_CurrentConfig.codeReadDatasetRyzenTweaked; + n = RandomX_CurrentConfig.codeReadDatasetRyzenTweakedSize; } else { - p = RandomX_CurrentConfig.codeReadDatasetTweaked; - n = RandomX_CurrentConfig.codeReadDatasetTweakedSize; + p = RandomX_CurrentConfig.codeReadDatasetTweaked; + n = RandomX_CurrentConfig.codeReadDatasetTweakedSize; } memcpy(code + codePos, p, n); codePos += n; diff --git a/src/crypto/randomx/jit_compiler_x86_static.hpp b/src/crypto/randomx/jit_compiler_x86_static.hpp index 151c1c58..b0a7c5ac 100644 --- a/src/crypto/randomx/jit_compiler_x86_static.hpp +++ b/src/crypto/randomx/jit_compiler_x86_static.hpp @@ -37,7 +37,7 @@ extern "C" { void randomx_program_loop_load(); void randomx_program_start(); void randomx_program_read_dataset(); - void randomx_program_read_dataset_ryzen(); + void randomx_program_read_dataset_ryzen(); void randomx_program_read_dataset_sshash_init(); void randomx_program_read_dataset_sshash_fin(); void randomx_program_loop_store(); From 028b335bacb6299b09d774b7e835e3deba3c1ea9 Mon Sep 17 00:00:00 2001 From: SChernykh Date: Sun, 8 Dec 2019 16:51:37 +0100 Subject: [PATCH 18/31] Fix GCC compilation --- src/crypto/randomx/vm_compiled.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/crypto/randomx/vm_compiled.cpp b/src/crypto/randomx/vm_compiled.cpp index 1ab76cd7..2dc0c025 100644 --- a/src/crypto/randomx/vm_compiled.cpp +++ b/src/crypto/randomx/vm_compiled.cpp @@ -43,7 +43,7 @@ namespace randomx { void CompiledVm::run(void* seed) { VmBase::generateProgram(seed); randomx_vm::initialize(); - compiler.generateProgram(program, config, getFlags()); + compiler.generateProgram(program, config, randomx_vm::getFlags()); mem.memory = datasetPtr->memory + datasetOffset; execute(); } From d32df84ca5189a98050a6a190f092e33600acbe6 Mon Sep 17 00:00:00 2001 From: XMRig Date: Sun, 8 Dec 2019 23:17:39 +0700 Subject: [PATCH 19/31] Memory allocation refactoring. --- CMakeLists.txt | 7 ++ src/backend/common/interfaces/IRxStorage.h | 3 +- src/backend/cpu/CpuBackend.cpp | 40 ++++---- src/backend/cpu/CpuWorker.cpp | 2 +- src/crypto/common/HugePagesInfo.cpp | 50 ++++++++++ src/crypto/common/HugePagesInfo.h | 67 ++++++++++++++ src/crypto/common/LinuxMemory.cpp | 103 +++++++++++++++++++++ src/crypto/common/LinuxMemory.h | 49 ++++++++++ src/crypto/common/MemoryPool.cpp | 2 +- src/crypto/common/VirtualMemory.cpp | 17 +++- src/crypto/common/VirtualMemory.h | 11 ++- src/crypto/common/VirtualMemory_unix.cpp | 17 +++- src/crypto/randomx/allocator.cpp | 10 +- src/crypto/randomx/common.hpp | 2 - src/crypto/randomx/dataset.hpp | 2 - src/crypto/randomx/randomx.cpp | 69 +++----------- src/crypto/randomx/randomx.h | 4 +- src/crypto/randomx/virtual_memory.cpp | 10 -- src/crypto/randomx/virtual_memory.hpp | 1 - src/crypto/rx/Rx.cpp | 8 +- src/crypto/rx/Rx.h | 5 +- src/crypto/rx/RxBasicStorage.cpp | 35 ++++--- src/crypto/rx/RxBasicStorage.h | 2 +- src/crypto/rx/RxCache.cpp | 46 +++++---- src/crypto/rx/RxCache.h | 16 ++-- src/crypto/rx/RxDataset.cpp | 90 +++++++++--------- src/crypto/rx/RxDataset.h | 13 ++- src/crypto/rx/RxNUMAStorage.cpp | 92 ++++++++++-------- src/crypto/rx/RxNUMAStorage.h | 2 +- src/crypto/rx/RxQueue.cpp | 4 +- src/crypto/rx/RxQueue.h | 3 +- src/net/JobResults.cpp | 2 +- 32 files changed, 514 insertions(+), 270 deletions(-) create mode 100644 src/crypto/common/HugePagesInfo.cpp create mode 100644 src/crypto/common/HugePagesInfo.h create mode 100644 src/crypto/common/LinuxMemory.cpp create mode 100644 src/crypto/common/LinuxMemory.h diff --git a/CMakeLists.txt b/CMakeLists.txt index e507a03c..b65d5337 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -76,6 +76,7 @@ set(HEADERS_CRYPTO src/crypto/cn/soft_aes.h src/crypto/common/Algorithm.h src/crypto/common/Coin.h + src/crypto/common/HugePagesInfo.h src/crypto/common/keccak.h src/crypto/common/MemoryPool.h src/crypto/common/Nonce.h @@ -115,6 +116,7 @@ set(SOURCES_CRYPTO src/crypto/cn/CnHash.cpp src/crypto/common/Algorithm.cpp src/crypto/common/Coin.cpp + src/crypto/common/HugePagesInfo.cpp src/crypto/common/keccak.cpp src/crypto/common/MemoryPool.cpp src/crypto/common/Nonce.cpp @@ -154,6 +156,11 @@ else() if (XMRIG_OS_ANDROID) set(EXTRA_LIBS pthread rt dl log) elseif (XMRIG_OS_LINUX) + list(APPEND SOURCES_OS + src/crypto/common/LinuxMemory.h + src/crypto/common/LinuxMemory.cpp + ) + set(EXTRA_LIBS pthread rt dl) elseif (XMRIG_OS_FREEBSD) set(EXTRA_LIBS kvm pthread) diff --git a/src/backend/common/interfaces/IRxStorage.h b/src/backend/common/interfaces/IRxStorage.h index 372eee6f..56788518 100644 --- a/src/backend/common/interfaces/IRxStorage.h +++ b/src/backend/common/interfaces/IRxStorage.h @@ -25,6 +25,7 @@ #include "crypto/rx/RxConfig.h" +#include "crypto/common/HugePagesInfo.h" #include @@ -44,8 +45,8 @@ class IRxStorage public: virtual ~IRxStorage() = default; + virtual HugePagesInfo hugePages() const = 0; virtual RxDataset *dataset(const Job &job, uint32_t nodeId) const = 0; - virtual std::pair hugePages() const = 0; virtual void init(const RxSeed &seed, uint32_t threads, bool hugePages, bool oneGbPages, RxConfig::Mode mode, int priority) = 0; }; diff --git a/src/backend/cpu/CpuBackend.cpp b/src/backend/cpu/CpuBackend.cpp index ddee6768..ac459497 100644 --- a/src/backend/cpu/CpuBackend.cpp +++ b/src/backend/cpu/CpuBackend.cpp @@ -68,17 +68,15 @@ static std::mutex mutex; struct CpuLaunchStatus { public: - inline size_t hugePages() const { return m_hugePages; } - inline size_t memory() const { return m_ways * m_memory; } - inline size_t pages() const { return m_pages; } - inline size_t threads() const { return m_threads; } - inline size_t ways() const { return m_ways; } + inline const HugePagesInfo &hugePages() const { return m_hugePages; } + inline size_t memory() const { return m_ways * m_memory; } + inline size_t threads() const { return m_threads; } + inline size_t ways() const { return m_ways; } inline void start(const std::vector &threads, size_t memory) { - m_hugePages = 0; + m_hugePages.reset(); m_memory = memory; - m_pages = 0; m_started = 0; m_errors = 0; m_threads = threads.size(); @@ -89,11 +87,9 @@ public: inline bool started(IWorker *worker, bool ready) { if (ready) { - auto hugePages = worker->memory()->hugePages(); - m_started++; - m_hugePages += hugePages.first; - m_pages += hugePages.second; + + m_hugePages += worker->memory()->hugePages(); m_ways += worker->intensity(); } else { @@ -115,19 +111,18 @@ public: tag, m_errors == 0 ? CYAN_BOLD_S : YELLOW_BOLD_S, m_started, m_threads, m_ways, - (m_hugePages == m_pages ? GREEN_BOLD_S : (m_hugePages == 0 ? RED_BOLD_S : YELLOW_BOLD_S)), - m_hugePages == 0 ? 0.0 : static_cast(m_hugePages) / m_pages * 100.0, - m_hugePages, m_pages, + (m_hugePages.isFullyAllocated() ? GREEN_BOLD_S : (m_hugePages.allocated == 0 ? RED_BOLD_S : YELLOW_BOLD_S)), + m_hugePages.percent(), + m_hugePages.allocated, m_hugePages.total, memory() / 1024, Chrono::steadyMSecs() - m_ts ); } private: + HugePagesInfo m_hugePages; size_t m_errors = 0; - size_t m_hugePages = 0; size_t m_memory = 0; - size_t m_pages = 0; size_t m_started = 0; size_t m_threads = 0; size_t m_ways = 0; @@ -169,18 +164,17 @@ public: rapidjson::Value hugePages(int version, rapidjson::Document &doc) { - std::pair pages(0, 0); + HugePagesInfo pages; # ifdef XMRIG_ALGO_RANDOMX if (algo.family() == Algorithm::RANDOM_X) { - pages = Rx::hugePages(); + pages += Rx::hugePages(); } # endif mutex.lock(); - pages.first += status.hugePages(); - pages.second += status.pages(); + pages += status.hugePages(); mutex.unlock(); @@ -188,11 +182,11 @@ public: if (version > 1) { hugepages.SetArray(); - hugepages.PushBack(pages.first, doc.GetAllocator()); - hugepages.PushBack(pages.second, doc.GetAllocator()); + hugepages.PushBack(static_cast(pages.allocated), doc.GetAllocator()); + hugepages.PushBack(static_cast(pages.total), doc.GetAllocator()); } else { - hugepages = pages.first == pages.second; + hugepages = pages.isFullyAllocated(); } return hugepages; diff --git a/src/backend/cpu/CpuWorker.cpp b/src/backend/cpu/CpuWorker.cpp index 5d58106d..f93f7dac 100644 --- a/src/backend/cpu/CpuWorker.cpp +++ b/src/backend/cpu/CpuWorker.cpp @@ -63,7 +63,7 @@ xmrig::CpuWorker::CpuWorker(size_t id, const CpuLaunchData &data) : m_miner(data.miner), m_ctx() { - m_memory = new VirtualMemory(m_algorithm.l3() * N, data.hugePages, true, m_node); + m_memory = new VirtualMemory(m_algorithm.l3() * N, data.hugePages, false, true, m_node); } diff --git a/src/crypto/common/HugePagesInfo.cpp b/src/crypto/common/HugePagesInfo.cpp new file mode 100644 index 00000000..3108c7de --- /dev/null +++ b/src/crypto/common/HugePagesInfo.cpp @@ -0,0 +1,50 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include "crypto/common/HugePagesInfo.h" +#include "crypto/common/VirtualMemory.h" + + +namespace xmrig { + +constexpr size_t twoMiB = 2U * 1024U * 1024U; +constexpr size_t oneGiB = 1024U * 1024U * 1024U; + +} // namespace xmrig + + +xmrig::HugePagesInfo::HugePagesInfo(const VirtualMemory *memory) +{ + if (memory->isOneGbPages()) { + size = VirtualMemory::align(memory->size(), oneGiB); + total = size / oneGiB; + allocated = size / oneGiB; + } + else { + size = memory->size(); + total = size / twoMiB; + allocated = memory->isHugePages() ? total : 0; + } +} diff --git a/src/crypto/common/HugePagesInfo.h b/src/crypto/common/HugePagesInfo.h new file mode 100644 index 00000000..1dc93bb4 --- /dev/null +++ b/src/crypto/common/HugePagesInfo.h @@ -0,0 +1,67 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef XMRIG_HUGEPAGESINFO_H +#define XMRIG_HUGEPAGESINFO_H + + +#include +#include + + +namespace xmrig { + + +class VirtualMemory; + + +class HugePagesInfo +{ +public: + HugePagesInfo() = default; + HugePagesInfo(const VirtualMemory *memory); + + size_t allocated = 0; + size_t total = 0; + size_t size = 0; + + inline bool isFullyAllocated() const { return allocated == total; } + inline double percent() const { return allocated == 0 ? 0.0 : static_cast(allocated) / total * 100.0; } + inline void reset() { allocated = 0; total = 0; size = 0; } + + inline HugePagesInfo &operator+=(const HugePagesInfo &other) + { + allocated += other.allocated; + total += other.total; + size += other.size; + + return *this; + } +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_HUGEPAGESINFO_H */ diff --git a/src/crypto/common/LinuxMemory.cpp b/src/crypto/common/LinuxMemory.cpp new file mode 100644 index 00000000..b7c2000f --- /dev/null +++ b/src/crypto/common/LinuxMemory.cpp @@ -0,0 +1,103 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//#include + +#include "crypto/common/LinuxMemory.h" +#include "base/io/log/Log.h" +#include "crypto/common/VirtualMemory.h" +#include "backend/cpu/Cpu.h" + + +#include +#include +#include +#include + + +namespace xmrig { + + +static std::mutex mutex; +constexpr size_t twoMiB = 2U * 1024U * 1024U; +constexpr size_t oneGiB = 1024U * 1024U * 1024U; + + +static inline std::string sysfs_path(uint32_t node, bool oneGbPages, bool nr) +{ + return "/sys/devices/system/node/node" + std::to_string(node) + "/hugepages/hugepages-" + (oneGbPages ? "1048576" : "2048") + "kB/" + (nr ? "nr" : "free") + "_hugepages"; +} + + +static inline bool write_nr_hugepages(uint32_t node, bool oneGbPages, uint64_t count) { return LinuxMemory::write(sysfs_path(node, oneGbPages, true).c_str(), count); } +static inline int64_t free_hugepages(uint32_t node, bool oneGbPages) { return LinuxMemory::read(sysfs_path(node, oneGbPages, false).c_str()); } +static inline int64_t nr_hugepages(uint32_t node, bool oneGbPages) { return LinuxMemory::read(sysfs_path(node, oneGbPages, true).c_str()); } + + +} // namespace xmrig + + +bool xmrig::LinuxMemory::reserve(size_t size, uint32_t node, bool oneGbPages) +{ + std::lock_guard lock(mutex); + + const size_t pageSize = oneGbPages ? oneGiB : twoMiB; + const size_t required = VirtualMemory::align(size, pageSize) / pageSize; + + const auto available = free_hugepages(node, oneGbPages); + if (available < 0 || static_cast(available) >= required) { + return false; + } + + return write_nr_hugepages(node, oneGbPages, std::max(nr_hugepages(node, oneGbPages), 0) + (required - available)); +} + + +bool xmrig::LinuxMemory::write(const char *path, uint64_t value) +{ + std::ofstream file(path, std::ios::out | std::ios::binary | std::ios::trunc); + if (!file.is_open()) { + return false; + } + + file << value; + file.flush(); + + return true; +} + + +int64_t xmrig::LinuxMemory::read(const char *path) +{ + std::ifstream file(path); + if (!file.is_open()) { + return -1; + } + + uint64_t value = 0; + file >> value; + + return value; +} diff --git a/src/crypto/common/LinuxMemory.h b/src/crypto/common/LinuxMemory.h new file mode 100644 index 00000000..aa46a6fc --- /dev/null +++ b/src/crypto/common/LinuxMemory.h @@ -0,0 +1,49 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef XMRIG_LINUXMEMORY_H +#define XMRIG_LINUXMEMORY_H + + +#include +#include + + +namespace xmrig { + + +class LinuxMemory +{ +public: + static bool reserve(size_t size, uint32_t node, bool oneGbPages = false); + + static bool write(const char *path, uint64_t value); + static int64_t read(const char *path); +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_LINUXMEMORY_H */ diff --git a/src/crypto/common/MemoryPool.cpp b/src/crypto/common/MemoryPool.cpp index 2b2da659..6e17d752 100644 --- a/src/crypto/common/MemoryPool.cpp +++ b/src/crypto/common/MemoryPool.cpp @@ -47,7 +47,7 @@ xmrig::MemoryPool::MemoryPool(size_t size, bool hugePages, uint32_t node) return; } - m_memory = new VirtualMemory(size * pageSize, hugePages, false, node); + m_memory = new VirtualMemory(size * pageSize, hugePages, false, false, node); } diff --git a/src/crypto/common/VirtualMemory.cpp b/src/crypto/common/VirtualMemory.cpp index 0eaef3c5..14909f1b 100644 --- a/src/crypto/common/VirtualMemory.cpp +++ b/src/crypto/common/VirtualMemory.cpp @@ -46,10 +46,13 @@ namespace xmrig { static IMemoryPool *pool = nullptr; static std::mutex mutex; +constexpr size_t twoMiB = 2U * 1024U * 1024U; +constexpr size_t oneGiB = 1024U * 1024U * 1024U; + } // namespace xmrig -xmrig::VirtualMemory::VirtualMemory(size_t size, bool hugePages, bool usePool, uint32_t node, size_t alignSize) : +xmrig::VirtualMemory::VirtualMemory(size_t size, bool hugePages, bool oneGbPages, bool usePool, uint32_t node, size_t alignSize) : m_size(align(size)), m_node(node) { @@ -68,6 +71,10 @@ xmrig::VirtualMemory::VirtualMemory(size_t size, bool hugePages, bool usePool, u } } + if (oneGbPages && allocateOneGbPagesMemory()) { + return; + } + if (hugePages && allocateLargePagesMemory()) { return; } @@ -86,7 +93,7 @@ xmrig::VirtualMemory::~VirtualMemory() std::lock_guard lock(mutex); pool->release(m_node); } - else if (isHugePages()) { + else if (isHugePages() || isOneGbPages()) { freeLargePagesMemory(); } else { @@ -95,6 +102,12 @@ xmrig::VirtualMemory::~VirtualMemory() } +xmrig::HugePagesInfo xmrig::VirtualMemory::hugePages() const +{ + return { this }; +} + + #ifndef XMRIG_FEATURE_HWLOC uint32_t xmrig::VirtualMemory::bindToNUMANode(int64_t) { diff --git a/src/crypto/common/VirtualMemory.h b/src/crypto/common/VirtualMemory.h index e5101f52..f27e5908 100644 --- a/src/crypto/common/VirtualMemory.h +++ b/src/crypto/common/VirtualMemory.h @@ -29,6 +29,7 @@ #include "base/tools/Object.h" +#include "crypto/common/HugePagesInfo.h" #include @@ -45,17 +46,16 @@ class VirtualMemory public: XMRIG_DISABLE_COPY_MOVE_DEFAULT(VirtualMemory) - VirtualMemory(size_t size, bool hugePages, bool usePool, uint32_t node = 0, size_t alignSize = 64); + VirtualMemory(size_t size, bool hugePages, bool oneGbPages, bool usePool, uint32_t node = 0, size_t alignSize = 64); ~VirtualMemory(); inline bool isHugePages() const { return m_flags.test(FLAG_HUGEPAGES); } + inline bool isOneGbPages() const { return m_flags.test(FLAG_1GB_PAGES); } inline size_t size() const { return m_size; } + inline uint8_t *raw() const { return m_scratchpad; } inline uint8_t *scratchpad() const { return m_scratchpad; } - inline std::pair hugePages() const - { - return { isHugePages() ? (align(size()) / 2097152) : 0, align(size()) / 2097152 }; - } + HugePagesInfo hugePages() const; static bool isHugepagesAvailable(); static bool isOneGbPagesAvailable(); @@ -75,6 +75,7 @@ public: private: enum Flags { FLAG_HUGEPAGES, + FLAG_1GB_PAGES, FLAG_LOCK, FLAG_EXTERNAL, FLAG_MAX diff --git a/src/crypto/common/VirtualMemory_unix.cpp b/src/crypto/common/VirtualMemory_unix.cpp index 39135171..0c77ba09 100644 --- a/src/crypto/common/VirtualMemory_unix.cpp +++ b/src/crypto/common/VirtualMemory_unix.cpp @@ -39,8 +39,11 @@ #endif -#if defined (XMRIG_OS_LINUX) && (defined(MAP_HUGE_1GB) || defined(MAP_HUGE_SHIFT)) -# define XMRIG_HAS_1GB_PAGES +#if defined(XMRIG_OS_LINUX) +# if (defined(MAP_HUGE_1GB) || defined(MAP_HUGE_SHIFT)) +# define XMRIG_HAS_1GB_PAGES +# endif +# include "crypto/common/LinuxMemory.h" #endif @@ -141,6 +144,10 @@ void xmrig::VirtualMemory::osInit(bool) bool xmrig::VirtualMemory::allocateLargePagesMemory() { +# if defined(XMRIG_OS_LINUX) + LinuxMemory::reserve(m_size, m_node); +# endif + m_scratchpad = static_cast(allocateLargePagesMemory(m_size)); if (m_scratchpad) { m_flags.set(FLAG_HUGEPAGES, true); @@ -160,9 +167,13 @@ bool xmrig::VirtualMemory::allocateLargePagesMemory() bool xmrig::VirtualMemory::allocateOneGbPagesMemory() { +# if defined(XMRIG_HAS_1GB_PAGES) + LinuxMemory::reserve(m_size, m_node, true); +# endif + m_scratchpad = static_cast(allocateOneGbPagesMemory(m_size)); if (m_scratchpad) { - m_flags.set(FLAG_HUGEPAGES, true); + m_flags.set(FLAG_1GB_PAGES, true); madvise(m_scratchpad, m_size, MADV_RANDOM | MADV_WILLNEED); diff --git a/src/crypto/randomx/allocator.cpp b/src/crypto/randomx/allocator.cpp index d46393f1..770e60f9 100644 --- a/src/crypto/randomx/allocator.cpp +++ b/src/crypto/randomx/allocator.cpp @@ -43,7 +43,7 @@ namespace randomx { } template - void AlignedAllocator::freeMemory(void* ptr, size_t count) { + void AlignedAllocator::freeMemory(void* ptr, size_t) { rx_aligned_free(ptr); } @@ -57,12 +57,4 @@ namespace randomx { freePagedMemory(ptr, count); }; - void* OneGbPageAllocator::allocMemory(size_t count) { - return allocOneGbPagesMemory(count); - } - - void OneGbPageAllocator::freeMemory(void* ptr, size_t count) { - freePagedMemory(ptr, count); - }; - } diff --git a/src/crypto/randomx/common.hpp b/src/crypto/randomx/common.hpp index 48f31bac..cea5f5be 100644 --- a/src/crypto/randomx/common.hpp +++ b/src/crypto/randomx/common.hpp @@ -167,7 +167,5 @@ namespace randomx { typedef void(ProgramFunc)(RegisterFile&, MemoryRegisters&, uint8_t* /* scratchpad */, uint64_t); typedef void(DatasetInitFunc)(randomx_cache* cache, uint8_t* dataset, uint32_t startBlock, uint32_t endBlock); - typedef void(DatasetDeallocFunc)(randomx_dataset*); - typedef void(CacheDeallocFunc)(randomx_cache*); typedef void(CacheInitializeFunc)(randomx_cache*, const void*, size_t); } diff --git a/src/crypto/randomx/dataset.hpp b/src/crypto/randomx/dataset.hpp index 6c179b5d..a40cf1d7 100644 --- a/src/crypto/randomx/dataset.hpp +++ b/src/crypto/randomx/dataset.hpp @@ -38,13 +38,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /* Global scope for C binding */ struct randomx_dataset { uint8_t* memory = nullptr; - randomx::DatasetDeallocFunc* dealloc; }; /* Global scope for C binding */ struct randomx_cache { uint8_t* memory = nullptr; - randomx::CacheDeallocFunc* dealloc; randomx::JitCompiler* jit; randomx::CacheInitializeFunc* initialize; randomx::DatasetInitFunc* datasetInit; diff --git a/src/crypto/randomx/randomx.cpp b/src/crypto/randomx/randomx.cpp index 08f4f241..8b1cfe2c 100644 --- a/src/crypto/randomx/randomx.cpp +++ b/src/crypto/randomx/randomx.cpp @@ -272,42 +272,24 @@ RandomX_ConfigurationBase RandomX_CurrentConfig; extern "C" { - randomx_cache *randomx_alloc_cache(randomx_flags flags) { + randomx_cache *randomx_create_cache(randomx_flags flags, uint8_t *memory) { randomx_cache *cache = nullptr; try { cache = new randomx_cache(); - switch (flags & (RANDOMX_FLAG_JIT | RANDOMX_FLAG_LARGE_PAGES)) { + switch (flags & RANDOMX_FLAG_JIT) { case RANDOMX_FLAG_DEFAULT: - cache->dealloc = &randomx::deallocCache; - cache->jit = nullptr; - cache->initialize = &randomx::initCache; - cache->datasetInit = &randomx::initDataset; - cache->memory = (uint8_t*)randomx::DefaultAllocator::allocMemory(RANDOMX_CACHE_MAX_SIZE); + cache->jit = nullptr; + cache->initialize = &randomx::initCache; + cache->datasetInit = &randomx::initDataset; + cache->memory = memory; break; case RANDOMX_FLAG_JIT: - cache->dealloc = &randomx::deallocCache; - cache->jit = new randomx::JitCompiler(); - cache->initialize = &randomx::initCacheCompile; - cache->datasetInit = cache->jit->getDatasetInitFunc(); - cache->memory = (uint8_t*)randomx::DefaultAllocator::allocMemory(RANDOMX_CACHE_MAX_SIZE); - break; - - case RANDOMX_FLAG_LARGE_PAGES: - cache->dealloc = &randomx::deallocCache; - cache->jit = nullptr; - cache->initialize = &randomx::initCache; - cache->datasetInit = &randomx::initDataset; - cache->memory = (uint8_t*)randomx::LargePageAllocator::allocMemory(RANDOMX_CACHE_MAX_SIZE); - break; - - case RANDOMX_FLAG_JIT | RANDOMX_FLAG_LARGE_PAGES: - cache->dealloc = &randomx::deallocCache; - cache->jit = new randomx::JitCompiler(); - cache->initialize = &randomx::initCacheCompile; - cache->datasetInit = cache->jit->getDatasetInitFunc(); - cache->memory = (uint8_t*)randomx::LargePageAllocator::allocMemory(RANDOMX_CACHE_MAX_SIZE); + cache->jit = new randomx::JitCompiler(); + cache->initialize = &randomx::initCacheCompile; + cache->datasetInit = cache->jit->getDatasetInitFunc(); + cache->memory = memory; break; default: @@ -331,35 +313,12 @@ extern "C" { } void randomx_release_cache(randomx_cache* cache) { - assert(cache != nullptr); - cache->dealloc(cache); delete cache; } - randomx_dataset *randomx_alloc_dataset(randomx_flags flags) { - randomx_dataset *dataset = nullptr; - - try { - dataset = new randomx_dataset(); - if (flags & RANDOMX_FLAG_1GB_PAGES) { - dataset->dealloc = &randomx::deallocDataset; - dataset->memory = (uint8_t*)randomx::OneGbPageAllocator::allocMemory(RANDOMX_DATASET_MAX_SIZE); - } - else if (flags & RANDOMX_FLAG_LARGE_PAGES) { - dataset->dealloc = &randomx::deallocDataset; - dataset->memory = (uint8_t*)randomx::LargePageAllocator::allocMemory(RANDOMX_DATASET_MAX_SIZE); - } - else { - dataset->dealloc = &randomx::deallocDataset; - dataset->memory = (uint8_t*)randomx::DefaultAllocator::allocMemory(RANDOMX_DATASET_MAX_SIZE); - } - } - catch (std::exception &ex) { - if (dataset != nullptr) { - randomx_release_dataset(dataset); - dataset = nullptr; - } - } + randomx_dataset *randomx_create_dataset(uint8_t *memory) { + auto dataset = new randomx_dataset(); + dataset->memory = memory; return dataset; } @@ -384,8 +343,6 @@ extern "C" { } void randomx_release_dataset(randomx_dataset *dataset) { - assert(dataset != nullptr); - dataset->dealloc(dataset); delete dataset; } diff --git a/src/crypto/randomx/randomx.h b/src/crypto/randomx/randomx.h index 84ae7dfc..92314229 100644 --- a/src/crypto/randomx/randomx.h +++ b/src/crypto/randomx/randomx.h @@ -215,7 +215,7 @@ extern "C" { * NULL is returned if memory allocation fails or if the RANDOMX_FLAG_JIT * is set and JIT compilation is not supported on the current platform. */ -RANDOMX_EXPORT randomx_cache *randomx_alloc_cache(randomx_flags flags); +RANDOMX_EXPORT randomx_cache *randomx_create_cache(randomx_flags flags, uint8_t *memory); /** * Initializes the cache memory and SuperscalarHash using the provided key value. @@ -242,7 +242,7 @@ RANDOMX_EXPORT void randomx_release_cache(randomx_cache* cache); * @return Pointer to an allocated randomx_dataset structure. * NULL is returned if memory allocation fails. */ -RANDOMX_EXPORT randomx_dataset *randomx_alloc_dataset(randomx_flags flags); +RANDOMX_EXPORT randomx_dataset *randomx_create_dataset(uint8_t *memory); /** * Gets the number of items contained in the dataset. diff --git a/src/crypto/randomx/virtual_memory.cpp b/src/crypto/randomx/virtual_memory.cpp index 48a8a8d2..06165ffb 100644 --- a/src/crypto/randomx/virtual_memory.cpp +++ b/src/crypto/randomx/virtual_memory.cpp @@ -53,16 +53,6 @@ void* allocLargePagesMemory(std::size_t bytes) { } -void* allocOneGbPagesMemory(std::size_t bytes) { - void* mem = xmrig::VirtualMemory::allocateOneGbPagesMemory(bytes); - if (mem == nullptr) { - throw std::runtime_error("Failed to allocate 1GB pages memory"); - } - - return mem; -} - - void freePagedMemory(void* ptr, std::size_t bytes) { xmrig::VirtualMemory::freeLargePagesMemory(ptr, bytes); } diff --git a/src/crypto/randomx/virtual_memory.hpp b/src/crypto/randomx/virtual_memory.hpp index 8c5b4900..d3b31db1 100644 --- a/src/crypto/randomx/virtual_memory.hpp +++ b/src/crypto/randomx/virtual_memory.hpp @@ -32,5 +32,4 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. void* allocExecutableMemory(std::size_t); void* allocLargePagesMemory(std::size_t); -void* allocOneGbPagesMemory(std::size_t); void freePagedMemory(void*, std::size_t); diff --git a/src/crypto/rx/Rx.cpp b/src/crypto/rx/Rx.cpp index 84c9b5fc..369b0aac 100644 --- a/src/crypto/rx/Rx.cpp +++ b/src/crypto/rx/Rx.cpp @@ -83,15 +83,15 @@ bool xmrig::Rx::isReady(const Job &job) } -xmrig::RxDataset *xmrig::Rx::dataset(const Job &job, uint32_t nodeId) +xmrig::HugePagesInfo xmrig::Rx::hugePages() { - return d_ptr->queue.dataset(job, nodeId); + return d_ptr->queue.hugePages(); } -std::pair xmrig::Rx::hugePages() +xmrig::RxDataset *xmrig::Rx::dataset(const Job &job, uint32_t nodeId) { - return d_ptr->queue.hugePages(); + return d_ptr->queue.dataset(job, nodeId); } diff --git a/src/crypto/rx/Rx.h b/src/crypto/rx/Rx.h index abaca920..d2630445 100644 --- a/src/crypto/rx/Rx.h +++ b/src/crypto/rx/Rx.h @@ -32,6 +32,9 @@ #include +#include "crypto/common/HugePagesInfo.h" + + namespace xmrig { @@ -49,8 +52,8 @@ class Rx public: static bool init(const Job &job, const RxConfig &config, const CpuConfig &cpu); static bool isReady(const Job &job); + static HugePagesInfo hugePages(); static RxDataset *dataset(const Job &job, uint32_t nodeId); - static std::pair hugePages(); static void destroy(); static void init(IRxListener *listener); }; diff --git a/src/crypto/rx/RxBasicStorage.cpp b/src/crypto/rx/RxBasicStorage.cpp index a0656e46..079bf95c 100644 --- a/src/crypto/rx/RxBasicStorage.cpp +++ b/src/crypto/rx/RxBasicStorage.cpp @@ -73,7 +73,7 @@ public: { const uint64_t ts = Chrono::steadyMSecs(); - m_dataset = new RxDataset(hugePages, oneGbPages, true, mode); + m_dataset = new RxDataset(hugePages, oneGbPages, true, mode, 0); printAllocStatus(ts); } @@ -94,18 +94,17 @@ private: void printAllocStatus(uint64_t ts) { if (m_dataset->get() != nullptr) { - const auto pages = m_dataset->hugePages(); - const double percent = pages.first == 0 ? 0.0 : static_cast(pages.first) / pages.second * 100.0; + const auto pages = m_dataset->hugePages(); LOG_INFO("%s" GREEN_BOLD("allocated") CYAN_BOLD(" %zu MB") BLACK_BOLD(" (%zu+%zu)") " huge pages %s%1.0f%% %u/%u" CLEAR " %sJIT" BLACK_BOLD(" (%" PRIu64 " ms)"), rx_tag(), - m_dataset->size() / oneMiB, + pages.size / oneMiB, RxDataset::maxSize() / oneMiB, RxCache::maxSize() / oneMiB, - (pages.first == pages.second ? GREEN_BOLD_S : (pages.first == 0 ? RED_BOLD_S : YELLOW_BOLD_S)), - percent, - pages.first, - pages.second, + (pages.isFullyAllocated() ? GREEN_BOLD_S : (pages.allocated == 0 ? RED_BOLD_S : YELLOW_BOLD_S)), + pages.percent(), + pages.allocated, + pages.total, m_dataset->cache()->isJIT() ? GREEN_BOLD_S "+" : RED_BOLD_S "-", Chrono::steadyMSecs() - ts ); @@ -137,6 +136,16 @@ xmrig::RxBasicStorage::~RxBasicStorage() } +xmrig::HugePagesInfo xmrig::RxBasicStorage::hugePages() const +{ + if (!d_ptr->dataset()) { + return {}; + } + + return d_ptr->dataset()->hugePages(); +} + + xmrig::RxDataset *xmrig::RxBasicStorage::dataset(const Job &job, uint32_t) const { if (!d_ptr->isReady(job)) { @@ -147,16 +156,6 @@ xmrig::RxDataset *xmrig::RxBasicStorage::dataset(const Job &job, uint32_t) const } -std::pair xmrig::RxBasicStorage::hugePages() const -{ - if (!d_ptr->dataset()) { - return { 0U, 0U }; - } - - return d_ptr->dataset()->hugePages(); -} - - void xmrig::RxBasicStorage::init(const RxSeed &seed, uint32_t threads, bool hugePages, bool oneGbPages, RxConfig::Mode mode, int priority) { d_ptr->setSeed(seed); diff --git a/src/crypto/rx/RxBasicStorage.h b/src/crypto/rx/RxBasicStorage.h index bd8a5e00..1f50af57 100644 --- a/src/crypto/rx/RxBasicStorage.h +++ b/src/crypto/rx/RxBasicStorage.h @@ -48,8 +48,8 @@ public: ~RxBasicStorage() override; protected: + HugePagesInfo hugePages() const override; RxDataset *dataset(const Job &job, uint32_t nodeId) const override; - std::pair hugePages() const override; void init(const RxSeed &seed, uint32_t threads, bool hugePages, bool oneGbPages, RxConfig::Mode mode, int priority) override; private: diff --git a/src/crypto/rx/RxCache.cpp b/src/crypto/rx/RxCache.cpp index a248ea5c..f58f3024 100644 --- a/src/crypto/rx/RxCache.cpp +++ b/src/crypto/rx/RxCache.cpp @@ -35,30 +35,25 @@ static_assert(RANDOMX_FLAG_LARGE_PAGES == 1, "RANDOMX_FLAG_LARGE_PAGES flag mism -xmrig::RxCache::RxCache(bool hugePages) +xmrig::RxCache::RxCache(bool hugePages, uint32_t nodeId) { - if (hugePages) { - m_flags = RANDOMX_FLAG_JIT | RANDOMX_FLAG_LARGE_PAGES; - m_cache = randomx_alloc_cache(static_cast(m_flags)); - } + m_memory = new VirtualMemory(maxSize(), hugePages, false, false, nodeId); - if (!m_cache) { - m_flags = RANDOMX_FLAG_JIT; - m_cache = randomx_alloc_cache(static_cast(m_flags)); - } + create(m_memory->raw()); +} - if (!m_cache) { - m_flags = RANDOMX_FLAG_DEFAULT; - m_cache = randomx_alloc_cache(static_cast(m_flags)); - } + +xmrig::RxCache::RxCache(uint8_t *memory) +{ + create(memory); } xmrig::RxCache::~RxCache() { - if (m_cache) { - randomx_release_cache(m_cache); - } + randomx_release_cache(m_cache); + + delete m_memory; } @@ -75,15 +70,18 @@ bool xmrig::RxCache::init(const Buffer &seed) } -std::pair xmrig::RxCache::hugePages() const +xmrig::HugePagesInfo xmrig::RxCache::hugePages() const { - constexpr size_t twoMiB = 2u * 1024u * 1024u; - constexpr size_t total = VirtualMemory::align(maxSize(), twoMiB) / twoMiB; + return m_memory ? m_memory->hugePages() : HugePagesInfo(); +} - uint32_t count = 0; - if (isHugePages()) { - count += total; + +void xmrig::RxCache::create(uint8_t *memory) +{ + m_cache = randomx_create_cache(RANDOMX_FLAG_JIT, memory); + + if (!m_cache) { + m_jit = false; + m_cache = randomx_create_cache(RANDOMX_FLAG_DEFAULT, memory); } - - return { count, total }; } diff --git a/src/crypto/rx/RxCache.h b/src/crypto/rx/RxCache.h index 84635292..d1822b30 100644 --- a/src/crypto/rx/RxCache.h +++ b/src/crypto/rx/RxCache.h @@ -33,6 +33,7 @@ #include "base/tools/Buffer.h" #include "base/tools/Object.h" +#include "crypto/common/HugePagesInfo.h" #include "crypto/randomx/configuration.h" @@ -48,24 +49,27 @@ class RxCache public: XMRIG_DISABLE_COPY_MOVE_DEFAULT(RxCache) - RxCache(bool hugePages = true); + RxCache(bool hugePages, uint32_t nodeId); + RxCache(uint8_t *memory); ~RxCache(); - inline bool isHugePages() const { return m_flags & 1; } - inline bool isJIT() const { return m_flags & 8; } + inline bool isJIT() const { return m_jit; } inline const Buffer &seed() const { return m_seed; } inline randomx_cache *get() const { return m_cache; } inline size_t size() const { return maxSize(); } bool init(const Buffer &seed); - std::pair hugePages() const; + HugePagesInfo hugePages() const; static inline constexpr size_t maxSize() { return RANDOMX_CACHE_MAX_SIZE; } private: + void create(uint8_t *memory); + + bool m_jit = true; Buffer m_seed; - int m_flags = 0; - randomx_cache *m_cache = nullptr; + randomx_cache *m_cache = nullptr; + VirtualMemory *m_memory = nullptr; }; diff --git a/src/crypto/rx/RxDataset.cpp b/src/crypto/rx/RxDataset.cpp index 5786926a..b2f934eb 100644 --- a/src/crypto/rx/RxDataset.cpp +++ b/src/crypto/rx/RxDataset.cpp @@ -38,9 +38,6 @@ #include -static_assert(RANDOMX_FLAG_LARGE_PAGES == 1, "RANDOMX_FLAG_LARGE_PAGES flag mismatch"); - - namespace xmrig { @@ -55,18 +52,26 @@ static void init_dataset_wrapper(randomx_dataset *dataset, randomx_cache *cache, } // namespace xmrig -xmrig::RxDataset::RxDataset(bool hugePages, bool oneGbPages, bool cache, RxConfig::Mode mode) : - m_mode(mode) +xmrig::RxDataset::RxDataset(bool hugePages, bool oneGbPages, bool cache, RxConfig::Mode mode, uint32_t node) : + m_mode(mode), + m_node(node) { allocate(hugePages, oneGbPages); + if (isOneGbPages()) { + m_cache = new RxCache(m_memory->raw() + VirtualMemory::align(maxSize())); + + return; + } + if (cache) { - m_cache = new RxCache(hugePages); + m_cache = new RxCache(hugePages, node); } } xmrig::RxDataset::RxDataset(RxCache *cache) : + m_node(0), m_cache(cache) { } @@ -74,11 +79,10 @@ xmrig::RxDataset::RxDataset(RxCache *cache) : xmrig::RxDataset::~RxDataset() { - if (m_dataset) { - randomx_release_dataset(m_dataset); - } + randomx_release_dataset(m_dataset); delete m_cache; + delete m_memory; } @@ -118,6 +122,30 @@ bool xmrig::RxDataset::init(const Buffer &seed, uint32_t numThreads, int priorit } +bool xmrig::RxDataset::isHugePages() const +{ + return m_memory && m_memory->isHugePages(); +} + + +bool xmrig::RxDataset::isOneGbPages() const +{ + return m_memory && m_memory->isOneGbPages(); +} + + +xmrig::HugePagesInfo xmrig::RxDataset::hugePages(bool cache) const +{ + auto pages = m_memory ? m_memory->hugePages() : HugePagesInfo(); + + if (cache && m_cache) { + pages += m_cache->hugePages(); + } + + return pages; +} + + size_t xmrig::RxDataset::size(bool cache) const { size_t size = 0; @@ -134,31 +162,6 @@ size_t xmrig::RxDataset::size(bool cache) const } -std::pair xmrig::RxDataset::hugePages(bool cache) const -{ - constexpr size_t twoMiB = 2U * 1024U * 1024U; - constexpr size_t oneGiB = 1024U * 1024U * 1024U; - constexpr size_t cacheSize = VirtualMemory::align(RxCache::maxSize(), twoMiB) / twoMiB; - size_t datasetPageSize = isOneGbPages() ? oneGiB : twoMiB; - size_t total = VirtualMemory::align(maxSize(), datasetPageSize) / datasetPageSize; - - uint32_t count = 0; - if (isHugePages() || isOneGbPages()) { - count += total; - } - - if (cache && m_cache) { - total += cacheSize; - - if (m_cache->isHugePages()) { - count += cacheSize; - } - } - - return { count, total }; -} - - void *xmrig::RxDataset::raw() const { return m_dataset ? randomx_get_dataset_memory(m_dataset) : nullptr; @@ -189,19 +192,12 @@ void xmrig::RxDataset::allocate(bool hugePages, bool oneGbPages) return; } - if (hugePages) { - m_flags = oneGbPages ? RANDOMX_FLAG_1GB_PAGES : RANDOMX_FLAG_LARGE_PAGES; - m_dataset = randomx_alloc_dataset(static_cast(m_flags)); + m_memory = new VirtualMemory(maxSize(), hugePages, oneGbPages, false, m_node); + m_dataset = randomx_create_dataset(m_memory->raw()); - if (oneGbPages && !m_dataset) { - LOG_ERR(CLEAR "%s" RED_BOLD_S "Failed to allocate RandomX dataset using 1GB pages", rx_tag()); - m_flags = RANDOMX_FLAG_LARGE_PAGES; - m_dataset = randomx_alloc_dataset(static_cast(m_flags)); - } - } - - if (!m_dataset) { - m_flags = RANDOMX_FLAG_DEFAULT; - m_dataset = randomx_alloc_dataset(static_cast(m_flags)); +# ifdef XMRIG_OS_LINUX + if (oneGbPages && !isOneGbPages()) { + LOG_ERR(CLEAR "%s" RED_BOLD_S "failed to allocate RandomX dataset using 1GB pages", rx_tag()); } +# endif } diff --git a/src/crypto/rx/RxDataset.h b/src/crypto/rx/RxDataset.h index ec9c7c8a..e46a93c6 100644 --- a/src/crypto/rx/RxDataset.h +++ b/src/crypto/rx/RxDataset.h @@ -30,6 +30,7 @@ #include "base/tools/Object.h" #include "crypto/common/Algorithm.h" +#include "crypto/common/HugePagesInfo.h" #include "crypto/randomx/configuration.h" #include "crypto/randomx/randomx.h" #include "crypto/rx/RxConfig.h" @@ -44,6 +45,7 @@ namespace xmrig class Buffer; class RxCache; +class VirtualMemory; class RxDataset @@ -51,19 +53,19 @@ class RxDataset public: XMRIG_DISABLE_COPY_MOVE_DEFAULT(RxDataset) - RxDataset(bool hugePages, bool oneGbPages, bool cache, RxConfig::Mode mode); + RxDataset(bool hugePages, bool oneGbPages, bool cache, RxConfig::Mode mode, uint32_t node); RxDataset(RxCache *cache); ~RxDataset(); - inline bool isHugePages() const { return m_flags & RANDOMX_FLAG_LARGE_PAGES; } - inline bool isOneGbPages() const { return m_flags & RANDOMX_FLAG_1GB_PAGES; } inline randomx_dataset *get() const { return m_dataset; } inline RxCache *cache() const { return m_cache; } inline void setCache(RxCache *cache) { m_cache = cache; } bool init(const Buffer &seed, uint32_t numThreads, int priority); + bool isHugePages() const; + bool isOneGbPages() const; + HugePagesInfo hugePages(bool cache = true) const; size_t size(bool cache = true) const; - std::pair hugePages(bool cache = true) const; void *raw() const; void setRaw(const void *raw); @@ -73,9 +75,10 @@ private: void allocate(bool hugePages, bool oneGbPages); const RxConfig::Mode m_mode = RxConfig::FastMode; - int m_flags = 0; + const uint32_t m_node; randomx_dataset *m_dataset = nullptr; RxCache *m_cache = nullptr; + VirtualMemory *m_memory = nullptr; }; diff --git a/src/crypto/rx/RxNUMAStorage.cpp b/src/crypto/rx/RxNUMAStorage.cpp index a6d19148..b139a14d 100644 --- a/src/crypto/rx/RxNUMAStorage.cpp +++ b/src/crypto/rx/RxNUMAStorage.cpp @@ -130,8 +130,10 @@ public: join(); - std::thread thread(allocateCache, this, m_nodeset.front(), hugePages); - thread.join(); + if (isCacheRequired()) { + std::thread thread(allocateCache, this, m_nodeset.front(), hugePages); + thread.join(); + } if (m_datasets.empty()) { m_datasets.insert({ m_nodeset.front(), new RxDataset(m_cache) }); @@ -139,7 +141,9 @@ public: LOG_WARN(CLEAR "%s" YELLOW_BOLD_S "failed to allocate RandomX datasets, switching to slow mode" BLACK_BOLD(" (%" PRIu64 " ms)"), rx_tag(), Chrono::steadyMSecs() - ts); } else { - dataset(m_nodeset.front())->setCache(m_cache); + if (m_cache) { + dataset(m_nodeset.front())->setCache(m_cache); + } printAllocStatus(ts); } @@ -148,6 +152,22 @@ public: } + inline bool isCacheRequired() const + { + if (m_datasets.empty()) { + return true; + } + + for (const auto kv : m_datasets) { + if (kv.second->isOneGbPages()) { + return false; + } + } + + return true; + } + + inline void initDatasets(uint32_t threads, int priority) { uint64_t ts = Chrono::steadyMSecs(); @@ -174,13 +194,11 @@ public: } - inline std::pair hugePages() const + inline HugePagesInfo hugePages() const { - auto pages = m_cache->hugePages(); + HugePagesInfo pages; for (auto const &item : m_datasets) { - const auto p = item.second->hugePages(false); - pages.first += p.first; - pages.second += p.second; + pages += item.second->hugePages(); } return pages; @@ -198,7 +216,7 @@ private: return; } - auto dataset = new RxDataset(hugePages, oneGbPages, false, RxConfig::FastMode); + auto dataset = new RxDataset(hugePages, oneGbPages, false, RxConfig::FastMode, nodeId); if (!dataset->get()) { printSkipped(nodeId, "failed to allocate dataset"); @@ -218,7 +236,7 @@ private: bindToNUMANode(nodeId); - auto cache = new RxCache(hugePages); + auto cache = new RxCache(hugePages, nodeId); std::lock_guard lock(mutex); d_ptr->m_cache = cache; @@ -238,15 +256,14 @@ private: void printAllocStatus(RxDataset *dataset, uint32_t nodeId, uint64_t ts) { - const auto pages = dataset->hugePages(); - const double percent = pages.first == 0 ? 0.0 : static_cast(pages.first) / pages.second * 100.0; + const auto pages = dataset->hugePages(); LOG_INFO("%s" CYAN_BOLD("#%u ") GREEN_BOLD("allocated") CYAN_BOLD(" %zu MB") " huge pages %s%3.0f%%" CLEAR BLACK_BOLD(" (%" PRIu64 " ms)"), rx_tag(), nodeId, - dataset->size() / oneMiB, - (pages.first == pages.second ? GREEN_BOLD_S : RED_BOLD_S), - percent, + pages.size / oneMiB, + (pages.isFullyAllocated() ? GREEN_BOLD_S : RED_BOLD_S), + pages.percent(), Chrono::steadyMSecs() - ts ); } @@ -254,15 +271,14 @@ private: void printAllocStatus(RxCache *cache, uint32_t nodeId, uint64_t ts) { - const auto pages = cache->hugePages(); - const double percent = pages.first == 0 ? 0.0 : static_cast(pages.first) / pages.second * 100.0; + const auto pages = cache->hugePages(); LOG_INFO("%s" CYAN_BOLD("#%u ") GREEN_BOLD("allocated") CYAN_BOLD(" %4zu MB") " huge pages %s%3.0f%%" CLEAR " %sJIT" BLACK_BOLD(" (%" PRIu64 " ms)"), rx_tag(), nodeId, cache->size() / oneMiB, - (pages.first == pages.second ? GREEN_BOLD_S : RED_BOLD_S), - percent, + (pages.isFullyAllocated() ? GREEN_BOLD_S : RED_BOLD_S), + pages.percent(), cache->isJIT() ? GREEN_BOLD_S "+" : RED_BOLD_S "-", Chrono::steadyMSecs() - ts ); @@ -271,21 +287,15 @@ private: void printAllocStatus(uint64_t ts) { - size_t memory = m_cache->size(); - auto pages = hugePages(); - const double percent = pages.first == 0 ? 0.0 : static_cast(pages.first) / pages.second * 100.0; - - for (auto const &item : m_datasets) { - memory += item.second->size(false); - } + auto pages = hugePages(); LOG_INFO("%s" CYAN_BOLD("-- ") GREEN_BOLD("allocated") CYAN_BOLD(" %4zu MB") " huge pages %s%3.0f%% %u/%u" CLEAR BLACK_BOLD(" (%" PRIu64 " ms)"), rx_tag(), - memory / oneMiB, - (pages.first == pages.second ? GREEN_BOLD_S : (pages.first == 0 ? RED_BOLD_S : YELLOW_BOLD_S)), - percent, - pages.first, - pages.second, + pages.size / oneMiB, + (pages.isFullyAllocated() ? GREEN_BOLD_S : (pages.allocated == 0 ? RED_BOLD_S : YELLOW_BOLD_S)), + pages.percent(), + pages.allocated, + pages.total, Chrono::steadyMSecs() - ts ); } @@ -326,6 +336,16 @@ xmrig::RxNUMAStorage::~RxNUMAStorage() } +xmrig::HugePagesInfo xmrig::RxNUMAStorage::hugePages() const +{ + if (!d_ptr->isAllocated()) { + return {}; + } + + return d_ptr->hugePages(); +} + + xmrig::RxDataset *xmrig::RxNUMAStorage::dataset(const Job &job, uint32_t nodeId) const { if (!d_ptr->isReady(job)) { @@ -336,16 +356,6 @@ xmrig::RxDataset *xmrig::RxNUMAStorage::dataset(const Job &job, uint32_t nodeId) } -std::pair xmrig::RxNUMAStorage::hugePages() const -{ - if (!d_ptr->isAllocated()) { - return { 0U, 0U }; - } - - return d_ptr->hugePages(); -} - - void xmrig::RxNUMAStorage::init(const RxSeed &seed, uint32_t threads, bool hugePages, bool oneGbPages, RxConfig::Mode, int priority) { d_ptr->setSeed(seed); diff --git a/src/crypto/rx/RxNUMAStorage.h b/src/crypto/rx/RxNUMAStorage.h index e7ea842f..80626c9a 100644 --- a/src/crypto/rx/RxNUMAStorage.h +++ b/src/crypto/rx/RxNUMAStorage.h @@ -51,8 +51,8 @@ public: ~RxNUMAStorage() override; protected: + HugePagesInfo hugePages() const override; RxDataset *dataset(const Job &job, uint32_t nodeId) const override; - std::pair hugePages() const override; void init(const RxSeed &seed, uint32_t threads, bool hugePages, bool oneGbPages, RxConfig::Mode mode, int priority) override; private: diff --git a/src/crypto/rx/RxQueue.cpp b/src/crypto/rx/RxQueue.cpp index 8ca4a379..b724e6af 100644 --- a/src/crypto/rx/RxQueue.cpp +++ b/src/crypto/rx/RxQueue.cpp @@ -86,11 +86,11 @@ xmrig::RxDataset *xmrig::RxQueue::dataset(const Job &job, uint32_t nodeId) } -std::pair xmrig::RxQueue::hugePages() +xmrig::HugePagesInfo xmrig::RxQueue::hugePages() { std::lock_guard lock(m_mutex); - return m_storage && m_state == STATE_IDLE ? m_storage->hugePages() : std::pair(0U, 0U); + return m_storage && m_state == STATE_IDLE ? m_storage->hugePages() : HugePagesInfo(); } diff --git a/src/crypto/rx/RxQueue.h b/src/crypto/rx/RxQueue.h index 8a362b8e..c83ae6d9 100644 --- a/src/crypto/rx/RxQueue.h +++ b/src/crypto/rx/RxQueue.h @@ -29,6 +29,7 @@ #include "base/tools/Object.h" +#include "crypto/common/HugePagesInfo.h" #include "crypto/rx/RxConfig.h" #include "crypto/rx/RxSeed.h" @@ -83,7 +84,7 @@ public: bool isReady(const Job &job); RxDataset *dataset(const Job &job, uint32_t nodeId); - std::pair hugePages(); + HugePagesInfo hugePages(); void enqueue(const RxSeed &seed, const std::vector &nodeset, uint32_t threads, bool hugePages, bool oneGbPages, RxConfig::Mode mode, int priority); private: diff --git a/src/net/JobResults.cpp b/src/net/JobResults.cpp index 26f16952..c9c699f9 100644 --- a/src/net/JobResults.cpp +++ b/src/net/JobResults.cpp @@ -105,7 +105,7 @@ static inline void checkHash(const JobBundle &bundle, std::vector &re static void getResults(JobBundle &bundle, std::vector &results, uint32_t &errors, bool hwAES) { const auto &algorithm = bundle.job.algorithm(); - auto memory = new VirtualMemory(algorithm.l3(), false, false); + auto memory = new VirtualMemory(algorithm.l3(), false, false, false); uint8_t hash[32]{ 0 }; if (algorithm.family() == Algorithm::RANDOM_X) { From d6582de09bec4f7f9add54526cacb0515a3b83df Mon Sep 17 00:00:00 2001 From: XMRig Date: Sun, 8 Dec 2019 23:23:03 +0700 Subject: [PATCH 20/31] v5.2.0-dev --- src/version.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/version.h b/src/version.h index 2cb9fef4..8b761e10 100644 --- a/src/version.h +++ b/src/version.h @@ -28,15 +28,15 @@ #define APP_ID "xmrig" #define APP_NAME "XMRig" #define APP_DESC "XMRig miner" -#define APP_VERSION "5.1.2-dev" +#define APP_VERSION "5.2.0-dev" #define APP_DOMAIN "xmrig.com" #define APP_SITE "www.xmrig.com" #define APP_COPYRIGHT "Copyright (C) 2016-2019 xmrig.com" #define APP_KIND "miner" #define APP_VER_MAJOR 5 -#define APP_VER_MINOR 1 -#define APP_VER_PATCH 2 +#define APP_VER_MINOR 2 +#define APP_VER_PATCH 0 #ifdef _MSC_VER # if (_MSC_VER >= 1920) From 558c524e2a65590659efd7ef7dedf1726fd393d7 Mon Sep 17 00:00:00 2001 From: XMRig Date: Mon, 9 Dec 2019 01:07:42 +0700 Subject: [PATCH 21/31] Added missing Cpu::release call. --- src/App.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/App.cpp b/src/App.cpp index 3acccc5d..17060f96 100644 --- a/src/App.cpp +++ b/src/App.cpp @@ -50,6 +50,8 @@ xmrig::App::App(Process *process) xmrig::App::~App() { + Cpu::release(); + delete m_signals; delete m_console; delete m_controller; From 3edaebb4cf1593b16562c630a76372db1bc8c2bd Mon Sep 17 00:00:00 2001 From: XMRig Date: Mon, 9 Dec 2019 21:42:40 +0700 Subject: [PATCH 22/31] Move "1gb-pages" option to "randomx" object. --- src/Summary.cpp | 15 +++++++++++++-- src/backend/cpu/CpuConfig.cpp | 3 --- src/backend/cpu/CpuConfig.h | 2 -- src/config.json | 2 +- src/crypto/rx/Rx.cpp | 2 +- src/crypto/rx/RxConfig.h | 10 ++++++---- src/crypto/rx/RxConfig_basic.cpp | 18 ++++++++++++------ src/crypto/rx/RxConfig_hwloc.cpp | 20 +++++++++++++------- 8 files changed, 46 insertions(+), 26 deletions(-) diff --git a/src/Summary.cpp b/src/Summary.cpp index 020eca61..695138d1 100644 --- a/src/Summary.cpp +++ b/src/Summary.cpp @@ -39,6 +39,11 @@ #include "version.h" +#ifdef XMRIG_ALGO_RANDOMX +# include "crypto/rx/RxConfig.h" +#endif + + namespace xmrig { @@ -64,11 +69,17 @@ static void print_memory(Config *config) # ifdef XMRIG_OS_WIN Log::print(GREEN_BOLD(" * ") WHITE_BOLD("%-13s") "%s", "HUGE PAGES", config->cpu().isHugePages() ? (VirtualMemory::isHugepagesAvailable() ? GREEN_BOLD("permission granted") : RED_BOLD("unavailable")) : RED_BOLD("disabled")); - Log::print(GREEN_BOLD(" * ") WHITE_BOLD("%-13s") "%s", "1GB PAGES", YELLOW_BOLD("unavailable")); # else Log::print(GREEN_BOLD(" * ") WHITE_BOLD("%-13s") "%s", "HUGE PAGES", config->cpu().isHugePages() ? GREEN_BOLD("supported") : RED_BOLD("disabled")); +# endif + +# ifdef XMRIG_ALGO_RANDOMX +# ifdef XMRIG_OS_LINUX Log::print(GREEN_BOLD(" * ") WHITE_BOLD("%-13s") "%s", - "1GB PAGES", (VirtualMemory::isOneGbPagesAvailable() ? (config->cpu().isOneGbPages() ? GREEN_BOLD("supported") : YELLOW_BOLD("disabled")) : YELLOW_BOLD("unavailable"))); + "1GB PAGES", (VirtualMemory::isOneGbPagesAvailable() ? (config->rx().isOneGbPages() ? GREEN_BOLD("supported") : YELLOW_BOLD("disabled")) : YELLOW_BOLD("unavailable"))); +# else + Log::print(GREEN_BOLD(" * ") WHITE_BOLD("%-13s") "%s", "1GB PAGES", YELLOW_BOLD("unavailable")); +# endif # endif } diff --git a/src/backend/cpu/CpuConfig.cpp b/src/backend/cpu/CpuConfig.cpp index fb95f942..a9e10338 100644 --- a/src/backend/cpu/CpuConfig.cpp +++ b/src/backend/cpu/CpuConfig.cpp @@ -34,7 +34,6 @@ namespace xmrig { static const char *kEnabled = "enabled"; static const char *kHugePages = "huge-pages"; -static const char *kOneGbPages = "1gb-pages"; static const char *kHwAes = "hw-aes"; static const char *kMaxThreadsHint = "max-threads-hint"; static const char *kMemoryPool = "memory-pool"; @@ -69,7 +68,6 @@ rapidjson::Value xmrig::CpuConfig::toJSON(rapidjson::Document &doc) const obj.AddMember(StringRef(kEnabled), m_enabled, allocator); obj.AddMember(StringRef(kHugePages), m_hugePages, allocator); - obj.AddMember(StringRef(kOneGbPages), m_oneGbPages, allocator); obj.AddMember(StringRef(kHwAes), m_aes == AES_AUTO ? Value(kNullType) : Value(m_aes == AES_HW), allocator); obj.AddMember(StringRef(kPriority), priority() != -1 ? Value(priority()) : Value(kNullType), allocator); obj.AddMember(StringRef(kMemoryPool), m_memoryPool < 1 ? Value(m_memoryPool < 0) : Value(m_memoryPool), allocator); @@ -123,7 +121,6 @@ void xmrig::CpuConfig::read(const rapidjson::Value &value) if (value.IsObject()) { m_enabled = Json::getBool(value, kEnabled, m_enabled); m_hugePages = Json::getBool(value, kHugePages, m_hugePages); - m_oneGbPages = Json::getBool(value, kOneGbPages, m_oneGbPages); m_limit = Json::getUint(value, kMaxThreadsHint, m_limit); m_yield = Json::getBool(value, kYield, m_yield); diff --git a/src/backend/cpu/CpuConfig.h b/src/backend/cpu/CpuConfig.h index 035a1673..a9c40806 100644 --- a/src/backend/cpu/CpuConfig.h +++ b/src/backend/cpu/CpuConfig.h @@ -54,7 +54,6 @@ public: inline bool isEnabled() const { return m_enabled; } inline bool isHugePages() const { return m_hugePages; } - inline bool isOneGbPages() const { return m_oneGbPages; } inline bool isShouldSave() const { return m_shouldSave; } inline bool isYield() const { return m_yield; } inline const Assembly &assembly() const { return m_assembly; } @@ -74,7 +73,6 @@ private: Assembly m_assembly; bool m_enabled = true; bool m_hugePages = true; - bool m_oneGbPages = false; bool m_shouldSave = false; bool m_yield = true; int m_memoryPool = 0; diff --git a/src/config.json b/src/config.json index dd64f407..ea2e4e15 100644 --- a/src/config.json +++ b/src/config.json @@ -17,12 +17,12 @@ "randomx": { "init": -1, "mode": "auto", + "1gb-pages": false, "numa": true }, "cpu": { "enabled": true, "huge-pages": true, - "1gb-pages": false, "hw-aes": null, "priority": null, "memory-pool": false, diff --git a/src/crypto/rx/Rx.cpp b/src/crypto/rx/Rx.cpp index 369b0aac..c311e533 100644 --- a/src/crypto/rx/Rx.cpp +++ b/src/crypto/rx/Rx.cpp @@ -71,7 +71,7 @@ bool xmrig::Rx::init(const Job &job, const RxConfig &config, const CpuConfig &cp return true; } - d_ptr->queue.enqueue(job, config.nodeset(), config.threads(cpu.limit()), cpu.isHugePages(), cpu.isOneGbPages(), config.mode(), cpu.priority()); + d_ptr->queue.enqueue(job, config.nodeset(), config.threads(cpu.limit()), cpu.isHugePages(), config.isOneGbPages(), config.mode(), cpu.priority()); return false; } diff --git a/src/crypto/rx/RxConfig.h b/src/crypto/rx/RxConfig.h index e3381fec..90beb4e8 100644 --- a/src/crypto/rx/RxConfig.h +++ b/src/crypto/rx/RxConfig.h @@ -57,14 +57,16 @@ public: const char *modeName() const; uint32_t threads(uint32_t limit = 100) const; - inline Mode mode() const { return m_mode; } + inline bool isOneGbPages() const { return m_oneGbPages; } + inline Mode mode() const { return m_mode; } private: Mode readMode(const rapidjson::Value &value) const; - bool m_numa = true; - int m_threads = -1; - Mode m_mode = AutoMode; + bool m_numa = true; + bool m_oneGbPages = false; + int m_threads = -1; + Mode m_mode = AutoMode; # ifdef XMRIG_FEATURE_HWLOC std::vector m_nodeset; diff --git a/src/crypto/rx/RxConfig_basic.cpp b/src/crypto/rx/RxConfig_basic.cpp index 1311b220..bf2a2c8f 100644 --- a/src/crypto/rx/RxConfig_basic.cpp +++ b/src/crypto/rx/RxConfig_basic.cpp @@ -30,8 +30,9 @@ namespace xmrig { -static const char *kInit = "init"; -static const char *kMode = "mode"; +static const char *kInit = "init"; +static const char *kMode = "mode"; +static const char *kOneGbPages = "1gb-pages"; } @@ -42,8 +43,9 @@ rapidjson::Value xmrig::RxConfig::toJSON(rapidjson::Document &doc) const auto &allocator = doc.GetAllocator(); Value obj(kObjectType); - obj.AddMember(StringRef(kInit), m_threads, allocator); - obj.AddMember(StringRef(kMode), StringRef(modeName()), allocator); + obj.AddMember(StringRef(kInit), m_threads, allocator); + obj.AddMember(StringRef(kMode), StringRef(modeName()), allocator); + obj.AddMember(StringRef(kOneGbPages), m_oneGbPages, allocator); return obj; } @@ -52,8 +54,12 @@ rapidjson::Value xmrig::RxConfig::toJSON(rapidjson::Document &doc) const bool xmrig::RxConfig::read(const rapidjson::Value &value) { if (value.IsObject()) { - m_threads = Json::getInt(value, kInit, m_threads); - m_mode = readMode(Json::getValue(value, kMode)); + m_threads = Json::getInt(value, kInit, m_threads); + m_mode = readMode(Json::getValue(value, kMode)); + +# ifdef XMRIG_OS_LINUX + m_oneGbPages = Json::getBool(value, kOneGbPages, m_oneGbPages); +# endif return true; } diff --git a/src/crypto/rx/RxConfig_hwloc.cpp b/src/crypto/rx/RxConfig_hwloc.cpp index 4fc57fb6..91104ef4 100644 --- a/src/crypto/rx/RxConfig_hwloc.cpp +++ b/src/crypto/rx/RxConfig_hwloc.cpp @@ -32,9 +32,10 @@ namespace xmrig { -static const char *kInit = "init"; -static const char *kMode = "mode"; -static const char *kNUMA = "numa"; +static const char *kInit = "init"; +static const char *kMode = "mode"; +static const char *kNUMA = "numa"; +static const char *kOneGbPages = "1gb-pages"; } @@ -46,8 +47,9 @@ rapidjson::Value xmrig::RxConfig::toJSON(rapidjson::Document &doc) const Value obj(kObjectType); - obj.AddMember(StringRef(kInit), m_threads, allocator); - obj.AddMember(StringRef(kMode), StringRef(modeName()), allocator); + obj.AddMember(StringRef(kInit), m_threads, allocator); + obj.AddMember(StringRef(kMode), StringRef(modeName()), allocator); + obj.AddMember(StringRef(kOneGbPages), m_oneGbPages, allocator); if (!m_nodeset.empty()) { Value numa(kArrayType); @@ -69,8 +71,12 @@ rapidjson::Value xmrig::RxConfig::toJSON(rapidjson::Document &doc) const bool xmrig::RxConfig::read(const rapidjson::Value &value) { if (value.IsObject()) { - m_threads = Json::getInt(value, kInit, m_threads); - m_mode = readMode(Json::getValue(value, kMode)); + m_threads = Json::getInt(value, kInit, m_threads); + m_mode = readMode(Json::getValue(value, kMode)); + +# ifdef XMRIG_OS_LINUX + m_oneGbPages = Json::getBool(value, kOneGbPages, m_oneGbPages); +# endif if (m_mode == LightMode) { m_numa = false; From 9bc13813ba2fb12436413fefa1fc1ba36c78cc19 Mon Sep 17 00:00:00 2001 From: SChernykh Date: Mon, 9 Dec 2019 18:59:49 +0100 Subject: [PATCH 23/31] Fixed assembly selection for RandomX when it's on Auto --- src/crypto/rx/RxVm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/crypto/rx/RxVm.cpp b/src/crypto/rx/RxVm.cpp index 8cdad371..486d83c2 100644 --- a/src/crypto/rx/RxVm.cpp +++ b/src/crypto/rx/RxVm.cpp @@ -45,7 +45,7 @@ xmrig::RxVm::RxVm(RxDataset *dataset, uint8_t *scratchpad, bool softAes, xmrig:: m_flags |= RANDOMX_FLAG_JIT; } - if (assembly == Assembly::RYZEN) { + if ((assembly == Assembly::RYZEN) || ((assembly == Assembly::AUTO) && (Cpu::info()->assembly() == Assembly::RYZEN))) { m_flags |= RANDOMX_FLAG_RYZEN; } From 763691fa4b7777a1798c9954fe0e3268f78e7d2d Mon Sep 17 00:00:00 2001 From: SChernykh Date: Mon, 9 Dec 2019 20:29:05 +0100 Subject: [PATCH 24/31] More optimizations for Ryzen --- .../randomx/asm/program_read_dataset_ryzen.inc | 15 +++++++-------- src/crypto/randomx/jit_compiler_x86.cpp | 10 +++++++++- src/crypto/randomx/jit_compiler_x86.hpp | 1 + src/crypto/randomx/jit_compiler_x86_static.asm | 1 + src/crypto/randomx/randomx.h | 4 ++-- 5 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/crypto/randomx/asm/program_read_dataset_ryzen.inc b/src/crypto/randomx/asm/program_read_dataset_ryzen.inc index 37e2104b..6bb87c8f 100644 --- a/src/crypto/randomx/asm/program_read_dataset_ryzen.inc +++ b/src/crypto/randomx/asm/program_read_dataset_ryzen.inc @@ -7,13 +7,12 @@ and edx, RANDOMX_DATASET_BASE_MASK prefetchnta byte ptr [rdi+rdx] ror rbp, 32 ;# swap "ma" and "mx" - add rcx, rdi ;# dataset cache line xor r8, rax - xor r9, qword ptr [rcx+8] - xor r10, qword ptr [rcx+16] - xor r11, qword ptr [rcx+24] - xor r12, qword ptr [rcx+32] - xor r13, qword ptr [rcx+40] - xor r14, qword ptr [rcx+48] - xor r15, qword ptr [rcx+56] + xor r9, qword ptr [rdi+rcx+8] + xor r10, qword ptr [rdi+rcx+16] + xor r11, qword ptr [rdi+rcx+24] + xor r12, qword ptr [rdi+rcx+32] + xor r13, qword ptr [rdi+rcx+40] + xor r14, qword ptr [rdi+rcx+48] + xor r15, qword ptr [rdi+rcx+56] \ No newline at end of file diff --git a/src/crypto/randomx/jit_compiler_x86.cpp b/src/crypto/randomx/jit_compiler_x86.cpp index 2528c2cf..84cfe39c 100644 --- a/src/crypto/randomx/jit_compiler_x86.cpp +++ b/src/crypto/randomx/jit_compiler_x86.cpp @@ -169,6 +169,7 @@ namespace randomx { static const uint8_t REX_DIVPD[] = { 0x66, 0x41, 0x0f, 0x5e }; static const uint8_t SQRTPD[] = { 0x66, 0x0f, 0x51 }; static const uint8_t AND_OR_MOV_LDMXCSR[] = { 0x25, 0x00, 0x60, 0x00, 0x00, 0x0D, 0xC0, 0x9F, 0x00, 0x00, 0x89, 0x44, 0x24, 0xFC, 0x0F, 0xAE, 0x54, 0x24, 0xFC }; + static const uint8_t AND_OR_MOV_LDMXCSR_RYZEN[] = { 0x25, 0x00, 0x60, 0x00, 0x00, 0x0D, 0xC0, 0x9F, 0x00, 0x00, 0x3B, 0x44, 0x24, 0xFC, 0x74, 0x09, 0x89, 0x44, 0x24, 0xFC, 0x0F, 0xAE, 0x54, 0x24, 0xFC }; static const uint8_t ROL_RAX[] = { 0x48, 0xc1, 0xc0 }; static const uint8_t XOR_ECX_ECX[] = { 0x33, 0xC9 }; static const uint8_t REX_CMP_R32I[] = { 0x41, 0x81 }; @@ -300,6 +301,8 @@ namespace randomx { } void JitCompilerX86::generateProgram(Program& prog, ProgramConfiguration& pcfg, uint32_t flags) { + vm_flags = flags; + generateProgramPrologue(prog, pcfg); uint8_t* p; @@ -1010,7 +1013,12 @@ namespace randomx { emit(ROL_RAX, p, pos); emitByte(rotate, p, pos); } - emit(AND_OR_MOV_LDMXCSR, p, pos); + if (vm_flags & RANDOMX_FLAG_RYZEN) { + emit(AND_OR_MOV_LDMXCSR_RYZEN, p, pos); + } + else { + emit(AND_OR_MOV_LDMXCSR, p, pos); + } codePos = pos; } diff --git a/src/crypto/randomx/jit_compiler_x86.hpp b/src/crypto/randomx/jit_compiler_x86.hpp index 0d515b0e..9354e5db 100644 --- a/src/crypto/randomx/jit_compiler_x86.hpp +++ b/src/crypto/randomx/jit_compiler_x86.hpp @@ -70,6 +70,7 @@ namespace randomx { uint8_t* allocatedCode; uint8_t* code; int32_t codePos; + uint32_t vm_flags; static bool BranchesWithin32B; diff --git a/src/crypto/randomx/jit_compiler_x86_static.asm b/src/crypto/randomx/jit_compiler_x86_static.asm index 7dd1232d..90395c52 100644 --- a/src/crypto/randomx/jit_compiler_x86_static.asm +++ b/src/crypto/randomx/jit_compiler_x86_static.asm @@ -81,6 +81,7 @@ randomx_program_prologue_first_load PROC and eax, RANDOMX_SCRATCHPAD_MASK ror rdx, 32 and edx, RANDOMX_SCRATCHPAD_MASK + stmxcsr dword ptr [rsp-20] jmp randomx_program_loop_begin randomx_program_prologue_first_load ENDP diff --git a/src/crypto/randomx/randomx.h b/src/crypto/randomx/randomx.h index 92314229..1ed5aa53 100644 --- a/src/crypto/randomx/randomx.h +++ b/src/crypto/randomx/randomx.h @@ -119,9 +119,9 @@ struct RandomX_ConfigurationBase rx_vec_i128 fillAes4Rx4_Key[8]; uint8_t codeShhPrefetchTweaked[20]; - uint8_t codeReadDatasetTweaked[72]; + uint8_t codeReadDatasetTweaked[256]; uint32_t codeReadDatasetTweakedSize; - uint8_t codeReadDatasetRyzenTweaked[72]; + uint8_t codeReadDatasetRyzenTweaked[256]; uint32_t codeReadDatasetRyzenTweakedSize; uint8_t codeReadDatasetLightSshInitTweaked[68]; uint8_t codePrefetchScratchpadTweaked[32]; From ef522f6404051e7f27456b21c49da835594a26d7 Mon Sep 17 00:00:00 2001 From: SChernykh Date: Mon, 9 Dec 2019 20:30:37 +0100 Subject: [PATCH 25/31] Update jit_compiler_x86_static.S --- src/crypto/randomx/jit_compiler_x86_static.S | 1 + 1 file changed, 1 insertion(+) diff --git a/src/crypto/randomx/jit_compiler_x86_static.S b/src/crypto/randomx/jit_compiler_x86_static.S index 8e1f9ef6..50019b7e 100644 --- a/src/crypto/randomx/jit_compiler_x86_static.S +++ b/src/crypto/randomx/jit_compiler_x86_static.S @@ -93,6 +93,7 @@ DECL(randomx_program_prologue_first_load): and eax, RANDOMX_SCRATCHPAD_MASK ror rdx, 32 and edx, RANDOMX_SCRATCHPAD_MASK + stmxcsr dword ptr [rsp-20] jmp DECL(randomx_program_loop_begin) .balign 64 From f3f75fb788d53854a094005738cecfbca45a9b89 Mon Sep 17 00:00:00 2001 From: xmrig Date: Tue, 10 Dec 2019 11:19:11 +0700 Subject: [PATCH 26/31] Update CHANGELOG.md --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f43e1197..e913a24e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +# v5.2.0 +- **[#1388](https://github.com/xmrig/xmrig/pull/1388) Added 1GB huge pages support for Linux.** + - Added new option `1gb-pages` in `randomx` object. + - Added automatic huge pages configuration on Linux if use the miner with root privileges. + - Added new [hugepages documentation](https://xmrig.com/docs/miner/hugepages) article. +- [#1396](https://github.com/xmrig/xmrig/pull/1396) [#1401](https://github.com/xmrig/xmrig/pull/1401) New performance optimizations for Ryzen CPUs. +- [#1385](https://github.com/xmrig/xmrig/issues/1385) Added `max-threads-hint` option support for RandomX dataset initialization threads. +- [#1386](https://github.com/xmrig/xmrig/issues/1386) Added `priority` option support for RandomX dataset initialization threads. +- For official builds all dependencies (libuv, hwloc, openssl) updated to recent versions. +- Windows `msvc` builds now use Visual Studio 2019 instead of 2017. + # v5.1.1 - [#1365](https://github.com/xmrig/xmrig/issues/1365) Fixed various system response/stability issues. - Added new CPU option `yield` and command line equivalent `--cpu-no-yield`. From 6163d27f145817c7215361125a088a943cb1b646 Mon Sep 17 00:00:00 2001 From: XMRig Date: Tue, 10 Dec 2019 11:56:31 +0700 Subject: [PATCH 27/31] Added command line option --randomx-1gb-pages --- README.md | 1 + src/base/kernel/interfaces/IConfig.h | 1 + src/core/config/ConfigTransform.cpp | 3 +++ src/core/config/Config_platform.h | 2 ++ src/core/config/usage.h | 1 + 5 files changed, 8 insertions(+) diff --git a/README.md b/README.md index 7563ff2d..ce223261 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,7 @@ CPU backend: --randomx-init=N threads count to initialize RandomX dataset --randomx-no-numa disable NUMA support for RandomX --randomx-mode=MODE RandomX mode: auto, fast, light + --randomx-1gb-pages use 1GB hugepages for dataset (Linux only) API: --api-worker-id=ID custom worker-id for API diff --git a/src/base/kernel/interfaces/IConfig.h b/src/base/kernel/interfaces/IConfig.h index dbbf82cd..94b5f90a 100644 --- a/src/base/kernel/interfaces/IConfig.h +++ b/src/base/kernel/interfaces/IConfig.h @@ -90,6 +90,7 @@ public: RandomXInitKey = 1022, RandomXNumaKey = 1023, RandomXModeKey = 1029, + RandomX1GbPagesKey = 1031, CPUMaxThreadsKey = 1026, MemoryPoolKey = 1027, YieldKey = 1030, diff --git a/src/core/config/ConfigTransform.cpp b/src/core/config/ConfigTransform.cpp index b9f66678..20a885ba 100644 --- a/src/core/config/ConfigTransform.cpp +++ b/src/core/config/ConfigTransform.cpp @@ -165,6 +165,9 @@ void xmrig::ConfigTransform::transform(rapidjson::Document &doc, int key, const case IConfig::RandomXModeKey: /* --randomx-mode */ return set(doc, kRandomX, "mode", arg); + + case IConfig::RandomX1GbPagesKey: /* --randomx-1gb-pages */ + return set(doc, kRandomX, "1gb-pages", true); # endif # ifdef XMRIG_FEATURE_OPENCL diff --git a/src/core/config/Config_platform.h b/src/core/config/Config_platform.h index a866c128..2a606501 100644 --- a/src/core/config/Config_platform.h +++ b/src/core/config/Config_platform.h @@ -98,6 +98,8 @@ static const option options[] = { { "randomx-init", 1, nullptr, IConfig::RandomXInitKey }, { "randomx-no-numa", 0, nullptr, IConfig::RandomXNumaKey }, { "randomx-mode", 1, nullptr, IConfig::RandomXModeKey }, + { "randomx-1gb-pages", 0, nullptr, IConfig::RandomX1GbPagesKey }, + { "1gb-pages", 0, nullptr, IConfig::RandomX1GbPagesKey }, # endif # ifdef XMRIG_FEATURE_OPENCL { "opencl", 0, nullptr, IConfig::OclKey }, diff --git a/src/core/config/usage.h b/src/core/config/usage.h index 38a48701..03526148 100644 --- a/src/core/config/usage.h +++ b/src/core/config/usage.h @@ -88,6 +88,7 @@ static inline const std::string &usage() u += " --randomx-init=N threads count to initialize RandomX dataset\n"; u += " --randomx-no-numa disable NUMA support for RandomX\n"; u += " --randomx-mode=MODE RandomX mode: auto, fast, light\n"; + u += " --randomx-1gb-pages use 1GB hugepages for dataset (Linux only)\n"; # endif # ifdef XMRIG_FEATURE_HTTP From 3b4b230cabc0c7df79be0fe9fb9ada3cf645f196 Mon Sep 17 00:00:00 2001 From: XMRig Date: Tue, 10 Dec 2019 12:49:42 +0700 Subject: [PATCH 28/31] Added CPU vendor enum. --- src/backend/cpu/interfaces/ICpuInfo.h | 7 +++++++ src/backend/cpu/platform/AdvancedCpuInfo.cpp | 15 +++++++++++---- src/backend/cpu/platform/AdvancedCpuInfo.h | 2 ++ src/backend/cpu/platform/BasicCpuInfo.cpp | 7 ++++--- src/backend/cpu/platform/BasicCpuInfo.h | 12 +++++++----- src/backend/cpu/platform/BasicCpuInfo_arm.cpp | 8 ++------ 6 files changed, 33 insertions(+), 18 deletions(-) diff --git a/src/backend/cpu/interfaces/ICpuInfo.h b/src/backend/cpu/interfaces/ICpuInfo.h index 2ffd00f2..674668b5 100644 --- a/src/backend/cpu/interfaces/ICpuInfo.h +++ b/src/backend/cpu/interfaces/ICpuInfo.h @@ -37,6 +37,12 @@ namespace xmrig { class ICpuInfo { public: + enum Vendor { + VENDOR_UNKNOWN, + VENDOR_INTEL, + VENDOR_AMD + }; + virtual ~ICpuInfo() = default; # if defined(__x86_64__) || defined(_M_AMD64) || defined (__arm64__) || defined (__aarch64__) @@ -58,6 +64,7 @@ public: virtual size_t nodes() const = 0; virtual size_t packages() const = 0; virtual size_t threads() const = 0; + virtual Vendor vendor() const = 0; }; diff --git a/src/backend/cpu/platform/AdvancedCpuInfo.cpp b/src/backend/cpu/platform/AdvancedCpuInfo.cpp index 37a958db..4a3c6f62 100644 --- a/src/backend/cpu/platform/AdvancedCpuInfo.cpp +++ b/src/backend/cpu/platform/AdvancedCpuInfo.cpp @@ -103,6 +103,13 @@ xmrig::AdvancedCpuInfo::AdvancedCpuInfo() : cpu_brand_string(m_brand, data.brand_str); snprintf(m_backend, sizeof m_backend, "libcpuid/%s", cpuid_lib_version()); + if (data.vendor == ::VENDOR_INTEL) { + m_vendor = VENDOR_INTEL; + } + else if (data.vendor == ::VENDOR_AMD) { + m_vendor = VENDOR_AMD; + } + m_threads = static_cast(data.total_logical_cpus); m_packages = std::max(threads() / static_cast(data.num_logical_cpus), 1); m_cores = static_cast(data.num_cores) * m_packages; @@ -111,13 +118,13 @@ xmrig::AdvancedCpuInfo::AdvancedCpuInfo() : const auto l2 = static_cast(data.l2_cache); // Workaround for AMD CPUs https://github.com/anrieff/libcpuid/issues/97 - if (data.vendor == VENDOR_AMD && data.ext_family >= 0x15 && data.ext_family < 0x17) { + if (m_vendor == VENDOR_AMD && data.ext_family >= 0x15 && data.ext_family < 0x17) { m_L2 = l2 * (cores() / 2) * m_packages; m_L2_exclusive = true; } // Workaround for Intel Pentium Dual-Core, Core Duo, Core 2 Duo, Core 2 Quad and their Xeon homologue // These processors have L2 cache shared by 2 cores. - else if (data.vendor == VENDOR_INTEL && data.ext_family == 0x06 && (data.ext_model == 0x0E || data.ext_model == 0x0F || data.ext_model == 0x17)) { + else if (m_vendor == VENDOR_INTEL && data.ext_family == 0x06 && (data.ext_model == 0x0E || data.ext_model == 0x0F || data.ext_model == 0x17)) { size_t l2_count_per_socket = cores() > 1 ? cores() / 2 : 1; m_L2 = data.l2_cache > 0 ? l2 * l2_count_per_socket * m_packages : 0; } @@ -131,10 +138,10 @@ xmrig::AdvancedCpuInfo::AdvancedCpuInfo() : if (data.flags[CPU_FEATURE_AES]) { m_aes = true; - if (data.vendor == VENDOR_AMD) { + if (m_vendor == VENDOR_AMD) { m_assembly = (data.ext_family >= 23) ? Assembly::RYZEN : Assembly::BULLDOZER; } - else if (data.vendor == VENDOR_INTEL) { + else if (m_vendor == VENDOR_INTEL) { m_assembly = Assembly::INTEL; } } diff --git a/src/backend/cpu/platform/AdvancedCpuInfo.h b/src/backend/cpu/platform/AdvancedCpuInfo.h index f590ccfb..f6691b8f 100644 --- a/src/backend/cpu/platform/AdvancedCpuInfo.h +++ b/src/backend/cpu/platform/AdvancedCpuInfo.h @@ -52,6 +52,7 @@ protected: inline size_t nodes() const override { return 0; } inline size_t packages() const override { return m_packages; } inline size_t threads() const override { return m_threads; } + inline Vendor vendor() const override { return m_vendor; } private: Assembly m_assembly; @@ -66,6 +67,7 @@ private: size_t m_L3 = 0; size_t m_packages = 1; size_t m_threads = 0; + Vendor m_vendor = VENDOR_UNKNOWN; }; diff --git a/src/backend/cpu/platform/BasicCpuInfo.cpp b/src/backend/cpu/platform/BasicCpuInfo.cpp index e2ad19ea..b586fad0 100644 --- a/src/backend/cpu/platform/BasicCpuInfo.cpp +++ b/src/backend/cpu/platform/BasicCpuInfo.cpp @@ -151,9 +151,7 @@ static inline bool has_pdpe1gb() xmrig::BasicCpuInfo::BasicCpuInfo() : - m_brand(), m_threads(std::thread::hardware_concurrency()), - m_assembly(Assembly::NONE), m_aes(has_aes_ni()), m_avx2(has_avx2()), m_pdpe1gb(has_pdpe1gb()) @@ -172,12 +170,15 @@ xmrig::BasicCpuInfo::BasicCpuInfo() : memcpy(vendor + 8, &data[2], 4); if (memcmp(vendor, "AuthenticAMD", 12) == 0) { + m_vendor = VENDOR_AMD; + cpuid(PROCESSOR_INFO, data); const int32_t family = get_masked(data[EAX_Reg], 12, 8) + get_masked(data[EAX_Reg], 28, 20); m_assembly = family >= 23 ? Assembly::RYZEN : Assembly::BULLDOZER; } - else { + else if (memcmp(vendor, "GenuineIntel", 12) == 0) { + m_vendor = VENDOR_INTEL; m_assembly = Assembly::INTEL; } } diff --git a/src/backend/cpu/platform/BasicCpuInfo.h b/src/backend/cpu/platform/BasicCpuInfo.h index ecbd3e23..019c1dc0 100644 --- a/src/backend/cpu/platform/BasicCpuInfo.h +++ b/src/backend/cpu/platform/BasicCpuInfo.h @@ -52,16 +52,18 @@ protected: inline size_t nodes() const override { return 0; } inline size_t packages() const override { return 1; } inline size_t threads() const override { return m_threads; } + inline Vendor vendor() const override { return m_vendor; } protected: - char m_brand[64 + 6]; + char m_brand[64 + 6]{}; size_t m_threads; private: - Assembly m_assembly; - bool m_aes; - const bool m_avx2; - const bool m_pdpe1gb; + Assembly m_assembly = Assembly::NONE; + bool m_aes = false; + const bool m_avx2 = false; + const bool m_pdpe1gb = false; + Vendor m_vendor = VENDOR_UNKNOWN; }; diff --git a/src/backend/cpu/platform/BasicCpuInfo_arm.cpp b/src/backend/cpu/platform/BasicCpuInfo_arm.cpp index 5dd54fe9..00f5f01f 100644 --- a/src/backend/cpu/platform/BasicCpuInfo_arm.cpp +++ b/src/backend/cpu/platform/BasicCpuInfo_arm.cpp @@ -22,7 +22,7 @@ * along with this program. If not, see . */ -#include +#include #include @@ -36,11 +36,7 @@ xmrig::BasicCpuInfo::BasicCpuInfo() : - m_brand(), - m_threads(std::thread::hardware_concurrency()), - m_aes(false), - m_avx2(false), - m_pdpe1gb(false) + m_threads(std::thread::hardware_concurrency()) { # ifdef XMRIG_ARMv8 memcpy(m_brand, "ARMv8", 5); From 96cfdda9a1cd355aa8db19350ae05a6ab8302f7b Mon Sep 17 00:00:00 2001 From: XMRig Date: Tue, 10 Dec 2019 23:57:29 +0700 Subject: [PATCH 29/31] Added RandomX option "wrmsr" with command line equivalent --randomx-wrmsr=N. --- README.md | 1 + cmake/randomx.cmake | 9 +-- src/base/kernel/interfaces/IConfig.h | 1 + src/config.json | 2 +- src/core/config/ConfigTransform.cpp | 3 + src/core/config/Config_platform.h | 1 + src/core/config/usage.h | 1 + src/crypto/rx/Rx.cpp | 17 +++- src/crypto/rx/Rx.h | 3 + src/crypto/rx/RxConfig.cpp | 111 +++++++++++++++++++++++++- src/crypto/rx/RxConfig.h | 3 + src/crypto/rx/RxConfig_basic.cpp | 68 ---------------- src/crypto/rx/RxConfig_hwloc.cpp | 115 --------------------------- src/crypto/rx/Rx_linux.cpp | 114 ++++++++++++++++++++++++++ 14 files changed, 257 insertions(+), 192 deletions(-) delete mode 100644 src/crypto/rx/RxConfig_basic.cpp delete mode 100644 src/crypto/rx/RxConfig_hwloc.cpp create mode 100644 src/crypto/rx/Rx_linux.cpp diff --git a/README.md b/README.md index ce223261..fdbf0f59 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,7 @@ CPU backend: --randomx-no-numa disable NUMA support for RandomX --randomx-mode=MODE RandomX mode: auto, fast, light --randomx-1gb-pages use 1GB hugepages for dataset (Linux only) + --randomx-wrmsr=N write value (0-15) to Intel MSR register 0x1a4 or do nothing (-1) (Linux only) API: --api-worker-id=ID custom worker-id for API diff --git a/cmake/randomx.cmake b/cmake/randomx.cmake index 290b8391..5a225c00 100644 --- a/cmake/randomx.cmake +++ b/cmake/randomx.cmake @@ -75,13 +75,12 @@ if (WITH_RANDOMX) ) list(APPEND SOURCES_CRYPTO - src/crypto/rx/RxConfig_hwloc.cpp src/crypto/rx/RxNUMAStorage.cpp ) - else() - list(APPEND SOURCES_CRYPTO - src/crypto/rx/RxConfig_basic.cpp - ) + endif() + + if (XMRIG_OS_LINUX) + list(APPEND SOURCES_CRYPTO src/crypto/rx/Rx_linux.cpp) endif() else() remove_definitions(/DXMRIG_ALGO_RANDOMX) diff --git a/src/base/kernel/interfaces/IConfig.h b/src/base/kernel/interfaces/IConfig.h index 94b5f90a..fe1a24b5 100644 --- a/src/base/kernel/interfaces/IConfig.h +++ b/src/base/kernel/interfaces/IConfig.h @@ -91,6 +91,7 @@ public: RandomXNumaKey = 1023, RandomXModeKey = 1029, RandomX1GbPagesKey = 1031, + RandomXWrmsrKey = 1032, CPUMaxThreadsKey = 1026, MemoryPoolKey = 1027, YieldKey = 1030, diff --git a/src/config.json b/src/config.json index ea2e4e15..52afb4cb 100644 --- a/src/config.json +++ b/src/config.json @@ -11,13 +11,13 @@ "restricted": true }, "autosave": true, - "version": 1, "background": false, "colors": true, "randomx": { "init": -1, "mode": "auto", "1gb-pages": false, + "wrmsr": 6, "numa": true }, "cpu": { diff --git a/src/core/config/ConfigTransform.cpp b/src/core/config/ConfigTransform.cpp index 20a885ba..bc1ce7fc 100644 --- a/src/core/config/ConfigTransform.cpp +++ b/src/core/config/ConfigTransform.cpp @@ -168,6 +168,9 @@ void xmrig::ConfigTransform::transform(rapidjson::Document &doc, int key, const case IConfig::RandomX1GbPagesKey: /* --randomx-1gb-pages */ return set(doc, kRandomX, "1gb-pages", true); + + case IConfig::RandomXWrmsrKey: /* --randomx-wrmsr */ + return set(doc, kRandomX, "wrmsr", static_cast(strtol(arg, nullptr, 10))); # endif # ifdef XMRIG_FEATURE_OPENCL diff --git a/src/core/config/Config_platform.h b/src/core/config/Config_platform.h index 2a606501..9b360b23 100644 --- a/src/core/config/Config_platform.h +++ b/src/core/config/Config_platform.h @@ -100,6 +100,7 @@ static const option options[] = { { "randomx-mode", 1, nullptr, IConfig::RandomXModeKey }, { "randomx-1gb-pages", 0, nullptr, IConfig::RandomX1GbPagesKey }, { "1gb-pages", 0, nullptr, IConfig::RandomX1GbPagesKey }, + { "randomx-wrmsr", 1, nullptr, IConfig::RandomXWrmsrKey }, # endif # ifdef XMRIG_FEATURE_OPENCL { "opencl", 0, nullptr, IConfig::OclKey }, diff --git a/src/core/config/usage.h b/src/core/config/usage.h index 03526148..27edee3d 100644 --- a/src/core/config/usage.h +++ b/src/core/config/usage.h @@ -89,6 +89,7 @@ static inline const std::string &usage() u += " --randomx-no-numa disable NUMA support for RandomX\n"; u += " --randomx-mode=MODE RandomX mode: auto, fast, light\n"; u += " --randomx-1gb-pages use 1GB hugepages for dataset (Linux only)\n"; + u += " --randomx-wrmsr=N write value (0-15) to Intel MSR register 0x1a4 or do nothing (-1) (Linux only)\n"; # endif # ifdef XMRIG_FEATURE_HTTP diff --git a/src/crypto/rx/Rx.cpp b/src/crypto/rx/Rx.cpp index c311e533..1de648c2 100644 --- a/src/crypto/rx/Rx.cpp +++ b/src/crypto/rx/Rx.cpp @@ -39,8 +39,9 @@ namespace xmrig { class RxPrivate; -static const char *tag = BLUE_BG(WHITE_BOLD_S " rx ") " "; -static RxPrivate *d_ptr = nullptr; +static bool osInitialized = false; +static const char *tag = BLUE_BG(WHITE_BOLD_S " rx ") " "; +static RxPrivate *d_ptr = nullptr; class RxPrivate @@ -71,6 +72,11 @@ bool xmrig::Rx::init(const Job &job, const RxConfig &config, const CpuConfig &cp return true; } + if (!osInitialized) { + osInit(config); + osInitialized = true; + } + d_ptr->queue.enqueue(job, config.nodeset(), config.threads(cpu.limit()), cpu.isHugePages(), config.isOneGbPages(), config.mode(), cpu.priority()); return false; @@ -107,3 +113,10 @@ void xmrig::Rx::init(IRxListener *listener) { d_ptr = new RxPrivate(listener); } + + +#ifndef XMRIG_OS_LINUX +void xmrig::Rx::osInit(const RxConfig &) +{ +} +#endif diff --git a/src/crypto/rx/Rx.h b/src/crypto/rx/Rx.h index d2630445..1a289b05 100644 --- a/src/crypto/rx/Rx.h +++ b/src/crypto/rx/Rx.h @@ -56,6 +56,9 @@ public: static RxDataset *dataset(const Job &job, uint32_t nodeId); static void destroy(); static void init(IRxListener *listener); + +private: + static void osInit(const RxConfig &config); }; diff --git a/src/crypto/rx/RxConfig.cpp b/src/crypto/rx/RxConfig.cpp index 7ae7d35d..cd8601a7 100644 --- a/src/crypto/rx/RxConfig.cpp +++ b/src/crypto/rx/RxConfig.cpp @@ -25,9 +25,15 @@ #include "crypto/rx/RxConfig.h" #include "backend/cpu/Cpu.h" +#include "base/io/json/Json.h" #include "rapidjson/document.h" +#ifdef XMRIG_FEATURE_HWLOC +# include "backend/cpu/platform/HwlocCpuInfo.h" +#endif + + #include #include #include @@ -40,11 +46,100 @@ namespace xmrig { +static const char *kInit = "init"; +static const char *kMode = "mode"; +static const char *kOneGbPages = "1gb-pages"; +static const char *kWrmsr = "wrmsr"; + +#ifdef XMRIG_FEATURE_HWLOC +static const char *kNUMA = "numa"; +#endif static const std::array modeNames = { "auto", "fast", "light" }; +} -} // namespace xmrig + +bool xmrig::RxConfig::read(const rapidjson::Value &value) +{ + if (value.IsObject()) { + m_threads = Json::getInt(value, kInit, m_threads); + m_mode = readMode(Json::getValue(value, kMode)); + m_wrmsr = readMSR(Json::getValue(value, kWrmsr)); + +# ifdef XMRIG_OS_LINUX + m_oneGbPages = Json::getBool(value, kOneGbPages, m_oneGbPages); +# endif + +# ifdef XMRIG_FEATURE_HWLOC + if (m_mode == LightMode) { + m_numa = false; + + return true; + } + + const auto &numa = Json::getValue(value, kNUMA); + if (numa.IsArray()) { + m_nodeset.reserve(numa.Size()); + + for (const auto &node : numa.GetArray()) { + if (node.IsUint()) { + m_nodeset.emplace_back(node.GetUint()); + } + } + } + else if (numa.IsBool()) { + m_numa = numa.GetBool(); + } +# endif + + return true; + } + + return false; +} + + +rapidjson::Value xmrig::RxConfig::toJSON(rapidjson::Document &doc) const +{ + using namespace rapidjson; + auto &allocator = doc.GetAllocator(); + + Value obj(kObjectType); + obj.AddMember(StringRef(kInit), m_threads, allocator); + obj.AddMember(StringRef(kMode), StringRef(modeName()), allocator); + obj.AddMember(StringRef(kOneGbPages), m_oneGbPages, allocator); + obj.AddMember(StringRef(kWrmsr), m_wrmsr < 0 ? Value(kFalseType) : Value(m_wrmsr), allocator); + +# ifdef XMRIG_FEATURE_HWLOC + if (!m_nodeset.empty()) { + Value numa(kArrayType); + + for (uint32_t i : m_nodeset) { + numa.PushBack(i, allocator); + } + + obj.AddMember(StringRef(kNUMA), numa, allocator); + } + else { + obj.AddMember(StringRef(kNUMA), m_numa, allocator); + } +# endif + + return obj; +} + + +#ifdef XMRIG_FEATURE_HWLOC +std::vector xmrig::RxConfig::nodeset() const +{ + if (!m_nodeset.empty()) { + return m_nodeset; + } + + return (m_numa && Cpu::info()->nodes() > 1) ? static_cast(Cpu::info())->nodeset() : std::vector(); +} +#endif const char *xmrig::RxConfig::modeName() const @@ -67,6 +162,20 @@ uint32_t xmrig::RxConfig::threads(uint32_t limit) const } +int xmrig::RxConfig::readMSR(const rapidjson::Value &value) const +{ + if (value.IsInt()) { + return std::min(value.GetInt(), 15); + } + + if (value.IsBool() && !value.GetBool()) { + return -1; + } + + return m_wrmsr; +} + + xmrig::RxConfig::Mode xmrig::RxConfig::readMode(const rapidjson::Value &value) const { if (value.IsUint()) { diff --git a/src/crypto/rx/RxConfig.h b/src/crypto/rx/RxConfig.h index 90beb4e8..bf5992a3 100644 --- a/src/crypto/rx/RxConfig.h +++ b/src/crypto/rx/RxConfig.h @@ -58,14 +58,17 @@ public: uint32_t threads(uint32_t limit = 100) const; inline bool isOneGbPages() const { return m_oneGbPages; } + inline int wrmsr() const { return m_wrmsr; } inline Mode mode() const { return m_mode; } private: + int readMSR(const rapidjson::Value &value) const; Mode readMode(const rapidjson::Value &value) const; bool m_numa = true; bool m_oneGbPages = false; int m_threads = -1; + int m_wrmsr = 6; Mode m_mode = AutoMode; # ifdef XMRIG_FEATURE_HWLOC diff --git a/src/crypto/rx/RxConfig_basic.cpp b/src/crypto/rx/RxConfig_basic.cpp deleted file mode 100644 index bf2a2c8f..00000000 --- a/src/crypto/rx/RxConfig_basic.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2017-2018 XMR-Stak , - * Copyright 2018-2019 SChernykh - * Copyright 2016-2019 XMRig , - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - - -#include "crypto/rx/RxConfig.h" -#include "base/io/json/Json.h" -#include "rapidjson/document.h" - - -namespace xmrig { - -static const char *kInit = "init"; -static const char *kMode = "mode"; -static const char *kOneGbPages = "1gb-pages"; - -} - - -rapidjson::Value xmrig::RxConfig::toJSON(rapidjson::Document &doc) const -{ - using namespace rapidjson; - auto &allocator = doc.GetAllocator(); - - Value obj(kObjectType); - obj.AddMember(StringRef(kInit), m_threads, allocator); - obj.AddMember(StringRef(kMode), StringRef(modeName()), allocator); - obj.AddMember(StringRef(kOneGbPages), m_oneGbPages, allocator); - - return obj; -} - - -bool xmrig::RxConfig::read(const rapidjson::Value &value) -{ - if (value.IsObject()) { - m_threads = Json::getInt(value, kInit, m_threads); - m_mode = readMode(Json::getValue(value, kMode)); - -# ifdef XMRIG_OS_LINUX - m_oneGbPages = Json::getBool(value, kOneGbPages, m_oneGbPages); -# endif - - return true; - } - - return false; -} diff --git a/src/crypto/rx/RxConfig_hwloc.cpp b/src/crypto/rx/RxConfig_hwloc.cpp deleted file mode 100644 index 91104ef4..00000000 --- a/src/crypto/rx/RxConfig_hwloc.cpp +++ /dev/null @@ -1,115 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2017-2018 XMR-Stak , - * Copyright 2018-2019 SChernykh - * Copyright 2016-2019 XMRig , - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - - -#include "backend/cpu/Cpu.h" -#include "backend/cpu/platform/HwlocCpuInfo.h" -#include "base/io/json/Json.h" -#include "crypto/rx/RxConfig.h" -#include "rapidjson/document.h" - - -namespace xmrig { - -static const char *kInit = "init"; -static const char *kMode = "mode"; -static const char *kNUMA = "numa"; -static const char *kOneGbPages = "1gb-pages"; - -} - - -rapidjson::Value xmrig::RxConfig::toJSON(rapidjson::Document &doc) const -{ - using namespace rapidjson; - auto &allocator = doc.GetAllocator(); - - Value obj(kObjectType); - - obj.AddMember(StringRef(kInit), m_threads, allocator); - obj.AddMember(StringRef(kMode), StringRef(modeName()), allocator); - obj.AddMember(StringRef(kOneGbPages), m_oneGbPages, allocator); - - if (!m_nodeset.empty()) { - Value numa(kArrayType); - - for (uint32_t i : m_nodeset) { - numa.PushBack(i, allocator); - } - - obj.AddMember(StringRef(kNUMA), numa, allocator); - } - else { - obj.AddMember(StringRef(kNUMA), m_numa, allocator); - } - - return obj; -} - - -bool xmrig::RxConfig::read(const rapidjson::Value &value) -{ - if (value.IsObject()) { - m_threads = Json::getInt(value, kInit, m_threads); - m_mode = readMode(Json::getValue(value, kMode)); - -# ifdef XMRIG_OS_LINUX - m_oneGbPages = Json::getBool(value, kOneGbPages, m_oneGbPages); -# endif - - if (m_mode == LightMode) { - m_numa = false; - - return true; - } - - const auto &numa = Json::getValue(value, kNUMA); - if (numa.IsArray()) { - m_nodeset.reserve(numa.Size()); - - for (const auto &node : numa.GetArray()) { - if (node.IsUint()) { - m_nodeset.emplace_back(node.GetUint()); - } - } - } - else if (numa.IsBool()) { - m_numa = numa.GetBool(); - } - - return true; - } - - return false; -} - - -std::vector xmrig::RxConfig::nodeset() const -{ - if (!m_nodeset.empty()) { - return m_nodeset; - } - - return (m_numa && Cpu::info()->nodes() > 1) ? static_cast(Cpu::info())->nodeset() : std::vector(); -} diff --git a/src/crypto/rx/Rx_linux.cpp b/src/crypto/rx/Rx_linux.cpp new file mode 100644 index 00000000..a513d38a --- /dev/null +++ b/src/crypto/rx/Rx_linux.cpp @@ -0,0 +1,114 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 tevador + * Copyright 2018-2019 SChernykh + * Copyright 2000 Transmeta Corporation + * Copyright 2004-2008 H. Peter Anvin + * Copyright 2016-2019 XMRig , + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include "crypto/rx/Rx.h" +#include "backend/common/Tags.h" +#include "backend/cpu/Cpu.h" +#include "base/io/log/Log.h" +#include "crypto/rx/RxConfig.h" + + +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace xmrig { + + +static inline int dir_filter(const struct dirent *dirp) +{ + return isdigit(dirp->d_name[0]) ? 1 : 0; +} + + +static bool wrmsr_on_cpu(uint32_t reg, uint32_t cpu, uint64_t value) +{ + char msr_file_name[64]{}; + + sprintf(msr_file_name, "/dev/cpu/%d/msr", cpu); + int fd = open(msr_file_name, O_WRONLY); + if (fd < 0) { + return false; + } + + const bool success = pwrite(fd, &value, sizeof value, reg) == sizeof value; + + close(fd); + + return success; +} + + +static bool wrmsr_on_all_cpus(uint32_t reg, uint64_t value) +{ + struct dirent **namelist; + int dir_entries = scandir("/dev/cpu", &namelist, dir_filter, 0); + int errors = 0; + + while (dir_entries--) { + if (!wrmsr_on_cpu(reg, strtoul(namelist[dir_entries]->d_name, nullptr, 10), value)) { + ++errors; + } + + free(namelist[dir_entries]); + } + + free(namelist); + + if (errors) { + LOG_WARN(CLEAR "%s" YELLOW_BOLD_S "cannot set MSR 0x%04" PRIx32 " to 0x%04" PRIx64, rx_tag(), reg, value); + } + + return errors == 0; +} + + +} // namespace xmrig + + +void xmrig::Rx::osInit(const RxConfig &config) +{ + if (config.wrmsr() < 0 || Cpu::info()->vendor() != ICpuInfo::VENDOR_INTEL) { + return; + } + + if (system("/sbin/modprobe msr > /dev/null 2>&1") != 0) { + LOG_WARN(CLEAR "%s" YELLOW_BOLD_S "msr kernel module is not available", rx_tag()); + + return; + } + + wrmsr_on_all_cpus(0x1a4, config.wrmsr()); +} From fc5b339f04fcc3f52f6ee420d636aa3b8ae96c88 Mon Sep 17 00:00:00 2001 From: XMRig Date: Wed, 11 Dec 2019 13:15:31 +0700 Subject: [PATCH 30/31] Added new screenshot. --- README.md | 2 +- doc/screenshot_v5_2_0.png | Bin 0 -> 48600 bytes 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 doc/screenshot_v5_2_0.png diff --git a/README.md b/README.md index fdbf0f59..7ed04212 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ XMRig High performance, open source, cross platform RandomX, CryptoNight and Arg - **OpenCL** for AMD GPUs. - **CUDA** for NVIDIA GPUs via external [CUDA plugin](https://github.com/xmrig/xmrig-cuda). - + ## Download * Binary releases: https://github.com/xmrig/xmrig/releases diff --git a/doc/screenshot_v5_2_0.png b/doc/screenshot_v5_2_0.png new file mode 100644 index 0000000000000000000000000000000000000000..630dbf971d92389dccea58b5ecaf76fa0d5bf686 GIT binary patch literal 48600 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

leipo)9hwxJwh?! zkG)NWUzF9Z{47-`_@<;;|LXah=G*0y?(gEOJRH1k&&}yx+g96je6w5j_0oikL2qjP z{~fx%Gje%*h!d8*^NZHK=-SKx2tZOnE#9Uz4q(R=NB7`0_yCa z*-n|h-DgI_jg>Fs*5{k$c+Tb3z3;ctp7-sCzxjE~x5RG`sTTcpc=e@dKkm4DpB%$> zd=h`H;Jc2|T-Nk^)r=oOFJ+$m`1f%`+wZ+c=E-S4cx1|ZJVoaJi?ct)&dzGf&8pwf zp%{PPTV^|3()H&OYcn@IbiK3eX;ZUj?Y$Dw>vrMsTIw*Wc&2 z6W^=Uxi76?Hk`aJR?H9TScUPQGEBvneV| z=48qbyZsdprTlK4n(Cg-QMUYi`4>Akodni?h2s0c&kmOy^4w-$^ZT9j)tc}9uAJ`S_&;4Q8dhPjq_p?tQoG_MXa6c!1!xta zwI4D6eljvV2%dk?U#daa=I`Y1(no%<&hOZ~F`V^%UDCgoCHx*`j0_C-Wx*@WK!=*a z8Q@k6123%g0%JT6V61iZ-WLp7L+^RwsMyXz#pKWrXXmSZS*E`~CeJ)}-mw`9yl>rq zKE0jz`|=IHegD~SS*Gq^^x)|4Sgu2|#~T~w-ewuga zDlNWuPU~5ogtTL-Qf%q03+_p3K967j4gI(KTx{si=a>68CBM#_Br{p!P|H8wZ;IA= zW+H`BpRcvb#2ueoX!fzkvx!HzF!I>EUMKzESA>>kzi`Og{oY;uSDoDY>}NJ_N`Ain zD^)vHe}3ERGCTA3q@|vBQ_nF>-SA*mT9#QS&!vdnw&GPW&z(&LbM8MBI=yTW`vYOq zm|4u}!D`}j9iF>Qfz58E2kgmtS(3MX@y@lNl|`E`|DRUocWb>uZT8f*WGmgRD|>qD zSMqF3owWAG8spj-%l(h>yf<1I^XaL6yzQ~|+YX%E5}G{2{KV^?OiB687fL^yuD-hC zQ?SIf!xPW`F7!YAQe^$@1z$dU9oMqXGbxR9f5opC+9L4l^Sz0eZYphitM~eX6?v#!`zf0k>hG5!81@5A_o%yxz*7&KfdsVFa z<#F|qALq+t8qzNo-1+-XY}w(PXDj8yDs9Yb9{)9;{CVGv|F6oreqWuZZ+fvf$2w;I z)TZnTjaJJa94`KIT#ek~VE&$5`# ztLZgYbcx5>!p(k%SM&&!7iFqQUw?gV@0+rtX(EOH zJ?4GAcddS}-V@80r=K3Kh(FAl>|qjlc1NJC_{&4JyxY&*K41EndGW)t{^`4V?$yL? zh)$Ys{4BL7jNS6!pK$T?zWe0@FP_!!OP&1aZbs&erR#Hdn>?TAwW!AD@X5)&GRGfg z^-F};-Qb)4B<@`HjtAbySlr%cIx^2}Sv*l9ILs#ZeDKeW>yDW_o~gWcp`2H!;gAUX zzP2{*{oSSkCH)#VSL<2TC(XT}kui7Ko`l^}?=~#cd3#-vce-GDPuu5t;g60?)%{=b zdHZiJ|5aNLms!fzyT_c+pam-=6ZnNp0-fRC?98H?qc6-v+ zpo3fN^X4dAo4T8;d!=<;QToa@M-;63|?TYmd2 zCvvJ@+OWUaK5+lv#JArMUegnQFZbbk&FeD#f9$J%T#uAGH;+GNrxnYh*H5L77G0fp zZKK~EgJWyT-mUXZ> z&%5A#e`0v2pHJ7XTYp@)Kz`}NBM;t2zYgSe=aoLFJlVYG*w1b*4D(z-ZIm@SKcK@bLeCh_ep&o)*^%rWN&oq8u*%F;KX={3iuhM^Gn!)KSDE!hTF(qg zd%y7Eu48YXy(#Dst8qEp8kL)W$K{nrXtu(%m8EE{kox zttwh}e7pABs{H;N4_Ex3`zz@4(|0^u-u)=QT+pdz{A;cDjqhbOD^q!&Z(Y3UQfrie z6rau7w#!wT)$x<>-}n@rReLH+|9AY?r`U&C_N>&@i*Ee%kge&8RlDBBG%M4G$~$lG zSeo#v?PmM-tGTlb_D(H&`hGQNo!!~qV6h$dPgk9nekHg0@J!8b&M7-5PE$;ZzBPNf zs_XfTnptz_E%?T_FJynwhRem*uc&q1F7;j6+H!5Xs%v82v7pWRSJ%5v*c-Fw+g-`+ z5iOe1^H#^3%dc}f>}h@HZ(YRUndj5mTpoDe&AHR)AN)H}I&R;meIa{&UT=!seM&+6 zM^N;(-)=qm(|<|F>{{k{t304)}PBY`1N!} z_}%k}qR=u6$@tor#m7p6-a%db11v7_$ulDOiDuAe^N+wn=* znDMWJ6P%QeEZp-AIxKZ3R=e`oif_4)gPbjdR^>G+}75s#|vuz zW~D`IU9EVcVq&Q zbTZLDTlkmq&BOV3 zVBJrYb-fxDAM2(!6&x>2NDz(LXXWKH|Ja!=B}PmiOoio*!~gLnb)Wb$t9be5Wxs4i zgY}CY)`l|Hoz8vEyvQ$S!IksNln$(STJiB_(w}pIJ@J-#Mnd|syAq!!eK>XSzS8$T zYiHdOWtF*YoNu)+*SH$iO;x(Tzm4z7w%>Ip=E(2$dAu==`CDK3WLDG8kFz!BZwuM8 zQ%9jXKS0IH{rcCvr`l(+J$w7>V5`LP`x9?R{1BcS9_nsb-!Gxiq02PS_ps`%Lt2x& z{a7ZS_Exrk|NPdanGD&;E8Ok^%+uJldq`R<*{ zp*dOoSlarRhkrZ+jh0khKQ(=S%}0s-YR$oR7lSq4^xglkx;S^P2l!0K+m{Qh9A}=@ zwHDs#=AILEXTl|um9i_}7ngA^w0|$FIae&{_Pb4S#XGkxk*VMMGy1mv;`&J@*Tr;a zy4A*GJo?k4ne%E3$Zx-+j*{ z7&P&--I`Aoc|VT*&R2V0yYc_kw<6zH+s0=tdVZs9hn+U-^$!7DldXQw`5v|M>rHu) zyq5m#9ho;jff^O7e|@>H0&i2uPyDCFZVo)Vku_txxtWm-2h1HR-+S`+0ts@9kHy>IuL7`CIYb9|tma z#BSdKYDCnnhBYFLEq7#`T5ZeTnD_5_v{~f6-f3--ziw^wF8;Xk$*JQeb8K#&C|qLs z{`1rJw*^19d^|emyKOSl;_q7)G|rv#UG3mB&y}X%j;$+K-T8jc!uM56E#D`0F~>U} zZoPk0N@JJ%lsBPj-6i;151~KT-k(vkGSxa}dGPXE)zePBwn_Q42w&^LNmoGL{Y^ov z`kl}3ZHw-CXI}kTG+%t`-Mnjj<~qp>ZcjTXb?;Wuz5MeZN>5Msn)i2MoA=wqJA0gC zcdp(rW7lKV@@ZKXi;fnz8|}PxWoqe7J=s~G?OqpOsPvmFe<0iX=Q9_zDISOao%^dY z|MR^qukSJ^p8GxRZQLLJkdNy(o6ND5-%(V`;d1@7dDqibwri8;?pS%YGT-%b_JZ|7 z-m%{gW$bvp;j`Q7x!bLduG+P8=bLZG{-xg)eVl!PujpR$^Xmn#mg=3ldvC?VLnY_7 zZM9&nSrcNhR`~n!opUdn#MJHY%f769PWIF6)8G2`-`#uv-Z#~$-V$q%-YknZY5jKY z3UBl8Df;%|ho64F*$p1*aL=?btoVJcD&%nBVYd$_p719BDKnPR+j#zDdhUF?Lx?ISiu$9B&7CtSoeH`6k^y;`uS?=JTCf{Nyw`pUxzpUXOH*FB4U zr^0JKL$c_!#hF_DRi(YV);_rscaZP;%gVfq+vo0Ymc3H2tZC|$BSqJLPx*dwclNyw zjsuf3EU+wVxj&_XH~h_i#@!DN|1OO0fUBdzLrzORvdZ(W0mF3m&#?Jiqo5 z$M^l0P49T_3(1w(TxPWL%suDBrM>XU&-&jhm`*-O6#e!Q&WxJOgdiSGjY3%#Ag4Qx$_Xo#k?YlF#@tW-aJSn}4 zDQ^2qS%2T_)8D%0;hwUPuw4P%PKO(pUoD8zPko>LNjkdDd#!)u+tmN7_t{wV_rCXE zou>7Dzi05K-&fyxpM4*+Y_7TK(NygKyE_gOqMlz+z2&o9twyC>db^0vU4!2rw(as2 zUba1G-Je?1IWzvR+F5vt>3enL`%34#$z>MCN9!WwHyqZyXe>29ZMpi2z0LYp*UP;5 za>sOQ=cBUN;4H<3UvoVJXUyJs<&F2(Y{3+L@kO;uU3+gld6ZtMb7%_h`K#+^^M@~d zxJ33{o&B1JXVQ(Gl|C%ZGkbSn`O3c=c=h8e<5$HkE4{fcHgy7b%}U+dwaUk~d;jIt zi@mJycK5a;>%ujr<>!n1Qk$Hy`_gIux%yYv$6nAo8~)r(;&0Ivhl-Q=IhFSBJa#m- zDc`=jeea1I?D1L^LA=v%ylTD~mv3K}sxt2m({r6$jmuZ)qrkk6+INdvw|9ZMf%>K%t^*c`}{jAK7`N=8uZhPFN2Zo>TO=;-9 zZXz?eP(L;Jj>XC4|1OEXDXxAkvzFCKSLDUj^lbanzw~Z?nto_j+rjmj969+r-%5p= z=gkXVe?IQeR>4a@Q~Oi4EK4-s(^0lCuhGd#d)AiKs*ElYpJ&9SdmZ1YvZp3>?Y6)3 zz2%RJ?R@XjQn+2XH1qDE-3MDEodwqEE)X_a@ww=`+VbVAQ`ZGs87hbL=iZ)bK1n`h zKCPYZ=FY1YB$a->peVfALqhfVi&n&90+3kOBx=H#$4t9ng-=QmSiCMYQ zu)MXqhnJz_WTv_>aFX-G_EZNfrGJTAdox+BW&8#Ied(pMrE}FZNM4nZ3y~Wb@NZW-asP)oR6+ zvBf*LS!urf_9;u|wEv%6&*I#RlQO?libEO)tIu1lWIev~y~|tG;C1o4EsV_uf(AA`Jdyox7z(~p16Op&ANxnQco{7J^O#{tIPG1b;RqhSzNz%KhkDt z+vS&D+g+SHH5a)a-nes{N1x74-Pys~yJI&>y56y$^#0P*D?u-J{@J!TCVID&-!oTT zk9Bj}ZeLoirfIn2)1J2IXS(jI*4Z0K z-9P8I=WN;|t;@Ul%hufK&mIMzoZ6phQMqa2p1mf|YxnKB$*2B#-L#@rYHmB`{$f>* z@#9pL()%&Vu(9>~nZHF!$EIw}GkLkY=kGhOqSuwp_r6xiCUh~UXYcs1JA&Eud&i`` zr&1=a6P99KdwGg@PWWNjYg3l~wOaDf=uywbf_tC6=Iqeg|5bXj{??rK*Cnq)s{I2i z=eGOAg{=GI0-7|bzy5Ul1TEhe|L>>ViC_BgPP_Wn!=>k(6p91(IiIW6Tx%8o?(?Ee zx6j^s*p|vua@#24aCG#!>ZO;aRmMo(PhIqI$+24{p)0QLD2hAmyR2~WpA~J&??N8s zKYd^E>eQ~B>3nA^=YO?)VYB=k|AVb_Yl@YGy_O`_Kd+l~zB2#T*LRHu&udTP7*lw6 zZsYB{kCNi;n`-u*`5Lt5e$JUw**kRqYOTIvk-B~LmXO1%Ciy*Fl{fEI-mU82^Gn-% z)`uL<{ho0`_(Mlquzo3cjjZ^x;%#e>YCcF_dMI=54*Roj=PY~p>33<&Y`MaB6Yj@s zEC2KM-QRE9^jF3l%FsQx?e{dzE#-ZA5_gu@{M;#}H~)OxpSb?$2dAX`{10>elk`0> zRs1d2xqVyZt`=O`AZTM$d01lq-5$^(y`Lg)FYo{G;pQCs$}MS|oP2qTcT{(_y`Em< zSG8`>l813ESNg)=>YlyzFz$%SI%}i8%UM4^y>N3pefsy>VUk(;4;2j=K~2mM^&bICy)+{+j;ND^E7bt_yD4 z_~Z7u`--bi6h_c}NnmbMvJX+}nV(4KK%>vL~k)ia=V9)Ep1 zuY;D!Zs4}t6W^{Xb9Y^(=lZw#@20ukm$+Ol8~%?GycR>Nx8Q&EwVwsI4AS1Fe5(8` zC$%p7PTo1b;~58&rTWfTP7d4XVEpv?;k{Gd#s|nonCqA(z_pAyzb`=wRks+doTQVQmW5OZ_#BdxBXYW zJi`uGzFo1Vq*0bnicjV`C9R4Sm2&3oA>MZ{{Q9tMyYKa-+pVy)Dwbzks8+c>zWnA3 z%hvJ_OMgvmo;z>%vgZ?~ZhJi|h?{e5zPH_!9)54l z!xLp+b4@ci_JtHg99G@-D*<&OY=()%x6f}yeqX-8cmMyhGOypI-dn>L+V9cY`*h2L zr@g$tijtol{wm3PJ%8SnY`sk$b@!yh>zdkrubpmEab}OXDrB+Pa{ zM_wyGIc>klH4~mXU+0AC)4A7I>?-^`hwXFwi(4Eo|7>zMin<&i9OvS>y}~Z$iFEI^ z_Fegw{@Ev|uM%JS>t)`*^Go}ay5Iavxl8saL*-;yJ&UI&env*EoV$9zcgiWF?N0si zr@;dbb6c-AOR8sDu2#-f-MlC9g%9&4hpe3!t%Dc;*1cxZ@EFo`aO{cy@N_4v-SDsc zrQQbT`G=nUJp zc+LztqK83>56%FOH8?=d?{GK_IV+CA;jm@MiW{>eHiHgS+dQ#bw(F+y?*DO^&1_GDm!zwE!lVe>YL+*+y9)I z>)sQ_a6moN!q1FFX^GPF+K8tg?|j%Q)ta5Y9Hl?cCH3ywf)y+BxJ_mT75tqStFrFn zj<(s;bo;NF{NKLH|G)31m_Lz^W1{QR3zxNZ7duCmXH7V=S8HnejMb+y#pfO?(Jt@w zN_9GEaDCeKj+?)p9oQigWAuDw_Wk_urMF*|-A+HAczWKI^H$E&FW2Av#{Kt9-{EWW z)AxV<6_vZ_^zy%cCNV$c7VSG1qB30uFeh6*O6 zMG2rDd4?8_@%&duLL)a!T_2|Evqo}$oNeJct@u|Klhk`p?Y1eX4BW|nFVgb;QkUrI z_nlUmEc{m>KNC53hu7AHi8Qnn{RDK9>Hw zLx07S#~l$u9g^?7mhCP*EMVWf$*g;`NsQh6y+8KKZ9h=k(ZhmT4gw|?9FSEq;hYVON6uSc10vP@(=UDm}rox4^5>isTsJ?|&v zd$017n+pTO4+h@!FF~6&JqR~w%sxMTme&k+lgs_Ht}<7bp^TTCXH+cTe$u$-PofUr zpUp<^%c{cBM$GTNULG5-wL136o`0_mO3zK(w)~;j*WAak=GN9RnTKw2O7-Q=f0^!i zcxV3Zw!WDUt0Wy{|YE& zOEE1z^7>n7xaPxqfrmSPuAe2nT&H0F^sn>Guh^!wPhKs#L^dyi*?P{E+{Lk%6>^KJ zKcAG4UnFF>_@iZ66;tGjhj;!MShD(Ri|*UE#>VQb%Fzo+TTd*iS<}yhT1K&3XIv z+v9LGp_A)1JdSmXOYK^4u5DUAj?wF>bI!6xE_}NEiK9gJjsm^1FTUr`wm;?(jaO6}@xb*>6Ev^QU^fk?N@V{ie?F@q-kluivf-%-nQl zrBCyn8~SGcuS|C6s{6r)Uq2VczGnKfujR2g>$bn{ zmbo&AG-RKA;W-z*ZcmBbe1@bB28MZ#-~r&q)2{VuUI(x4`FrF1R#o|rn@&ve#3<{R zFJ~MQqOPwvhMCbQF(vO!$mT|e!E|kEQ;RgZKL(Mw`k|uO<&J4M#NODUVUEm z{r1(D;!1bF-TZh#^o-qg*Ccn%`FDC-{EPH5nQi}e{8?M&{_07rVy?~fpS{yIKQE5! z(M-OZaVbOcvuy6k+n?8k-{SKrWN3JPwcug-Mz*dkPAB6^FKW8Ib_sTTZSs`$HqvT* zi)52I-}eYD&wOlgTy_<&c0PTbudTPk@}N-l zou`x5M{j++{x**_ryx5+g%j`cXMIN}ue)U;6DV7xG&@(Sd zXW2^oHEqWHM%Eio*d5yD^m}%mL{s{;NJKZs&gHOcro+;kCvTr*%Re5aZ6Xsr|Hk1t zeqL{KO&0%Qa=lH9IwO#3#&S84fOFL?eV zDDth+^RMykD~q>nvE>b4l`s8%*Ug&`y@b#CT`0);?PKpPb*}cdX^GDAhdq9;CH`J} zyLaavnJWeN{;gP3l4yOQfT3aerGhB^o0GgPKHi&pKPcNjtor8vRr>trk{|!RylUpD zzxkJ@ys6vzJLvQ7DVgqaEBA zc^P6^{;p=89Y^g8F(HHV#^2`!ulxRtYngk4U3=cj^PiH9(q8>~c;lBy(L#TN(;b~B zHf*@`-ET(Zh6%=TFa4K)-t@8I(A|lLO&|BiNUPfx%)k8A&H7c}&xc3y*c`9CdM%r@ za?+74R?i|@GMBxqJZ;7A>-+ppT zQToX-DP>Q@qJ>@0q=a;BvGMEY5r68>J{kSEB%P*=})&f^LfgIK*|U;KlAF6mKo zYGUWkEPTK4ZcM=;(CJ_Q*$zNY%RcZB#(L-f(5~(|e?`DKUU19bLIwoE%m)Pw7BW!Q zgMtSI3=9nA3{#_*s0W|i_d;d!d#%}Pg5yGK*Pps>sV3umJF>3QI;Ym??S-%R6Q56c z%{56pKm4S3xs2D%W!Im(KHnCuw8Am3#Z_X`sZY`6u@l5C)VFh3+%eSYkFZVIUQ_!2 zcaO_8{&NqHy}l}VTH@Afzdbd%wVb_4bF3;~%u-wHxIl8f*1|9UKO3}rrmnfS?sDn- zuQtcFY;txkSS-AzXvyU5<>A(XJNGdkv|YJmjc{%LvI!zx`hH#osb2MRYW9ggLd^6T z3X0OB3%L)otn#WYTQ>3OnSYzsZe6-uJXWpZOkA$^HoKW+X0PTaKVS0t)061Q7BZ19 z@BG?P6q)+^$=2{)j>Sg*Hhn4WyR}QFYsoiezVi8TRV(|xzJC6=py`!G@$v~S+3co_ zX1%|1{5J=GGT6##le0&txX5|hz5ZjlMLRyQ{ju9LW$yQv4$FBKe>5)JDy+K0yyE3P zR^4cg1%LK@Rk%~k@>6(OS7@c8M(m+t_J+&HsqS+(-Z1CN`DujW+Gw>=u{UvX(_Q;EqT)n@JfqE?B^zl>|npNiT(fBnnn>3=>i zT(Yh1*!uUcWB%!K9X)w=x{X2Ysq;}yuYY|_XKye5%W^CKsVU#<>r<4rF1zvTkeTeE zQ-6L-tM+aC6SKbm*7E0n)+tw?&fa(QPwtW8lPl|6>NM3SU9pvR`&!bnY^_HAzI6-D zZk@j&^QhpE=`q6@3H!FJY&H)y-*Wiu<+5UnI~jdeCFf%Dje9O%wY|bKd86vSmpizR zXKpmIn!52hx3FG&_8k3~_3vcnKkrJuwf6giR2K*P>TR;$@>ajy%jcXecj?>H9bdkE zd-uCf`ES#|*zKAW=f^{t~1If{-I?BG|Q7xs3~dByhV9kae37WK-y zBw)VsbFBPT@28p56bddcf0$U`QuC^RS+>`wf2)5Qf8O))dG(JOnRA{Me0u-6TE3n8 z{jSd?S46)aTU!}varySqk1d_d)3@s`Z=JPJYgXp@Pur%NEIt1*FXgGZ_`aP*-{*f= z|NN@`E7!?7Z=YM$nl&voeYM}X_xD7Zyq^Up_>WsJJQ7pAbyw`eJ98d9fAFVZ(nZtF z)6YEin{+((==51`K^xsz`w;6XANiiw9+vezG2P=_-O}TWX6oO%cH%qtr}Eg(U1ygT@7PkN z^mAsN$3yOhj+1e&Z?^9Cd%I<=@bVqAcpjG}GJJp461?6tY2|_cM;<=$j@|J>tN*a& z8t0OzWZe?&mO0IjtrBZDy`6LH;fvR|&ij66k^S>NSK# zuER3#ihuo7;_tOLo>~2IS)&R^_R*tm09(T+F2@(U~; zwkw~Seg5?}cD>%io?fP=A(h%`y&h^!5Hjahsah-I_B0cWZ78abIzpe*v4%c_Hie=anKKRQRsjh_zR5tZUqUbpAx~ zBb63>(_{bJ%B;KiZ|CZQLw|R&w@=Q#bk;~YJ#U|7@sk-stF0OjZ{)r;<#|rw4qdZk znR6fS_cqGwbsygNcH1e5DN~f!s_#9n)4sasm-uD<8TaoVeOR__a%jWVNxJ2_v1jS9-W{u|n!IeCeBWCCi(cDj`CmTr%k1!>Q`Z(3q+QWg3Dmazen-Yxmx-&~ zOXupk%xo8(ujz5`#5$M0`aIizC);a|_RFT%1vAR~J~o;A1*q>7%(mVb@#@#IyVlcY zMgGs>yS{?m%!B{VaxT$-mZsBgaeQando1RMx6jW#8!~4fKHK88^Gw>Z87Ci>?3wz2 z@zC^T$t$ZE@22UL?)k<3ry#CMXKMcTZMorEQaeBDDVCjok+33a!L#BWs(xDiA%VYb z+86&~{k?nV#w*NkTO#PR zDB+F}@Kfjf$e}dGJQXafhY< zzi#@kH?t-__G{ZB4ey2XZvEtJ{CfVL?)DRRE$>{|cQ0=G{yd|i4av`^o!*~qdw<;q zmxU{>-#^)w6JtGh+Uu3;U%d!<_j;c7mK$Y>+KsQ+mOR)l_ASr(=KGbC+btHU2`%zF z%2^h1Wu8asvf6m7zZ2(OPyW1cna=TTyZ5b&3|x51^*smQ#ixhw9No3iNA_k(`R>x( zuG;V2+qYR9JQwVos{ZY=P|@FYB}EGDkq_H9?mp+)-~H#`>*qdUp-RPS+`gOVOmtY) zEPk#gTz}>3yE}X5v9FzFG1csU58vbKdfAgAKur}b)dnZ7&0nVHuGQ5&lvI@ay?X1~ z8^2O+-CR-MH+S;7)06Xk9~Go5{eH!L>-0;)-cv8FT*fJAv})mrFu$|%m-wIWDHea4 zk}mZ=HYQ+8!^YUxMXzkGW;wEp7peIhl;>rvmHlYhc;Mou?A>2VzF*$4SH|zdlE~RB zGOs*;`p+V2^5HFFQe1NicEA0w`O=efp4M-wgJ$bn?|x~|wOu^m@1dRlOLo4i!I;uV#P1#wexNGWzbhqXuU%ndLD%1$$Ivn}; z`c)0#l){Cbd6f%aO?bHV!}Hy)0h>x*2wU@Sy7g%J!SxRq`HuIxFXZ?>?b+Wb#Ul0e zTjq8`v%X*arGC3JE_`2{mThHl*$k`qJI-(R$=dj+K=r>@fji&#^bJwb@slT|2UynC zA6x%EvgO6|=xj5NjUg+`zgTjwy~p=rXwbE&gOK+VoIl6ZK^w;yFiI2Z!s^7`8&OW|vn#1lhOvxMTq-^J3e6;v@W2H^xm)!fopR~>! zZK`|x{8#I8X6@SBkBj`gzn;DGFZ+#m)XqyAx60@x%e;ta6DhJNk~{ODK(O;rrI#Dx;AhU|2sjOw?c5S8(0rrQF+2J}fIfb))p{qXHJo zWQ#eyd#oHUpZTqGyED%(aj{Nn_p2pwS-00kUAk*AN1}~phkw4mX*Nfy$f9tT+|yR?13Sg-+CTr-ycu2n^>5kFI*Xq^&kgVGzg_evEbo<< zUCrC>+TAfWo!7gcrc5n$&d=^zt$tg7(o{*qu=yWblDGfAClk#%Jy~5!I%(NWp5KQ~ zn0{0&I^TW0!F+qsotXb#B4#w*hM%??tIz)s1d_mH(%ysX} zBw8kFWfoO_dSh<*&t8}zD?XO*`=Wnyy&v7MjF|MV;9{s;`{&f2-mM*KmcCeVNPXuIhR)~vo|l(zZ9got?wa=bW!vOV zZ}OaXf0k82o%_z|C5NV}-&lCueY)NG^R2tjPyDR>S=x4|#r;mV#xtdN?<`H@F7Eo$ z-Rh@#$?UsYY{tHOS04ZQQC?t?=zb*S^~vh9pa0bNDpY2g_)XGk`zwCQaGDjr+OH=f z!Nn^Vf9_4)Id@4Xqj--6_h*Ya&%9U9WtG3jv~F$0t67I+9{5b3bM==@d-t{a@SN{G z5+C<{HNNxy*_!lE(u>?LUF&%}PiEauRleov)17>b&l#NEJF(le_+A{l^y|drmHQ7o zRN{S{T)5Z*(r((T`ZM;)tfyCBrR5vW{NHun@?p{2Cq+ACzx~hWo4(&*;=_V7#rB_e zpM2Z+uwebU+Op)8B|HBgySmhdb?5U0t?={Hx1KNk8DlLe)3^J4deM%hHpN|>e+xAK zo;wz#`n~AKO{cp>ztS9r<<;l1G*K;|(=RxbvR$j2&F#T}kJB__1`{o9NT3}Ch zDEH0yRQL9+;?C)>f8O2M*Sp?dFTLx!eRuGt>r>^um+E(J`1~y|-0{SWm9N{AUk7iQ z=xcJM$|5QD`CQ96G1she!BL3#g|9R zwbn(gi;UVF^=sPkhtp=p?yz|l;H}nv`Qz?+Resa6;@&S=lYcbm{=L7<(z440*PlLZ z=KnvoGG}giRr-Nk4fQ*G*3l}5eow9a_Bm;#O#9{eU%S}#r&-x&>M!%OKOXUTVbaPs z#pnOsJ@f5Ak;TiKT;e@$r(^AJs-+9b7H_)kakn&Q&)=q^LvJ@sKKfAU?-VZoU~)L{JKLCS-Eo) zjF#+_dGhSwuFu~b_PqOg*mD2svIA_1cZ8Ex{<)Q9yR}AOriWdVilqLuj1-G|nbsG7 zrQCSmwWlw&J<6(Gbh2oRo$B$2(>hjNPPwyn+S6ZWo^n4gvN(DE z&Ahhx7uWPouG``7`SQ!Ww#1Jo{}yuX-)Fwf^h0p>{ugT}C`uo^bZw{Wkso=&9WM+{ zO-f&~n%!#qi9dbsWV|FZR)tQQvGnKZWfyj@HO~ptTUy>6=e@Xq`JG%h$2XH1YFbjy zjz*U3z1X^R_v!AVKi;1T7Fdutx8P@)*~6U|zTNb{clm(5{&PF_YK}j9Sr5AGt?Sz; z-f+(_v^A7#e)Y5W&$hO2y?-nAzS+6G`E#RNbk2V`%(}DgPo4X%&WfTbC;u@#!2lo>SoMa`pYHko^+R)?HaETw`*$$~GqK|JUN~J!f9JUfbzf z)YdeiT=ZVe^S$#Y?{wYs$>O3>@vB`BmG_5urq{g-LQGgCgqE|(~IQnh+t_|ueAKb}mPAc$xhE_wZF(&}ZlYxey1KYaDw*H0Ix z_;5L|`_s46^}s`o6?YC5*1E~-sd{Iq8=2Q^y&3;a*mkzV4PB+)n_rg8H~JXG?sL1$ zbJ!ww{r1;d+!>2cxfZ>6a6$9w#QS$!=EpA3@C^#w{&?r!>*p`l3Qc&-z%cLE+1pvG zLcCVAUitY_|8`V#RfXBk2fz2;3Ih#*r;A`8F5DO3d{_? zHSf^W=e{o<6ueuz@7+05P1XHI8kcv4etfG^Go_|lnRj`4^OnGuK})B*8~7+rFY<27 zlsc#Pyw3dL&3(Uam9N|8pKe+&lY9U7g|+SeZCnfvw`ATFap!;Vzp&@KzvVjK`DaQc ztfjx6e-!toH1);OU1ir+ZJm-9e(&%3rv;kJ*PQmhcuQ){wQJXxJdq6PyFdTq>)%$> z>$A_YDNfV+x0E%MedXq@@%mp=SKNNBGxKWSyk%Vz9Aj9TqGlhPt2te2|}{UH#RO?%ekaZaxsMrAIu9Z z{zq6WV^7XF6TdWc-er&AmhP0ZGP7m!%$N7XhEIF8H^g?@MqN!82^sGP1+%}~J6*_8 zId(m}yZ=GRPsv%|?J?Uc_s&0TiNENx$?10C!yvEYw^mth-|VIp9p}IH`1WPDEN+X4 z8vi-}b??MFi(`EXx!r85l_su~=_{OO_%`LQ%;LAb+>Mj=Klfa^{%aGbbIf<2_uF2| z+SDqVR9PeAZc#-w)H(Q$4drF1x(b2kn>wLQFep}fmvA1qBFxUvRPtH#4 zE8+;xyBWb9zufuQ;lha<>~5C%JudiC`t-J7c*@^`+uDEq9v6Jfbl5j1*qAxEZpL9w z#^38j_Fie-HTT%VS+{=eUB})2SSHrz=c)DIs%F0rT5won$yV`M9t9TX&RwY5b?ea< z7e4jdFP3^eEns=rz1{rQ$?ZRjwXGi%@ZHSfeirNeWv}T{uZ0D(R_+wJ_%keX>GtsV zMS{x}7#RA`Jv515rF6KaOy_-o&eS#j`{eba{>#=KOuqh9RK;LJ^7r}mzsv9KH}%(+ z_!lPgvDeKq<}YhXy>(ig<@Z`-wRus&-j13+4}m%k6&*- z{N8m1Qf=;*JGXP)+OGxH@AgZ~yIt^k<(|nHL*WW2W!N%9Jc)Sl>f&y0dLj@^``~iGbVHe zbOruxKH+j`jp;iq!wt)?Z7lpc@yis?$rmQa7`*>?KKY}z;h*)BTSXjCF?qjz@#(Y0 zoG0mJ{)f+JTs!7ze`ooUk}Chh8-6_bXVDseU;4I`(&pPYbfr1Ge%^c+yW;IV*4L}M zr&w3Z?#k2ZZe78fX5I8OahB<=y?lO_x$1W=PnZ*@QZTjft;#2soBxVZ{15-R{3eQH z-QnkaEQkD`9qcUS`j&d?P>ka7)5`m$-Cz9Dx>F}rb?~yroOl0LU4HeJw{)TP77YQm zC~2Rs^$gWg3=cB@7BCdFf15mCOr>L~U2NV&g=xRIJ-iq{B^H7os($HrawaI1WO+Gmb;`WB$MhX!RC zJ}byf0|R(Q0aPhKXBkkc2DsQAd*&QVRtAQOO@XsdNM%2DV9sP*cxm0H+g49QuYO-O zXVUA9CvDBs+3xX0&-bw|`CER&^5w2QPnKu$cdyz%ovreAz=SR3p2`6tshXvd*Ew%> zUs93YYT5WTcSm%dV1WgfBxjvzjn}rD{VDghYc1XTR;9|jDSmy2$+jz|FJxkVO8FY| zJTBRL?^+dSua@}B^6Qq>{nOp+zNhZa-!-@Yb-C-yC;x2sPgUH`!C>=s_V?S1>;pny zZ8omaOaC@KcdfduO}KIF%kNpy8^501vN`a-jP>%E>8saIU7~pGs=afA&w1gp2kLFv znhT=Vw3VzrD=M=$zVH728HWR_R-QD`n|t`@$_n$}fj=~U{wq>Aoa! z+y7>Pc46;N=2_n_zCLx;`{_I<*UU1*ujii>$jpCiF{k{4p3J-R30t@AQ&T;^pya{SV_U;pxf_>FW4juh`~98#==!+pJ7;|_ILdh7{(*;ok{`~H>se@6 zAG%bsEeF@K@NK_xzjx^}&&&HL;YaW#2w+CI5{{{7#Ub2{W^dxpKZX+7(Ela}Z-dr8^Qb$jMT_2>I; zE!^g6^1k!rtc}Inp4omrb1n7azfVVIeOLT$RJAO9>iee!OFnnMDPNirlXSLO#O7{N z)TM?u7ob(Zt8cf?ho9l~&bsq^Dq|;e!}4bZMY=vx?GJWdKcwe!NMMu3BVq)9qz}%GD&!L*n`Qg6i>WZmxgRaM&_s zwQkU_xj&{q_Pm`C)di{sUVdFVbq~ME`^A2a3=DE}55Fwj*TwUhbK{k-5p@m5v72Ok zc-nv8*xdFe@A^~GoIhSwelzPhy3hK!+LFb)Xlv8M0#~c; zr=J$gnHy{=bG`pWyF`Be`7;mm%-*~_>ULh!e*WQ~f6u*M@bTWRgAaAwr^H$9k8eKA zcwlmoh3UD;*>7dj1NYZWSKm4P=YE?V#j?`z^JVMSd;HyOp;{fg^Yz0yuWv>1AHN!J zb6}}Zt;JTcB1axesp8WM!#6wS#$QT3p=wa(hB0b=e*NorUkfJjY_+-&xuUhuPv>jt zLf_V??q&QRpT?R`6KU^05|myQeuB05^O>_ZEVZXey>u6qE$#g}UA=GVsk=*-xyY1M z-mrZ7+v1Mav{PTLuNHRx<=rWBZsYp0U2G4djs!72(6pR0eTC8!!K9G?3PuwRvU{s1 zZ&Nwj?smKLn)+eB`2RW98(+U#S(+15`((?hC7v%l^-f&5zg0LU;$-0t)7Hz&cX_%` zyIzw1wpgIZGTr}h#&?rHJ!!dfz8tRg@>>4ggs0p8U4QV8uX{JHRW#b&dv{agf+_Rg z-fO+}E_TDdT^>6(Utjoo*`|n&TM65gBjlDvF24Kv!IaBohirLIzI3&*JO6$03E`CA zrzhI-7KL>YMIfXWn<=>(Ou4>ub8j_nl*I0QVbO zLH&lJU8*x)mFK+4WN29a^)TZB*<1c~Q4!0W89X)qD z@%7})_I3aHtkq+uPybU=UfXnM`RwUoBB`2R#jkhX=)R=F|8?{J>1In;e4lFlOpLL8 z@q~#Fu5aO8zxC;=?&n?KfJobrqB#55*_Zr7jh25u!Ps<1iQ&VQ{CoM`{~SVBo-i1Kr@LZm*D6aT?URZ6^kvtjD?!fN0}q$A6|O$1T)d}! z=EL{XAAi?>Rxl+l%c$4;c|o04+}9;*)TF<*Jr@7;_o+kQeG}XGs~1;)`NVN;R(Re? zgUH@Bn@{b$?_05L^_5+_f`84@$~&)nyISS3YhOG=MG~L*r=EFA{G7AC|I=(Yc_05{ zzVG(Uf&X>h9gPcL7pL`I#OO^yjo$RbmFzRaKW=rpt{D~GeD&6^SyOD9PksDtTvr?a z{8Rqgn+ivQj_%*G;MIrP-EXgXPu-l8Yr4i}VNm#IuQ|!4J6;s6Ze7X}ls--E?6t*L zuNbLcFYf%Az4o74;&J`(sOXb2dP?5@g*z^Aioe!(-?D8@nyl`VqnVdoO}=chm=pdW z^Tzz(eX|d*O?{Rw8od8{rR?-z+p^kdB64F9$#R(d)K+Ed)l@682+*GJ(uS` z{w0B@_vdOYRl`|s`bsjY+0Sk~DcEh9`exUr9n&?crf8aN%zy7!uBAp)X@_F8l{XKIxo4lWX+_hct`gy}!@$(`++dYYMJO5?Pr@b+q9ovRA6X7N9Z-v~U*>-uG$s-h0n>i@aDWmc0FfOVi=PvbJr~-0#7?0}bJ_ zHCL>*Z`bns@#wGWqbJ>_*Qd%BShVMCb$#KyIpSBcS^H&miLMs{hc0>DW1sbXO0WP! z!}5m(v-CqJe*e`H@v2J4{=iH<&E4$V{uEsN_5K65+-|+~36H~rUQKp?_-NymguDxX z)>=H0?k(E!Ox$8l(Gto1vz#}mglkLig)b@I9m97V)J}|^sQ5|o*zeQE#XDy6R#$Dj zSbXaIqk?;i=lP#+Slt_uHRtfna+$n^5N z5kWF~LdYG3EfK5kN~_#ndwYjgRkzcc{WgU} zo_sqY%BH>e*X}nT_9=p@2v5rx(Iu-sgjeqsJU!v&)?<4;qbEIcUi{YO_2k6%_4V84 z-YET={qE@X;u(xAwo@9n*75Q*-~l{C?xI!7W=}`^Ayc>VFI7 zPrCHdmiO?Z$-f_q)YazaWOtm&OWvNNpz$<)clp9?)#7W`>jk}gQ~F}pN*%F9W;5P$ zOl!})mhs)n;^~&T1^&)ORTD3ShE={@>s8T|Q*XIxd1Br!+`R?%m%pN_R(r3!@@U(J z2@hMBA-W4O>$V;Yxqr6LmY3nfmO2ZD9nQZ``g02%37W6B-9y0pFRRK@hMUF=GADGI z-h4drceN-ZgF+&QzyB33hJPV->t*&YE| zdsh95n;KkX>*5!K=VXuZ#iU+*^*Un;FU>3>=Ns@37lYo&WFoI2yO{PpMNE9}?( z5XeJ^RDvDv_n0yK<)ng=i%NxqmfLfOHqOUwyJt>+4NP zyXnTiPj9<6O>SQAE`Q9*L50`w-I6=6FIic+g|5DHC9WiN=c#XeR=odZ+82Kbnw%M9 z)vg@$anTU1$F54$O7H60@<8a}YgUX7qI>9{Cr)zHSuk=FwZ@Y@V2JKw#a1*rDD0X4f zwyU=mSkv&-ta$lA3BvvO;Fz2-SS{qa|F|GxFbKeu1Y5lCA3C_XBn^vAxtWv`c> zirltMDr{cB%GH&s=Qc{kl3avFZDVUCa&p8xQ|He#p>P!a4chDlgBTWfPZiw4a`3X=ZsR&rfpqI-Rvu zfnW5J*Sw$ZEuoTTai?9_ebu+P>0b|bCitcwUY4hGm2*>@%(~U*pB6M7@O4uT&02Ub zt#fV1v%Qc7K=$pzOdHcCiB^5z7}9937lHCs*)nb<(TUH*=O% z=Fhz$(jIMpJY#oN(%Ih=swHhNZ$9f>fEZp?bqw< z9{1(P@2%rKTg^~W#;1P$5X-FBGiA;t$|_}Ltl1cKU0OzOjsLc}mn~#!V}F(%5skFJ zqnh73(U|xBj&-eTJ4?-UE%_}jAD(MDM_|K$uZ!NXD{g1XtXsME{_9-@7UwP=owEK< z*81%~kFi#*JbFXdWa5K@Idd1jJ$A@icjDobW>P<{GMR^oD%Q+?oAvgIch)!10FvMQ z9MAyLI``PXRU%yJ)@v4a)SWk5pVRoP_KGG4!-2)c7H6lgI#FHgq*H1;P5yz;^eK0l zqw98L{4M{%+8;YTe8WfWjrZ?X#`aCujeGZ#{apW2FTU%4nA(H;Odf|9b4+`Z#d`Q* zS4rOV(+@4IpBGwuVpaGg@c8YknU;6Xw%P8z@xt=y^J0rvcQq#-a^bS3g`JJWkVU6N6E(ZTo5BIQ$DfSC2{>47A zvn4Y7&g?C!nRX_({I=Lhmi_pZe!DX^eE+k!npzdx)JrN~M8r$Z^H=H>$EqHDc*=6p zF0KVTvghn&jL}`TP*`%#rsauSs-^1OHk14*#|vLW8(DhUuXUn8YNa}-`%OEa+tHsaU7y!1jJvhM z8rF{pKfE}_=l4hX7fWxw(f{3fZ_X*;C{Vjeorzm#azOYGc5r3AX91S%;xZ6A?yH zxy+?K{`1@U{5GwsPVu^X2rMdhx%`)00o z%bp*1cjN3k%u%p%-u3c$%Zd=DQTf>h=W#=yM>EAa0 zYjMxF>FU?^@b8>{(b#r%jea}#-1wON+?Fxn`CkgR#yEM(ofZ}9-nF;vqvZD8Z+`1P zjA(c%$S`mE;geep8q2EH+Kbb81x-z^MXY_zt0#0#{r0&`nZAANYOEi1t&x8>$^C%P zp9MQb^5j1BKHDq#O8oAd@St!13q*Whi?q!ou(%(_+pu@6acE?Pfd6bS<6q_2l=%uCMnW)~|T8 zYfn=S+qPeKbGSbm^oV=+);t!$T{V}7G>FWhqa z2g7zC0;q5ml{hk|j$F1a;_`BPO*4?|OaBcUss^;c9=davKSatvD#^sC*^RDr; zGn8l8|IBo3igvo8kl#wHrUSFQNI;2(Qsh?srOSKmz(tMl-~UK z;X%uo+%oT!rB7^S%PSoZXW#!j>#XvQ{SR6|YYzJgEMAyEDgoGd5`4k|x|xB2fdQHI zpJ&5U5e9|_C*5+Dvh^o&h0NCpxrN+qct6L*IuJ{3kTvz8$@_nMR>&-jUB2n(u1@Ff zO(9QHk{X>-7~VDQSyX;Tmx<3^QTgEdW3wiODmk0Z_VsvHyV~sc>AFSRRepT!@<{fc z&1c0P4xfYG8t1h5lFGaW&b@(5M6Y|caxu2bL83V5+uuUh&8N$jUc0c4z2M+a&`t*CdC~v*TN2;64oj;U4O(fthrlndsKv%LTOs&r5xKRU)|y& z+vzj*Uwf4=RQ9K6>DH-d9y+bnJ^A35#pBgNzY$Xpw+bnodhiIwHTB>a&~~`iqHsyp z^S_*+V!Oa%WqWS3RpXRbIA^6@zTEKHwW(`C>)U)2~;mYO+`LZq{8? z>F;x`XYOT__fvoE3G|8GilvfJo*N%^_lv2>67inZlXo4<-F5F7 zz282I79XA@^}R0D;Ss}wm6mg2xP>#erx#hYm##SJ9F1)fqHc%xJ+8%D53luF>#5g% z`ErM*&Z}G2H3a6PpWnt_Q#2pVecyL^byfA}$IIj|9u-{uPsr-0(Cojb_$=mJK6|UF z^7@3=A=C5J|2?g^WH#g4rGvb#C3h;8PTdo~ced-CCI$u@tMeCd{pzFhkGzUV;# z!-pj@ejgc&*6w|*^Xj^t{G{rccqXbJIqKS9yRB(bSI@OmK~17}?)2}f8-yMfeEzcN z{^jluA0#)1>|VB(fI6GEd7)7UGGZS`=+l~T~E#3%NJ`t^{CSo zw#~8!KN*~rdK<^Se9kuEcbOi3`c6AeO?%7gce&Tbaoc{Go0kpB8OwB8t}iHNXL_jq z`N0t@*55{{iR&hS3d3IIl3LMs^O(2au6!E3=R>I+!x-{{j+{ErzAG+ z)8#MjTmAOliPro)MY7`LjosHSnH`PGV|@1qsh6=d#j!8u)yk8g!KnJ1qzp!-?p<>2 zn^lVE^MAISCv=$@{Qo}tyX^&k$kbnNc;=s;`8PE>+nfLV<|AeI-(TI9^m=k;`r?0< zWqv#L!sE3)oqH@jZ5>x1+-kXZ*DsrO6^pLko4E6iwR`w%JNtYm_V(iCuU@|sS+&MS z+jr(1w#TQRP1HJBr+7b}@5v9}%i>|Od%g7g6V_HfoyIfy>ItjWbAI1#Nq@cUT4>0r z6_{6$+$X;7CaZTb@8 z%{8}R2Ms`Xp$6lN^p1e+a(D~ zhac<_v?=?CvqEXv4_fAgzd{iqJ(ex&md9BXTnJ0WPD=?S-_9bcY^C|PsKaA0G`}%D9T$yu4E-#mUe%k<<;XXS3#MWtgq@VpEebrO4XW9(lwXd6fDoHV``DR~2<&3o6vh~fuez!Tl zo2V=oSBy=~<`ReXOx`i!?wNqrKkcbY=M1$hznW*gx#QixRmQKuYoK;%ta$qAn(7S5 zcI%2Ldl`m13!y7!{ijE(bS(YHG%2XzjU)r#6EVh1wuLJ=7#KL6E27HJTSZ`z2p3W;B!_k*W$FMzUlTe z`n_(`?JCW*-{AR;n0Jw@u+49Qy^b$USFKqTSLl~F z@xQ3n*;iqCS2y44tasL1o&M+BzE_FMZ=}CIzkPDdl*?bgmI>G2O)-}0KC^AER!IHi z$?iEB>vw&)B_#atLYMQxS&XdL{^fnqm!Dp}Z9DU~vq!zo_9s@w+O==bt9p_mKl5!% zF8@)B(oLMHf-AR_eGR{p&frnCdHXHj(4}_PK8co__vd8iX4=XvX1a>C*0IivuMIo3 zTuXOK;Nh}hm!0X;awq)x9BAtt^y)=@uKJa=0x@qtT?@Z@dhVOcU(X9HxxJ%x{q@3Y zT-j}xF3xzhLUZ%JxZ93%@0z^YY~I>6scOM~51;Lt%m3>7Sv>r`@w?ZZ&sVqF?<&^V zJa@y}#2If=`GRL(VKKUO_4566h7&U+R=hkgTSv&`J$N(IhAA6MC=ony4_GHcfS+9iCd zwc=?V-|_9j;g{dks@~aIbjXu!`&EIy8|NPsL_K`9=#2N?)!$j}uQz}0DSvy$xs89` zd)fE?NiSySQ~xpfghKJoGb>Z>>EHI-^zZG{*yZyC@2vix`up~sq_;9zS+BqED%)5l zSNm?S_TA?_F5Uml&Ys`5c>tzS*kr-ziP^n z45`enf*m^Z&p*4fXLj(hb=u8`S1u}-1$7yc&M^EZd%jn@w4i{Ry6yXq$eOhRd zZWcIs=C)&5vH1l%P*>#MuhXx*C~^P#rsZ~mpn3PSzXjEna~4G&t55rRt$RsXulrZE zuX(k5Spy!MgzNpy^L3Co|LoW4O^5rx7d%v4sIW@DYh&o%y=Uj2+cf{t_HA!dekvww z9u?l}Q+?NDTX_x`uT7thC5R#jKot}9`#KOy1xZo%vuekZ;u zSVUUC$YIiS|3daHTmOlh_$$58>)WoP>xV6`Z`~^Q z&R1_@)U-_%`sZqDyTaPsZW}co=Dc=oTJZ~q4`#nrA5JZA-ZU*g z!HwaQ)&@RZel%!j*{5ednNzPfXdUwmTe&<$*MFJSO}p6oU31@jK0MRY=akDn$Uml?*9uwU9h<485mn#t{#s)d@ujj$Mfa#4omEw&N}>3 zsaw@-mD7sinTf^R-Nw(wH0xs84*O2tEP1edDWBSzS5tn^7QgdCJ6P^b<_$0XNw@v( zJ=t{h``4-?FS;((?Uj3{^E>lx*2ljy&n=$5bARZr*hwxU7V%1>f;4tN;JBV?MzA z^C827`doWQ9Z^{h*)LOmfBPh-n%p=fS?83{1OUS`)D8du literal 0 HcmV?d00001 From e84bad4ecae875b682b31afbeda997957d46a365 Mon Sep 17 00:00:00 2001 From: xmrig Date: Wed, 11 Dec 2019 13:41:48 +0700 Subject: [PATCH 31/31] Update CHANGELOG.md --- CHANGELOG.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e913a24e..4b92cb29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,9 @@ # v5.2.0 -- **[#1388](https://github.com/xmrig/xmrig/pull/1388) Added 1GB huge pages support for Linux.** - - Added new option `1gb-pages` in `randomx` object. +- **[#1388](https://github.com/xmrig/xmrig/pull/1388) Added [1GB huge pages support](https://xmrig.com/docs/miner/hugepages#onegb-huge-pages) for Linux.** + - Added new option `1gb-pages` in `randomx` object with command line equivalent `--randomx-1gb-pages`. - Added automatic huge pages configuration on Linux if use the miner with root privileges. - - Added new [hugepages documentation](https://xmrig.com/docs/miner/hugepages) article. +- **Added [automatic Intel prefetchers configuration](https://xmrig.com/docs/miner/randomx-optimization-guide#intel-specific-optimizations) on Linux.** + - Added new option `wrmsr` in `randomx` object with command line equivalent `--randomx-wrmsr`. - [#1396](https://github.com/xmrig/xmrig/pull/1396) [#1401](https://github.com/xmrig/xmrig/pull/1401) New performance optimizations for Ryzen CPUs. - [#1385](https://github.com/xmrig/xmrig/issues/1385) Added `max-threads-hint` option support for RandomX dataset initialization threads. - [#1386](https://github.com/xmrig/xmrig/issues/1386) Added `priority` option support for RandomX dataset initialization threads.