CernVM-FS  2.11.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 #include "cvmfs_config.h"
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 
17 #include "glue_buffer.h"
18 #include "shortstring.h"
19 #include "util/logging.h"
20 #include "util/platform.h"
21 #include "util/posix.h"
22 #include "util/smalloc.h"
23 
24 using namespace std; // NOLINT
25 
27  : timeout_s_((timeout_s == 0) ? 0 : (timeout_s + kTimeoutSafetyMarginSec))
28 {
29  status_ = reinterpret_cast<atomic_int32 *>(smalloc(sizeof(atomic_int32)));
30  atomic_init32(status_);
31 }
32 
33 
35  free(status_);
36 }
37 
38 
41 }
42 
43 
44 //------------------------------------------------------------------------------
45 
46 
48 const unsigned FuseInvalidator::kCheckTimeoutFreqMs = 100;
49 const unsigned FuseInvalidator::kCheckTimeoutFreqOps = 256;
50 
52 
64  return FuseInvalidator::g_fuse_notify_invalidation_ && (FUSE_VERSION >= 29);
65 }
66 
67 
69  glue::InodeTracker *inode_tracker,
70  glue::DentryTracker *dentry_tracker,
71  void **fuse_channel_or_session,
72  bool fuse_notify_invalidation)
73  : inode_tracker_(inode_tracker)
74  , dentry_tracker_(dentry_tracker)
75  , fuse_channel_or_session_(fuse_channel_or_session)
76  , spawned_(false)
77 {
78  g_fuse_notify_invalidation_ = fuse_notify_invalidation;
80  memset(&thread_invalidator_, 0, sizeof(thread_invalidator_));
81  atomic_init32(&terminated_);
82 }
83 
84 
86  atomic_cas32(&terminated_, 0, 1);
87  if (spawned_) {
88  char c = 'Q';
89  WritePipe(pipe_ctrl_[1], &c, 1);
90  pthread_join(thread_invalidator_, NULL);
91  }
93 }
94 
95 
97  assert(handle != NULL);
98  char c = 'I';
99  WritePipe(pipe_ctrl_[1], &c, 1);
100  WritePipe(pipe_ctrl_[1], &handle, sizeof(handle));
101 }
102 
104  uint64_t parent_ino, const NameString &name)
105 {
106  char c = 'D';
107  WritePipe(pipe_ctrl_[1], &c, 1);
108  WritePipe(pipe_ctrl_[1], &parent_ino, sizeof(parent_ino));
109  unsigned len = name.GetLength();
110  WritePipe(pipe_ctrl_[1], &len, sizeof(len));
111  WritePipe(pipe_ctrl_[1], name.GetChars(), len);
112 }
113 
114 
116  FuseInvalidator *invalidator = reinterpret_cast<FuseInvalidator *>(data);
117  LogCvmfs(kLogCvmfs, kLogDebug, "starting dentry invalidator thread");
118 
119  bool reported_missing_inval_support = false;
120  char c;
121  Handle *handle;
122  while (true) {
123  ReadPipe(invalidator->pipe_ctrl_[0], &c, 1);
124  if (c == 'Q')
125  break;
126 
127  if (c == 'D') {
128  uint64_t parent_ino;
129  unsigned len;
130  ReadPipe(invalidator->pipe_ctrl_[0], &parent_ino, sizeof(parent_ino));
131  ReadPipe(invalidator->pipe_ctrl_[0], &len, sizeof(len));
132  char *name = static_cast<char *>(smalloc(len + 1));
133  ReadPipe(invalidator->pipe_ctrl_[0], name, len);
134  name[len] = '\0';
135  if (invalidator->fuse_channel_or_session_ == NULL) {
136  if (!reported_missing_inval_support) {
138  "missing fuse support for dentry invalidation (%d/%s)",
139  parent_ino, name);
140  reported_missing_inval_support = true;
141  }
142  free(name);
143  continue;
144  }
145  LogCvmfs(kLogCvmfs, kLogDebug, "evicting single dentry %" PRIu64 "/%s",
146  parent_ino, name);
147 #if CVMFS_USE_LIBFUSE == 2
148  fuse_lowlevel_notify_inval_entry(*reinterpret_cast<struct fuse_chan**>(
149  invalidator->fuse_channel_or_session_), parent_ino, name, len);
150 #else
151  fuse_lowlevel_notify_inval_entry(*reinterpret_cast<struct fuse_session**>(
152  invalidator->fuse_channel_or_session_), parent_ino, name, len);
153 #endif
154  free(name);
155  continue;
156  }
157 
158  assert(c == 'I');
159  ReadPipe(invalidator->pipe_ctrl_[0], &handle, sizeof(handle));
160  LogCvmfs(kLogCvmfs, kLogDebug, "invalidating kernel caches, timeout %u",
161  handle->timeout_s_);
162 
163  uint64_t deadline = platform_monotonic_time() + handle->timeout_s_;
164 
165  // Fallback: drainout by timeout
166  if ((invalidator->fuse_channel_or_session_ == NULL) ||
168  {
169  while (platform_monotonic_time() < deadline) {
171  if (atomic_read32(&invalidator->terminated_) == 1) {
173  "cancel cache eviction due to termination");
174  break;
175  }
176  }
177  handle->SetDone();
178  continue;
179  }
180 
181  // We must not hold a lock when calling fuse_lowlevel_notify_inval_entry.
182  // Therefore, we first copy all the inodes into a temporary data structure.
183  glue::InodeTracker::Cursor inode_cursor(
184  invalidator->inode_tracker_->BeginEnumerate());
185  uint64_t inode;
186  while (invalidator->inode_tracker_->NextInode(&inode_cursor, &inode))
187  {
188  invalidator->evict_list_.PushBack(inode);
189  }
190  invalidator->inode_tracker_->EndEnumerate(&inode_cursor);
191 
192  unsigned i = 0;
193  unsigned N = invalidator->evict_list_.size();
194  while (i < N) {
195  uint64_t inode = invalidator->evict_list_.At(i);
196  if (inode == 0)
197  inode = FUSE_ROOT_ID;
198  // Can fail, e.g. the inode might be already evicted
199 
200  int dbg_retval;
201 
202 #if CVMFS_USE_LIBFUSE == 2
203  dbg_retval = fuse_lowlevel_notify_inval_inode(
204  *reinterpret_cast<struct fuse_chan**>(
205  invalidator->fuse_channel_or_session_), inode, 0, 0);
206 #else
207  dbg_retval = fuse_lowlevel_notify_inval_inode(
208  *reinterpret_cast<struct fuse_session**>(
209  invalidator->fuse_channel_or_session_), inode, 0, 0);
210 #endif
212  "evicting inode %" PRIu64 " with retval: %d",
213  inode, dbg_retval);
214 
215  (void) dbg_retval; // prevent compiler complaining
216 
217  if ((++i % kCheckTimeoutFreqOps) == 0) {
218  if (platform_monotonic_time() >= deadline) {
220  "cancel cache eviction after %u entries due to timeout", i);
221  break;
222  }
223  if (atomic_read32(&invalidator->terminated_) == 1) {
225  "cancel cache eviction due to termination");
226  break;
227  }
228  }
229  }
230 
231  // Do the dentry tracker last to increase the effectiveness of pruning
232  invalidator->dentry_tracker_->Prune();
233  // Copy and empty the dentry tracker in a single atomic operation
234  glue::DentryTracker *dentries_copy = invalidator->dentry_tracker_->Move();
235  glue::DentryTracker::Cursor dentry_cursor = dentries_copy->BeginEnumerate();
236  uint64_t entry_parent;
237  NameString entry_name;
238  i = 0;
239  while (dentries_copy->NextEntry(&dentry_cursor, &entry_parent, &entry_name))
240  {
241  LogCvmfs(kLogCvmfs, kLogDebug, "evicting dentry %d --> %s",
242  entry_parent, entry_name.c_str());
243  // Can fail, e.g. the entry might be already evicted
244 #if CVMFS_USE_LIBFUSE == 2
245  struct fuse_chan* channel_or_session =
246  *reinterpret_cast<struct fuse_chan**>(
247  invalidator->fuse_channel_or_session_);
248 #else
249  struct fuse_session* channel_or_session =
250  *reinterpret_cast<struct fuse_session**>(
251  invalidator->fuse_channel_or_session_);
252 #endif
253 
254 // we do not care if fuse kernel supports expire_entry as if it is
255 // not support it will just be handled like a fuse_inval
256 #ifdef FUSE_CAP_EXPIRE_ONLY
257  fuse_lowlevel_notify_expire_entry(channel_or_session,
258  entry_parent, entry_name.GetChars(), entry_name.GetLength(),
259  FUSE_LL_EXPIRE_ONLY);
260 #else
261  fuse_lowlevel_notify_inval_entry(channel_or_session,
262  entry_parent, entry_name.GetChars(), entry_name.GetLength());
263 #endif
264 
265  if ((++i % kCheckTimeoutFreqOps) == 0) {
266  if (atomic_read32(&invalidator->terminated_) == 1) {
268  "cancel cache eviction due to termination");
269  break;
270  }
271  }
272  }
273  dentries_copy->EndEnumerate(&dentry_cursor);
274  delete dentries_copy;
275 
276  handle->SetDone();
277  invalidator->evict_list_.Clear();
278  }
279 
280  LogCvmfs(kLogCvmfs, kLogDebug, "stopping dentry invalidator thread");
281  return NULL;
282 }
283 
284 
286  int retval;
287  retval = pthread_create(&thread_invalidator_, NULL, MainInvalidator, this);
288  assert(retval == 0);
289  spawned_ = true;
290 }
#define LogCvmfs(source, mask,...)
Definition: logging.h:25
atomic_int32 terminated_
Definition: fuse_evict.h:104
Item At(const size_t index) const
Definition: bigvector.h:50
Cursor BeginEnumerate()
Definition: glue_buffer.h:753
glue::DentryTracker * dentry_tracker_
Definition: fuse_evict.h:92
FuseInvalidator(glue::InodeTracker *inode_tracker, glue::DentryTracker *dentry_tracker, void **fuse_channel_or_session, bool fuse_notify_invalidation)
Definition: fuse_evict.cc:68
assert((mem||(size==0))&&"Out Of Memory")
static const unsigned kCheckTimeoutFreqMs
Definition: fuse_evict.h:82
bool NextEntry(Cursor *cursor, uint64_t *inode_parent, NameString *name)
Definition: glue_buffer.cc:246
void EndEnumerate(Cursor *cursor)
Definition: glue_buffer.h:778
void MakePipe(int pipe_fd[2])
Definition: posix.cc:488
static bool HasFuseNotifyInval()
Definition: fuse_evict.cc:53
static const unsigned kTimeoutSafetyMarginSec
Definition: fuse_evict.h:78
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:87
BigVector< uint64_t > evict_list_
Definition: fuse_evict.h:105
void ** fuse_channel_or_session_
Definition: fuse_evict.h:96
bool NextInode(Cursor *cursor, uint64_t *inode)
Definition: glue_buffer.h:774
atomic_int32 * status_
Definition: fuse_evict.h:61
void Clear()
Definition: bigvector.h:76
Handle(unsigned timeout_s)
Definition: fuse_evict.cc:26
DentryTracker * Move()
Definition: glue_buffer.cc:160
uint64_t platform_monotonic_time()
void InvalidateDentry(uint64_t parent_ino, const NameString &name)
Definition: fuse_evict.cc:103
void PushBack(const Item &item)
Definition: bigvector.h:60
glue::InodeTracker * inode_tracker_
Definition: fuse_evict.h:91
static bool g_fuse_notify_invalidation_
Definition: fuse_evict.h:107
int pipe_ctrl_[2]
Definition: fuse_evict.h:98
void InvalidateInodes(Handle *handle)
Definition: fuse_evict.cc:96
pthread_t thread_invalidator_
Definition: fuse_evict.h:99
void SafeSleepMs(const unsigned ms)
Definition: posix.cc:1968
unsigned GetLength() const
Definition: shortstring.h:131
const char * c_str() const
Definition: shortstring.h:145
const char * GetChars() const
Definition: shortstring.h:123
void WritePipe(int fd, const void *buf, size_t nbyte)
Definition: posix.cc:497
void ReadPipe(int fd, void *buf, size_t nbyte)
Definition: posix.cc:509
void ClosePipe(int pipe_fd[2])
Definition: posix.cc:547
static void * MainInvalidator(void *data)
Definition: fuse_evict.cc:115
size_t size() const
Definition: bigvector.h:121