Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
/*
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