1*51c0b2f7Stbbdev /* 2*51c0b2f7Stbbdev Copyright (c) 2019-2020 Intel Corporation 3*51c0b2f7Stbbdev 4*51c0b2f7Stbbdev Licensed under the Apache License, Version 2.0 (the "License"); 5*51c0b2f7Stbbdev you may not use this file except in compliance with the License. 6*51c0b2f7Stbbdev You may obtain a copy of the License at 7*51c0b2f7Stbbdev 8*51c0b2f7Stbbdev http://www.apache.org/licenses/LICENSE-2.0 9*51c0b2f7Stbbdev 10*51c0b2f7Stbbdev Unless required by applicable law or agreed to in writing, software 11*51c0b2f7Stbbdev distributed under the License is distributed on an "AS IS" BASIS, 12*51c0b2f7Stbbdev WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*51c0b2f7Stbbdev See the License for the specific language governing permissions and 14*51c0b2f7Stbbdev limitations under the License. 15*51c0b2f7Stbbdev */ 16*51c0b2f7Stbbdev 17*51c0b2f7Stbbdev #include "../tbb/assert_impl.h" // Out-of-line TBB assertion handling routines are instantiated here. 18*51c0b2f7Stbbdev #include "tbb/detail/_assert.h" 19*51c0b2f7Stbbdev 20*51c0b2f7Stbbdev #if _MSC_VER && !__INTEL_COMPILER 21*51c0b2f7Stbbdev #pragma warning( push ) 22*51c0b2f7Stbbdev #pragma warning( disable : 4100 ) 23*51c0b2f7Stbbdev #endif 24*51c0b2f7Stbbdev #include <hwloc.h> 25*51c0b2f7Stbbdev #if _MSC_VER && !__INTEL_COMPILER 26*51c0b2f7Stbbdev #pragma warning( pop ) 27*51c0b2f7Stbbdev #endif 28*51c0b2f7Stbbdev 29*51c0b2f7Stbbdev #include <vector> 30*51c0b2f7Stbbdev 31*51c0b2f7Stbbdev // Most of hwloc calls returns negative exit code on error. 32*51c0b2f7Stbbdev // This macro tracks error codes that are returned from the hwloc interfaces. 33*51c0b2f7Stbbdev #define assertion_hwloc_wrapper(command, ...) \ 34*51c0b2f7Stbbdev __TBB_ASSERT_EX( (command(__VA_ARGS__)) >= 0, "Error occurred during call to hwloc API."); 35*51c0b2f7Stbbdev 36*51c0b2f7Stbbdev namespace tbb { 37*51c0b2f7Stbbdev namespace detail { 38*51c0b2f7Stbbdev namespace r1 { 39*51c0b2f7Stbbdev 40*51c0b2f7Stbbdev //------------------------------------------------------------------------ 41*51c0b2f7Stbbdev // Information about the machine's hardware TBB is happen to work on 42*51c0b2f7Stbbdev //------------------------------------------------------------------------ 43*51c0b2f7Stbbdev class platform_topology { 44*51c0b2f7Stbbdev friend class numa_affinity_handler; 45*51c0b2f7Stbbdev 46*51c0b2f7Stbbdev // TODO: add the `my_` prefix to the members 47*51c0b2f7Stbbdev hwloc_topology_t topology; 48*51c0b2f7Stbbdev hwloc_cpuset_t process_cpu_affinity_mask; 49*51c0b2f7Stbbdev hwloc_nodeset_t process_node_affinity_mask; 50*51c0b2f7Stbbdev std::vector<hwloc_cpuset_t> affinity_masks_list; 51*51c0b2f7Stbbdev 52*51c0b2f7Stbbdev std::vector<int> default_concurrency_list; 53*51c0b2f7Stbbdev std::vector<int> numa_indexes_list; 54*51c0b2f7Stbbdev int numa_nodes_count; 55*51c0b2f7Stbbdev 56*51c0b2f7Stbbdev enum init_stages { uninitialized, 57*51c0b2f7Stbbdev started, 58*51c0b2f7Stbbdev topology_allocated, 59*51c0b2f7Stbbdev topology_loaded, 60*51c0b2f7Stbbdev topology_parsed } initialization_state; 61*51c0b2f7Stbbdev 62*51c0b2f7Stbbdev // Binding threads to NUMA nodes that locates in another Windows Processor groups 63*51c0b2f7Stbbdev // is allowed only if machine topology contains several Windows Processors groups 64*51c0b2f7Stbbdev // and process affinity mask wasn`t limited manually (affinity mask cannot violates 65*51c0b2f7Stbbdev // processors group boundaries). 66*51c0b2f7Stbbdev bool intergroup_binding_allowed(size_t groups_num) { return groups_num > 1; } 67*51c0b2f7Stbbdev 68*51c0b2f7Stbbdev platform_topology() : topology(nullptr), 69*51c0b2f7Stbbdev process_cpu_affinity_mask(nullptr), 70*51c0b2f7Stbbdev process_node_affinity_mask(nullptr), 71*51c0b2f7Stbbdev numa_nodes_count(0), 72*51c0b2f7Stbbdev initialization_state(uninitialized) {} 73*51c0b2f7Stbbdev 74*51c0b2f7Stbbdev public: 75*51c0b2f7Stbbdev typedef hwloc_cpuset_t affinity_mask; 76*51c0b2f7Stbbdev typedef hwloc_const_cpuset_t const_affinity_mask; 77*51c0b2f7Stbbdev 78*51c0b2f7Stbbdev static platform_topology& instance() { 79*51c0b2f7Stbbdev static platform_topology topology; 80*51c0b2f7Stbbdev return topology; 81*51c0b2f7Stbbdev } 82*51c0b2f7Stbbdev 83*51c0b2f7Stbbdev bool is_topology_parsed() { return initialization_state == topology_parsed; } 84*51c0b2f7Stbbdev 85*51c0b2f7Stbbdev void initialize( size_t groups_num ) { 86*51c0b2f7Stbbdev if ( initialization_state != uninitialized ) 87*51c0b2f7Stbbdev return; 88*51c0b2f7Stbbdev initialization_state = started; 89*51c0b2f7Stbbdev 90*51c0b2f7Stbbdev // Parse topology 91*51c0b2f7Stbbdev if ( hwloc_topology_init( &topology ) == 0 ) { 92*51c0b2f7Stbbdev initialization_state = topology_allocated; 93*51c0b2f7Stbbdev if ( hwloc_topology_load( topology ) == 0 ) { 94*51c0b2f7Stbbdev initialization_state = topology_loaded; 95*51c0b2f7Stbbdev } 96*51c0b2f7Stbbdev } 97*51c0b2f7Stbbdev 98*51c0b2f7Stbbdev // Fill parameters with stubs if topology parsing is broken. 99*51c0b2f7Stbbdev if ( initialization_state != topology_loaded ) { 100*51c0b2f7Stbbdev if ( initialization_state == topology_allocated ) { 101*51c0b2f7Stbbdev hwloc_topology_destroy(topology); 102*51c0b2f7Stbbdev } 103*51c0b2f7Stbbdev numa_nodes_count = 1; 104*51c0b2f7Stbbdev numa_indexes_list.push_back(-1); 105*51c0b2f7Stbbdev default_concurrency_list.push_back(-1); 106*51c0b2f7Stbbdev return; 107*51c0b2f7Stbbdev } 108*51c0b2f7Stbbdev 109*51c0b2f7Stbbdev // Getting process affinity mask 110*51c0b2f7Stbbdev if ( intergroup_binding_allowed(groups_num) ) { 111*51c0b2f7Stbbdev process_cpu_affinity_mask = hwloc_bitmap_dup(hwloc_topology_get_complete_cpuset (topology)); 112*51c0b2f7Stbbdev process_node_affinity_mask = hwloc_bitmap_dup(hwloc_topology_get_complete_nodeset(topology)); 113*51c0b2f7Stbbdev } else { 114*51c0b2f7Stbbdev process_cpu_affinity_mask = hwloc_bitmap_alloc(); 115*51c0b2f7Stbbdev process_node_affinity_mask = hwloc_bitmap_alloc(); 116*51c0b2f7Stbbdev 117*51c0b2f7Stbbdev assertion_hwloc_wrapper(hwloc_get_cpubind, topology, process_cpu_affinity_mask, 0); 118*51c0b2f7Stbbdev hwloc_cpuset_to_nodeset(topology, process_cpu_affinity_mask, process_node_affinity_mask); 119*51c0b2f7Stbbdev } 120*51c0b2f7Stbbdev 121*51c0b2f7Stbbdev // Get the number of available NUMA nodes 122*51c0b2f7Stbbdev // If system contains no NUMA nodes, HWLOC 1.11 returns an infinitely filled bitmap. 123*51c0b2f7Stbbdev // hwloc_bitmap_weight() returns negative value for such bitmaps, so we use this check 124*51c0b2f7Stbbdev // to change way of topology initialization. 125*51c0b2f7Stbbdev numa_nodes_count = hwloc_bitmap_weight(process_node_affinity_mask); 126*51c0b2f7Stbbdev if (numa_nodes_count <= 0) { 127*51c0b2f7Stbbdev // numa_nodes_count may be empty if the process affinity mask is empty too (invalid case) 128*51c0b2f7Stbbdev // or if some internal HWLOC error occurred. 129*51c0b2f7Stbbdev // So we place -1 as index in this case. 130*51c0b2f7Stbbdev numa_indexes_list.push_back(numa_nodes_count == 0 ? -1 : 0); 131*51c0b2f7Stbbdev numa_nodes_count = 1; 132*51c0b2f7Stbbdev default_concurrency_list.push_back(hwloc_bitmap_weight(process_cpu_affinity_mask)); 133*51c0b2f7Stbbdev 134*51c0b2f7Stbbdev affinity_masks_list.push_back(hwloc_bitmap_dup(process_cpu_affinity_mask)); 135*51c0b2f7Stbbdev initialization_state = topology_parsed; 136*51c0b2f7Stbbdev return; 137*51c0b2f7Stbbdev } 138*51c0b2f7Stbbdev 139*51c0b2f7Stbbdev // Get NUMA logical indexes list 140*51c0b2f7Stbbdev unsigned counter = 0; 141*51c0b2f7Stbbdev int i = 0; 142*51c0b2f7Stbbdev int max_numa_index = -1; 143*51c0b2f7Stbbdev numa_indexes_list.resize(numa_nodes_count); 144*51c0b2f7Stbbdev hwloc_obj_t node_buffer; 145*51c0b2f7Stbbdev hwloc_bitmap_foreach_begin(i, process_node_affinity_mask) { 146*51c0b2f7Stbbdev node_buffer = hwloc_get_obj_by_type(topology, HWLOC_OBJ_NUMANODE, i); 147*51c0b2f7Stbbdev numa_indexes_list[counter] = static_cast<int>(node_buffer->logical_index); 148*51c0b2f7Stbbdev 149*51c0b2f7Stbbdev if ( numa_indexes_list[counter] > max_numa_index ) { 150*51c0b2f7Stbbdev max_numa_index = numa_indexes_list[counter]; 151*51c0b2f7Stbbdev } 152*51c0b2f7Stbbdev 153*51c0b2f7Stbbdev counter++; 154*51c0b2f7Stbbdev } hwloc_bitmap_foreach_end(); 155*51c0b2f7Stbbdev __TBB_ASSERT(max_numa_index >= 0, "Maximal NUMA index must not be negative"); 156*51c0b2f7Stbbdev 157*51c0b2f7Stbbdev // Fill concurrency and affinity masks lists 158*51c0b2f7Stbbdev default_concurrency_list.resize(max_numa_index + 1); 159*51c0b2f7Stbbdev affinity_masks_list.resize(max_numa_index + 1); 160*51c0b2f7Stbbdev 161*51c0b2f7Stbbdev int index = 0; 162*51c0b2f7Stbbdev hwloc_bitmap_foreach_begin(i, process_node_affinity_mask) { 163*51c0b2f7Stbbdev node_buffer = hwloc_get_obj_by_type(topology, HWLOC_OBJ_NUMANODE, i); 164*51c0b2f7Stbbdev index = static_cast<int>(node_buffer->logical_index); 165*51c0b2f7Stbbdev 166*51c0b2f7Stbbdev hwloc_cpuset_t& current_mask = affinity_masks_list[index]; 167*51c0b2f7Stbbdev current_mask = hwloc_bitmap_dup(node_buffer->cpuset); 168*51c0b2f7Stbbdev 169*51c0b2f7Stbbdev hwloc_bitmap_and(current_mask, current_mask, process_cpu_affinity_mask); 170*51c0b2f7Stbbdev __TBB_ASSERT(!hwloc_bitmap_iszero(current_mask), "hwloc detected unavailable NUMA node"); 171*51c0b2f7Stbbdev default_concurrency_list[index] = hwloc_bitmap_weight(current_mask); 172*51c0b2f7Stbbdev } hwloc_bitmap_foreach_end(); 173*51c0b2f7Stbbdev initialization_state = topology_parsed; 174*51c0b2f7Stbbdev } 175*51c0b2f7Stbbdev 176*51c0b2f7Stbbdev ~platform_topology() { 177*51c0b2f7Stbbdev if ( is_topology_parsed() ) { 178*51c0b2f7Stbbdev for (int i = 0; i < numa_nodes_count; i++) { 179*51c0b2f7Stbbdev hwloc_bitmap_free(affinity_masks_list[numa_indexes_list[i]]); 180*51c0b2f7Stbbdev } 181*51c0b2f7Stbbdev hwloc_bitmap_free(process_node_affinity_mask); 182*51c0b2f7Stbbdev hwloc_bitmap_free(process_cpu_affinity_mask); 183*51c0b2f7Stbbdev } 184*51c0b2f7Stbbdev 185*51c0b2f7Stbbdev if ( initialization_state >= topology_allocated ) { 186*51c0b2f7Stbbdev hwloc_topology_destroy(topology); 187*51c0b2f7Stbbdev } 188*51c0b2f7Stbbdev 189*51c0b2f7Stbbdev initialization_state = uninitialized; 190*51c0b2f7Stbbdev } 191*51c0b2f7Stbbdev 192*51c0b2f7Stbbdev void fill(int& nodes_count, int*& indexes_list, int*& concurrency_list ) { 193*51c0b2f7Stbbdev __TBB_ASSERT(is_topology_parsed(), "Trying to get access to uninitialized platform_topology"); 194*51c0b2f7Stbbdev nodes_count = numa_nodes_count; 195*51c0b2f7Stbbdev indexes_list = &numa_indexes_list.front(); 196*51c0b2f7Stbbdev concurrency_list = &default_concurrency_list.front(); 197*51c0b2f7Stbbdev } 198*51c0b2f7Stbbdev 199*51c0b2f7Stbbdev affinity_mask allocate_process_affinity_mask() { 200*51c0b2f7Stbbdev __TBB_ASSERT(is_topology_parsed(), "Trying to get access to uninitialized platform_topology"); 201*51c0b2f7Stbbdev return hwloc_bitmap_dup(process_cpu_affinity_mask); 202*51c0b2f7Stbbdev } 203*51c0b2f7Stbbdev 204*51c0b2f7Stbbdev void free_affinity_mask( affinity_mask mask_to_free ) { 205*51c0b2f7Stbbdev hwloc_bitmap_free(mask_to_free); // If bitmap is nullptr, no operation is performed. 206*51c0b2f7Stbbdev } 207*51c0b2f7Stbbdev 208*51c0b2f7Stbbdev void store_current_affinity_mask( affinity_mask current_mask ) { 209*51c0b2f7Stbbdev assertion_hwloc_wrapper(hwloc_get_cpubind, topology, current_mask, HWLOC_CPUBIND_THREAD); 210*51c0b2f7Stbbdev 211*51c0b2f7Stbbdev hwloc_bitmap_and(current_mask, current_mask, process_cpu_affinity_mask); 212*51c0b2f7Stbbdev __TBB_ASSERT(!hwloc_bitmap_iszero(current_mask), 213*51c0b2f7Stbbdev "Current affinity mask must intersects with process affinity mask"); 214*51c0b2f7Stbbdev } 215*51c0b2f7Stbbdev 216*51c0b2f7Stbbdev void set_new_affinity_mask( const_affinity_mask new_mask ) { 217*51c0b2f7Stbbdev assertion_hwloc_wrapper(hwloc_set_cpubind, topology, new_mask, HWLOC_CPUBIND_THREAD); 218*51c0b2f7Stbbdev } 219*51c0b2f7Stbbdev 220*51c0b2f7Stbbdev const_affinity_mask get_node_affinity_mask( int node_index ) { 221*51c0b2f7Stbbdev __TBB_ASSERT((int)affinity_masks_list.size() > node_index, 222*51c0b2f7Stbbdev "Trying to get affinity mask for uninitialized NUMA node"); 223*51c0b2f7Stbbdev return affinity_masks_list[node_index]; 224*51c0b2f7Stbbdev } 225*51c0b2f7Stbbdev }; 226*51c0b2f7Stbbdev 227*51c0b2f7Stbbdev class binding_handler { 228*51c0b2f7Stbbdev // Following vector saves thread affinity mask on scheduler entry to return it to this thread 229*51c0b2f7Stbbdev // on scheduler exit. 230*51c0b2f7Stbbdev typedef std::vector<platform_topology::affinity_mask> affinity_masks_container; 231*51c0b2f7Stbbdev affinity_masks_container affinity_backup; 232*51c0b2f7Stbbdev 233*51c0b2f7Stbbdev public: 234*51c0b2f7Stbbdev binding_handler( size_t size ) : affinity_backup(size) { 235*51c0b2f7Stbbdev for (affinity_masks_container::iterator it = affinity_backup.begin(); 236*51c0b2f7Stbbdev it != affinity_backup.end(); it++) { 237*51c0b2f7Stbbdev *it = platform_topology::instance().allocate_process_affinity_mask(); 238*51c0b2f7Stbbdev } 239*51c0b2f7Stbbdev } 240*51c0b2f7Stbbdev 241*51c0b2f7Stbbdev ~binding_handler() { 242*51c0b2f7Stbbdev for (affinity_masks_container::iterator it = affinity_backup.begin(); 243*51c0b2f7Stbbdev it != affinity_backup.end(); it++) { 244*51c0b2f7Stbbdev platform_topology::instance().free_affinity_mask(*it); 245*51c0b2f7Stbbdev } 246*51c0b2f7Stbbdev } 247*51c0b2f7Stbbdev 248*51c0b2f7Stbbdev void bind_thread_to_node( unsigned slot_num, unsigned numa_node_id ) { 249*51c0b2f7Stbbdev __TBB_ASSERT(slot_num < affinity_backup.size(), 250*51c0b2f7Stbbdev "The slot number is greater than the number of slots in the arena"); 251*51c0b2f7Stbbdev __TBB_ASSERT(platform_topology::instance().is_topology_parsed(), 252*51c0b2f7Stbbdev "Trying to get access to uninitialized platform_topology"); 253*51c0b2f7Stbbdev platform_topology::instance().store_current_affinity_mask(affinity_backup[slot_num]); 254*51c0b2f7Stbbdev 255*51c0b2f7Stbbdev platform_topology::instance().set_new_affinity_mask( 256*51c0b2f7Stbbdev platform_topology::instance().get_node_affinity_mask(numa_node_id)); 257*51c0b2f7Stbbdev } 258*51c0b2f7Stbbdev 259*51c0b2f7Stbbdev void restore_previous_affinity_mask( unsigned slot_num ) { 260*51c0b2f7Stbbdev __TBB_ASSERT(platform_topology::instance().is_topology_parsed(), 261*51c0b2f7Stbbdev "Trying to get access to uninitialized platform_topology"); 262*51c0b2f7Stbbdev platform_topology::instance().set_new_affinity_mask(affinity_backup[slot_num]); 263*51c0b2f7Stbbdev }; 264*51c0b2f7Stbbdev 265*51c0b2f7Stbbdev }; 266*51c0b2f7Stbbdev 267*51c0b2f7Stbbdev extern "C" { // exported to TBB interfaces 268*51c0b2f7Stbbdev 269*51c0b2f7Stbbdev void __TBB_internal_initialize_numa_topology( size_t groups_num, int& nodes_count, int*& indexes_list, int*& concurrency_list ) { 270*51c0b2f7Stbbdev platform_topology::instance().initialize(groups_num); 271*51c0b2f7Stbbdev platform_topology::instance().fill(nodes_count, indexes_list, concurrency_list); 272*51c0b2f7Stbbdev } 273*51c0b2f7Stbbdev 274*51c0b2f7Stbbdev binding_handler* __TBB_internal_allocate_binding_handler(int slot_num) { 275*51c0b2f7Stbbdev __TBB_ASSERT(slot_num > 0, "Trying to create numa handler for 0 threads."); 276*51c0b2f7Stbbdev return new binding_handler(slot_num); 277*51c0b2f7Stbbdev } 278*51c0b2f7Stbbdev 279*51c0b2f7Stbbdev void __TBB_internal_deallocate_binding_handler(binding_handler* handler_ptr) { 280*51c0b2f7Stbbdev __TBB_ASSERT(handler_ptr != nullptr, "Trying to deallocate nullptr pointer."); 281*51c0b2f7Stbbdev delete handler_ptr; 282*51c0b2f7Stbbdev } 283*51c0b2f7Stbbdev 284*51c0b2f7Stbbdev void __TBB_internal_bind_to_node(binding_handler* handler_ptr, int slot_num, int numa_id) { 285*51c0b2f7Stbbdev __TBB_ASSERT(handler_ptr != nullptr, "Trying to get access to uninitialized metadata."); 286*51c0b2f7Stbbdev __TBB_ASSERT(platform_topology::instance().is_topology_parsed(), 287*51c0b2f7Stbbdev "Trying to get access to uninitialized platform_topology."); 288*51c0b2f7Stbbdev handler_ptr->bind_thread_to_node(slot_num, numa_id); 289*51c0b2f7Stbbdev } 290*51c0b2f7Stbbdev 291*51c0b2f7Stbbdev void __TBB_internal_restore_affinity(binding_handler* handler_ptr, int slot_num) { 292*51c0b2f7Stbbdev __TBB_ASSERT(handler_ptr != nullptr, "Trying to get access to uninitialized metadata."); 293*51c0b2f7Stbbdev __TBB_ASSERT(platform_topology::instance().is_topology_parsed(), 294*51c0b2f7Stbbdev "Trying to get access to uninitialized platform_topology."); 295*51c0b2f7Stbbdev handler_ptr->restore_previous_affinity_mask(slot_num); 296*51c0b2f7Stbbdev } 297*51c0b2f7Stbbdev 298*51c0b2f7Stbbdev } // extern "C" 299*51c0b2f7Stbbdev 300*51c0b2f7Stbbdev } // namespace r1 301*51c0b2f7Stbbdev } // namespace detail 302*51c0b2f7Stbbdev } // namespace tbb 303*51c0b2f7Stbbdev 304*51c0b2f7Stbbdev #undef assertion_hwloc_wrapper 305