CernVM-FS  2.10.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
fuse_remount.cc
Go to the documentation of this file.
1 
5 #include "cvmfs_config.h"
6 #include "fuse_remount.h"
7 
8 #include <errno.h>
9 #include <poll.h>
10 #include <unistd.h>
11 
12 #include <cassert>
13 #include <cstdlib>
14 #include <cstring>
15 
16 #include "backoff.h"
17 #include "catalog_mgr_client.h"
18 #include "fuse_inode_gen.h"
19 #include "logging.h"
20 #include "lru_md.h"
21 #include "mountpoint.h"
22 #include "platform.h"
23 #include "statistics.h"
24 #include "util/exception.h"
25 #include "util/posix.h"
26 
27 using namespace std; // NOLINT
28 
29 
31  if (mountpoint_->catalog_mgr()->GetRootHash() == root_hash)
32  return kStatusUp2Date;
33 
34  FenceGuard fence_guard(&fence_maintenance_);
35  if (IsInMaintenanceMode())
36  return kStatusMaintenance;
37 
38  if (atomic_cas32(&drainout_mode_, 0, 1)) {
39  // As of this point, fuse callbacks return zero as cache timeout
40  LogCvmfs(kLogCvmfs, kLogDebug, "chroot, draining out meta-data caches");
41  invalidator_handle_.Reset();
42  invalidator_->InvalidateInodes(&invalidator_handle_);
43  atomic_inc32(&drainout_mode_);
44  // drainout_mode_ == 2, IsInDrainoutMode is now 'true'
45  } else {
46  LogCvmfs(kLogCvmfs, kLogDebug, "already in drainout mode, leaving");
47  return kStatusDraining;
48  }
49 
50  int32_t drainout_code = 0;
51  BackoffThrottle throttle;
52  do {
53  TryFinish(root_hash);
54  drainout_code = atomic_read32(&drainout_mode_);
55  if (drainout_code == 0)
56  break;
57  throttle.Throttle();
58  } while (true);
59 
60  if (mountpoint_->catalog_mgr()->GetRootHash() == root_hash)
61  return kStatusUp2Date;
62  return kStatusFailGeneral;
63 }
64 
65 
71  FenceGuard fence_guard(&fence_maintenance_);
72  if (IsInMaintenanceMode())
73  return kStatusMaintenance;
74 
76  "catalog TTL expired, checking revision against blacklists");
77  if (mountpoint_->ReloadBlacklists() &&
78  mountpoint_->catalog_mgr()->IsRevisionBlacklisted())
79  {
81  "repository revision blacklisted, aborting");
82  }
83 
84  LogCvmfs(kLogCvmfs, kLogDebug, "remounting root catalog");
85  catalog::LoadError retval = mountpoint_->catalog_mgr()->Remount(true);
86  switch (retval) {
87  case catalog::kLoadNew:
88  SetOfflineMode(false);
89  if (atomic_cas32(&drainout_mode_, 0, 1)) {
90  // As of this point, fuse callbacks return zero as cache timeout
92  "new catalog revision available, "
93  "draining out meta-data caches");
94  invalidator_handle_.Reset();
95  invalidator_->InvalidateInodes(&invalidator_handle_);
96  atomic_inc32(&drainout_mode_);
97  // drainout_mode_ == 2, IsInDrainoutMode is now 'true'
98  } else {
99  LogCvmfs(kLogCvmfs, kLogDebug, "already in drainout mode, leaving");
100  }
101  return kStatusDraining;
102  case catalog::kLoadFail:
105  "reload failed (%s), applying short term TTL",
106  catalog::Code2Ascii(retval));
107  SetOfflineMode(true);
108  catalogs_valid_until_ = time(NULL) + MountPoint::kShortTermTTL;
109  SetAlarm(MountPoint::kShortTermTTL);
110  return (retval == catalog::kLoadFail) ?
111  kStatusFailGeneral : kStatusFailNoSpace;
112  case catalog::kLoadUp2Date: {
114  "catalog up to date (could be offline mode)");
115  SetOfflineMode(mountpoint_->catalog_mgr()->offline_mode());
116  unsigned ttl = offline_mode_ ?
117  MountPoint::kShortTermTTL : mountpoint_->GetEffectiveTtlSec();
118  catalogs_valid_until_ = time(NULL) + ttl;
119  SetAlarm(ttl);
120  return kStatusUp2Date;
121  }
122  default:
123  PANIC(NULL);
124  }
125 }
126 
127 
133  BackoffThrottle throttle;
134  while (true) {
135  Status status = Check();
136  switch (status) {
137  case kStatusDraining:
138  TryFinish();
139  break;
140  default:
141  return status;
142  }
143  throttle.Throttle();
144  }
145 }
146 
147 
149  fence_maintenance_.Drain();
150  atomic_cas32(&maintenance_mode_, 0, 1);
151  fence_maintenance_.Open();
152 
153  // All running Check() and TryFinish() methods returned. Both methods now
154  // return immediately as noops.
155 
156  // Flush caches before reload of fuse module
157  invalidator_handle_.Reset();
158  invalidator_->InvalidateInodes(&invalidator_handle_);
159  invalidator_handle_.WaitFor();
160 }
161 
163  cvmfs::InodeGenerationInfo *inode_generation_info,
164  void **fuse_channel_or_session,
165  bool fuse_notify_invalidation)
166  : mountpoint_(mountpoint),
167  inode_generation_info_(inode_generation_info),
168  invalidator_(new FuseInvalidator(mountpoint->inode_tracker(),
169  mountpoint->nentry_tracker(),
170  fuse_channel_or_session,
171  fuse_notify_invalidation)),
172  invalidator_handle_(static_cast<int>(mountpoint->kcache_timeout_sec())),
173  fence_(new Fence()),
174  offline_mode_(false),
175  catalogs_valid_until_(MountPoint::kIndefiniteDeadline) {
178  atomic_init32(&drainout_mode_);
179  atomic_init32(&maintenance_mode_);
180  atomic_init32(&critical_section_);
181 }
182 
184  if (HasRemountTrigger()) {
185  char quit = 'Q';
186  WritePipe(pipe_remount_trigger_[1], &quit, 1);
187  pthread_join(thread_remount_trigger_, NULL);
189  }
190  delete invalidator_;
191  delete fence_;
192 }
193 
194 
200  FuseRemounter *remounter = reinterpret_cast<FuseRemounter *>(data);
201  LogCvmfs(kLogCvmfs, kLogDebug, "starting remount trigger");
202  char c;
203  int timeout_ms = -1;
204  uint64_t deadline = 0;
205  struct pollfd watch_ctrl;
206  watch_ctrl.fd = remounter->pipe_remount_trigger_[0];
207  watch_ctrl.events = POLLIN | POLLPRI;
208  while (true) {
209  watch_ctrl.revents = 0;
210  int retval = poll(&watch_ctrl, 1, timeout_ms);
211  if (retval < 0) {
212  if (errno == EINTR) {
213  if (timeout_ms >= 0) {
214  uint64_t now = platform_monotonic_time();
215  timeout_ms = (now > deadline) ? 0 : (deadline - now) * 1000;
216  }
217  continue;
218  }
220  "remount trigger connection failure (%d)", errno);
221  }
222 
223  if (retval == 0) {
224  remounter->Check();
225  timeout_ms = -1;
226  continue;
227  }
228 
229  assert(watch_ctrl.revents != 0);
230 
231  ReadPipe(remounter->pipe_remount_trigger_[0], &c, 1);
232  if (c == 'Q')
233  break;
234  assert(c == 'T');
235  ReadPipe(remounter->pipe_remount_trigger_[0], &timeout_ms, sizeof(int));
236  deadline = platform_monotonic_time() + timeout_ms / 1000;
237  }
238  LogCvmfs(kLogCvmfs, kLogDebug, "stopping remount trigger");
239  return NULL;
240 }
241 
242 
243 void FuseRemounter::SetAlarm(int timeout) {
244  // Remounting could be called for a non auto-update repository
245  if (!HasRemountTrigger())
246  return;
247 
248  timeout *= 1000; // timeout given in ms
249  const unsigned buf_size = 1 + sizeof(int);
250  char buf[buf_size];
251  buf[0] = 'T';
252  memcpy(&buf[1], &timeout, sizeof(timeout));
253  WritePipe(pipe_remount_trigger_[1], buf, buf_size);
254 }
255 
256 
258  if (value == offline_mode_)
259  return;
260  offline_mode_ = value;
261 
262  if (offline_mode_) {
264  "warning, could not apply updated catalog revision, "
265  "entering offline mode");
267  } else {
268  LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslog, "recovered from offline mode");
269  }
270 }
271 
272 
274  invalidator_->Spawn();
275  if (!mountpoint_->fixed_catalog()) {
277  int retval = pthread_create(
279  assert(retval == 0);
280 
282  unsigned ttl = offline_mode_ ?
284  catalogs_valid_until_ = time(NULL) + ttl;
285  SetAlarm(ttl);
286  }
287 }
288 
289 
296 void FuseRemounter::TryFinish(const shash::Any &root_hash) {
297  FenceGuard fence_guard(&fence_maintenance_);
298  if (IsInMaintenanceMode())
299  return;
300  if (!EnterCriticalSection())
301  return;
302  if (!IsInDrainoutMode()) {
304  return;
305  }
306 
307  // No one else is in this code path and we have a valid FuseInvalidator handle
308 
309  if (!invalidator_handle_.IsDone()) {
311  return;
312  }
313  LogCvmfs(kLogCvmfs, kLogDebug, "caches drained out, applying new catalog");
314 
315  // No new inserts into caches
322 
323  // Ensure that all Fuse callbacks left the catalog query code
324  fence_->Drain();
325  catalog::LoadError retval;
326  if (root_hash.IsNull()) {
327  retval = mountpoint_->catalog_mgr()->Remount(false);
328  } else {
329  retval = mountpoint_->catalog_mgr()->ChangeRoot(root_hash);
330  }
331  if (mountpoint_->inode_annotation()) {
334  }
336  fence_->Open();
337 
341 
342  atomic_xadd32(&drainout_mode_, -2); // 2 --> 0, end of drainout mode
343 
344  if ((retval == catalog::kLoadFail) || (retval == catalog::kLoadNoSpace)) {
345  // Can temporarily "escape" offline mode if update came from updated
346  // alien cache
347  SetOfflineMode(true);
350  } else {
351  SetOfflineMode(false);
352  LogCvmfs(kLogCvmfs, kLogSyslog, "switched to catalog revision %d",
356  }
357 
359 }
LoadError ChangeRoot(const shash::Any &root_hash)
#define LogCvmfs(source, mask,...)
Definition: logging.h:20
bool fixed_catalog()
Definition: mountpoint.h:439
atomic_int32 critical_section_
Definition: fuse_remount.h:139
bool IsInDrainoutMode()
Definition: fuse_remount.h:64
bool IsNull() const
Definition: hash.h:382
int pipe_remount_trigger_[2]
Definition: fuse_remount.h:105
bool IsDone() const
Definition: fuse_evict.h:53
const char * Code2Ascii(const LoadError error)
Definition: catalog_mgr.h:56
string * mountpoint_
Definition: auto_umount.cc:26
FileSystem * file_system()
Definition: mountpoint.h:442
void TryFinish(const shash::Any &root_hash=shash::Any())
void EnterMaintenanceMode()
InodeGenerationInfo inode_generation_info_
Definition: cvmfs.cc:126
bool HasRemountTrigger()
Definition: fuse_remount.h:73
FuseInvalidator::Handle invalidator_handle_
Definition: fuse_remount.h:89
#define PANIC(...)
Definition: exception.h:26
static void * MainRemountTrigger(void *data)
Fence fence_maintenance_
Definition: fuse_remount.h:99
lru::InodeCache * inode_cache()
Definition: mountpoint.h:450
bool EnterCriticalSection()
Definition: fuse_remount.h:76
Status Check()
Definition: fuse_remount.cc:70
assert((mem||(size==0))&&"Out Of Memory")
static const unsigned kShortTermTTL
Definition: mountpoint.h:414
atomic_int32 drainout_mode_
Definition: fuse_remount.h:128
lru::Md5PathCache * md5path_cache()
Definition: mountpoint.h:452
void ReEvaluateAuthz()
Definition: mountpoint.cc:1734
void MakePipe(int pipe_fd[2])
Definition: posix.cc:525
MountPoint * mountpoint_
Not owned.
Definition: fuse_remount.h:83
void Throttle()
Definition: backoff.cc:50
LoadError Remount(const bool dry_run)
cvmfs::InodeGenerationInfo * inode_generation_info_
Not owned.
Definition: fuse_remount.h:84
FuseInvalidator * invalidator_
Definition: fuse_remount.h:85
time_t catalogs_valid_until_
Definition: fuse_remount.h:117
catalog::ClientCatalogManager * catalog_mgr()
Definition: mountpoint.h:429
void Check()
Status CheckSynchronously()
virtual inode_t GetGeneration()=0
catalog::InodeAnnotation * inode_annotation()
Definition: mountpoint.h:446
lru::PathCache * path_cache()
Definition: mountpoint.h:455
Fence * fence_
Definition: fuse_remount.h:94
unsigned GetEffectiveTtlSec()
Definition: mountpoint.cc:1621
void Drain()
Definition: fence.h:52
void Drop()
Definition: lru_md.h:69
IoErrorInfo * io_error_info()
Definition: mountpoint.h:218
Status ChangeRoot(const shash::Any &root_hash)
Definition: fuse_remount.cc:30
uint64_t platform_monotonic_time()
void SetOfflineMode(bool value)
void Pause()
Definition: lru.h:711
void Drop()
Definition: lru_md.h:102
bool IsInMaintenanceMode()
Definition: fuse_remount.h:65
pthread_t thread_remount_trigger_
Definition: fuse_remount.h:100
Definition: fence.h:25
void SetAlarm(int timeout)
void LeaveCriticalSection()
Definition: fuse_remount.h:79
FuseRemounter(MountPoint *mountpoint, cvmfs::InodeGenerationInfo *inode_generation_info, void **fuse_channel_or_session, bool fuse_notify_invalidation)
void Resume()
Definition: lru.h:717
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
atomic_int32 maintenance_mode_
Definition: fuse_remount.h:133
void Open()
Definition: fence.h:59