CernVM-FS  2.13.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
fuse_evict.cc
Go to the documentation of this file.
1 
5 #define __STDC_FORMAT_MACROS
6 
7 
8 #include "fuse_evict.h"
9 
10 #include <inttypes.h>
11 #include <stdint.h>
12 
13 #include <cassert>
14 #include <cstdlib>
15 #include <cstring>
16 #include <new>
17 #include <vector>
18 
19 #include "glue_buffer.h"
20 #include "mountpoint.h"
21 #include "shortstring.h"
22 #include "util/logging.h"
23 #include "util/platform.h"
24 #include "util/posix.h"
25 #include "util/smalloc.h"
26 
27 using namespace std; // NOLINT
28 
30  : timeout_s_((timeout_s == 0) ? 0 : (timeout_s + kTimeoutSafetyMarginSec)) {
31  status_ = reinterpret_cast<atomic_int32 *>(smalloc(sizeof(atomic_int32)));
32  atomic_init32(status_);
33 }
34 
35 
37 
38 
40  while (!IsDone())
42 }
43 
44 
45 //------------------------------------------------------------------------------
46 
47 
49 const unsigned FuseInvalidator::kCheckTimeoutFreqMs = 100;
50 const unsigned FuseInvalidator::kCheckTimeoutFreqOps = 256;
51 
53 
65  return FuseInvalidator::g_fuse_notify_invalidation_ && (FUSE_VERSION >= 29);
66 }
67 
68 
70  void **fuse_channel_or_session,
71  bool fuse_notify_invalidation)
72  : mount_point_(mount_point)
73  , inode_tracker_(mount_point->inode_tracker())
74  , dentry_tracker_(mount_point->dentry_tracker())
75  , fuse_channel_or_session_(fuse_channel_or_session)
76  , spawned_(false) {
77  g_fuse_notify_invalidation_ = fuse_notify_invalidation;
78  memset(&thread_invalidator_, 0, sizeof(thread_invalidator_));
79  atomic_init32(&terminated_);
80 }
81 
83  glue::DentryTracker *dentry_tracker,
84  void **fuse_channel_or_session,
85  bool fuse_notify_invalidation)
86  : mount_point_(NULL)
87  , inode_tracker_(inode_tracker)
88  , dentry_tracker_(dentry_tracker)
89  , fuse_channel_or_session_(fuse_channel_or_session)
90  , spawned_(false) {
91  g_fuse_notify_invalidation_ = fuse_notify_invalidation;
92  memset(&thread_invalidator_, 0, sizeof(thread_invalidator_));
93  atomic_init32(&terminated_);
94 }
95 
96 
98  atomic_cas32(&terminated_, 0, 1);
99  if (spawned_) {
100  QuitCommand *cmd = new (smalloc(sizeof(QuitCommand))) QuitCommand();
101  channel_.PushBack(cmd);
102  pthread_join(thread_invalidator_, NULL);
103  }
104 }
105 
106 
108  assert(handle != NULL);
109  InvalInodesCommand *inval_inodes_command = new (
110  smalloc(sizeof(InvalInodesCommand))) InvalInodesCommand();
111  inval_inodes_command->handle = handle;
112  channel_.PushBack(inval_inodes_command);
113 }
114 
115 void FuseInvalidator::InvalidateDentry(uint64_t parent_ino,
116  const NameString &name) {
117  InvalDentryCommand *inval_dentry_command;
118  vector<Command *> *items = channel_.StartEnqueueing();
119  for (size_t i = 0; i < items->size(); ++i) {
120  inval_dentry_command = dynamic_cast<InvalDentryCommand *>(items->at(i));
121  if (!inval_dentry_command)
122  continue;
123  if (inval_dentry_command->parent_ino != parent_ino)
124  continue;
125  if (inval_dentry_command->name != name)
126  continue;
127  channel_.AbortEnqueueing();
128  return;
129  }
130 
131  inval_dentry_command = new (smalloc(sizeof(InvalDentryCommand)))
133  inval_dentry_command->parent_ino = parent_ino;
134  inval_dentry_command->name = name;
135  items->push_back(inval_dentry_command);
136  channel_.CommitEnqueueing();
137 }
138 
140  FuseInvalidator *invalidator = reinterpret_cast<FuseInvalidator *>(data);
141  LogCvmfs(kLogCvmfs, kLogDebug, "starting dentry invalidator thread");
142 
143  bool reported_missing_inval_support = false;
144  while (true) {
145  Command *command = invalidator->channel_.PopFront();
146 
147  if (dynamic_cast<QuitCommand *>(command)) {
148  command->~Command();
149  free(command);
150  break;
151  }
152 
154  *inval_dentry_command = dynamic_cast<InvalDentryCommand *>(command);
155  if (inval_dentry_command) {
156  if (invalidator->fuse_channel_or_session_ == NULL) {
157  if (!reported_missing_inval_support) {
159  "missing fuse support for dentry invalidation "
160  "(%" PRIu64 "/%s)",
161  inval_dentry_command->parent_ino,
162  inval_dentry_command->name.ToString().c_str());
163  reported_missing_inval_support = true;
164  }
165  inval_dentry_command->~InvalDentryCommand();
166  free(inval_dentry_command);
167  continue;
168  }
169  LogCvmfs(kLogCvmfs, kLogDebug, "evicting single dentry %" PRIu64 "/%s",
170  inval_dentry_command->parent_ino,
171  inval_dentry_command->name.ToString().c_str());
172 #if CVMFS_USE_LIBFUSE == 2
173  fuse_lowlevel_notify_inval_entry(
174  *reinterpret_cast<struct fuse_chan **>(
175  invalidator->fuse_channel_or_session_),
176  inval_dentry_command->parent_ino,
177  inval_dentry_command->name.GetChars(),
178  inval_dentry_command->name.GetLength());
179 #else
180  fuse_lowlevel_notify_inval_entry(
181  *reinterpret_cast<struct fuse_session **>(
182  invalidator->fuse_channel_or_session_),
183  inval_dentry_command->parent_ino,
184  inval_dentry_command->name.GetChars(),
185  inval_dentry_command->name.GetLength());
186 #endif
187  inval_dentry_command->~InvalDentryCommand();
188  free(inval_dentry_command);
189  continue;
190  }
191 
193  *inval_inodes_command = dynamic_cast<InvalInodesCommand *>(command);
194  assert(inval_inodes_command);
195 
196  Handle *handle = inval_inodes_command->handle;
197  LogCvmfs(kLogCvmfs, kLogDebug, "invalidating kernel caches, timeout %u",
198  handle->timeout_s_);
199  inval_inodes_command->~InvalInodesCommand();
200  free(inval_inodes_command);
201 
202  uint64_t deadline = platform_monotonic_time() + handle->timeout_s_;
203 
204  // Fallback: drainout by timeout
205  if ((invalidator->fuse_channel_or_session_ == NULL)
206  || !HasFuseNotifyInval()) {
207  while (platform_monotonic_time() < deadline) {
209  if (atomic_read32(&invalidator->terminated_) == 1) {
211  "cancel cache eviction due to termination");
212  break;
213  }
214  }
215  handle->SetDone();
216  continue;
217  }
218 
219  // We must not hold a lock when calling fuse_lowlevel_notify_inval_entry.
220  // Therefore, we first copy all the inodes into a temporary data structure.
221  glue::InodeTracker::Cursor inode_cursor(
222  invalidator->inode_tracker_->BeginEnumerate());
223  uint64_t inode;
224  while (invalidator->inode_tracker_->NextInode(&inode_cursor, &inode)) {
225  invalidator->evict_list_.PushBack(inode);
226  }
227  invalidator->inode_tracker_->EndEnumerate(&inode_cursor);
228 
229  unsigned i = 0;
230  unsigned N = invalidator->evict_list_.size();
231  while (i < N) {
232  uint64_t inode = invalidator->evict_list_.At(i);
233  if (inode == 0)
234  inode = FUSE_ROOT_ID;
235  // Can fail, e.g. the inode might be already evicted
236 
237  int dbg_retval;
238 
239 #if CVMFS_USE_LIBFUSE == 2
240  dbg_retval = fuse_lowlevel_notify_inval_inode(
241  *reinterpret_cast<struct fuse_chan **>(
242  invalidator->fuse_channel_or_session_),
243  inode, 0, 0);
244 #else
245  dbg_retval = fuse_lowlevel_notify_inval_inode(
246  *reinterpret_cast<struct fuse_session **>(
247  invalidator->fuse_channel_or_session_),
248  inode, 0, 0);
249 #endif
251  "evicting inode %" PRIu64 " with retval: %d", inode, dbg_retval);
252 
253  (void)dbg_retval; // prevent compiler complaining
254 
255  if ((++i % kCheckTimeoutFreqOps) == 0) {
256  if (platform_monotonic_time() >= deadline) {
258  "cancel cache eviction after %u entries due to timeout", i);
259  break;
260  }
261  if (atomic_read32(&invalidator->terminated_) == 1) {
263  "cancel cache eviction due to termination");
264  break;
265  }
266  }
267  }
268 
269  // Do the dentry tracker last to increase the effectiveness of pruning
270  invalidator->dentry_tracker_->Prune();
271  // Copy and empty the dentry tracker in a single atomic operation
272  glue::DentryTracker *dentries_copy = invalidator->dentry_tracker_->Move();
273  glue::DentryTracker::Cursor dentry_cursor = dentries_copy->BeginEnumerate();
274  uint64_t entry_parent;
275  NameString entry_name;
276  i = 0;
277 
278 #if CVMFS_USE_LIBFUSE == 2
279  int (*notify_func)(struct fuse_chan *, fuse_ino_t, const char *, size_t);
280  notify_func = &fuse_lowlevel_notify_inval_entry;
281 #else
282  int (*notify_func)(struct fuse_session *, fuse_ino_t, const char *, size_t);
283  notify_func = &fuse_lowlevel_notify_inval_entry;
284 #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 16)
285  // must be libfuse >= 3.16, otherwise the signature is wrong and it
286  // will fail building
287  // mount_point can only be NULL for unittests
288  if (invalidator->mount_point_ != NULL
289  && invalidator->mount_point_->fuse_expire_entry()) {
290  notify_func = &fuse_lowlevel_notify_expire_entry;
291  }
292 #endif
293 #endif
294 
295  while (
296  dentries_copy->NextEntry(&dentry_cursor, &entry_parent, &entry_name)) {
297  LogCvmfs(kLogCvmfs, kLogDebug, "evicting dentry %lu --> %s", entry_parent,
298  entry_name.c_str());
299  // Can fail, e.g. the entry might be already evicted
300 #if CVMFS_USE_LIBFUSE == 2
301  struct fuse_chan
302  *channel_or_session = *reinterpret_cast<struct fuse_chan **>(
303  invalidator->fuse_channel_or_session_);
304 #else
305  struct fuse_session
306  *channel_or_session = *reinterpret_cast<struct fuse_session **>(
307  invalidator->fuse_channel_or_session_);
308 #endif
309 
310  notify_func(channel_or_session, entry_parent, entry_name.GetChars(),
311  entry_name.GetLength());
312 
313  if ((++i % kCheckTimeoutFreqOps) == 0) {
314  if (atomic_read32(&invalidator->terminated_) == 1) {
316  "cancel cache eviction due to termination");
317  break;
318  }
319  }
320  }
321  dentries_copy->EndEnumerate(&dentry_cursor);
322  delete dentries_copy;
323 
324  handle->SetDone();
325  invalidator->evict_list_.Clear();
326  }
327 
328  LogCvmfs(kLogCvmfs, kLogDebug, "stopping dentry invalidator thread");
329  return NULL;
330 }
331 
332 
334  int retval;
335  retval = pthread_create(&thread_invalidator_, NULL, MainInvalidator, this);
336  assert(retval == 0);
337  spawned_ = true;
338 }
atomic_int32 terminated_
Definition: fuse_evict.h:129
Item At(const size_t index) const
Definition: bigvector.h:48
bool fuse_expire_entry()
Definition: mountpoint.h:526
Cursor BeginEnumerate()
Definition: glue_buffer.h:737
glue::DentryTracker * dentry_tracker_
Definition: fuse_evict.h:117
Channel< Command > channel_
Definition: fuse_evict.h:123
assert((mem||(size==0))&&"Out Of Memory")
MountPoint * mount_point_
Definition: cvmfs.cc:131
static const unsigned kCheckTimeoutFreqMs
Definition: fuse_evict.h:105
bool NextEntry(Cursor *cursor, uint64_t *inode_parent, NameString *name)
Definition: glue_buffer.cc:242
void EndEnumerate(Cursor *cursor)
Definition: glue_buffer.h:762
static bool HasFuseNotifyInval()
Definition: fuse_evict.cc:54
static const unsigned kTimeoutSafetyMarginSec
Definition: fuse_evict.h:101
int32_t atomic_int32
Definition: atomic.h:17
void EndEnumerate(Cursor *cursor)
Definition: glue_buffer.cc:256
static const unsigned kCheckTimeoutFreqOps
Definition: fuse_evict.h:110
BigVector< uint64_t > evict_list_
Definition: fuse_evict.h:130
void ** fuse_channel_or_session_
Definition: fuse_evict.h:121
bool NextInode(Cursor *cursor, uint64_t *inode)
Definition: glue_buffer.h:758
atomic_int32 * status_
Definition: fuse_evict.h:65
void Clear()
Definition: bigvector.h:72
Handle(unsigned timeout_s)
Definition: fuse_evict.cc:29
DentryTracker * Move()
Definition: glue_buffer.cc:155
uint64_t platform_monotonic_time()
MountPoint * mount_point_
Definition: fuse_evict.h:114
void InvalidateDentry(uint64_t parent_ino, const NameString &name)
Definition: fuse_evict.cc:115
void PushBack(const Item &item)
Definition: bigvector.h:58
glue::InodeTracker * inode_tracker_
Definition: fuse_evict.h:116
std::string ToString() const
Definition: shortstring.h:139
static bool g_fuse_notify_invalidation_
Definition: fuse_evict.h:132
void InvalidateInodes(Handle *handle)
Definition: fuse_evict.cc:107
pthread_t thread_invalidator_
Definition: fuse_evict.h:124
void SafeSleepMs(const unsigned ms)
Definition: posix.cc:2024
unsigned GetLength() const
Definition: shortstring.h:131
const char * c_str() const
Definition: shortstring.h:143
const char * GetChars() const
Definition: shortstring.h:123
static void * MainInvalidator(void *data)
Definition: fuse_evict.cc:139
FuseInvalidator(MountPoint *mountpoint, void **fuse_channel_or_session, bool fuse_notify_invalidation)
Definition: fuse_evict.cc:69
size_t size() const
Definition: bigvector.h:117
CVMFS_EXPORT void LogCvmfs(const LogSource source, const int mask, const char *format,...)
Definition: logging.cc:545