pool.cpp 5.74 KiB
Newer Older
/*
  Copyright 2018-2020, Barcelona Supercomputing Center (BSC), Spain
  Copyright 2015-2020, Johannes Gutenberg Universitaet Mainz, Germany

  This software was partially supported by the
  EC H2020 funded project NEXTGenIO (Project ID: 671951, www.nextgenio.eu).

  This software was partially supported by the
  ADA-FS project under the SPPEXA project funded by the DFG.

  SPDX-License-Identifier: MIT
*/

#include <libpmem.h>
#include <boost/filesystem.hpp>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <boost/uuid/uuid_generators.hpp>

#include <daemon/backend/data/pmdk/pool.hpp>
#include <daemon/backend/data/data_module.hpp>

namespace fs = boost::filesystem;

namespace {

/**
 * Generate a random filename for a non-volatile memory pool using the Mersenne
 * Twister PRNG. Note that the function does not verify whether the path
 * returned actually exists or can be created. Neither does it verifies that
 * `subdir` exists or that the user actually has permissions to create
 *
 * @param[in] subdir an optional parent directory to prepend to the generated
 *                   path (Default: "")
 * @returns a randomly generated path
 */
fs::path
generate_pool_path(const fs::path& subdir = "") {

    static boost::mt19937 rng;
    using RngType = decltype(rng);

    const auto uuid = boost::uuids::basic_random_generator<RngType>(rng)();
    
    return subdir / boost::uuids::to_string(uuid);
}

} // namespace

namespace pmdk {

/**
 * Create a pool of non-volatile memory of a fixed `size` under
 * `parent_dir`. Give it a random name.
 *
 * @param[in]  parent_dir the parent directory where the memory pool backing 
 *                        file will be stored
 * @param[in]  size the maximum capacity in bytes of the pool
 * @returns the newly created pool
 *
 * @warning The pool construction may fail for a number of reasons, but 
 * the constructor will not throw (though the error itself will be logged).
 * Boolean operator overloads are provided so that it is simple to check
 * if a pool has been correctly constructed:
 *
 * .. code-block:: cpp
 *
 *    pool p{"/foo/bar", 42000};
 *
 *    if(!p) {
 *      abort("pool is invalid!")
 *    }
 */
pool::pool(const fs::path& parent_dir, std::size_t size) noexcept {

    // get logger instance and set it for data module and chunk storage
    auto log_ = spdlog::get(GKFS_DATA_MOD->LOGGER_NAME);
    assert(log_);

    if(!fs::exists(parent_dir)) {
        log_->error("Error creating PMDK pool: parent directory {} "
                    "does not exist", parent_dir.string());
        return;
    }

    if(size <= 0) {
        log_->error("Error creating PMDK pool: invalid pool size {}", size);
        return;
    }

    fs::path pool_path = ::generate_pool_path(parent_dir);
    void* pool_address = nullptr;
    std::size_t pool_length = 0;
    int is_pmem = 0;

    // if the pool already exists in the host file system, it might be
    // a stale file or we might have a collision. Either way, abort the
    // pool creation.
    if(fs::exists(pool_path)) {
        log_->warn("PMDK pool file {} already exists (possible collision or "
                   "stale file)", pool_path.string());
        return;

    }

    pool_address = ::pmem_map_file(pool_path.c_str(), size, 
            PMEM_FILE_CREATE | PMEM_FILE_EXCL | PMEM_FILE_SPARSE,
            0666, &pool_length, &is_pmem);

    if(pool_address == nullptr) {
        log_->critical("Error creating PMDK pool file {}: {}",
                pool_path.string(), ::strerror(errno));
        return;
    }

    path_ = pool_path;
    data_ = pool_address;
    size_ = pool_length;
    is_persistent_ = is_pmem;
    is_valid_ = true;
}

/**
 * Destroy an existing pool.
 */
pool::~pool() {
    if(data_ != nullptr) {
        ::pmem_unmap(data_, size_);
    }
}

/**
 * Returns the path to a pool's data storage in the file system.
 * 
 * @returns If the pool was correctly created, the function returns a 
 *          `boost::fylesystem::path` with the path to the file backing the
 *          pool in the file system. Otherwise, `boost::filesystem::path{}`
 *          is returned.
 */
fs::path
pool::path() const noexcept {
    return path_;
}

/**
 * Returns the address to a pool's data region in non-volatile memory.
 * 
 * @returns If the pool was correctly created, the function returns a 
 *          void pointer to the non-volatile memory region containing the
 *          pool's data. Otherwise, `nullptr` is returned.
 */
void*
pool::data() const noexcept {
    return data_;
}

/**
 * Returns the size of a pool's data region in non-volatile memory.
 * 
 * @returns If the pool was correctly created, the function returns the 
 *          size of the non-volatile memory region containing
 *          the pool's data. Otherwise, `0` is returned.
 */
std::size_t
pool::size() const noexcept {
    return size_;
}

/**
 * Returns whether a pool's data is actually stored in non-volatile 
 * memory.
 * 
 * @returns The function returns `true` if the pool was correctly created,
 *          and the pool's data is stored in NVM. Otherwise, `false` is
 *          returned.
 */
bool
pool::is_persistent() const noexcept {
    return is_persistent_;
}

/**
 *
 * Check whether a pool is valid.
 *
 * @returns `true` if the pool was correctly created. Otherwise it
 *           returns `false`.
 */
pool::operator bool() const noexcept {
    return valid();
}

/**
 *
 * Check whether a pool is invalid.
 *
 * @returns `true` if the pool was not correctly created. Otherwise it
 *           returns `false`.
 */
bool
pool::operator!() const noexcept {
    return !valid();
}

/**
 *
 * Check whether a pool is valid.
 *
 * @returns `true` if the pool was correctly created. Otherwise it
 *           returns `false`.
 */
bool
pool::valid() const noexcept {
    return is_valid_;
}

} // namespace pmdk