Skip to content

Commit ad7ea90

Browse files
foxengwkozaczuk
authored andcommitted
virtio-fs: make DAX manager testable
This mainly includes changes allowing the actual DAX window operations to be replaced with stubs. To achieve that we: 1. Introduce a new class virtiofs::dax_window_impl, encapsulating the DAX window and its operations (previously those were members of dax_manager). 2. Introduce a stub version of the DAX window, virtiofs::dax_window_stub. 3. Turn the dax_manager class into a template, with the DAX window flavour (normal or stub) as its parameter. This was deemed cleaner than going with run-time polymorphism. 4. Make all previously private members of dax_manager protected, allowing them to be accessed by the test code. Signed-off-by: Fotis Xenakis <[email protected]> Message-Id: <VI1PR03MB3773E191D8AA7D58FAEE8FBEA6959@VI1PR03MB3773.eurprd03.prod.outlook.com>
1 parent 2275ed4 commit ad7ea90

File tree

4 files changed

+108
-51
lines changed

4 files changed

+108
-51
lines changed

fs/virtiofs/virtiofs.hh

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include "drivers/virtio-fs.hh"
1818
#include "fuse_kernel.h"
19+
#include "virtiofs_dax.hh"
1920

2021
#define VIRTIOFS_DEBUG_ENABLED 1
2122

@@ -25,15 +26,9 @@
2526
#define virtiofs_debug(...)
2627
#endif
2728

28-
// Necessary pre-declaration because virtiofs::dax depends on virtiofs_inode,
29-
// declared below.
30-
namespace virtiofs {
31-
class dax_manager;
32-
}
33-
3429
struct virtiofs_mount_data {
3530
virtio::fs* drv;
36-
std::shared_ptr<virtiofs::dax_manager> dax_mgr;
31+
std::shared_ptr<virtiofs::dax_manager_impl> dax_mgr;
3732
};
3833

