Program Listing for File umap.cpp

Return to documentation for file (umap/umap.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 <cinttypes>
#include <errno.h>              // strerror()
#include <string.h>             // strerror()
#include <sys/mman.h>

#include "umap/config.h"

#include "umap/RegionManager.hpp"
#include "umap/umap.h"
#include "umap/store/Store.hpp"
#include "umap/util/Macros.hpp"

void*
umap(
    void* region_addr
  , uint64_t region_size
  , int prot
  , int flags
  , int fd
  , off_t offset
)
{
  UMAP_LOG(Debug,
      "region_addr: " << region_addr
      << ", region_size: " << region_size
      << ", prot: " << prot
      << ", flags: " << flags
      << ", offset: " << offset
  );
  return Umap::umap_ex(region_addr, region_size, prot, flags, fd, 0, nullptr, false, 0);
}

int
uunmap(void*  addr, uint64_t length)
{
  UMAP_LOG(Debug, "addr: " << addr << ", length: " << length);
  auto& rm = Umap::RegionManager::getInstance();
  rm.removeRegion((char*)addr);
  UMAP_LOG(Debug, "Done");
  return 0;
}

//Todo: Might replace this implementation with a call to a function within Umap namespace

int umap_flush(){

  UMAP_LOG(Debug,  "umap_flush " );

  return Umap::RegionManager::getInstance().flush_buffer();

}


void umap_prefetch( int npages, umap_prefetch_item* page_array, int client_fd/*default=0*/)
{
  Umap::RegionManager::getInstance().prefetch(npages, page_array, client_fd);
}


void umap_fetch_and_pin( char* paddr, uint64_t size )
{
  Umap::RegionManager::getInstance().fetch_and_pin(paddr, size);
}


long
umapcfg_get_system_page_size( void )
{
  return Umap::RegionManager::getInstance().get_system_page_size();
}

uint64_t
umapcfg_get_max_pages_in_buffer( void )
{
  return Umap::RegionManager::getInstance().get_max_pages_in_buffer();
}

uint64_t
umapcfg_get_umap_page_size( void )
{
  return Umap::RegionManager::getInstance().get_umap_page_size();
}

uint64_t
umapcfg_get_num_fillers( void )
{
  return Umap::RegionManager::getInstance().get_num_fillers();
}

uint64_t
umapcfg_get_num_evictors( void )
{
  return Umap::RegionManager::getInstance().get_num_evictors();
}

int
umapcfg_get_evict_low_water_threshold( void )
{
  return Umap::RegionManager::getInstance().get_evict_low_water_threshold();
}

int
umapcfg_get_evict_high_water_threshold( void )
{
  return Umap::RegionManager::getInstance().get_evict_high_water_threshold();
}

uint64_t
umapcfg_get_max_fault_events( void )
{
  return Umap::RegionManager::getInstance().get_max_fault_events();
}

namespace Umap {
  // A global variable to ensure thread-safety
  std::mutex g_mutex;

void
terminate_handler(int client_fd)
{
  UMAP_LOG(Info, "terminating handler for client_fd: " << client_fd);
  auto& rm = Umap::RegionManager::getInstance();
  rm.terminateUffdHandler(client_fd);
  UMAP_LOG(Info, "Done");
}

bool
uunmap_server(void *mmap_region, uint64_t length, int client_fd, int file_fd, bool client_term){
  UMAP_LOG(Debug, "addr: " << mmap_region << ", length: " << length);
  auto& rm = Umap::RegionManager::getInstance();
  auto umap_psize = rm.get_umap_page_size();
  void *umap_region = (void*)((uint64_t)mmap_region + umap_psize - 1);
  umap_region = (void*)((uint64_t)umap_region & ~(umap_psize - 1));
  bool ret = rm.removeRegion((char*)umap_region, client_fd, file_fd, client_term);
  UMAP_LOG(Debug, "Done");
  return ret;
}

void*
umap_ex(
    void* region_addr //Umap server should pass a mmaped region address, does not need to be umap page size aligned
  , uint64_t region_size //Umap server passes mmaped region size which has an additional page over the aligned address needed to fit the file size
  , int prot
  , int flags
  , int fd
  , off_t offset
  , Store* store
  , bool server
  , int client_uffd
  , void* remote_base  //Has to be an aligned remote base address
)
{
  std::lock_guard<std::mutex> lock(g_mutex);
  void* umap_region = NULL;
  uint64_t umap_size = 0;
  uint64_t mmap_size = 0;
  void* mmap_region = 0;
  void *reg_desc = NULL;

  auto& rm = RegionManager::getInstance();
  auto umap_psize = rm.get_umap_page_size();
  //check if the file has already been mapped before

  UMAP_LOG(Info,
      "region_addr: " << region_addr
      << ", region_size: " << region_size
      << ", prot: " << prot
      << ", flags: " << flags
      << ", offset: " << offset
      << ", store: " << store
      << ", umap_psize: " << umap_psize
  );

#ifdef UMAP_RO_MODE
  if( !server && prot != PROT_READ )
    UMAP_ERROR("only PROT_READ is supported in UMAP_RO_MODE compilation");
#else
  if( prot & ~(PROT_READ|PROT_WRITE) )
    UMAP_ERROR("only PROT_READ or PROT_WRITE is supported in UMap");
#endif

  //
  // TODO: Allow for non-page-multiple size and zero-fill like mmap does
  //
  if ( ( region_size % umap_psize ) ) {
    UMAP_ERROR("Region size " << region_size
                << " is not a multple of umapPageSize ("
                << rm.get_umap_page_size() << ")");
  }
  if(!server){
    if ( (uint64_t)region_addr & (umap_psize - 1) ) {
       UMAP_ERROR("region_addr must be page aligned: " << region_addr
        << ", page size is: " << rm.get_umap_page_size());
    }

    if (!(flags & UMAP_PRIVATE) || flags & ~(UMAP_PRIVATE|UMAP_FIXED)) {
      UMAP_ERROR("Invalid flags: " << std::hex << flags << std::dec);
    }
    //
    // When dealing with umap-page-sizes that could be multiples of the actual
    // system-page-size, it is possible for mmap() to provide a region that is on
    // a system-page-boundary, but not necessarily on a umap-page-size boundary.
    //
    // We always allocate an additional umap-page-size set of bytes so that we can
    // make certain that the umap-region begins on a umap-page-size boundary.
    //
    mmap_size = region_size + umap_psize;
    mmap_region = mmap(region_addr, mmap_size,
                     prot, flags | (MAP_ANONYMOUS | MAP_NORESERVE), -1, 0);

    if (mmap_region == MAP_FAILED) {
      UMAP_ERROR("mmap failed: " << strerror(errno));
      return UMAP_FAILED;
    }
    umap_size = region_size;
    umap_region = (void*)((uint64_t)mmap_region + umap_psize - 1);
    umap_region = (void*)((uint64_t)umap_region & ~(umap_psize - 1));

    if ( store == nullptr )
      store = Store::make_store(umap_region, umap_size, umap_psize, fd);
    rm.addRegion(fd, store, umap_region, umap_size, (char*)mmap_region, mmap_size, server, client_uffd, remote_base);
  }else{
    reg_desc = rm.isFDRegionPresent(fd);
    if(reg_desc==NULL){
      mmap_size = region_size;
      mmap_region = region_addr;
      umap_size =  mmap_size - umap_psize;
      umap_region = (void*)((uint64_t)mmap_region + umap_psize - 1);
      umap_region = (void*)((uint64_t)umap_region & ~(umap_psize - 1));

      if ( store == nullptr )
        store = Store::make_store(umap_region, umap_size, umap_psize, fd);
      rm.addRegion(fd, store, umap_region, umap_size, (char*)mmap_region, mmap_size, server, client_uffd, remote_base);
    }else{
      umap_region = (void *)rm.associateRegion(fd, reg_desc, server, client_uffd, remote_base);
    }
  }
  return umap_region;
}
} // namespace Umap