Program Listing for File RegionManager.cpp

Return to documentation for file (umap/RegionManager.cpp)

//////////////////////////////////////////////////////////////////////////////
// Copyright 2017-2020 Lawrence Livermore National Security, LLC and other
// UMAP Project Developers. See the top-level LICENSE file for details.
//
// SPDX-License-Identifier: LGPL-2.1-only
//////////////////////////////////////////////////////////////////////////////
#include "umap/config.h"

#include <cstdint>        // uint64_t
#include <fstream>        // for reading meminfo
#include <mutex>
#include <stdlib.h>       // getenv()
#include <sstream>        // string to integer operations
#include <string>         // string to integer operations
#include <thread>         // for max_concurrency
#include <unordered_map>
#include <unistd.h>       // sysconf()

#include "umap/Buffer.hpp"
#include "umap/EvictManager.hpp"
#include "umap/FillWorkers.hpp"
#include "umap/RegionManager.hpp"
#include "umap/RegionDescriptor.hpp"
#include "umap/store/Store.hpp"
#include "umap/util/Macros.hpp"

namespace Umap {

RegionManager&
RegionManager::getInstance( void )
{
  static RegionManager region_manager_instance;

  return region_manager_instance;
}

void
RegionManager::addRegion(Store* store, char* region, uint64_t region_size, char* mmap_region, uint64_t mmap_region_size)
{
  std::lock_guard<std::mutex> lock(m_mutex);

  if ( m_active_regions.empty() ) {
    UMAP_LOG(Debug, "No active regions, initializing engine");
    m_buffer = new Buffer();
    m_uffd = new Uffd();
    m_fill_workers = new FillWorkers();
    m_evict_manager = new EvictManager();
  }

  auto rd = new RegionDescriptor(region, region_size, mmap_region, mmap_region_size, store);
  m_active_regions[(void*)region] = rd;

  UMAP_LOG(Debug,
      "region: " << (void*)(rd->start()) << " - " << (void*)(rd->end())
      << ", region_size: " << rd->size()
      << ", number of regions: " << m_active_regions.size() + 1
  );

  m_uffd->register_region(rd);
  m_last_iter = m_active_regions.end();
}

void
RegionManager::removeRegion( char* region )
{
  std::lock_guard<std::mutex> lock(m_mutex);
  auto it = m_active_regions.find(region);

  if (it == m_active_regions.end())
    UMAP_ERROR("umap fault monitor not found for: " << (void*)region);

  UMAP_LOG(Debug,
      "region: " << (void*)(it->second->start()) << " - " << (void*)(it->second->end())
      << ", region_size: " << it->second->size()
      << ", number of regions: " << m_active_regions.size()
  );

  m_uffd->unregister_region(it->second);

  delete it->second;
  m_active_regions.erase(it);

  m_last_iter = m_active_regions.end();

  if ( m_active_regions.empty() ) {
    delete m_evict_manager; m_evict_manager = nullptr;
    delete m_fill_workers; m_fill_workers = nullptr;
    delete m_uffd; m_uffd = nullptr;
    delete m_buffer; m_buffer = nullptr;
  }
}

int
RegionManager::flush_buffer(){

  std::lock_guard<std::mutex> lock(m_mutex);

  m_buffer->flush_dirty_pages();

  return 0;
}

void
RegionManager::prefetch(int npages, umap_prefetch_item* page_array)
{
  for (int i{0}; i < npages; ++i)
    m_uffd->process_page(false, (char*)(page_array[i].page_base_addr));
}

RegionManager::RegionManager()
{
  m_version.major = UMAP_VERSION_MAJOR;
  m_version.minor = UMAP_VERSION_MINOR;
  m_version.patch = UMAP_VERSION_PATCH;

  m_last_iter = m_active_regions.end();

  m_system_page_size = sysconf(_SC_PAGESIZE);

  const uint64_t MAX_FAULT_EVENTS = 256;
  uint64_t env_value = 0;
  if ( (read_env_var("UMAP_MAX_FAULT_EVENTS", &env_value)) != nullptr )
    set_max_fault_events(env_value);
  else
    set_max_fault_events(MAX_FAULT_EVENTS);

  unsigned int nthreads = std::thread::hardware_concurrency();
  nthreads = (nthreads == 0) ? 16 : nthreads;

  if ( (read_env_var("UMAP_PAGE_FILLERS", &env_value)) != nullptr )
    set_num_fillers(env_value);
  else
    set_num_fillers(nthreads);

  if ( (read_env_var("UMAP_PAGE_EVICTORS", &env_value)) != nullptr )
    set_num_evictors(env_value);
  else
    set_num_evictors(nthreads);

  if ( (read_env_var("UMAP_EVICT_HIGH_WATER_THRESHOLD", &env_value)) != nullptr )
    set_evict_high_water_threshold(env_value);
  else
    set_evict_high_water_threshold(90);

  if ( (read_env_var("UMAP_EVICT_LOW_WATER_THRESHOLD", &env_value)) != nullptr )
    set_evict_low_water_threshold(env_value);
  else
    set_evict_low_water_threshold(70);

  if ( (read_env_var("UMAP_PAGESIZE", &env_value)) != nullptr )
    set_umap_page_size(env_value);
  else
    set_umap_page_size(m_system_page_size);

  if ( (read_env_var("UMAP_BUFSIZE", &env_value)) != nullptr )
    set_max_pages_in_buffer(env_value);
  else
    set_max_pages_in_buffer( get_max_pages_in_memory() );

  if ( (read_env_var("UMAP_READ_AHEAD", &env_value)) != nullptr )
    set_read_ahead(env_value);
  else
    set_read_ahead(0);
}

uint64_t
RegionManager::get_max_pages_in_memory( void )
{
  static uint64_t total_mem_kb = 0;
  const uint64_t oneK = 1024;
  const uint64_t percent = 90;  // 90% of available memory

  // Lazily set total_mem_kb global
  if ( ! total_mem_kb ) {
    std::string token;
    std::ifstream file("/proc/meminfo");
    while (file >> token) {
      if (token == "MemFree:") {
        unsigned long mem;
        if (file >> mem) {
          total_mem_kb = mem;
        } else {
          UMAP_ERROR("UMAP unable to determine system memory size\n");
        }
      }
      // ignore rest of the line
      file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    }
  }
  return ( ((total_mem_kb / (get_umap_page_size() / oneK)) * percent) / 100 );
}

void
RegionManager::set_max_pages_in_buffer( uint64_t max_pages )
{
  uint64_t max_pages_in_mem = get_max_pages_in_memory();
  uint64_t old_max_pages_in_buffer = get_max_pages_in_buffer();

  if ( max_pages > max_pages_in_mem ) {
    UMAP_ERROR("Cannot set maximum pages to "
        << max_pages
        << " because it must be less than the maximum pages in memory "
        << max_pages_in_mem);
  }

  m_max_pages_in_buffer = max_pages;

  UMAP_LOG(Debug,
    "Maximum pages in page buffer changed from "
    << old_max_pages_in_buffer
    << " to " << get_max_pages_in_buffer() << " pages");
}

void
RegionManager::set_read_ahead(uint64_t num_pages)
{
  m_read_ahead = num_pages;
}

void
RegionManager::set_umap_page_size( uint64_t page_size )
{
  //
  // Must be multiple of system page size
  //
  if ( page_size % get_system_page_size() ) {
    UMAP_ERROR("Specified page size (" << page_size
        << ") must be a multiple of the system page size ("
        << get_system_page_size() << ")");
  }

  UMAP_LOG(Debug,
      "Adjusting page size from "
      << get_umap_page_size() << " to " << page_size);

  m_umap_page_size = page_size;
}

uint64_t*
RegionManager::read_env_var( const char* env, uint64_t*  val )
{
  // return a pointer to val on success, null on failure
  char* val_ptr = 0;
  if ( (val_ptr = getenv(env)) ) {
    uint64_t env_val;

    std::string s(val_ptr);
    std::stringstream ss(s);
    ss >> env_val;

    if (env_val != 0) {
      *val = env_val;
      return val;
    }
  }
  return nullptr;
}

RegionDescriptor*
RegionManager::containing_region( char* vaddr )
{
  std::lock_guard<std::mutex> lock(m_mutex);

  //
  // Since the list of pages coming in are usually sorted, we have a special
  // check here to see if the region found for the previous check will work.
  // If this is the case, we can return early.
  //
  if ( m_last_iter != m_active_regions.end() ) {
    char* b = m_last_iter->second->start();
    char* e = m_last_iter->second->end();

    if ( vaddr >= b && vaddr < e )
      return m_last_iter->second;
  }

  auto iter = m_active_regions.upper_bound(reinterpret_cast<void*>(vaddr));

  if ( iter != m_active_regions.begin() ) {
    // Back up the iterator
    --iter;

    char* b = iter->second->start();
    char* e = iter->second->end();

    if ( vaddr >= b && vaddr < e ) {
      m_last_iter = iter;
      return iter->second;
    }
  }

  UMAP_LOG(Debug, "Unable to find addr: "
      << (void*)vaddr
      << " in region map. Ignoring"
  );

  return nullptr;
}

void
RegionManager::set_num_fillers( uint64_t num_fillers )
{
  m_num_fillers = num_fillers;
}

void
RegionManager::set_num_evictors( uint64_t num_evictors )
{
  m_num_evictors = num_evictors;
}
void
RegionManager::set_evict_high_water_threshold( int percent )
{
  m_evict_high_water_threshold = percent;
}
void
RegionManager::set_evict_low_water_threshold( int percent )
{
  m_evict_low_water_threshold = percent;
}
void
RegionManager::set_max_fault_events( uint64_t max_events )
{
  m_max_fault_events = max_events;
}
} // end of namespace Umap