CernVM-FS  2.10.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 "logging.h"
19 #include "platform.h"
20 #include "shortstring.h"
21 #include "smalloc.h"
22 #include "util/posix.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::NentryTracker *nentry_tracker,
71  void **fuse_channel_or_session,
72  bool fuse_notify_invalidation)
73  : inode_tracker_(inode_tracker)
74  , nentry_tracker_(nentry_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 
103 
105  FuseInvalidator *invalidator = reinterpret_cast<FuseInvalidator *>(data);
106  LogCvmfs(kLogCvmfs, kLogDebug, "starting dentry invalidator thread");
107 
108  char c;
109  Handle *handle;
110  while (true) {
111  ReadPipe(invalidator->pipe_ctrl_[0], &c, 1);
112  if (c == 'Q')
113  break;
114 
115  assert(c == 'I');
116  ReadPipe(invalidator->pipe_ctrl_[0], &handle, sizeof(handle));
117  LogCvmfs(kLogCvmfs, kLogDebug, "invalidating kernel caches, timeout %u",
118  handle->timeout_s_);
119 
120  uint64_t deadline = platform_monotonic_time() + handle->timeout_s_;
121 
122  // Fallback: drainout by timeout
123  if ((invalidator->fuse_channel_or_session_ == NULL) ||
125  {
126  while (platform_monotonic_time() < deadline) {
128  if (atomic_read32(&invalidator->terminated_) == 1) {
130  "cancel cache eviction due to termination");
131  break;
132  }
133  }
134  handle->SetDone();
135  continue;
136  }
137 
138  // We must not hold a lock when calling fuse_lowlevel_notify_inval_entry.
139  // Therefore, we first copy all the inodes into a temporary data structure.
140  glue::InodeTracker::Cursor inode_cursor(
141  invalidator->inode_tracker_->BeginEnumerate());
142  uint64_t inode;
143  while (invalidator->inode_tracker_->NextInode(&inode_cursor, &inode))
144  {
145  invalidator->evict_list_.PushBack(inode);
146  }
147  invalidator->inode_tracker_->EndEnumerate(&inode_cursor);
148 
149  unsigned i = 0;
150  unsigned N = invalidator->evict_list_.size();
151  while (i < N) {
152  uint64_t inode = invalidator->evict_list_.At(i);
153  if (inode == 0)
154  inode = FUSE_ROOT_ID;
155  // Can fail, e.g. the inode might be already evicted
156 #if CVMFS_USE_LIBFUSE == 2
157  fuse_lowlevel_notify_inval_inode(*reinterpret_cast<struct fuse_chan**>(
158  invalidator->fuse_channel_or_session_), inode, 0, 0);
159 #else
160  fuse_lowlevel_notify_inval_inode(*reinterpret_cast<struct fuse_session**>(
161  invalidator->fuse_channel_or_session_), inode, 0, 0);
162 #endif
163  LogCvmfs(kLogCvmfs, kLogDebug, "evicting inode %" PRIu64, inode);
164 
165  if ((++i % kCheckTimeoutFreqOps) == 0) {
166  if (platform_monotonic_time() >= deadline) {
168  "cancel cache eviction after %u entries due to timeout", i);
169  break;
170  }
171  if (atomic_read32(&invalidator->terminated_) == 1) {
173  "cancel cache eviction due to termination");
174  break;
175  }
176  }
177  }
178 
179  // Do the nentry tracker last to increase the effectiveness of pruning
180  invalidator->nentry_tracker_->Prune();
181  // Copy and empty the nentry tracker in a single atomic operation
182  glue::NentryTracker *nentries_copy = invalidator->nentry_tracker_->Move();
183  glue::NentryTracker::Cursor nentry_cursor = nentries_copy->BeginEnumerate();
184  uint64_t entry_parent;
185  NameString entry_name;
186  i = 0;
187  while (nentries_copy->NextEntry(&nentry_cursor, &entry_parent, &entry_name))
188  {
189  // Can fail, e.g. the entry might be already evicted
190 #if CVMFS_USE_LIBFUSE == 2
191  fuse_lowlevel_notify_inval_entry(*reinterpret_cast<struct fuse_chan**>(
192  invalidator->fuse_channel_or_session_),
193  entry_parent, entry_name.GetChars(), entry_name.GetLength());
194 #else
195  fuse_lowlevel_notify_inval_entry(*reinterpret_cast<struct fuse_session**>(
196  invalidator->fuse_channel_or_session_),
197  entry_parent, entry_name.GetChars(), entry_name.GetLength());
198 #endif
199  if ((++i % kCheckTimeoutFreqOps) == 0) {
200  if (atomic_read32(&invalidator->terminated_) == 1) {
202  "cancel cache eviction due to termination");
203  break;
204  }
205  }
206  }
207  nentries_copy->EndEnumerate(&nentry_cursor);
208  delete nentries_copy;
209 
210  handle->SetDone();
211  invalidator->evict_list_.Clear();
212  }
213 
214  LogCvmfs(kLogCvmfs, kLogDebug, "stopping dentry invalidator thread");
215  return NULL;
216 }
217 
218 
220  int retval;
221  retval = pthread_create(&thread_invalidator_, NULL, MainInvalidator, this);
222  assert(retval == 0);
223  spawned_ = true;
224 }
#define LogCvmfs(source, mask,...)
Definition: logging.h:20
atomic_int32 terminated_
Definition: fuse_evict.h:102
Item At(const size_t index) const
Definition: bigvector.h:50
void EndEnumerate(Cursor *cursor)
Definition: glue_buffer.cc:257
bool NextEntry(Cursor *cursor, uint64_t *inode_parent, NameString *name)
Definition: glue_buffer.cc:242
NentryTracker * Move()
Definition: glue_buffer.cc:156
Cursor BeginEnumerate()
Definition: glue_buffer.h:582
assert((mem||(size==0))&&"Out Of Memory")
static const unsigned kCheckTimeoutFreqMs
Definition: fuse_evict.h:80
FuseInvalidator(glue::InodeTracker *inode_tracker, glue::NentryTracker *nentry_tracker, void **fuse_channel_or_session, bool fuse_notify_invalidation)
Definition: fuse_evict.cc:68
void EndEnumerate(Cursor *cursor)
Definition: glue_buffer.h:607
void MakePipe(int pipe_fd[2])
Definition: posix.cc:525
static bool HasFuseNotifyInval()
Definition: fuse_evict.cc:53
static const unsigned kTimeoutSafetyMarginSec
Definition: fuse_evict.h:76
int32_t atomic_int32
Definition: atomic.h:17
glue::NentryTracker * nentry_tracker_
Definition: fuse_evict.h:90
static const unsigned kCheckTimeoutFreqOps
Definition: fuse_evict.h:85
BigVector< uint64_t > evict_list_
Definition: fuse_evict.h:103
void ** fuse_channel_or_session_
Definition: fuse_evict.h:94
bool NextInode(Cursor *cursor, uint64_t *inode)
Definition: glue_buffer.h:603
atomic_int32 * status_
Definition: fuse_evict.h:61
void Clear()
Definition: bigvector.h:71
Handle(unsigned timeout_s)
Definition: fuse_evict.cc:26
uint64_t platform_monotonic_time()
void PushBack(const Item &item)
Definition: bigvector.h:60
glue::InodeTracker * inode_tracker_
Definition: fuse_evict.h:89
static bool g_fuse_notify_invalidation_
Definition: fuse_evict.h:105
int pipe_ctrl_[2]
Definition: fuse_evict.h:96
void InvalidateInodes(Handle *handle)
Definition: fuse_evict.cc:96
pthread_t thread_invalidator_
Definition: fuse_evict.h:97
void SafeSleepMs(const unsigned ms)
Definition: posix.cc:1918
unsigned GetLength() const
Definition: shortstring.h:104
const char * GetChars() const
Definition: shortstring.h:96
void WritePipe(int fd, const void *buf, size_t nbyte)
Definition: posix.cc:534
void ReadPipe(int fd, void *buf, size_t nbyte)
Definition: posix.cc:546
void ClosePipe(int pipe_fd[2])
Definition: posix.cc:584
static void * MainInvalidator(void *data)
Definition: fuse_evict.cc:104
size_t size() const
Definition: bigvector.h:100