1 /*
   2  * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 
  25 #include "precompiled.hpp"
  26 #include "gc/g1/g1NUMA.inline.hpp"
  27 #include "gc/g1/heapRegion.hpp"
  28 
  29 G1NUMA* G1NUMA::_inst = NULL;
  30 
  31 void G1NUMA::init_numa_id_to_index_map(const int* numa_ids, uint num_numa_ids) {
  32   int max_numa_id = 0;
  33   for (uint i = 0; i < num_numa_ids; i++) {
  34     if (numa_ids[i] > max_numa_id) {
  35       max_numa_id = numa_ids[i];
  36     }
  37   }
  38 
  39   _len_numa_id_to_index_map = max_numa_id + 1;
  40   _numa_id_to_index_map = NEW_C_HEAP_ARRAY(uint, _len_numa_id_to_index_map, mtGC);
  41   // Set all indices with invalid numa id.
  42   memset(_numa_id_to_index_map, 
  43          G1MemoryNodeManager::InvalidNodeIndex,
  44          sizeof(uint) * _len_numa_id_to_index_map);
  45 
  46   // Set the indices for the actually retrieved numa ids.
  47   for (uint i = 0; i < num_numa_ids; i++) {
  48      int numa_id = numa_ids[i];
  49     guarantee(is_valid_numa_id(numa_id), "must be representable in map, numa id(%d)", numa_id);
  50      _numa_id_to_index_map[numa_id] = i;
  51   }
  52 }
  53 
  54 void G1NUMA::touch_memory_whole(address aligned_address, size_t size_in_bytes, uint numa_index) {
  55     log_debug(gc, heap, numa)("Touch memory [" PTR_FORMAT ", " PTR_FORMAT ") to be numa id (%d).",
  56                              p2i(aligned_address), p2i(aligned_address + size_in_bytes), _numa_ids[numa_index]);
  57 
  58     // If we have preferred numa id, set the whole given area with it.
  59     os::numa_make_local((char*)aligned_address, size_in_bytes, _numa_ids[numa_index]);
  60 }
  61 
  62 void G1NUMA::touch_memory_roundrobin(address aligned_address, size_t size_in_bytes) {
  63   // If we don't have preferred numa id, touch the given area with round-robin manner.
  64   size_t chunk_size;
  65   if (HeapRegion::GrainBytes >= _page_size) {
  66     chunk_size = HeapRegion::GrainBytes;
  67   } else {
  68     chunk_size = _page_size;
  69   }
  70 
  71   assert(is_aligned(size_in_bytes, chunk_size), "Size to touch " SIZE_FORMAT " should be aligned to " SIZE_FORMAT,
  72          size_in_bytes, chunk_size);
  73 
  74   address start_addr = aligned_address;
  75   address end_addr = aligned_address + size_in_bytes;
  76 
  77   log_debug(gc, heap, numa)("Start touch memory [" PTR_FORMAT ", " PTR_FORMAT
  78                             "), chunk_size=" SIZE_FORMAT " with round-robin manner.",
  79                             p2i(start_addr), p2i(end_addr), chunk_size);
  80 
  81   do {
  82     uint numa_index = next_numa_index();
  83 
  84     log_trace(gc, heap, numa)("Touch memory [" PTR_FORMAT ", " PTR_FORMAT ") to be numa id (%d).",
  85                              p2i(start_addr), p2i(start_addr + chunk_size), _numa_ids[numa_index]);
  86     os::numa_make_local((char*)start_addr, chunk_size, _numa_ids[numa_index]);
  87 
  88     start_addr += chunk_size;
  89   } while (start_addr < end_addr);
  90 }
  91 
  92 void G1NUMA::touch_memory(address aligned_address, size_t size_in_bytes, uint numa_index) {
  93   if (size_in_bytes == 0) {
  94     return;
  95   }
  96 
  97   if (is_valid_numa_index(numa_index)) {
  98     touch_memory_whole(aligned_address, size_in_bytes,  numa_index);
  99   } else {
 100     touch_memory_roundrobin(aligned_address, size_in_bytes);
 101   }
 102 }
 103 
 104 bool G1NUMA::initialize() {
 105   assert(UseNUMA, "Invariant");
 106 
 107   size_t num_numa_ids = os::numa_get_groups_num();
 108 
 109   _numa_ids = NEW_C_HEAP_ARRAY(int, num_numa_ids, mtGC);
 110   _num_active_numa_ids = (uint)os::numa_get_leaf_groups(_numa_ids, num_numa_ids);
 111 
 112   // To start from 0 at the first call of next_numa_index(), set to the greatest one.
 113   _next_numa_index = _num_active_numa_ids - 1;
 114 
 115   init_numa_id_to_index_map(_numa_ids, _num_active_numa_ids);
 116 
 117   return true;
 118 }
 119 
 120 void G1NUMA::set_page_size(size_t page_size) {
 121   _page_size = page_size;
 122 }
 123 
 124 G1NUMA::~G1NUMA() {
 125   FREE_C_HEAP_ARRAY(int, _numa_id_to_index_map);
 126   FREE_C_HEAP_ARRAY(int, _numa_ids);
 127 }
 128 
 129 uint G1NUMA::index_of_address(HeapWord* addr) const {
 130   int numa_id = os::numa_get_address_id((uintptr_t)addr);
 131 
 132   return index_of_numa_id(numa_id);
 133 }