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