Verified Commit f3d5cbcd authored by Alberto Miranda's avatar Alberto Miranda ♨️
Browse files

PMDK pools can now be created

parent 110a81fe
Loading
Loading
Loading
Loading
Loading
+162 −0
Original line number Diff line number Diff line
/*
  Copyright 2018-2021, Barcelona Supercomputing Center (BSC), Spain
  Copyright 2015-2021, 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.

  This file is part of GekkoFS.

  GekkoFS is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  GekkoFS is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with GekkoFS.  If not, see <https://www.gnu.org/licenses/>.

  SPDX-License-Identifier: GPL-3.0-or-later
*/

#ifndef GEKKOFS_DAEMON_DATA_PMDK_POOL_HPP
#define GEKKOFS_DAEMON_DATA_PMDK_POOL_HPP

#include <filesystem>
#include <spdlog/spdlog.h>

namespace fs = std::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
 No newline at end of file
+4 −0
Original line number Diff line number Diff line
@@ -26,6 +26,9 @@
# SPDX-License-Identifier: GPL-3.0-or-later                                    #
################################################################################

# import PMDK storage backend
add_subdirectory(pmdk)

add_library(data_module
  STATIC
)
@@ -58,6 +61,7 @@ target_link_libraries(storage
    spdlog
    data_module
    path_util
    pmdk_storage
    # open issue for std::filesystem https://gitlab.kitware.com/cmake/cmake/-/issues/17834
    stdc++fs
    -ldl
+47 −0
Original line number Diff line number Diff line
################################################################################
# Copyright 2018-2021, Barcelona Supercomputing Center (BSC), Spain            #
# Copyright 2015-2021, 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.                   #
#                                                                              #
# This file is part of GekkoFS.                                                #
#                                                                              #
# GekkoFS is free software: you can redistribute it and/or modify              #
# it under the terms of the GNU General Public License as published by         #
# the Free Software Foundation, either version 3 of the License, or            #
# (at your option) any later version.                                          #
#                                                                              #
# GekkoFS is distributed in the hope that it will be useful,                   #
# but WITHOUT ANY WARRANTY; without even the implied warranty of               #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                #
# GNU General Public License for more details.                                 #
#                                                                              #
# You should have received a copy of the GNU General Public License            #
# along with GekkoFS.  If not, see <https://www.gnu.org/licenses/>.            #
#                                                                              #
# SPDX-License-Identifier: GPL-3.0-or-later                                    #
################################################################################

add_library(pmdk_storage
  STATIC
)

target_sources(pmdk_storage
  PRIVATE
    ${CMAKE_CURRENT_LIST_DIR}/pool.cpp
  PUBLIC
    ${INCLUDE_DIR}/daemon/backend/data/pmdk/pool.hpp
)

target_link_libraries(pmdk_storage
  PUBLIC
    PMDK::pmem
    data_module
  Boost::system
    std::filesystem
    path_util
)
+235 −0
Original line number Diff line number Diff line
/*
  Copyright 2018-2021, Barcelona Supercomputing Center (BSC), Spain
  Copyright 2015-2021, 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.

  This file is part of GekkoFS.

  GekkoFS is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  GekkoFS is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with GekkoFS.  If not, see <https://www.gnu.org/licenses/>.

  SPDX-License-Identifier: GPL-3.0-or-later
*/

#include <filesystem>

#include <libpmem.h>
#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 = std::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
 No newline at end of file
+3 −0
Original line number Diff line number Diff line
@@ -67,6 +67,7 @@ add_executable(tests
  test_utils_arithmetic.cpp
  test_helpers.cpp
    test_guided_distributor.cpp
    test_storage_pmdk.cpp
)

target_link_libraries(tests
@@ -75,6 +76,8 @@ target_link_libraries(tests
  helpers
  arithmetic
    distributor
    data_module
    pmdk_storage
)

# Catch2's contrib folder includes some helper functions
Loading