CernVM-FS  2.13.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
fuse_remount.cc
Go to the documentation of this file.
1 
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 "lru_md.h"
20 #include "mountpoint.h"
21 #include "statistics.h"
22 #include "util/exception.h"
23 #include "util/logging.h"
24 #include "util/platform.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()) {
80  "repository revision blacklisted, aborting");
81  }
82 
83  LogCvmfs(kLogCvmfs, kLogDebug, "remounting root catalog");
84  const catalog::LoadReturn retval = mountpoint_->catalog_mgr()
85  ->RemountDryrun();
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) ? kStatusFailGeneral
111  : 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_ ? MountPoint::kShortTermTTL
117  : 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(
169  mountpoint, fuse_channel_or_session, fuse_notify_invalidation))
170  , invalidator_handle_(static_cast<int>(mountpoint->kcache_timeout_sec()))
171  , fence_(new Fence())
172  , offline_mode_(false)
173  , catalogs_valid_until_(MountPoint::kIndefiniteDeadline) {
176  atomic_init32(&drainout_mode_);
177  atomic_init32(&maintenance_mode_);
178  atomic_init32(&critical_section_);
179 }
180 
182  if (HasRemountTrigger()) {
183  char quit = 'Q';
184  WritePipe(pipe_remount_trigger_[1], &quit, 1);
185  pthread_join(thread_remount_trigger_, NULL);
187  }
188  delete invalidator_;
189  delete fence_;
190 }
191 
192 
198  FuseRemounter *remounter = reinterpret_cast<FuseRemounter *>(data);
199  LogCvmfs(kLogCvmfs, kLogDebug, "starting remount trigger");
200  char c;
201  int timeout_ms = -1;
202  uint64_t deadline = 0;
203  struct pollfd watch_ctrl;
204  watch_ctrl.fd = remounter->pipe_remount_trigger_[0];
205  watch_ctrl.events = POLLIN | POLLPRI;
206  while (true) {
207  watch_ctrl.revents = 0;
208  int retval = poll(&watch_ctrl, 1, timeout_ms);
209  if (retval < 0) {
210  if (errno == EINTR) {
211  if (timeout_ms >= 0) {
212  uint64_t now = platform_monotonic_time();
213  timeout_ms = (now > deadline) ? 0 : (deadline - now) * 1000;
214  }
215  continue;
216  }
218  "remount trigger connection failure (%d)", errno);
219  }
220 
221  if (retval == 0) {
222  remounter->Check();
223  timeout_ms = -1;
224  continue;
225  }
226 
227  assert(watch_ctrl.revents != 0);
228 
229  ReadPipe(remounter->pipe_remount_trigger_[0], &c, 1);
230  if (c == 'Q')
231  break;
232  assert(c == 'T');
233  ReadPipe(remounter->pipe_remount_trigger_[0], &timeout_ms, sizeof(int));
234  deadline = platform_monotonic_time() + timeout_ms / 1000;
235  }
236  LogCvmfs(kLogCvmfs, kLogDebug, "stopping remount trigger");
237  return NULL;
238 }
239 
240 
241 void FuseRemounter::SetAlarm(int timeout) {
242  // Remounting could be called for a non auto-update repository
243  if (!HasRemountTrigger())
244  return;
245 
246  timeout *= 1000; // timeout given in ms
247  const unsigned buf_size = 1 + sizeof(int);
248  char buf[buf_size];
249  buf[0] = 'T';
250  memcpy(&buf[1], &timeout, sizeof(timeout));
251  WritePipe(pipe_remount_trigger_[1], buf, buf_size);
252 }
253 
254 
256  if (value == offline_mode_)
257  return;
258  offline_mode_ = value;
259 
260  if (offline_mode_) {
262  "warning, could not apply updated catalog revision, "
263  "entering offline mode");
265  } else {
266  LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslog, "recovered from offline mode");
267  }
268 }
269 
270 
272  invalidator_->Spawn();
273  if (!mountpoint_->fixed_catalog()) {
275  int retval = pthread_create(&thread_remount_trigger_, NULL,
276  MainRemountTrigger, this);
277  assert(retval == 0);
278 
282  catalogs_valid_until_ = time(NULL) + ttl;
283  SetAlarm(ttl);
284  }
285 }
286 
287 
294 void FuseRemounter::TryFinish(const shash::Any &root_hash) {
295  FenceGuard fence_guard(&fence_maintenance_);
296  if (IsInMaintenanceMode())
297  return;
298  if (!EnterCriticalSection())
299  return;
300  if (!IsInDrainoutMode()) {
302  return;
303  }
304 
305  // No one else is in this code path and we have a valid FuseInvalidator handle
306 
307  if (!invalidator_handle_.IsDone()) {
309  return;
310  }
311  LogCvmfs(kLogCvmfs, kLogDebug, "caches drained out, applying new catalog");
312 
313  // No new inserts into caches
320 
321  // Ensure that all Fuse callbacks left the catalog query code
322  fence_->Drain();
323  catalog::LoadReturn retval;
324  if (root_hash.IsNull()) {
325  retval = mountpoint_->catalog_mgr()->Remount();
326  } else {
327  retval = mountpoint_->catalog_mgr()->ChangeRoot(root_hash);
328  }
329  if (mountpoint_->inode_annotation()) {
331  ->GetGeneration();
332  }
334  fence_->Open();
335 
339 
340  atomic_xadd32(&drainout_mode_, -2); // 2 --> 0, end of drainout mode
341 
342  if ((retval == catalog::kLoadFail) || (retval == catalog::kLoadNoSpace)) {
343  // Can temporarily "escape" offline mode if update came from updated
344  // alien cache
345  SetOfflineMode(true);
348  } else {
349  SetOfflineMode(false);
350  LogCvmfs(kLogCvmfs, kLogSyslog, "switched to catalog revision %" PRIu64,
354  }
355 
357 }
bool fixed_catalog()
Definition: mountpoint.h:517
atomic_int32 critical_section_
Definition: fuse_remount.h:141
bool IsInDrainoutMode()
Definition: fuse_remount.h:64
bool IsNull() const
Definition: hash.h:371
int pipe_remount_trigger_[2]
Definition: fuse_remount.h:107
bool IsDone() const
Definition: fuse_evict.h:57
string * mountpoint_
Definition: auto_umount.cc:26
FileSystem * file_system()
Definition: mountpoint.h:521
void TryFinish(const shash::Any &root_hash=shash::Any())
void EnterMaintenanceMode()
InodeGenerationInfo inode_generation_info_
Definition: cvmfs.cc:136
bool HasRemountTrigger()
Definition: fuse_remount.h:77
FuseInvalidator::Handle invalidator_handle_
Definition: fuse_remount.h:91
#define PANIC(...)
Definition: exception.h:29
static void * MainRemountTrigger(void *data)
Fence fence_maintenance_
Definition: fuse_remount.h:101
lru::InodeCache * inode_cache()
Definition: mountpoint.h:529
bool EnterCriticalSection()
Definition: fuse_remount.h:80
Status Check()
Definition: fuse_remount.cc:70
assert((mem||(size==0))&&"Out Of Memory")
static const unsigned kShortTermTTL
Definition: mountpoint.h:492
atomic_int32 drainout_mode_
Definition: fuse_remount.h:130
lru::Md5PathCache * md5path_cache()
Definition: mountpoint.h:531
void ReEvaluateAuthz()
Definition: mountpoint.cc:1909
void MakePipe(int pipe_fd[2])
Definition: posix.cc:487
MountPoint * mountpoint_
Not owned.
Definition: fuse_remount.h:85
void Throttle()
Definition: backoff.cc:48
cvmfs::InodeGenerationInfo * inode_generation_info_
Not owned.
Definition: fuse_remount.h:86
FuseInvalidator * invalidator_
Definition: fuse_remount.h:87
LoadReturn ChangeRoot(const shash::Any &root_hash)
time_t catalogs_valid_until_
Definition: fuse_remount.h:119
catalog::ClientCatalogManager * catalog_mgr()
Definition: mountpoint.h:507
void Check()
Status CheckSynchronously()
virtual inode_t GetGeneration()=0
catalog::InodeAnnotation * inode_annotation()
Definition: mountpoint.h:527
lru::PathCache * path_cache()
Definition: mountpoint.h:535
Fence * fence_
Definition: fuse_remount.h:96
unsigned GetEffectiveTtlSec()
Definition: mountpoint.cc:1793
void Drain()
Definition: fence.h:48
void Drop()
Definition: lru_md.h:65
IoErrorInfo * io_error_info()
Definition: mountpoint.h:233
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:717
void Drop()
Definition: lru_md.h:94
bool IsInMaintenanceMode()
Definition: fuse_remount.h:65
pthread_t thread_remount_trigger_
Definition: fuse_remount.h:102
Definition: fence.h:25
const char * Code2Ascii(const LoadReturn error)
Definition: catalog_mgr.h:167
void SetAlarm(int timeout)
void LeaveCriticalSection()
Definition: fuse_remount.h:81
FuseRemounter(MountPoint *mountpoint, cvmfs::InodeGenerationInfo *inode_generation_info, void **fuse_channel_or_session, bool fuse_notify_invalidation)
void Resume()
Definition: lru.h:723
void WritePipe(int fd, const void *buf, size_t nbyte)
Definition: posix.cc:496
void ReadPipe(int fd, void *buf, size_t nbyte)
Definition: posix.cc:508
void ClosePipe(int pipe_fd[2])
Definition: posix.cc:559
atomic_int32 maintenance_mode_
Definition: fuse_remount.h:135
void Open()
Definition: fence.h:55
CVMFS_EXPORT void LogCvmfs(const LogSource source, const int mask, const char *format,...)
Definition: logging.cc:545