3934
struct virtiofs_inode {

fs/virtiofs/virtiofs_dax.cc

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <algorithm>
99
#include <mutex>
1010

11+
#include <api/assert.h>
1112
#include <osv/debug.h>
1213
#include <osv/uio.h>
1314

@@ -18,8 +19,9 @@
1819

1920
namespace virtiofs {
2021

21-
int dax_manager::read(virtiofs_inode& inode, uint64_t file_handle, u64 read_amt,
22-
struct uio& uio, bool aggressive)
22+
template<typename W>
23+
int dax_manager<W>::read(virtiofs_inode& inode, uint64_t file_handle,
24+
u64 read_amt, struct uio& uio, bool aggressive)
2325
{
2426
std::lock_guard<mutex> guard {_lock};
2527

@@ -63,7 +65,7 @@ int dax_manager::read(virtiofs_inode& inode, uint64_t file_handle, u64 read_amt,
6365
}
6466

6567
out:
66-
auto req_data = _window->addr + (mp.mstart * _chunk_size) + coffset;
68+
auto req_data = _window.data() + (mp.mstart * _chunk_size) + coffset;
6769
auto read_amt_act = std::min<size_t>(read_amt,
6870
(mp.nchunks * _chunk_size) - coffset);
6971
// NOTE: It shouldn't be necessary to use the mmio* interface (i.e. volatile
@@ -76,7 +78,8 @@ int dax_manager::read(virtiofs_inode& inode, uint64_t file_handle, u64 read_amt,
7678
return error;
7779
}
7880

79-
int dax_manager::map(uint64_t nodeid, uint64_t file_handle, chunk nchunks,
81+
template<typename W>
82+
int dax_manager<W>::map(uint64_t nodeid, uint64_t file_handle, chunk nchunks,
8083
chunk fstart, mapping_part& mapped, bool evict)
8184
{
8285
// If necessary, unmap just enough chunks
@@ -99,7 +102,8 @@ int dax_manager::map(uint64_t nodeid, uint64_t file_handle, chunk nchunks,
99102

100103
// Map new chunks
101104
auto mstart = _window_chunks - empty;
102-
auto error = map_ll(nodeid, file_handle, to_map, fstart, mstart);
105+
auto error = _window.map(nodeid, file_handle, to_map * _chunk_size,
106+
fstart * _chunk_size, mstart * _chunk_size);
103107
if (error) {
104108
return error;
105109
}
@@ -119,7 +123,8 @@ int dax_manager::map(uint64_t nodeid, uint64_t file_handle, chunk nchunks,
119123
return 0;
120124
}
121125

122-
int dax_manager::unmap(chunk nchunks, mapping_part& unmapped, bool deep)
126+
template<typename W>
127+
int dax_manager<W>::unmap(chunk nchunks, mapping_part& unmapped, bool deep)
123128
{
124129
// Determine necessary changes
125130
chunk to_unmap = 0;
@@ -148,7 +153,8 @@ int dax_manager::unmap(chunk nchunks, mapping_part& unmapped, bool deep)
148153
// Apply changes
149154
if (deep) {
150155
auto mstart = first_empty() - to_unmap;
151-
auto error = unmap_ll(to_unmap, mstart);
156+
auto error = _window.unmap(to_unmap * _chunk_size,
157+
mstart * _chunk_size);
152158
if (error) {
153159
return error;
154160
}
@@ -163,10 +169,10 @@ int dax_manager::unmap(chunk nchunks, mapping_part& unmapped, bool deep)
163169
return 0;
164170
}
165171

166-
int dax_manager::map_ll(uint64_t nodeid, uint64_t file_handle, chunk nchunks,
167-
chunk fstart, chunk mstart)
172+
int dax_window_impl::map(uint64_t nodeid, uint64_t fh, uint64_t len,
173+
uint64_t fstart, uint64_t mstart)
168174
{
169-
assert(mstart + nchunks <= _window_chunks);
175+
assert(mstart + len <= _window->len);
170176

171177
// NOTE: There are restrictions on the arguments to FUSE_SETUPMAPPING, from
172178
// the spec: "Alignment constraints for FUSE_SETUPMAPPING and
@@ -177,18 +183,18 @@ int dax_manager::map_ll(uint64_t nodeid, uint64_t file_handle, chunk nchunks,
177183
// - moffset: multiple of map_alignment from FUSE_INIT
178184
// In practice, map_alignment is the host's page size, because foffset and
179185
// moffset are passed to mmap() on the host. These are satisfied by
180-
// _chunk_size being a multiple of map_alignment.
186+
// the caller (chunk size being a multiple of map alignment in dax_manager).
181187

182188
std::unique_ptr<fuse_setupmapping_in> in_args {
183189
new (std::nothrow) fuse_setupmapping_in()};
184190
if (!in_args) {
185191
return ENOMEM;
186192
}
187-
in_args->fh = file_handle;
188-
in_args->foffset = fstart * _chunk_size;
189-
in_args->len = nchunks * _chunk_size;
193+
in_args->fh = fh;
194+
in_args->foffset = fstart;
195+
in_args->len = len;
190196
in_args->flags = 0; // Read-only
191-
in_args->moffset = mstart * _chunk_size;
197+
in_args->moffset = mstart;
192198

193199
virtiofs_debug("inode %lld, setting up mapping (foffset=%lld, len=%lld, "
194200
"moffset=%lld)\n", nodeid, in_args->foffset, in_args->len,
@@ -203,9 +209,9 @@ int dax_manager::map_ll(uint64_t nodeid, uint64_t file_handle, chunk nchunks,
203209
return 0;
204210
}
205211

206-
int dax_manager::unmap_ll(chunk nchunks, chunk mstart)
212+
int dax_window_impl::unmap(uint64_t len, uint64_t mstart)
207213
{
208-
assert(mstart + nchunks <= _window_chunks);
214+
assert(mstart + len <= _window->len);
209215

210216
// NOTE: FUSE_REMOVEMAPPING accepts a fuse_removemapping_in followed by
211217
// fuse_removemapping_in.count fuse_removemapping_one arguments in general.
@@ -219,8 +225,8 @@ int dax_manager::unmap_ll(chunk nchunks, chunk mstart)
219225
auto r_one = new (in_args.get() + sizeof(fuse_removemapping_in))
220226
fuse_removemapping_one();
221227
r_in->count = 1;
222-
r_one->moffset = mstart * _chunk_size;
223-
r_one->len = nchunks * _chunk_size;
228+
r_one->moffset = mstart;
229+
r_one->len = len;
224230

225231
// The nodeid is irrelevant for the current implementation of
226232
// FUSE_REMOVEMAPPING. If it needed to be set, would we need to make a
@@ -239,7 +245,9 @@ int dax_manager::unmap_ll(chunk nchunks, chunk mstart)
239245
return 0;
240246
}
241247

242-
bool dax_manager::find(uint64_t nodeid, chunk fstart, mapping_part& found) const
248+
template<typename W>
249+
bool dax_manager<W>::find(uint64_t nodeid, chunk fstart, mapping_part& found)
250+
const
243251
{
244252
for (auto& m : _mappings) {
245253
if (m.nodeid == nodeid &&
@@ -256,7 +264,8 @@ bool dax_manager::find(uint64_t nodeid, chunk fstart, mapping_part& found) const
256264
return false;
257265
}
258266

259-
dax_manager::chunk dax_manager::first_empty() const
267+
template<typename W>
268+
typename dax_manager<W>::chunk dax_manager<W>::first_empty() const
260269
{
261270
if (_mappings.empty()) {
262271
return 0;
@@ -265,4 +274,8 @@ dax_manager::chunk dax_manager::first_empty() const
265274
return m.mstart + m.nchunks;
266275
}
267276

277+
// Explicitly instantiate the only uses of dax_manager.
278+
template class dax_manager<dax_window_impl>;
279+
template class dax_manager<dax_window_stub>;
280+
268281
}

fs/virtiofs/virtiofs_dax.hh

Lines changed: 69 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,71 @@
55
* BSD license as described in the LICENSE file in the top-level directory.
66
*/
77

8+
#ifndef VIRTIOFS_DAX_HH
9+
#define VIRTIOFS_DAX_HH
10+
811
#include <vector>
912

1013
#include <api/assert.h>
1114
#include <osv/mutex.h>
1215
#include <osv/uio.h>
1316

1417
#include "drivers/virtio-fs.hh"
15-
#include "virtiofs.hh"
18+
19+
// Necessary pre-declaration because virtiofs_inode is declared in virtiofs.hh,
20+
// which depends on this for defining virtiofs_mount_data.
21+
struct virtiofs_inode;
1622

1723
namespace virtiofs {
1824

25+
// A thin abstraction layer over the actual DAX window, taking care of all the
26+
// low-level operations, interfacing with the driver.
27+
class dax_window_impl {
28+
public:
29+
// Construct a new window for the DAX window associated with @drv (as
30+
// returned by drv.get_dax()).
31+
dax_window_impl(virtio::fs& drv)
32+
: _drv {drv},
33+
_window {drv.get_dax()} {}
34+
35+
// Returns the size of the window in bytes.
36+
u64 size() const { return _window->len; }
37+
// Returns a pointer to the underlying memory region.
38+
mmioaddr_t data() const { return _window->addr; }
39+
// Returns the map alignment for the window.
40+
int map_alignment() const { return _drv.get_map_alignment(); }
41+
// Map @len bytes of the file with @nodeid (opened as @fh), starting at
42+
// byte @fstart of the file and byte @mstart of the window. Returns
43+
// non-zero on failure.
44+
int map(uint64_t nodeid, uint64_t fh, uint64_t len, uint64_t fstart,
45+
uint64_t mstart);
46+
// Unmap @len bytes, starting at byte @mstart of the window. Returns
47+
// non-zero on failure.
48+
int unmap(uint64_t len, uint64_t mstart);
49+
50+
private:
51+
virtio::fs& _drv;
52+
const virtio::fs::dax_window* const _window;
53+
};
54+
55+
// A stub DAX window, used for testing. Defined here to facilitate instantiation
56+
// of dax_manager<dax_window_stub> (see virtiofs_dax.cc).
57+
class dax_window_stub {
58+
public:
59+
dax_window_stub(u64 len)
60+
: _len {len} {}
61+
62+
u64 size() const { return _len; }
63+
mmioaddr_t data() const { return nullptr; }
64+
int map_alignment() const { return 12; /* 4KiB */ }
65+
int map(uint64_t nodeid, uint64_t fh, uint64_t len, uint64_t fstart,
66+
uint64_t mstart) { return 0; };
67+
int unmap(uint64_t len, uint64_t mstart) { return 0; };
68+
69+
private:
70+
u64 _len;
71+
};
72+
1973
// A manager for the DAX window of a virtio-fs device. This implements a
2074
// straight-forward scheme for file mappings:
2175
// - The window is split into equally-sized chunks. Each mapping occupies an
@@ -24,21 +78,19 @@ namespace virtiofs {
2478
// - When there are not enough chunks available for a new mapping, the highest
2579
// (i.e. most recently mapped) chunks occupied are evicted. Thus, chunks are
2680
// mapped in a LIFO manner (the window resembles a stack).
81+
template<typename W>
2782
class dax_manager {
2883
public:
2984
static constexpr size_t DEFAULT_CHUNK_SIZE = 1 << 21; // 2MiB
3085

31-
// Construct a new manager for the DAX window associated with @drv (as
32-
// returned by drv.get_dax()). The alignment constraint of the device (as
33-
// reported by drv.get_map_alignment()) should be compatible with
34-
// @chunk_size.
35-
dax_manager(virtio::fs& drv, size_t chunk_size = DEFAULT_CHUNK_SIZE)
36-
: _drv {drv},
37-
_window {drv.get_dax()},
86+
// Construct a new manager for @window. The @chunk_size should be compatible
87+
// with the alignment constraint of @window.
88+
dax_manager(W window, size_t chunk_size = DEFAULT_CHUNK_SIZE)
89+
: _window {window},
3890
_chunk_size {chunk_size},
39-
_window_chunks {_window->len / _chunk_size} {
91+
_window_chunks {_window.size() / _chunk_size} {
4092

41-
assert(_chunk_size % (1ull << _drv.get_map_alignment()) == 0);
93+
assert(_chunk_size % (1ull << _window.map_alignment()) == 0);
4294

4395
// NOTE: If _window->len % CHUNK_SIZE > 0, that remainder (< CHUNK_SIZE)
4496
// is effectively ignored.
@@ -49,7 +101,7 @@ public:
49101
int read(virtiofs_inode& inode, uint64_t file_handle, u64 read_amt,
50102
struct uio& uio, bool aggressive = false);
51103

52-
private:
104+
protected:
53105
// Helper type to better distinguish referring to chunks vs bytes
54106
using chunk = size_t;
55107

@@ -80,14 +132,6 @@ private:
80132
// if @deep. Returns in @unmapped what was unmapped and non-zero on failure.
81133
// Called with _lock held (for writing).
82134
int unmap(chunk nchunks, mapping_part& unmapped, bool deep = false);
83-
// Map @nchunks chunks of the file with @nodeid (opened as @fh), starting at
84-
// chunk @fstart of the file and chunk @mstart of the window. Returns
85-
// non-zero on failure. Called with _lock held (for writing).
86-
int map_ll(uint64_t nodeid, uint64_t fh, chunk nchunks, chunk fstart,
87-
chunk mstart);
88-
// Unmap @nchunks chunks, starting at chunk @mstart of the window. Returns
89-
// non-zero on failure. Called with _lock held (for writing).
90-
int unmap_ll(chunk nchunks, chunk mstart);
91135

92136
// Return in @found the largest contiguous existing mapping for @nodeid
93137
// starting at @fstart. If none found, returns false. Called with _lock held
@@ -97,13 +141,17 @@ private:
97141
// window is full. Called with _lock held (for reading).
98142
chunk first_empty() const;
99143

100-
virtio::fs& _drv;
101-
const virtio::fs::dax_window* const _window;
144+
W _window;
102145
const size_t _chunk_size;
103146
const chunk _window_chunks;
104147
// TODO OPT: Switch to rwlock
105148
mutex _lock;
106149
std::vector<mapping> _mappings;
107150
};
108151

152+
using dax_manager_impl = dax_manager<dax_window_impl>;
153+
using dax_manager_stub = dax_manager<dax_window_stub>;
154+
109155
}
156+
157+
#endif

fs/virtiofs/virtiofs_vfsops.cc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ using fuse_request = virtio::fs::fuse_request;
2828
static std::atomic<uint64_t> fuse_unique_id(1);
2929

3030
static struct {
31-
std::unordered_map<virtio::fs*, std::shared_ptr<virtiofs::dax_manager>,
31+
std::unordered_map<virtio::fs*, std::shared_ptr<virtiofs::dax_manager_impl>,
3232
virtio::fs::hasher> mgrs;
3333
mutex lock;
3434
} dax_managers;
@@ -151,7 +151,8 @@ static int virtiofs_mount(struct mount* mp, const char* dev, int flags,
151151
// device is already mounted)
152152
m_data->dax_mgr = found->second;
153153
} else {
154-
m_data->dax_mgr = std::make_shared<virtiofs::dax_manager>(*drv);
154+
auto w {virtiofs::dax_window_impl(*drv)};
155+
m_data->dax_mgr = std::make_shared<virtiofs::dax_manager_impl>(w);
155156
if (!m_data->dax_mgr) {
156157
return ENOMEM;
157158
}

0 commit comments

Comments
 (0)