151c0b2f7Stbbdev /* 251c0b2f7Stbbdev Copyright (c) 2019-2020 Intel Corporation 351c0b2f7Stbbdev 451c0b2f7Stbbdev Licensed under the Apache License, Version 2.0 (the "License"); 551c0b2f7Stbbdev you may not use this file except in compliance with the License. 651c0b2f7Stbbdev You may obtain a copy of the License at 751c0b2f7Stbbdev 851c0b2f7Stbbdev http://www.apache.org/licenses/LICENSE-2.0 951c0b2f7Stbbdev 1051c0b2f7Stbbdev Unless required by applicable law or agreed to in writing, software 1151c0b2f7Stbbdev distributed under the License is distributed on an "AS IS" BASIS, 1251c0b2f7Stbbdev WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1351c0b2f7Stbbdev See the License for the specific language governing permissions and 1451c0b2f7Stbbdev limitations under the License. 1551c0b2f7Stbbdev */ 1651c0b2f7Stbbdev 1751c0b2f7Stbbdev #include "../tbb/assert_impl.h" // Out-of-line TBB assertion handling routines are instantiated here. 18*49e08aacStbbdev #include "oneapi/tbb/detail/_assert.h" 1951c0b2f7Stbbdev 2051c0b2f7Stbbdev #if _MSC_VER && !__INTEL_COMPILER 2151c0b2f7Stbbdev #pragma warning( push ) 2251c0b2f7Stbbdev #pragma warning( disable : 4100 ) 2351c0b2f7Stbbdev #endif 2451c0b2f7Stbbdev #include <hwloc.h> 2551c0b2f7Stbbdev #if _MSC_VER && !__INTEL_COMPILER 2651c0b2f7Stbbdev #pragma warning( pop ) 2751c0b2f7Stbbdev #endif 2851c0b2f7Stbbdev 2951c0b2f7Stbbdev #include <vector> 3051c0b2f7Stbbdev 3151c0b2f7Stbbdev // Most of hwloc calls returns negative exit code on error. 3251c0b2f7Stbbdev // This macro tracks error codes that are returned from the hwloc interfaces. 3351c0b2f7Stbbdev #define assertion_hwloc_wrapper(command, ...) \ 3451c0b2f7Stbbdev __TBB_ASSERT_EX( (command(__VA_ARGS__)) >= 0, "Error occurred during call to hwloc API."); 3551c0b2f7Stbbdev 3651c0b2f7Stbbdev namespace tbb { 3751c0b2f7Stbbdev namespace detail { 3851c0b2f7Stbbdev namespace r1 { 3951c0b2f7Stbbdev 4051c0b2f7Stbbdev //------------------------------------------------------------------------ 4151c0b2f7Stbbdev // Information about the machine's hardware TBB is happen to work on 4251c0b2f7Stbbdev //------------------------------------------------------------------------ 4351c0b2f7Stbbdev class platform_topology { 4451c0b2f7Stbbdev friend class numa_affinity_handler; 4551c0b2f7Stbbdev 4651c0b2f7Stbbdev // TODO: add the `my_` prefix to the members 4751c0b2f7Stbbdev hwloc_topology_t topology; 4851c0b2f7Stbbdev hwloc_cpuset_t process_cpu_affinity_mask; 4951c0b2f7Stbbdev hwloc_nodeset_t process_node_affinity_mask; 5051c0b2f7Stbbdev std::vector<hwloc_cpuset_t> affinity_masks_list; 5151c0b2f7Stbbdev 5251c0b2f7Stbbdev std::vector<int> default_concurrency_list; 5351c0b2f7Stbbdev std::vector<int> numa_indexes_list; 5451c0b2f7Stbbdev int numa_nodes_count; 5551c0b2f7Stbbdev 5651c0b2f7Stbbdev enum init_stages { uninitialized, 5751c0b2f7Stbbdev started, 5851c0b2f7Stbbdev topology_allocated, 5951c0b2f7Stbbdev topology_loaded, 6051c0b2f7Stbbdev topology_parsed } initialization_state; 6151c0b2f7Stbbdev 6251c0b2f7Stbbdev // Binding threads to NUMA nodes that locates in another Windows Processor groups 6351c0b2f7Stbbdev // is allowed only if machine topology contains several Windows Processors groups 6451c0b2f7Stbbdev // and process affinity mask wasn`t limited manually (affinity mask cannot violates 6551c0b2f7Stbbdev // processors group boundaries). 6651c0b2f7Stbbdev bool intergroup_binding_allowed(size_t groups_num) { return groups_num > 1; } 6751c0b2f7Stbbdev 6851c0b2f7Stbbdev platform_topology() : topology(nullptr), 6951c0b2f7Stbbdev process_cpu_affinity_mask(nullptr), 7051c0b2f7Stbbdev process_node_affinity_mask(nullptr), 7151c0b2f7Stbbdev numa_nodes_count(0), 7251c0b2f7Stbbdev initialization_state(uninitialized) {} 7351c0b2f7Stbbdev 7451c0b2f7Stbbdev public: 7551c0b2f7Stbbdev typedef hwloc_cpuset_t affinity_mask; 7651c0b2f7Stbbdev typedef hwloc_const_cpuset_t const_affinity_mask; 7751c0b2f7Stbbdev 7851c0b2f7Stbbdev static platform_topology& instance() { 7951c0b2f7Stbbdev static platform_topology topology; 8051c0b2f7Stbbdev return topology; 8151c0b2f7Stbbdev } 8251c0b2f7Stbbdev 8351c0b2f7Stbbdev bool is_topology_parsed() { return initialization_state == topology_parsed; } 8451c0b2f7Stbbdev 8551c0b2f7Stbbdev void initialize( size_t groups_num ) { 8651c0b2f7Stbbdev if ( initialization_state != uninitialized ) 8751c0b2f7Stbbdev return; 8851c0b2f7Stbbdev initialization_state = started; 8951c0b2f7Stbbdev 9051c0b2f7Stbbdev // Parse topology 9151c0b2f7Stbbdev if ( hwloc_topology_init( &topology ) == 0 ) { 9251c0b2f7Stbbdev initialization_state = topology_allocated; 9351c0b2f7Stbbdev if ( hwloc_topology_load( topology ) == 0 ) { 9451c0b2f7Stbbdev initialization_state = topology_loaded; 9551c0b2f7Stbbdev } 9651c0b2f7Stbbdev } 9751c0b2f7Stbbdev 9851c0b2f7Stbbdev // Fill parameters with stubs if topology parsing is broken. 9951c0b2f7Stbbdev if ( initialization_state != topology_loaded ) { 10051c0b2f7Stbbdev if ( initialization_state == topology_allocated ) { 10151c0b2f7Stbbdev hwloc_topology_destroy(topology); 10251c0b2f7Stbbdev } 10351c0b2f7Stbbdev numa_nodes_count = 1; 10451c0b2f7Stbbdev numa_indexes_list.push_back(-1); 10551c0b2f7Stbbdev default_concurrency_list.push_back(-1); 10651c0b2f7Stbbdev return; 10751c0b2f7Stbbdev } 10851c0b2f7Stbbdev 10951c0b2f7Stbbdev // Getting process affinity mask 11051c0b2f7Stbbdev if ( intergroup_binding_allowed(groups_num) ) { 11151c0b2f7Stbbdev process_cpu_affinity_mask = hwloc_bitmap_dup(hwloc_topology_get_complete_cpuset (topology)); 11251c0b2f7Stbbdev process_node_affinity_mask = hwloc_bitmap_dup(hwloc_topology_get_complete_nodeset(topology)); 11351c0b2f7Stbbdev } else { 11451c0b2f7Stbbdev process_cpu_affinity_mask = hwloc_bitmap_alloc(); 11551c0b2f7Stbbdev process_node_affinity_mask = hwloc_bitmap_alloc(); 11651c0b2f7Stbbdev 11751c0b2f7Stbbdev assertion_hwloc_wrapper(hwloc_get_cpubind, topology, process_cpu_affinity_mask, 0); 11851c0b2f7Stbbdev hwloc_cpuset_to_nodeset(topology, process_cpu_affinity_mask, process_node_affinity_mask); 11951c0b2f7Stbbdev } 12051c0b2f7Stbbdev 12151c0b2f7Stbbdev // Get the number of available NUMA nodes 12251c0b2f7Stbbdev // If system contains no NUMA nodes, HWLOC 1.11 returns an infinitely filled bitmap. 12351c0b2f7Stbbdev // hwloc_bitmap_weight() returns negative value for such bitmaps, so we use this check 12451c0b2f7Stbbdev // to change way of topology initialization. 12551c0b2f7Stbbdev numa_nodes_count = hwloc_bitmap_weight(process_node_affinity_mask); 12651c0b2f7Stbbdev if (numa_nodes_count <= 0) { 12751c0b2f7Stbbdev // numa_nodes_count may be empty if the process affinity mask is empty too (invalid case) 12851c0b2f7Stbbdev // or if some internal HWLOC error occurred. 12951c0b2f7Stbbdev // So we place -1 as index in this case. 13051c0b2f7Stbbdev numa_indexes_list.push_back(numa_nodes_count == 0 ? -1 : 0); 13151c0b2f7Stbbdev numa_nodes_count = 1; 13251c0b2f7Stbbdev default_concurrency_list.push_back(hwloc_bitmap_weight(process_cpu_affinity_mask)); 13351c0b2f7Stbbdev 13451c0b2f7Stbbdev affinity_masks_list.push_back(hwloc_bitmap_dup(process_cpu_affinity_mask)); 13551c0b2f7Stbbdev initialization_state = topology_parsed; 13651c0b2f7Stbbdev return; 13751c0b2f7Stbbdev } 13851c0b2f7Stbbdev 13951c0b2f7Stbbdev // Get NUMA logical indexes list 14051c0b2f7Stbbdev unsigned counter = 0; 14151c0b2f7Stbbdev int i = 0; 14251c0b2f7Stbbdev int max_numa_index = -1; 14351c0b2f7Stbbdev numa_indexes_list.resize(numa_nodes_count); 14451c0b2f7Stbbdev hwloc_obj_t node_buffer; 14551c0b2f7Stbbdev hwloc_bitmap_foreach_begin(i, process_node_affinity_mask) { 14651c0b2f7Stbbdev node_buffer = hwloc_get_obj_by_type(topology, HWLOC_OBJ_NUMANODE, i); 14751c0b2f7Stbbdev numa_indexes_list[counter] = static_cast<int>(node_buffer->logical_index); 14851c0b2f7Stbbdev 14951c0b2f7Stbbdev if ( numa_indexes_list[counter] > max_numa_index ) { 15051c0b2f7Stbbdev max_numa_index = numa_indexes_list[counter]; 15151c0b2f7Stbbdev } 15251c0b2f7Stbbdev 15351c0b2f7Stbbdev counter++; 15451c0b2f7Stbbdev } hwloc_bitmap_foreach_end(); 15551c0b2f7Stbbdev __TBB_ASSERT(max_numa_index >= 0, "Maximal NUMA index must not be negative"); 15651c0b2f7Stbbdev 15751c0b2f7Stbbdev // Fill concurrency and affinity masks lists 15851c0b2f7Stbbdev default_concurrency_list.resize(max_numa_index + 1); 15951c0b2f7Stbbdev affinity_masks_list.resize(max_numa_index + 1); 16051c0b2f7Stbbdev 16151c0b2f7Stbbdev int index = 0; 16251c0b2f7Stbbdev hwloc_bitmap_foreach_begin(i, process_node_affinity_mask) { 16351c0b2f7Stbbdev node_buffer = hwloc_get_obj_by_type(topology, HWLOC_OBJ_NUMANODE, i); 16451c0b2f7Stbbdev index = static_cast<int>(node_buffer->logical_index); 16551c0b2f7Stbbdev 16651c0b2f7Stbbdev hwloc_cpuset_t& current_mask = affinity_masks_list[index]; 16751c0b2f7Stbbdev current_mask = hwloc_bitmap_dup(node_buffer->cpuset); 16851c0b2f7Stbbdev 16951c0b2f7Stbbdev hwloc_bitmap_and(current_mask, current_mask, process_cpu_affinity_mask); 17051c0b2f7Stbbdev __TBB_ASSERT(!hwloc_bitmap_iszero(current_mask), "hwloc detected unavailable NUMA node"); 17151c0b2f7Stbbdev default_concurrency_list[index] = hwloc_bitmap_weight(current_mask); 17251c0b2f7Stbbdev } hwloc_bitmap_foreach_end(); 17351c0b2f7Stbbdev initialization_state = topology_parsed; 17451c0b2f7Stbbdev } 17551c0b2f7Stbbdev 17651c0b2f7Stbbdev ~platform_topology() { 17751c0b2f7Stbbdev if ( is_topology_parsed() ) { 17851c0b2f7Stbbdev for (int i = 0; i < numa_nodes_count; i++) { 17951c0b2f7Stbbdev hwloc_bitmap_free(affinity_masks_list[numa_indexes_list[i]]); 18051c0b2f7Stbbdev } 18151c0b2f7Stbbdev hwloc_bitmap_free(process_node_affinity_mask); 18251c0b2f7Stbbdev hwloc_bitmap_free(process_cpu_affinity_mask); 18351c0b2f7Stbbdev } 18451c0b2f7Stbbdev 18551c0b2f7Stbbdev if ( initialization_state >= topology_allocated ) { 18651c0b2f7Stbbdev hwloc_topology_destroy(topology); 18751c0b2f7Stbbdev } 18851c0b2f7Stbbdev 18951c0b2f7Stbbdev initialization_state = uninitialized; 19051c0b2f7Stbbdev } 19151c0b2f7Stbbdev 19251c0b2f7Stbbdev void fill(int& nodes_count, int*& indexes_list, int*& concurrency_list ) { 19351c0b2f7Stbbdev __TBB_ASSERT(is_topology_parsed(), "Trying to get access to uninitialized platform_topology"); 19451c0b2f7Stbbdev nodes_count = numa_nodes_count; 19551c0b2f7Stbbdev indexes_list = &numa_indexes_list.front(); 19651c0b2f7Stbbdev concurrency_list = &default_concurrency_list.front(); 19751c0b2f7Stbbdev } 19851c0b2f7Stbbdev 19951c0b2f7Stbbdev affinity_mask allocate_process_affinity_mask() { 20051c0b2f7Stbbdev __TBB_ASSERT(is_topology_parsed(), "Trying to get access to uninitialized platform_topology"); 20151c0b2f7Stbbdev return hwloc_bitmap_dup(process_cpu_affinity_mask); 20251c0b2f7Stbbdev } 20351c0b2f7Stbbdev 20451c0b2f7Stbbdev void free_affinity_mask( affinity_mask mask_to_free ) { 20551c0b2f7Stbbdev hwloc_bitmap_free(mask_to_free); // If bitmap is nullptr, no operation is performed. 20651c0b2f7Stbbdev } 20751c0b2f7Stbbdev 20851c0b2f7Stbbdev void store_current_affinity_mask( affinity_mask current_mask ) { 20951c0b2f7Stbbdev assertion_hwloc_wrapper(hwloc_get_cpubind, topology, current_mask, HWLOC_CPUBIND_THREAD); 21051c0b2f7Stbbdev 21151c0b2f7Stbbdev hwloc_bitmap_and(current_mask, current_mask, process_cpu_affinity_mask); 21251c0b2f7Stbbdev __TBB_ASSERT(!hwloc_bitmap_iszero(current_mask), 21351c0b2f7Stbbdev "Current affinity mask must intersects with process affinity mask"); 21451c0b2f7Stbbdev } 21551c0b2f7Stbbdev 21651c0b2f7Stbbdev void set_new_affinity_mask( const_affinity_mask new_mask ) { 21751c0b2f7Stbbdev assertion_hwloc_wrapper(hwloc_set_cpubind, topology, new_mask, HWLOC_CPUBIND_THREAD); 21851c0b2f7Stbbdev } 21951c0b2f7Stbbdev 22051c0b2f7Stbbdev const_affinity_mask get_node_affinity_mask( int node_index ) { 22151c0b2f7Stbbdev __TBB_ASSERT((int)affinity_masks_list.size() > node_index, 22251c0b2f7Stbbdev "Trying to get affinity mask for uninitialized NUMA node"); 22351c0b2f7Stbbdev return affinity_masks_list[node_index]; 22451c0b2f7Stbbdev } 22551c0b2f7Stbbdev }; 22651c0b2f7Stbbdev 22751c0b2f7Stbbdev class binding_handler { 22851c0b2f7Stbbdev // Following vector saves thread affinity mask on scheduler entry to return it to this thread 22951c0b2f7Stbbdev // on scheduler exit. 23051c0b2f7Stbbdev typedef std::vector<platform_topology::affinity_mask> affinity_masks_container; 23151c0b2f7Stbbdev affinity_masks_container affinity_backup; 23251c0b2f7Stbbdev 23351c0b2f7Stbbdev public: 23451c0b2f7Stbbdev binding_handler( size_t size ) : affinity_backup(size) { 23551c0b2f7Stbbdev for (affinity_masks_container::iterator it = affinity_backup.begin(); 23651c0b2f7Stbbdev it != affinity_backup.end(); it++) { 23751c0b2f7Stbbdev *it = platform_topology::instance().allocate_process_affinity_mask(); 23851c0b2f7Stbbdev } 23951c0b2f7Stbbdev } 24051c0b2f7Stbbdev 24151c0b2f7Stbbdev ~binding_handler() { 24251c0b2f7Stbbdev for (affinity_masks_container::iterator it = affinity_backup.begin(); 24351c0b2f7Stbbdev it != affinity_backup.end(); it++) { 24451c0b2f7Stbbdev platform_topology::instance().free_affinity_mask(*it); 24551c0b2f7Stbbdev } 24651c0b2f7Stbbdev } 24751c0b2f7Stbbdev 24851c0b2f7Stbbdev void bind_thread_to_node( unsigned slot_num, unsigned numa_node_id ) { 24951c0b2f7Stbbdev __TBB_ASSERT(slot_num < affinity_backup.size(), 25051c0b2f7Stbbdev "The slot number is greater than the number of slots in the arena"); 25151c0b2f7Stbbdev __TBB_ASSERT(platform_topology::instance().is_topology_parsed(), 25251c0b2f7Stbbdev "Trying to get access to uninitialized platform_topology"); 25351c0b2f7Stbbdev platform_topology::instance().store_current_affinity_mask(affinity_backup[slot_num]); 25451c0b2f7Stbbdev 25551c0b2f7Stbbdev platform_topology::instance().set_new_affinity_mask( 25651c0b2f7Stbbdev platform_topology::instance().get_node_affinity_mask(numa_node_id)); 25751c0b2f7Stbbdev } 25851c0b2f7Stbbdev 25951c0b2f7Stbbdev void restore_previous_affinity_mask( unsigned slot_num ) { 26051c0b2f7Stbbdev __TBB_ASSERT(platform_topology::instance().is_topology_parsed(), 26151c0b2f7Stbbdev "Trying to get access to uninitialized platform_topology"); 26251c0b2f7Stbbdev platform_topology::instance().set_new_affinity_mask(affinity_backup[slot_num]); 26351c0b2f7Stbbdev }; 26451c0b2f7Stbbdev 26551c0b2f7Stbbdev }; 26651c0b2f7Stbbdev 26751c0b2f7Stbbdev extern "C" { // exported to TBB interfaces 26851c0b2f7Stbbdev 26951c0b2f7Stbbdev void __TBB_internal_initialize_numa_topology( size_t groups_num, int& nodes_count, int*& indexes_list, int*& concurrency_list ) { 27051c0b2f7Stbbdev platform_topology::instance().initialize(groups_num); 27151c0b2f7Stbbdev platform_topology::instance().fill(nodes_count, indexes_list, concurrency_list); 27251c0b2f7Stbbdev } 27351c0b2f7Stbbdev 27451c0b2f7Stbbdev binding_handler* __TBB_internal_allocate_binding_handler(int slot_num) { 27551c0b2f7Stbbdev __TBB_ASSERT(slot_num > 0, "Trying to create numa handler for 0 threads."); 27651c0b2f7Stbbdev return new binding_handler(slot_num); 27751c0b2f7Stbbdev } 27851c0b2f7Stbbdev 27951c0b2f7Stbbdev void __TBB_internal_deallocate_binding_handler(binding_handler* handler_ptr) { 28051c0b2f7Stbbdev __TBB_ASSERT(handler_ptr != nullptr, "Trying to deallocate nullptr pointer."); 28151c0b2f7Stbbdev delete handler_ptr; 28251c0b2f7Stbbdev } 28351c0b2f7Stbbdev 28451c0b2f7Stbbdev void __TBB_internal_bind_to_node(binding_handler* handler_ptr, int slot_num, int numa_id) { 28551c0b2f7Stbbdev __TBB_ASSERT(handler_ptr != nullptr, "Trying to get access to uninitialized metadata."); 28651c0b2f7Stbbdev __TBB_ASSERT(platform_topology::instance().is_topology_parsed(), 28751c0b2f7Stbbdev "Trying to get access to uninitialized platform_topology."); 28851c0b2f7Stbbdev handler_ptr->bind_thread_to_node(slot_num, numa_id); 28951c0b2f7Stbbdev } 29051c0b2f7Stbbdev 29151c0b2f7Stbbdev void __TBB_internal_restore_affinity(binding_handler* handler_ptr, int slot_num) { 29251c0b2f7Stbbdev __TBB_ASSERT(handler_ptr != nullptr, "Trying to get access to uninitialized metadata."); 29351c0b2f7Stbbdev __TBB_ASSERT(platform_topology::instance().is_topology_parsed(), 29451c0b2f7Stbbdev "Trying to get access to uninitialized platform_topology."); 29551c0b2f7Stbbdev handler_ptr->restore_previous_affinity_mask(slot_num); 29651c0b2f7Stbbdev } 29751c0b2f7Stbbdev 29851c0b2f7Stbbdev } // extern "C" 29951c0b2f7Stbbdev 30051c0b2f7Stbbdev } // namespace r1 30151c0b2f7Stbbdev } // namespace detail 30251c0b2f7Stbbdev } // namespace tbb 30351c0b2f7Stbbdev 30451c0b2f7Stbbdev #undef assertion_hwloc_wrapper 305