Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/tests/sys/fs/fusefs/cache.cc
39536 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2020 Alan Somers
5
*
6
* Redistribution and use in source and binary forms, with or without
7
* modification, are permitted provided that the following conditions
8
* are met:
9
* 1. Redistributions of source code must retain the above copyright
10
* notice, this list of conditions and the following disclaimer.
11
* 2. Redistributions in binary form must reproduce the above copyright
12
* notice, this list of conditions and the following disclaimer in the
13
* documentation and/or other materials provided with the distribution.
14
*
15
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25
* SUCH DAMAGE.
26
*/
27
28
extern "C" {
29
#include <sys/param.h>
30
#include <fcntl.h>
31
}
32
33
#include "mockfs.hh"
34
#include "utils.hh"
35
36
/*
37
* Tests for thorny cache problems not specific to any one opcode
38
*/
39
40
using namespace testing;
41
42
/*
43
* Parameters
44
* - reopen file - If true, close and reopen the file between reads
45
* - cache lookups - If true, allow lookups to be cached
46
* - cache attrs - If true, allow file attributes to be cached
47
* - cache_mode - uncached, writeback, or writethrough
48
* - initial size - File size before truncation
49
* - truncated size - File size after truncation
50
*/
51
typedef tuple<tuple<bool, bool, bool>, cache_mode, ssize_t, ssize_t> CacheParam;
52
53
class Cache: public FuseTest, public WithParamInterface<CacheParam> {
54
public:
55
bool m_direct_io;
56
57
Cache(): m_direct_io(false) {};
58
59
virtual void SetUp() {
60
int cache_mode = get<1>(GetParam());
61
switch (cache_mode) {
62
case Uncached:
63
m_direct_io = true;
64
break;
65
case WritebackAsync:
66
m_async = true;
67
/* FALLTHROUGH */
68
case Writeback:
69
m_init_flags |= FUSE_WRITEBACK_CACHE;
70
/* FALLTHROUGH */
71
case Writethrough:
72
break;
73
default:
74
FAIL() << "Unknown cache mode";
75
}
76
m_noatime = true; // To prevent SETATTR for atime on close
77
78
FuseTest::SetUp();
79
if (IsSkipped())
80
return;
81
}
82
83
void expect_getattr(uint64_t ino, int times, uint64_t size, uint64_t attr_valid)
84
{
85
EXPECT_CALL(*m_mock, process(
86
ResultOf([=](auto in) {
87
return (in.header.opcode == FUSE_GETATTR &&
88
in.header.nodeid == ino);
89
}, Eq(true)),
90
_)
91
).Times(times)
92
.WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
93
SET_OUT_HEADER_LEN(out, attr);
94
out.body.attr.attr_valid = attr_valid;
95
out.body.attr.attr.ino = ino;
96
out.body.attr.attr.mode = S_IFREG | 0644;
97
out.body.attr.attr.size = size;
98
})));
99
}
100
101
void expect_lookup(const char *relpath, uint64_t ino,
102
uint64_t size, uint64_t entry_valid, uint64_t attr_valid)
103
{
104
EXPECT_LOOKUP(FUSE_ROOT_ID, relpath)
105
.WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
106
SET_OUT_HEADER_LEN(out, entry);
107
out.body.entry.attr.mode = S_IFREG | 0644;
108
out.body.entry.nodeid = ino;
109
out.body.entry.attr.nlink = 1;
110
out.body.entry.attr_valid = attr_valid;
111
out.body.entry.attr.size = size;
112
out.body.entry.entry_valid = entry_valid;
113
})));
114
}
115
116
void expect_open(uint64_t ino, int times)
117
{
118
FuseTest::expect_open(ino, m_direct_io ? FOPEN_DIRECT_IO: 0, times);
119
}
120
121
void expect_release(uint64_t ino, ProcessMockerT r)
122
{
123
EXPECT_CALL(*m_mock, process(
124
ResultOf([=](auto in) {
125
return (in.header.opcode == FUSE_RELEASE &&
126
in.header.nodeid == ino);
127
}, Eq(true)),
128
_)
129
).WillRepeatedly(Invoke(r));
130
}
131
132
};
133
134
// If the server truncates the file behind the kernel's back, the kernel should
135
// invalidate cached pages beyond the new EOF
136
TEST_P(Cache, truncate_by_surprise_invalidates_cache)
137
{
138
const char FULLPATH[] = "mountpoint/some_file.txt";
139
const char RELPATH[] = "some_file.txt";
140
const char *CONTENTS = "abcdefghijklmnopqrstuvwxyz";
141
uint64_t ino = 42;
142
uint64_t attr_valid, entry_valid;
143
int fd;
144
ssize_t bufsize = strlen(CONTENTS);
145
uint8_t buf[bufsize];
146
bool reopen = get<0>(get<0>(GetParam()));
147
bool cache_lookups = get<1>(get<0>(GetParam()));
148
bool cache_attrs = get<2>(get<0>(GetParam()));
149
ssize_t osize = get<2>(GetParam());
150
ssize_t nsize = get<3>(GetParam());
151
152
ASSERT_LE(osize, bufsize);
153
ASSERT_LE(nsize, bufsize);
154
if (cache_attrs)
155
attr_valid = UINT64_MAX;
156
else
157
attr_valid = 0;
158
if (cache_lookups)
159
entry_valid = UINT64_MAX;
160
else
161
entry_valid = 0;
162
163
expect_lookup(RELPATH, ino, osize, entry_valid, attr_valid);
164
expect_open(ino, 1);
165
if (!cache_attrs)
166
expect_getattr(ino, 2, osize, attr_valid);
167
expect_read(ino, 0, osize, osize, CONTENTS);
168
169
fd = open(FULLPATH, O_RDONLY);
170
ASSERT_LE(0, fd) << strerror(errno);
171
172
ASSERT_EQ(osize, read(fd, buf, bufsize)) << strerror(errno);
173
ASSERT_EQ(0, memcmp(buf, CONTENTS, osize));
174
175
// Now truncate the file behind the kernel's back. The next read
176
// should discard cache and fetch from disk again.
177
if (reopen) {
178
// Close and reopen the file
179
expect_flush(ino, 1, ReturnErrno(ENOSYS));
180
expect_release(ino, ReturnErrno(0));
181
ASSERT_EQ(0, close(fd));
182
expect_lookup(RELPATH, ino, nsize, entry_valid, attr_valid);
183
expect_open(ino, 1);
184
fd = open(FULLPATH, O_RDONLY);
185
ASSERT_LE(0, fd) << strerror(errno);
186
}
187
188
if (!cache_attrs)
189
expect_getattr(ino, 1, nsize, attr_valid);
190
expect_read(ino, 0, nsize, nsize, CONTENTS);
191
ASSERT_EQ(0, lseek(fd, 0, SEEK_SET));
192
ASSERT_EQ(nsize, read(fd, buf, bufsize)) << strerror(errno);
193
ASSERT_EQ(0, memcmp(buf, CONTENTS, nsize));
194
195
leak(fd);
196
}
197
198
INSTANTIATE_TEST_SUITE_P(Cache, Cache,
199
Combine(
200
/* Test every combination that:
201
* - does not cache at least one of entries and attrs
202
* - either doesn't cache attrs, or reopens the file
203
* In the other combinations, the kernel will never learn that
204
* the file's size has changed.
205
*/
206
Values(
207
std::make_tuple(false, false, false),
208
std::make_tuple(false, true, false),
209
std::make_tuple(true, false, false),
210
std::make_tuple(true, false, true),
211
std::make_tuple(true, true, false)
212
),
213
Values(Writethrough, Writeback),
214
/* Test both reductions and extensions to file size */
215
Values(20),
216
Values(10, 25)
217
)
218
);
219
220