CernVM-FS  2.12.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 "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())
79  {
81  "repository revision blacklisted, aborting");
82  }
83 
84  LogCvmfs(kLogCvmfs, kLogDebug, "remounting root catalog");
85  const catalog::LoadReturn retval =
86  mountpoint_->catalog_mgr()->RemountDryrun();
87  switch (retval) {
88  case catalog::kLoadNew:
89  SetOfflineMode(false);
90  if (atomic_cas32(&drainout_mode_, 0, 1)) {
91  // As of this point, fuse callbacks return zero as cache timeout
93  "new catalog revision available, "
94  "draining out meta-data caches");
95  invalidator_handle_.Reset();
96  invalidator_->InvalidateInodes(&invalidator_handle_);
97  atomic_inc32(&drainout_mode_);
98  // drainout_mode_ == 2, IsInDrainoutMode is now 'true'
99  } else {
100  LogCvmfs(kLogCvmfs, kLogDebug, "already in drainout mode, leaving");
101  }
102  return kStatusDraining;
103  case catalog::kLoadFail:
106  "reload failed (%s), applying short term TTL",
107  catalog::Code2Ascii(retval));
108  SetOfflineMode(true);
109  catalogs_valid_until_ = time(NULL) + MountPoint::kShortTermTTL;
110  SetAlarm(MountPoint::kShortTermTTL);
111  return (retval == catalog::kLoadFail) ?
112  kStatusFailGeneral : kStatusFailNoSpace;
113  case catalog::kLoadUp2Date: {
115  "catalog up to date (could be offline mode)");
116  SetOfflineMode(mountpoint_->catalog_mgr()->offline_mode());
117  unsigned ttl = offline_mode_ ?
118  MountPoint::kShortTermTTL : mountpoint_->GetEffectiveTtlSec();
119  catalogs_valid_until_ = time(NULL) + ttl;
120  SetAlarm(ttl);
121  return kStatusUp2Date;
122  }
123  default:
124  PANIC(NULL);
125  }
126 }
127 
128 
134  BackoffThrottle throttle;
135  while (true) {
136  Status status = Check();
137  switch (status) {
138  case kStatusDraining:
139  TryFinish();
140  break;
141  default:
142  return status;
143  }
144  throttle.Throttle();
145  }
146 }
147 
148 
150  fence_maintenance_.Drain();
151  atomic_cas32(&maintenance_mode_, 0, 1);
152  fence_maintenance_.Open();
153 
154  // All running Check() and TryFinish() methods returned. Both methods now
155  // return immediately as noops.
156 
157  // Flush caches before reload of fuse module
158  invalidator_handle_.Reset();
159  invalidator_->InvalidateInodes(&invalidator_handle_);
160  invalidator_handle_.WaitFor();
161 }
162 
164  cvmfs::InodeGenerationInfo *inode_generation_info,
165  void **fuse_channel_or_session,
166  bool fuse_notify_invalidation)
167  : mountpoint_(mountpoint),
168  inode_generation_info_(inode_generation_info),
169  invalidator_(new FuseInvalidator(mountpoint,
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::LoadReturn retval;
326  if (root_hash.IsNull()) {
327  retval = mountpoint_->catalog_mgr()->Remount();
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 %" PRIu64,
356  }
357 
359 }
bool fixed_catalog()
Definition: mountpoint.h:511
atomic_int32 critical_section_
Definition: fuse_remount.h:143
bool IsInDrainoutMode()
Definition: fuse_remount.h:64
bool IsNull() const
Definition: hash.h:383
int pipe_remount_trigger_[2]
Definition: fuse_remount.h:109
bool IsDone() const
Definition: fuse_evict.h:56
string * mountpoint_
Definition: auto_umount.cc:26
FileSystem * file_system()
Definition: mountpoint.h:515
void TryFinish(const shash::Any &root_hash=shash::Any())
void EnterMaintenanceMode()
InodeGenerationInfo inode_generation_info_
Definition: cvmfs.cc:133
bool HasRemountTrigger()
Definition: fuse_remount.h:77
FuseInvalidator::Handle invalidator_handle_
Definition: fuse_remount.h:93
#define PANIC(...)
Definition: exception.h:29
static void * MainRemountTrigger(void *data)
Fence fence_maintenance_
Definition: fuse_remount.h:103
lru::InodeCache * inode_cache()
Definition: mountpoint.h:525
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:486
atomic_int32 drainout_mode_
Definition: fuse_remount.h:132
lru::Md5PathCache * md5path_cache()
Definition: mountpoint.h:527
void ReEvaluateAuthz()
Definition: mountpoint.cc:1934
void MakePipe(int pipe_fd[2])
Definition: posix.cc:492
MountPoint * mountpoint_
Not owned.
Definition: fuse_remount.h:87
void Throttle()
Definition: backoff.cc:50
cvmfs::InodeGenerationInfo * inode_generation_info_
Not owned.
Definition: fuse_remount.h:88
FuseInvalidator * invalidator_
Definition: fuse_remount.h:89
LoadReturn ChangeRoot(const shash::Any &root_hash)
time_t catalogs_valid_until_
Definition: fuse_remount.h:121
catalog::ClientCatalogManager * catalog_mgr()
Definition: mountpoint.h:501
void Check()
Status CheckSynchronously()
virtual inode_t GetGeneration()=0
catalog::InodeAnnotation * inode_annotation()
Definition: mountpoint.h:521
lru::PathCache * path_cache()
Definition: mountpoint.h:531
Fence * fence_
Definition: fuse_remount.h:98
unsigned GetEffectiveTtlSec()
Definition: mountpoint.cc:1816
void Drain()
Definition: fence.h:52
void Drop()
Definition: lru_md.h:69
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:711
void Drop()
Definition: lru_md.h:102
bool IsInMaintenanceMode()
Definition: fuse_remount.h:65
pthread_t thread_remount_trigger_
Definition: fuse_remount.h:104
Definition: fence.h:25
const char * Code2Ascii(const LoadReturn error)
Definition: catalog_mgr.h:164
void SetAlarm(int timeout)
void LeaveCriticalSection()
Definition: fuse_remount.h:83
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:501
void ReadPipe(int fd, void *buf, size_t nbyte)
Definition: posix.cc:513
void ClosePipe(int pipe_fd[2])
Definition: posix.cc:551
atomic_int32 maintenance_mode_
Definition: fuse_remount.h:137
void Open()
Definition: fence.h:59
CVMFS_EXPORT void LogCvmfs(const LogSource source, const int mask, const char *format,...)
Definition: logging.cc:528