CernVM-FS  2.13.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
glue_buffer.h
Go to the documentation of this file.
1 
11 #include <gtest/gtest_prod.h>
12 #include <pthread.h>
13 #include <sched.h>
14 #include <stdint.h>
15 
16 #include <cassert>
17 #include <cstring>
18 #include <map>
19 #include <string>
20 #include <vector>
21 
22 #include "bigqueue.h"
23 #include "bigvector.h"
24 #include "crypto/hash.h"
25 #include "directory_entry.h"
26 #include "shortstring.h"
27 #include "smallhash.h"
28 #include "util/atomic.h"
29 #include "util/exception.h"
30 #include "util/mutex.h"
31 #include "util/platform.h"
32 #include "util/posix.h"
33 #include "util/smalloc.h"
34 #include "util/string.h"
35 
36 #ifndef CVMFS_GLUE_BUFFER_H_
37 #define CVMFS_GLUE_BUFFER_H_
38 
39 namespace glue {
40 
49 class InodeEx {
50  private:
51  // Extracts the file type bits from the POSIX mode field and shifts them to
52  // the right so that they align with EFileType constants.
53  static inline uint64_t ShiftMode(unsigned mode) { return (mode >> 12) & 017; }
54 
55  public:
56  enum EFileType {
58  kRegular = 010,
59  kSymlink = 012,
60  kDirectory = 004,
61  kFifo = 001,
62  kSocket = 014,
63  kCharDev = 002,
64  kBulkDev = 006,
65  };
66 
67  InodeEx() : inode_ex_(0) { }
68  InodeEx(uint64_t inode, EFileType type)
69  : inode_ex_(inode | (static_cast<uint64_t>(type) << 60)) { }
70  InodeEx(uint64_t inode, unsigned mode)
71  : inode_ex_(inode | (ShiftMode(mode) << 60)) { }
72 
73  inline uint64_t GetInode() const { return inode_ex_ & ~(uint64_t(15) << 60); }
74  inline EFileType GetFileType() const {
75  return static_cast<EFileType>(inode_ex_ >> 60);
76  }
77 
78  inline bool operator==(const InodeEx &other) const {
79  return GetInode() == other.GetInode();
80  }
81  inline bool operator!=(const InodeEx &other) const {
82  return GetInode() != other.GetInode();
83  }
84 
85  inline bool IsCompatibleFileType(unsigned mode) const {
86  return (static_cast<uint64_t>(GetFileType()) == ShiftMode(mode))
87  || (GetFileType() == kUnknownType);
88  }
89 
90  private:
91  uint64_t inode_ex_;
92 };
93 
94 static inline uint32_t hasher_md5(const shash::Md5 &key) {
95  // Don't start with the first bytes, because == is using them as well
96  return (uint32_t) * (reinterpret_cast<const uint32_t *>(key.digest) + 1);
97 }
98 
99 static inline uint32_t hasher_inode(const uint64_t &inode) {
100  return MurmurHash2(&inode, sizeof(inode), 0x07387a4f);
101 }
102 
103 static inline uint32_t hasher_inode_ex(const InodeEx &inode_ex) {
104  return hasher_inode(inode_ex.GetInode());
105 }
106 
107 
108 //------------------------------------------------------------------------------
109 
110 
114 class StringRef {
115  public:
116  StringRef() { length_ = NULL; }
117 
118  uint16_t length() const { return *length_; }
119  uint16_t size() const { return sizeof(uint16_t) + *length_; }
120  static uint16_t size(const uint16_t length) {
121  return sizeof(uint16_t) + length;
122  }
123  char *data() const { return reinterpret_cast<char *>(length_ + 1); }
124  static StringRef Place(const uint16_t length, const char *str, void *addr) {
125  StringRef result;
126  result.length_ = reinterpret_cast<uint16_t *>(addr);
127  *result.length_ = length;
128  if (length > 0)
129  memcpy(result.length_ + 1, str, length);
130  return result;
131  }
132 
133  private:
134  uint16_t *length_;
135 };
136 
137 
138 //------------------------------------------------------------------------------
139 
140 
146 class StringHeap : public SingleCopy {
147  public:
149  Init(128 * 1024); // 128kB (should be >= 64kB+2B which is largest string)
150  }
151 
152  explicit StringHeap(const uint64_t minimum_size) { Init(minimum_size); }
153 
154  void Init(const uint64_t minimum_size) {
155  size_ = 0;
156  used_ = 0;
157 
158  // Initial bin: 128kB or smallest power of 2 >= minimum size
159  uint64_t pow2_size = 128 * 1024;
160  while (pow2_size < minimum_size)
161  pow2_size *= 2;
162  AddBin(pow2_size);
163  }
164 
166  for (unsigned i = 0; i < bins_.size(); ++i) {
167  smunmap(bins_.At(i));
168  }
169  }
170 
171  StringRef AddString(const uint16_t length, const char *str) {
172  const uint16_t str_size = StringRef::size(length);
173  const uint64_t remaining_bin_size = bin_size_ - bin_used_;
174  // May require opening of new bin
175  if (remaining_bin_size < str_size) {
176  size_ += remaining_bin_size;
177  AddBin(2 * bin_size_);
178  }
179  StringRef result = StringRef::Place(
180  length, str,
181  static_cast<char *>(bins_.At(bins_.size() - 1)) + bin_used_);
182  size_ += str_size;
183  used_ += str_size;
184  bin_used_ += str_size;
185  return result;
186  }
187 
188  void RemoveString(const StringRef str_ref) { used_ -= str_ref.size(); }
189 
190  double GetUsage() const {
191  if (size_ == 0)
192  return 1.0;
193  return static_cast<double>(used_) / static_cast<double>(size_);
194  }
195 
196  uint64_t used() const { return used_; }
197 
198  // mmap'd bytes, used for testing
199  uint64_t GetSizeAlloc() const {
200  uint64_t s = bin_size_;
201  uint64_t result = 0;
202  for (unsigned i = 0; i < bins_.size(); ++i) {
203  result += s;
204  s /= 2;
205  }
206  return result;
207  }
208 
209  private:
210  void AddBin(const uint64_t size) {
211  void *bin = smmap(size);
212  bins_.PushBack(bin);
213  bin_size_ = size;
214  bin_used_ = 0;
215  }
216 
217  uint64_t size_;
218  uint64_t used_;
219  uint64_t bin_size_;
220  uint64_t bin_used_;
222 };
223 
224 
225 //------------------------------------------------------------------------------
226 
227 
228 class PathStore {
229  public:
233  struct Cursor {
234  Cursor() : idx(0) { }
235  uint32_t idx;
236  };
237 
238 
240  map_.Init(16, shash::Md5(shash::AsciiPtr("!")), hasher_md5);
241  string_heap_ = new StringHeap();
242  }
243 
244  ~PathStore() { delete string_heap_; }
245 
246  explicit PathStore(const PathStore &other);
247  PathStore &operator=(const PathStore &other);
248 
249  void Insert(const shash::Md5 &md5path, const PathString &path) {
250  PathInfo info;
251  bool found = map_.Lookup(md5path, &info);
252  if (found) {
253  info.refcnt++;
254  map_.Insert(md5path, info);
255  return;
256  }
257 
258  PathInfo new_entry;
259  if (path.IsEmpty()) {
260  new_entry.name = string_heap_->AddString(0, "");
261  map_.Insert(md5path, new_entry);
262  return;
263  }
264 
265  PathString parent_path = GetParentPath(path);
266  new_entry.parent = shash::Md5(parent_path.GetChars(),
267  parent_path.GetLength());
268  Insert(new_entry.parent, parent_path);
269 
270  const uint16_t name_length = path.GetLength() - parent_path.GetLength() - 1;
271  const char *name_str = path.GetChars() + parent_path.GetLength() + 1;
272  new_entry.name = string_heap_->AddString(name_length, name_str);
273  map_.Insert(md5path, new_entry);
274  }
275 
276  bool Lookup(const shash::Md5 &md5path, PathString *path) {
277  PathInfo info;
278  bool retval = map_.Lookup(md5path, &info);
279  if (!retval)
280  return false;
281 
282  if (info.parent.IsNull())
283  return true;
284 
285  retval = Lookup(info.parent, path);
286  assert(retval);
287  path->Append("/", 1);
288  path->Append(info.name.data(), info.name.length());
289  return true;
290  }
291 
292  void Erase(const shash::Md5 &md5path) {
293  PathInfo info;
294  bool found = map_.Lookup(md5path, &info);
295  if (!found)
296  return;
297 
298  info.refcnt--;
299  if (info.refcnt == 0) {
300  map_.Erase(md5path);
302  if (string_heap_->GetUsage() < 0.75) {
303  StringHeap *new_string_heap = new StringHeap(string_heap_->used());
304  shash::Md5 empty_path = map_.empty_key();
305  for (unsigned i = 0; i < map_.capacity(); ++i) {
306  if (map_.keys()[i] != empty_path) {
307  (map_.values() + i)->name = new_string_heap->AddString(
308  map_.values()[i].name.length(), map_.values()[i].name.data());
309  }
310  }
311  delete string_heap_;
312  string_heap_ = new_string_heap;
313  }
314  Erase(info.parent);
315  } else {
316  map_.Insert(md5path, info);
317  }
318  }
319 
320  void Clear() {
321  map_.Clear();
322  delete string_heap_;
323  string_heap_ = new StringHeap();
324  }
325 
326  Cursor BeginEnumerate() { return Cursor(); }
327 
328  bool Next(Cursor *cursor, shash::Md5 *parent, StringRef *name) {
329  shash::Md5 empty_key = map_.empty_key();
330  while (cursor->idx < map_.capacity()) {
331  if (map_.keys()[cursor->idx] == empty_key) {
332  cursor->idx++;
333  continue;
334  }
335  *parent = map_.values()[cursor->idx].parent;
336  *name = map_.values()[cursor->idx].name;
337  cursor->idx++;
338  return true;
339  }
340  return false;
341  }
342 
343  private:
344  struct PathInfo {
345  PathInfo() { refcnt = 1; }
347  uint32_t refcnt;
349  };
350 
351  void CopyFrom(const PathStore &other);
352 
355 };
356 
357 
358 //------------------------------------------------------------------------------
359 
360 
368 class StatStore {
369  public:
370  int32_t Add(const struct stat &info) {
371  // We don't support more that 2B open files
372  assert(store_.size() < (1LU << 31));
373  int32_t index = static_cast<int>(store_.size());
374  store_.PushBack(info);
375  return index;
376  }
377 
378  // Note that that if the last element is removed, no swap has taken place
379  uint64_t Erase(int32_t index) {
380  struct stat info_back = store_.At(store_.size() - 1);
381  store_.Replace(index, info_back);
382  store_.SetSize(store_.size() - 1);
384  return info_back.st_ino;
385  }
386 
387  struct stat Get(int32_t index) const { return store_.At(index); }
388 
389  private:
391 };
392 
393 
394 //------------------------------------------------------------------------------
395 
396 
397 class PathMap {
398  public:
400 
401  bool LookupPath(const shash::Md5 &md5path, PathString *path) {
402  bool found = path_store_.Lookup(md5path, path);
403  return found;
404  }
405 
406  uint64_t LookupInodeByPath(const PathString &path) {
407  uint64_t inode;
408  bool found = map_.Lookup(shash::Md5(path.GetChars(), path.GetLength()),
409  &inode);
410  if (found)
411  return inode;
412  return 0;
413  }
414 
415  uint64_t LookupInodeByMd5Path(const shash::Md5 &md5path) {
416  uint64_t inode;
417  bool found = map_.Lookup(md5path, &inode);
418  if (found)
419  return inode;
420  return 0;
421  }
422 
423  shash::Md5 Insert(const PathString &path, const uint64_t inode) {
424  shash::Md5 md5path(path.GetChars(), path.GetLength());
425  if (!map_.Contains(md5path)) {
426  path_store_.Insert(md5path, path);
427  map_.Insert(md5path, inode);
428  }
429  return md5path;
430  }
431 
432  void Erase(const shash::Md5 &md5path) {
433  bool found = map_.Contains(md5path);
434  if (found) {
435  path_store_.Erase(md5path);
436  map_.Erase(md5path);
437  }
438  }
439 
440  void Replace(const shash::Md5 &md5path, uint64_t new_inode) {
441  map_.Insert(md5path, new_inode);
442  }
443 
444  void Clear() {
445  map_.Clear();
446  path_store_.Clear();
447  }
448 
449  // For enumerating
451 
452  private:
455 };
456 
457 
458 //------------------------------------------------------------------------------
459 
460 
465 class InodeExMap {
466  public:
468 
469  bool LookupMd5Path(InodeEx *inode_ex, shash::Md5 *md5path) {
470  bool found = map_.LookupEx(inode_ex, md5path);
471  return found;
472  }
473 
474  void Insert(const InodeEx inode_ex, const shash::Md5 &md5path) {
475  map_.Insert(inode_ex, md5path);
476  }
477 
478  void Erase(const uint64_t inode) {
479  map_.Erase(InodeEx(inode, InodeEx::kUnknownType));
480  }
481 
482  void Clear() { map_.Clear(); }
483 
484  private:
486 };
487 
488 
489 //------------------------------------------------------------------------------
490 
491 
493  public:
497  struct Cursor {
498  Cursor() : idx(0) { }
499  uint32_t idx;
500  };
501 
503 
504  bool Get(const uint64_t inode, const uint32_t by) {
505  uint32_t refcounter = 0;
506  const bool found = map_.Lookup(inode, &refcounter);
507  const bool new_inode = !found;
508  refcounter += by; // This is 0 if the inode is not found
509  map_.Insert(inode, refcounter);
510  return new_inode;
511  }
512 
513  bool Put(const uint64_t inode, const uint32_t by) {
514  uint32_t refcounter;
515  bool found = map_.Lookup(inode, &refcounter);
516  if (!found) {
517  // May happen if a retired inode is cleared, i.e. if a file with
518  // outdated content is closed
519  return false;
520  }
521 
522  if (refcounter < by) {
524  "inode tracker refcount mismatch, inode % " PRIu64
525  ", refcounts %u / %u",
526  inode, refcounter, by);
527  }
528 
529  if (refcounter == by) {
530  map_.Erase(inode);
531  return true;
532  }
533  refcounter -= by;
534  map_.Insert(inode, refcounter);
535  return false;
536  }
537 
538  void Replace(const uint64_t old_inode, const uint64_t new_inode) {
539  map_.Erase(old_inode);
540  map_.Insert(new_inode, 0);
541  }
542 
543  void Clear() { map_.Clear(); }
544 
545  Cursor BeginEnumerate() { return Cursor(); }
546 
547  bool Next(Cursor *cursor, uint64_t *inode) {
548  uint64_t empty_key = map_.empty_key();
549  while (cursor->idx < map_.capacity()) {
550  if (map_.keys()[cursor->idx] == empty_key) {
551  cursor->idx++;
552  continue;
553  }
554  *inode = map_.keys()[cursor->idx];
555  cursor->idx++;
556  return true;
557  }
558  return false;
559  }
560 
561  private:
563 };
564 
565 
566 //------------------------------------------------------------------------------
567 
568 
573  public:
577  struct Cursor {
578  explicit Cursor(const PathStore::Cursor &p,
579  const InodeReferences::Cursor &i)
580  : csr_paths(p), csr_inos(i) { }
583  };
584 
591  class VfsPutRaii {
592  public:
593  explicit VfsPutRaii(InodeTracker *t) : tracker_(t) { tracker_->Lock(); }
595 
596  bool VfsPut(const uint64_t inode, const uint32_t by) {
597  bool removed = tracker_->inode_references_.Put(inode, by);
598  if (removed) {
599  // TODO(jblomer): pop operation (Lookup+Erase)
600  shash::Md5 md5path;
601  InodeEx inode_ex(inode, InodeEx::kUnknownType);
602  bool found = tracker_->inode_ex_map_.LookupMd5Path(&inode_ex, &md5path);
603  if (!found) {
605  "inode tracker ref map and path map out of sync: %" PRIu64,
606  inode);
607  }
608  tracker_->inode_ex_map_.Erase(inode);
609  tracker_->path_map_.Erase(md5path);
610  atomic_inc64(&tracker_->statistics_.num_removes);
611  }
612  atomic_xadd64(&tracker_->statistics_.num_references, -int32_t(by));
613  return removed;
614  }
615 
616  private:
618  };
619 
620  // Cannot be moved to the statistics manager because it has to survive
621  // reloads. Added manually in the fuse module initialization and in talk.cc.
622  struct Statistics {
624  atomic_init64(&num_inserts);
625  atomic_init64(&num_removes);
626  atomic_init64(&num_references);
627  atomic_init64(&num_hits_inode);
628  atomic_init64(&num_hits_path);
629  atomic_init64(&num_misses_path);
630  }
631  std::string Print() {
632  return "inserts: " + StringifyInt(atomic_read64(&num_inserts))
633  + " removes: " + StringifyInt(atomic_read64(&num_removes))
634  + " references: " + StringifyInt(atomic_read64(&num_references))
635  + " hits(inode): " + StringifyInt(atomic_read64(&num_hits_inode))
636  + " hits(path): " + StringifyInt(atomic_read64(&num_hits_path))
637  + " misses(path): "
638  + StringifyInt(atomic_read64(&num_misses_path));
639  }
646  };
648 
649  InodeTracker();
650  explicit InodeTracker(const InodeTracker &other);
651  InodeTracker &operator=(const InodeTracker &other);
652  ~InodeTracker();
653 
654  void VfsGetBy(const InodeEx inode_ex, const uint32_t by,
655  const PathString &path) {
656  uint64_t inode = inode_ex.GetInode();
657  Lock();
658  bool is_new_inode = inode_references_.Get(inode, by);
659  shash::Md5 md5path = path_map_.Insert(path, inode);
660  inode_ex_map_.Insert(inode_ex, md5path);
661  Unlock();
662 
663  atomic_xadd64(&statistics_.num_references, by);
664  if (is_new_inode)
665  atomic_inc64(&statistics_.num_inserts);
666  }
667 
668  void VfsGet(const InodeEx inode_ex, const PathString &path) {
669  VfsGetBy(inode_ex, 1, path);
670  }
671 
672  VfsPutRaii GetVfsPutRaii() { return VfsPutRaii(this); }
673 
674  bool FindPath(InodeEx *inode_ex, PathString *path) {
675  Lock();
676  shash::Md5 md5path;
677  bool found = inode_ex_map_.LookupMd5Path(inode_ex, &md5path);
678  if (found) {
679  found = path_map_.LookupPath(md5path, path);
680  assert(found);
681  }
682  Unlock();
683 
684  if (found) {
685  atomic_inc64(&statistics_.num_hits_path);
686  } else {
687  atomic_inc64(&statistics_.num_misses_path);
688  }
689  return found;
690  }
691 
692  uint64_t FindInode(const PathString &path) {
693  Lock();
694  uint64_t inode = path_map_.LookupInodeByPath(path);
695  Unlock();
696  atomic_inc64(&statistics_.num_hits_inode);
697  return inode;
698  }
699 
700  bool FindDentry(uint64_t ino, uint64_t *parent_ino, NameString *name) {
701  PathString path;
702  InodeEx inodex(ino, InodeEx::kUnknownType);
703  shash::Md5 md5path;
704 
705  Lock();
706  bool found = inode_ex_map_.LookupMd5Path(&inodex, &md5path);
707  if (found) {
708  found = path_map_.LookupPath(md5path, &path);
709  assert(found);
710  *name = GetFileName(path);
711  path = GetParentPath(path);
712  *parent_ino = path_map_.LookupInodeByPath(path);
713  }
714  Unlock();
715  return found;
716  }
717 
722  bool ReplaceInode(uint64_t old_inode, const InodeEx &new_inode) {
723  shash::Md5 md5path;
724  InodeEx old_inode_ex(old_inode, InodeEx::kUnknownType);
725  Lock();
726  bool found = inode_ex_map_.LookupMd5Path(&old_inode_ex, &md5path);
727  if (found) {
728  inode_references_.Replace(old_inode, new_inode.GetInode());
729  path_map_.Replace(md5path, new_inode.GetInode());
730  inode_ex_map_.Erase(old_inode);
731  inode_ex_map_.Insert(new_inode, md5path);
732  }
733  Unlock();
734  return found;
735  }
736 
738  Lock();
741  }
742 
743  bool NextEntry(Cursor *cursor, uint64_t *inode_parent, NameString *name) {
744  shash::Md5 parent_md5;
745  StringRef name_ref;
746  bool result = path_map_.path_store()->Next(&(cursor->csr_paths),
747  &parent_md5, &name_ref);
748  if (!result)
749  return false;
750  if (parent_md5.IsNull())
751  *inode_parent = 0;
752  else
753  *inode_parent = path_map_.LookupInodeByMd5Path(parent_md5);
754  name->Assign(name_ref.data(), name_ref.length());
755  return true;
756  }
757 
758  bool NextInode(Cursor *cursor, uint64_t *inode) {
759  return inode_references_.Next(&(cursor->csr_inos), inode);
760  }
761 
762  void EndEnumerate(Cursor *cursor) { Unlock(); }
763 
764  private:
765  static const unsigned kVersion = 4;
766 
767  void InitLock();
768  void CopyFrom(const InodeTracker &other);
769  inline void Lock() const {
770  int retval = pthread_mutex_lock(lock_);
771  assert(retval == 0);
772  }
773  inline void Unlock() const {
774  int retval = pthread_mutex_unlock(lock_);
775  assert(retval == 0);
776  }
777 
778  unsigned version_;
779  pthread_mutex_t *lock_;
784 }; // class InodeTracker
785 
786 
792  FRIEND_TEST(T_GlueBuffer, DentryTracker);
793 
794  private:
795  struct Entry {
796  Entry() : expiry(0), inode_parent(0) { }
797  Entry(uint64_t e, uint64_t p, const char *n)
798  : expiry(e), inode_parent(p), name(n, strlen(n)) { }
799  uint64_t expiry;
800  uint64_t inode_parent;
802  };
803 
804  public:
805  struct Cursor {
806  explicit Cursor(Entry *h) : head(h), pos(0) { }
808  size_t pos;
809  };
810 
811  // Cannot be moved to the statistics manager because it has to survive
812  // reloads. Added manually in the fuse module initialization and in talk.cc.
813  struct Statistics {
815  int64_t num_insert;
816  int64_t num_remove;
817  int64_t num_prune;
818  };
820 
821  static void *MainCleaner(void *data);
822 
823  DentryTracker();
824  DentryTracker(const DentryTracker &other);
825  DentryTracker &operator=(const DentryTracker &other);
826  ~DentryTracker();
827 
831  DentryTracker *Move();
832 
833  void Add(const uint64_t inode_parent, const char *name, uint64_t timeout_s) {
834  if (!is_active_)
835  return;
836  if (timeout_s == 0)
837  return;
838 
839  uint64_t now = platform_monotonic_time();
840  Lock();
841  entries_.PushBack(Entry(now + timeout_s, inode_parent, name));
843  DoPrune(now);
844  Unlock();
845  }
846 
847  void Prune();
852  void Disable() { is_active_ = false; }
853  bool is_active() const { return is_active_; }
854 
855  void SpawnCleaner(unsigned interval_s);
856 
857  Cursor BeginEnumerate();
858  bool NextEntry(Cursor *cursor, uint64_t *inode_parent, NameString *name);
859  void EndEnumerate(Cursor *cursor);
860 
861  private:
862  static const unsigned kVersion = 0;
863 
864  void CopyFrom(const DentryTracker &other);
865 
866  void InitLock();
867  inline void Lock() const {
868  int retval = pthread_mutex_lock(lock_);
869  assert(retval == 0);
870  }
871  inline void Unlock() const {
872  int retval = pthread_mutex_unlock(lock_);
873  assert(retval == 0);
874  }
875 
876  void DoPrune(uint64_t now) {
877  Entry *entry;
878  while (entries_.Peek(&entry)) {
879  if (entry->expiry >= now)
880  break;
881  entries_.PopFront();
883  }
885  }
886 
887  pthread_mutex_t *lock_;
888  unsigned version_;
892 
895  pthread_t thread_cleaner_;
896 }; // class DentryTracker
897 
903  private:
904  struct Entry {
905  Entry() : nopen(0), idx_stat(-1) { }
906  Entry(int32_t n, int32_t i, const shash::Any &h)
907  : nopen(n), idx_stat(i), hash(h) { }
913  int32_t nopen;
917  int32_t idx_stat;
923  };
924 
925  public:
931  static const unsigned int kBitDirectIo = 62;
932 
936  struct OpenDirectives {
949  bool direct_io;
950 
951  // Defaults to the old (pre v2.10) behavior: always flush the cache, never
952  // use direct I/O.
953  OpenDirectives() : keep_cache(false), direct_io(false) { }
954 
955  OpenDirectives(bool k, bool d) : keep_cache(k), direct_io(d) { }
956  };
957 
964  class EvictRaii {
965  public:
966  explicit EvictRaii(PageCacheTracker *t);
967  ~EvictRaii();
968  void Evict(uint64_t inode);
969 
970  private:
972  };
973 
974  // Cannot be moved to the statistics manager because it has to survive
975  // reloads. Added manually in the fuse module initialization and in talk.cc.
976  struct Statistics {
978  : n_insert(0)
979  , n_remove(0)
980  , n_open_direct(0)
981  , n_open_flush(0)
982  , n_open_cached(0) { }
983  uint64_t n_insert;
984  uint64_t n_remove;
985  uint64_t n_open_direct;
986  uint64_t n_open_flush;
987  uint64_t n_open_cached;
988  };
990 
992  explicit PageCacheTracker(const PageCacheTracker &other);
995 
996  OpenDirectives Open(uint64_t inode, const shash::Any &hash,
997  const struct stat &info);
1002  OpenDirectives OpenDirect();
1003  void Close(uint64_t inode);
1004 
1005  bool GetInfoIfOpen(uint64_t inode, shash::Any *hash, struct stat *info) {
1006  MutexLockGuard guard(lock_);
1007  Entry entry;
1008  bool retval = map_.Lookup(inode, &entry);
1009  if (retval && (entry.nopen != 0)) {
1010  assert(entry.idx_stat >= 0);
1011  *hash = entry.hash;
1012  if (info != NULL)
1013  *info = stat_store_.Get(entry.idx_stat);
1014  return true;
1015  }
1016  return false;
1017  }
1018 
1025  bool IsStale(const catalog::DirectoryEntry &dirent) {
1026  Entry entry;
1027  const MutexLockGuard guard(lock_);
1028 
1029  const bool retval = map_.Lookup(dirent.inode(), &entry);
1030  if (!retval)
1031  return false;
1032  if (entry.hash.IsNull()) {
1033  // A previous call to IsStale() returned true (see below)
1034  return true;
1035  }
1036  if (entry.nopen == 0)
1037  return false;
1038  if (entry.hash == dirent.checksum())
1039  return false;
1040 
1041  bool is_stale = true;
1042  if (dirent.IsChunkedFile()) {
1043  // Shortcut for chunked files: go by last modified timestamp
1044  is_stale = stat_store_.Get(entry.idx_stat).st_mtime != dirent.mtime();
1045  }
1046  if (is_stale) {
1047  // We mark that inode as "stale" by setting its hash to NULL.
1048  // When we check next time IsStale(), it is returned stale even
1049  // if it is not open.
1050  // The call to GetInfoIfOpen() will from now on return the null hash.
1051  // That works, the caller will still assume that the version in the
1052  // page cache tracker is different from any inode in the catalogs.
1053  entry.hash = shash::Any();
1054  map_.Insert(dirent.inode(), entry);
1055  }
1056  return is_stale;
1057  }
1058 
1059  EvictRaii GetEvictRaii() { return EvictRaii(this); }
1060 
1061  // Used in RestoreState to prevent using the page cache tracker from a
1062  // previous version after hotpatch
1063  void Disable() { is_active_ = false; }
1064 
1065  private:
1066  static const unsigned kVersion = 0;
1067 
1068  void InitLock();
1069  void CopyFrom(const PageCacheTracker &other);
1070 
1071  pthread_mutex_t *lock_;
1072  unsigned version_;
1082 };
1083 
1084 
1085 } // namespace glue
1086 
1087 #endif // CVMFS_GLUE_BUFFER_H_
bool GetInfoIfOpen(uint64_t inode, shash::Any *hash, struct stat *info)
Definition: glue_buffer.h:1005
BigVector< void * > bins_
Definition: glue_buffer.h:221
VfsPutRaii GetVfsPutRaii()
Definition: glue_buffer.h:672
uint64_t inode_parent
Definition: glue_buffer.h:800
InodeReferences inode_references_
Definition: glue_buffer.h:782
bool IsNull() const
Definition: hash.h:371
InodeReferences::Cursor csr_inos
Definition: glue_buffer.h:582
void Lock() const
Definition: glue_buffer.h:769
void Unlock() const
Definition: glue_buffer.h:773
int64_t atomic_int64
Definition: atomic.h:18
static uint32_t hasher_md5(const shash::Md5 &key)
Definition: glue_buffer.h:94
Cursor(const PathStore::Cursor &p, const InodeReferences::Cursor &i)
Definition: glue_buffer.h:578
Item At(const size_t index) const
Definition: bigvector.h:48
bool ReplaceInode(uint64_t old_inode, const InodeEx &new_inode)
Definition: glue_buffer.h:722
time_t mtime() const
struct stat Get(int32_t index) const
Definition: glue_buffer.h:387
FRIEND_TEST(T_GlueBuffer, DentryTracker)
Statistics GetStatistics()
Definition: glue_buffer.h:647
NameString GetFileName(const PathString &path)
Definition: shortstring.cc:28
pthread_mutex_t * lock_
Definition: glue_buffer.h:1071
bool IsChunkedFile() const
void DoPrune(uint64_t now)
Definition: glue_buffer.h:876
NameString name
Definition: glue_buffer.h:801
PageCacheTracker & operator=(const PageCacheTracker &other)
Definition: glue_buffer.cc:280
uint64_t GetInode() const
Definition: glue_buffer.h:73
Definition: glue_buffer.h:904
#define PANIC(...)
Definition: exception.h:29
InodeEx(uint64_t inode, unsigned mode)
Definition: glue_buffer.h:70
Definition: glue_buffer.h:795
void Assign(const char *chars, const unsigned length)
Definition: shortstring.h:61
Cursor BeginEnumerate()
Definition: glue_buffer.h:737
double GetUsage() const
Definition: glue_buffer.h:190
PathStore::Cursor csr_paths
Definition: glue_buffer.h:581
void CopyFrom(const DentryTracker &other)
Definition: glue_buffer.cc:145
bool FindDentry(uint64_t ino, uint64_t *parent_ino, NameString *name)
Definition: glue_buffer.h:700
PathStore path_store_
Definition: glue_buffer.h:454
bool Put(const uint64_t inode, const uint32_t by)
Definition: glue_buffer.h:513
static uint16_t size(const uint16_t length)
Definition: glue_buffer.h:120
pthread_t thread_cleaner_
Definition: glue_buffer.h:895
void Unlock() const
Definition: glue_buffer.h:871
void Insert(const InodeEx inode_ex, const shash::Md5 &md5path)
Definition: glue_buffer.h:474
inode_t inode() const
assert((mem||(size==0))&&"Out Of Memory")
bool LookupMd5Path(InodeEx *inode_ex, shash::Md5 *md5path)
Definition: glue_buffer.h:469
SmallHashDynamic< uint64_t, uint32_t > map_
Definition: glue_buffer.h:562
BigVector< struct stat > store_
Definition: glue_buffer.h:390
bool NextEntry(Cursor *cursor, uint64_t *inode_parent, NameString *name)
Definition: glue_buffer.cc:242
StringHeap * string_heap_
Definition: glue_buffer.h:354
void EndEnumerate(Cursor *cursor)
Definition: glue_buffer.h:762
shash::Any checksum() const
bool VfsPut(const uint64_t inode, const uint32_t by)
Definition: glue_buffer.h:596
uint64_t bin_used_
Definition: glue_buffer.h:220
char * data() const
Definition: glue_buffer.h:123
uint64_t LookupInodeByMd5Path(const shash::Md5 &md5path)
Definition: glue_buffer.h:415
unsigned char digest[digest_size_]
Definition: hash.h:121
PathStore * path_store()
Definition: glue_buffer.h:450
void Lock() const
Definition: glue_buffer.h:867
uint16_t * length_
Definition: glue_buffer.h:134
bool IsStale(const catalog::DirectoryEntry &dirent)
Definition: glue_buffer.h:1025
uint64_t LookupInodeByPath(const PathString &path)
Definition: glue_buffer.h:406
bool operator==(const InodeEx &other) const
Definition: glue_buffer.h:78
int32_t nopen
Definition: glue_buffer.h:913
static uint32_t hasher_inode_ex(const InodeEx &inode_ex)
Definition: glue_buffer.h:103
void Insert(const shash::Md5 &md5path, const PathString &path)
Definition: glue_buffer.h:249
uint64_t inode_ex_
Definition: glue_buffer.h:91
shash::Md5 Insert(const PathString &path, const uint64_t inode)
Definition: glue_buffer.h:423
static uint32_t hasher_inode(const uint64_t &inode)
Definition: glue_buffer.h:99
bool Next(Cursor *cursor, shash::Md5 *parent, StringRef *name)
Definition: glue_buffer.h:328
uint64_t GetSizeAlloc() const
Definition: glue_buffer.h:199
void EndEnumerate(Cursor *cursor)
Definition: glue_buffer.cc:256
bool is_active() const
Definition: glue_buffer.h:853
SmallHashDynamic< InodeEx, shash::Md5 > map_
Definition: glue_buffer.h:485
uint16_t size() const
Definition: glue_buffer.h:119
void CopyFrom(const InodeTracker &other)
Definition: glue_buffer.cc:68
uint64_t FindInode(const PathString &path)
Definition: glue_buffer.h:692
InodeTracker & operator=(const InodeTracker &other)
Definition: glue_buffer.cc:90
uint32_t capacity() const
Definition: smallhash.h:290
Statistics GetStatistics()
Definition: glue_buffer.h:989
uint64_t bin_size_
Definition: glue_buffer.h:219
Statistics GetStatistics()
Definition: glue_buffer.h:819
void Add(const uint64_t inode_parent, const char *name, uint64_t timeout_s)
Definition: glue_buffer.h:833
void Replace(const uint64_t old_inode, const uint64_t new_inode)
Definition: glue_buffer.h:538
bool NextEntry(Cursor *cursor, uint64_t *inode_parent, NameString *name)
Definition: glue_buffer.h:743
void SetSize(const size_t new_size)
Definition: bigvector.h:112
StringHeap(const uint64_t minimum_size)
Definition: glue_buffer.h:152
InodeEx(uint64_t inode, EFileType type)
Definition: glue_buffer.h:68
Statistics statistics_
Definition: glue_buffer.h:783
bool NextInode(Cursor *cursor, uint64_t *inode)
Definition: glue_buffer.h:758
static StringRef Place(const uint16_t length, const char *str, void *addr)
Definition: glue_buffer.h:124
void SpawnCleaner(unsigned interval_s)
Definition: glue_buffer.cc:165
uint16_t length() const
Definition: glue_buffer.h:118
int32_t idx_stat
Definition: glue_buffer.h:917
bool Lookup(const shash::Md5 &md5path, PathString *path)
Definition: glue_buffer.h:276
void Insert(const Key &key, const Value &value)
Definition: smallhash.h:106
int32_t Add(const struct stat &info)
Definition: glue_buffer.h:370
Key * keys() const
Definition: smallhash.h:148
void ShrinkIfOversized()
Definition: bigvector.h:95
DentryTracker * Move()
Definition: glue_buffer.cc:155
Entry(uint64_t e, uint64_t p, const char *n)
Definition: glue_buffer.h:797
bool Next(Cursor *cursor, uint64_t *inode)
Definition: glue_buffer.h:547
static const unsigned kVersion
Definition: glue_buffer.h:1066
void Append(const char *chars, const unsigned length)
Definition: shortstring.h:80
string StringifyInt(const int64_t value)
Definition: string.cc:77
void VfsGetBy(const InodeEx inode_ex, const uint32_t by, const PathString &path)
Definition: glue_buffer.h:654
pthread_mutex_t * lock_
Definition: glue_buffer.h:779
void Erase(const uint64_t inode)
Definition: glue_buffer.h:478
uint64_t platform_monotonic_time()
Entry(int32_t n, int32_t i, const shash::Any &h)
Definition: glue_buffer.h:906
static const unsigned kVersion
Definition: glue_buffer.h:765
InodeExMap inode_ex_map_
Definition: glue_buffer.h:781
SmallHashDynamic< shash::Md5, PathInfo > map_
Definition: glue_buffer.h:353
uint64_t used() const
Definition: glue_buffer.h:196
EFileType GetFileType() const
Definition: glue_buffer.h:74
SmallHashDynamic< shash::Md5, uint64_t > map_
Definition: glue_buffer.h:453
Entry()
Definition: glue_buffer.h:796
bool Contains(const Key &key) const
Definition: smallhash.h:99
PathStore & operator=(const PathStore &other)
Definition: glue_buffer.cc:31
OpenDirectives Open(uint64_t inode, const shash::Any &hash, const struct stat &info)
Definition: glue_buffer.cc:309
void PushBack(const Item &item)
Definition: bigvector.h:58
bool IsEmpty() const
Definition: shortstring.h:137
void Clear()
Definition: smallhash.h:131
uint64_t expiry
Definition: glue_buffer.h:799
bool IsCompatibleFileType(unsigned mode) const
Definition: glue_buffer.h:85
void Close(uint64_t inode)
Definition: glue_buffer.cc:397
OpenDirectives OpenDirect()
Definition: glue_buffer.cc:386
EvictRaii(PageCacheTracker *t)
Definition: glue_buffer.cc:449
void RemoveString(const StringRef str_ref)
Definition: glue_buffer.h:188
Definition: mutex.h:42
Entry()
Definition: glue_buffer.h:905
void Replace(size_t index, const Item &item)
Definition: bigvector.h:65
void Erase(const shash::Md5 &md5path)
Definition: glue_buffer.h:432
PathString GetParentPath(const PathString &path)
Definition: shortstring.cc:14
void CopyFrom(const PathStore &other)
Definition: glue_buffer.cc:44
uint64_t Erase(int32_t index)
Definition: glue_buffer.h:379
EvictRaii GetEvictRaii()
Definition: glue_buffer.h:1059
bool Erase(const Key &key)
Definition: smallhash.h:112
static uint64_t ShiftMode(unsigned mode)
Definition: glue_buffer.h:53
bool LookupPath(const shash::Md5 &md5path, PathString *path)
Definition: glue_buffer.h:401
DentryTracker & operator=(const DentryTracker &other)
Definition: glue_buffer.cc:134
void CopyFrom(const PageCacheTracker &other)
Definition: glue_buffer.cc:290
void Erase(const shash::Md5 &md5path)
Definition: glue_buffer.h:292
BigQueue< Entry > entries_
Definition: glue_buffer.h:891
unsigned GetLength() const
Definition: shortstring.h:131
bool Lookup(const Key &key, Value *value) const
Definition: smallhash.h:70
static void * MainCleaner(void *data)
Definition: glue_buffer.cc:176
static const unsigned kVersion
Definition: glue_buffer.h:862
void Init(uint32_t expected_size, Key empty, uint32_t(*hasher)(const Key &key))
Definition: smallhash.h:58
void Replace(const shash::Md5 &md5path, uint64_t new_inode)
Definition: glue_buffer.h:440
static const unsigned int kBitDirectIo
Definition: glue_buffer.h:931
const char * GetChars() const
Definition: shortstring.h:123
void Init(const uint64_t minimum_size)
Definition: glue_buffer.h:154
void AddBin(const uint64_t size)
Definition: glue_buffer.h:210
bool Get(const uint64_t inode, const uint32_t by)
Definition: glue_buffer.h:504
StringRef AddString(const uint16_t length, const char *str)
Definition: glue_buffer.h:171
static void size_t size
Definition: smalloc.h:54
Key empty_key() const
Definition: smallhash.h:147
pthread_mutex_t * lock_
Definition: glue_buffer.h:887
SmallHashDynamic< uint64_t, Entry > map_
Definition: glue_buffer.h:1080
void VfsGet(const InodeEx inode_ex, const PathString &path)
Definition: glue_buffer.h:668
shash::Any hash
Definition: glue_buffer.h:922
bool FindPath(InodeEx *inode_ex, PathString *path)
Definition: glue_buffer.h:674
Cursor BeginEnumerate()
Definition: glue_buffer.h:326
uint32_t MurmurHash2(const void *key, int len, uint32_t seed)
Definition: murmur.hxx:23
bool operator!=(const InodeEx &other) const
Definition: glue_buffer.h:81
size_t size() const
Definition: bigvector.h:117
Statistics statistics_
Definition: glue_buffer.h:889