Verified Commit 145bcb42 authored by Alberto Miranda's avatar Alberto Miranda

PMDK pools can now be created

parent 2c2661c9
Pipeline #1325 failed with stages
in 6 minutes and 35 seconds
/*
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
*/
#ifndef GEKKOFS_DAEMON_DATA_PMDK_POOL_HPP
#define GEKKOFS_DAEMON_DATA_PMDK_POOL_HPP
#include <spdlog/spdlog.h>
#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;
namespace pmdk {
/**
* This class implements a RAII-enabled non-volatile memory pool based on
* Intel's PMDK C library.
*/
class pool {
public:
/**
* 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
*/
pool(const fs::path& parent_dir, std::size_t size) noexcept;
pool(pool&& rhs) = default;
pool(const pool& other) = delete;
pool& operator=(pool&& rhs) = default;
pool& operator=(const pool& other) = delete;
/**
* Destroy an existing pool.
*/
~pool();
/**
* 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 path() const noexcept;
/**
* 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* data() const noexcept;
/**
* 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 size() const noexcept;
/**
* 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 is_persistent() const noexcept;
/**
*
* Check whether a pool is valid.
*
* @returns `true` if the pool was correctly created. Otherwise it
* returns `false`.
*/
explicit operator bool() const noexcept;
/**
*
* Check whether a pool is invalid.
*
* @returns `true` if the pool was not correctly created. Otherwise it
* returns `false`.
*/
bool operator!() const noexcept;
/**
*
* Check whether a pool is valid.
*
* @returns `true` if the pool was correctly created. Otherwise it
* returns `false`.
*/
bool valid() const noexcept;
private:
/// Logger instance
std::shared_ptr<spdlog::logger> log_;
/// Path to the pool file in the file system
fs::path path_;
/// Address of the pool's non-volatile memory region
void* data_ = nullptr;
/// Size of the pool's non-volatile memory region
std::size_t size_ = 0;
/// Flag set if the pool file is actually stored in non-volatile memory
bool is_persistent_ = false;
/// Flag set if the pool was created correctly
bool is_valid_ = false;
};
} // namespace pmdk
#endif // GEKKOFS_DAEMON_DATA_PMDK_POOL_HPP
# PMDK backend
add_subdirectory(pmdk)
add_library(storage STATIC)
target_sources(storage
......@@ -16,9 +19,11 @@ target_link_libraries(storage
PRIVATE
spdlog
Boost::filesystem
pmdk_storage
-ldl
)
#target_include_directories(storage
# PRIVATE
# )
##
# 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
##
add_library(pmdk_storage
STATIC
)
target_sources(pmdk_storage
PRIVATE
pool.cpp
PUBLIC
${INCLUDE_DIR}/daemon/backend/data/pmdk/pool.hpp
)
target_link_libraries(pmdk_storage
PUBLIC
PMDK::pmem
spdlog
)
/*
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
......@@ -34,11 +34,14 @@ target_link_libraries(catch2_main
add_executable(tests
test_example_00.cpp
test_example_01.cpp
test_storage_pmdk.cpp
)
target_link_libraries(tests
catch2_main
fmt::fmt
pmdk_storage
Boost::filesystem
)
# Catch2's contrib folder includes some helper functions
......
/*
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 <catch2/catch.hpp>
#include <fmt/format.h>
#include <daemon/backend/data/pmdk/pool.hpp>
#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;
// FIXME: temporary solution to have correct loggers for tests
#include <spdlog/sinks/basic_file_sink.h>
#include <vector>
#include <list>
namespace {
void
patch_gkfs_loggers(const std::vector<std::string>& loggers_name,
spdlog::level::level_enum level, const std::string& path) {
/* Create common sink */
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(path);
/* Create and configure loggers */
auto loggers = std::list<std::shared_ptr<spdlog::logger>>();
for (const auto& name: loggers_name) {
if(spdlog::get(name)) {
continue;
}
auto logger = std::make_shared<spdlog::logger>(name, file_sink);
logger->flush_on(spdlog::level::trace);
loggers.push_back(logger);
}
/* register loggers */
for (const auto& logger: loggers) {
spdlog::register_logger(logger);
}
// set logger format
spdlog::set_pattern("[%C-%m-%d %H:%M:%S.%f] %P [%L][%n] %v");
spdlog::set_level(level);
}
} // namespace
// end of temporary solution
namespace {
fs::path
create_temporary_directory() {
auto tmpdir = fs::temp_directory_path() / fs::unique_path();
boost::system::error_code ec;
if(!fs::create_directory(tmpdir, ec)) {
throw std::runtime_error(ec.message());
}
return tmpdir;
}
} // namespace
SCENARIO( "PMDK pools can be created", "[pmdk_storage_pool]" ) {
auto logger_names = std::vector<std::string>{
"main",
"MetadataDB",
"DataModule",
};
::patch_gkfs_loggers(logger_names, spdlog::level::debug, "/dev/stdout");
GIVEN( "A valid parent directory " ) {
WHEN( "Size is a positive integer" ) {
std::size_t pool_size = 420;
auto tmpdir = create_temporary_directory();
pmdk::pool p(tmpdir, pool_size);
THEN( "The pool object is valid " ) {
REQUIRE(p);
REQUIRE(p.valid());
REQUIRE(p.size() == pool_size);
REQUIRE(p.data() != nullptr);
}
AND_THEN( "The mapping file exists and has the correct size" ) {
REQUIRE(fs::exists(p.path()));
REQUIRE(fs::file_size(p.path()) == pool_size);
}
}
WHEN( "Size is zero" ) {
auto tmpdir = create_temporary_directory();
pmdk::pool p(tmpdir, 0);
THEN( "The pool object is invalid " ) {
REQUIRE(!p);
REQUIRE(!p.valid());
AND_THEN( "Internal members are default initialized" ) {
REQUIRE(p.path().empty());
REQUIRE(p.data() == nullptr);
REQUIRE(p.size() == 0);
}
}
}
WHEN( "Size is negative" ) {
auto tmpdir = create_temporary_directory();
pmdk::pool p(tmpdir, -1);
THEN( "The pool object is invalid " ) {
REQUIRE(!p);
REQUIRE(!p.valid());
AND_THEN( "Internal members are default initialized" ) {
REQUIRE(p.path().empty());
REQUIRE(p.data() == nullptr);
REQUIRE(p.size() == 0);
}
}
}
}
GIVEN( "An invalid parent directory " ) {
WHEN( "Parent directory does not exist" ) {
std::size_t pool_size = 420;
auto tmpdir = create_temporary_directory();
pmdk::pool p("/foo/bar/", pool_size);
THEN( "The pool object is invalid " ) {
REQUIRE(!p);
REQUIRE(!p.valid());
AND_THEN( "Internal members are default initialized" ) {
REQUIRE(p.path().empty());
REQUIRE(p.data() == nullptr);
REQUIRE(p.size() == 0);
}
}
}
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment