CernVM-FS  2.12.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  { }
71  InodeEx(uint64_t inode, unsigned mode)
72  : inode_ex_(inode | (ShiftMode(mode) << 60))
73  { }
74 
75  inline uint64_t GetInode() const { return inode_ex_ & ~(uint64_t(15) << 60); }
76  inline EFileType GetFileType() const {
77  return static_cast<EFileType>(inode_ex_ >> 60);
78  }
79 
80  inline bool operator==(const InodeEx &other) const {
81  return GetInode() == other.GetInode();
82  }
83  inline bool operator!=(const InodeEx &other) const {
84  return GetInode() != other.GetInode();
85  }
86 
87  inline bool IsCompatibleFileType(unsigned mode) const {
88  return (static_cast<uint64_t>(GetFileType()) == ShiftMode(mode)) ||
90  }
91 
92  private:
93  uint64_t inode_ex_;
94 };
95 
96 static inline uint32_t hasher_md5(const shash::Md5 &key) {
97  // Don't start with the first bytes, because == is using them as well
98  return (uint32_t) *(reinterpret_cast<const uint32_t *>(key.digest) + 1);
99 }
100 
101 static inline uint32_t hasher_inode(const uint64_t &inode) {
102  return MurmurHash2(&inode, sizeof(inode), 0x07387a4f);
103 }
104 
105 static inline uint32_t hasher_inode_ex(const InodeEx &inode_ex) {
106  return hasher_inode(inode_ex.GetInode());
107 }
108 
109 
110 //------------------------------------------------------------------------------
111 
112 
116 class StringRef {
117  public:
119  length_ = NULL;
120  }
121 
122  uint16_t length() const { return *length_; }
123  uint16_t size() const { return sizeof(uint16_t) + *length_; }
124  static uint16_t size(const uint16_t length) {
125  return sizeof(uint16_t) + length;
126  }
127  char *data() const { return reinterpret_cast<char *>(length_ + 1); }
128  static StringRef Place(const uint16_t length, const char *str,
129  void *addr)
130  {
131  StringRef result;
132  result.length_ = reinterpret_cast<uint16_t *>(addr);
133  *result.length_ = length;
134  if (length > 0)
135  memcpy(result.length_ + 1, str, length);
136  return result;
137  }
138  private:
139  uint16_t *length_;
140 };
141 
142 
143 //------------------------------------------------------------------------------
144 
145 
151 class StringHeap : public SingleCopy {
152  public:
154  Init(128*1024); // 128kB (should be >= 64kB+2B which is largest string)
155  }
156 
157  explicit StringHeap(const uint64_t minimum_size) {
158  Init(minimum_size);
159  }
160 
161  void Init(const uint64_t minimum_size) {
162  size_ = 0;
163  used_ = 0;
164 
165  // Initial bin: 128kB or smallest power of 2 >= minimum size
166  uint64_t pow2_size = 128 * 1024;
167  while (pow2_size < minimum_size)
168  pow2_size *= 2;
169  AddBin(pow2_size);
170  }
171 
173  for (unsigned i = 0; i < bins_.size(); ++i) {
174  smunmap(bins_.At(i));
175  }
176  }
177 
178  StringRef AddString(const uint16_t length, const char *str) {
179  const uint16_t str_size = StringRef::size(length);
180  const uint64_t remaining_bin_size = bin_size_ - bin_used_;
181  // May require opening of new bin
182  if (remaining_bin_size < str_size) {
183  size_ += remaining_bin_size;
184  AddBin(2*bin_size_);
185  }
186  StringRef result =
187  StringRef::Place(length, str,
188  static_cast<char *>(bins_.At(bins_.size()-1))+bin_used_);
189  size_ += str_size;
190  used_ += str_size;
191  bin_used_ += str_size;
192  return result;
193  }
194 
195  void RemoveString(const StringRef str_ref) {
196  used_ -= str_ref.size();
197  }
198 
199  double GetUsage() const {
200  if (size_ == 0) return 1.0;
201  return static_cast<double>(used_) / static_cast<double>(size_);
202  }
203 
204  uint64_t used() const { return used_; }
205 
206  // mmap'd bytes, used for testing
207  uint64_t GetSizeAlloc() const {
208  uint64_t s = bin_size_;
209  uint64_t result = 0;
210  for (unsigned i = 0; i < bins_.size(); ++i) {
211  result += s;
212  s /= 2;
213  }
214  return result;
215  }
216 
217  private:
218  void AddBin(const uint64_t size) {
219  void *bin = smmap(size);
220  bins_.PushBack(bin);
221  bin_size_ = size;
222  bin_used_ = 0;
223  }
224 
225  uint64_t size_;
226  uint64_t used_;
227  uint64_t bin_size_;
228  uint64_t bin_used_;
230 };
231 
232 
233 //------------------------------------------------------------------------------
234 
235 
236 class PathStore {
237  public:
241  struct Cursor {
242  Cursor() : idx(0) { }
243  uint32_t idx;
244  };
245 
246 
248  map_.Init(16, shash::Md5(shash::AsciiPtr("!")), hasher_md5);
249  string_heap_ = new StringHeap();
250  }
251 
253  delete string_heap_;
254  }
255 
256  explicit PathStore(const PathStore &other);
257  PathStore &operator= (const PathStore &other);
258 
259  void Insert(const shash::Md5 &md5path, const PathString &path) {
260  PathInfo info;
261  bool found = map_.Lookup(md5path, &info);
262  if (found) {
263  info.refcnt++;
264  map_.Insert(md5path, info);
265  return;
266  }
267 
268  PathInfo new_entry;
269  if (path.IsEmpty()) {
270  new_entry.name = string_heap_->AddString(0, "");
271  map_.Insert(md5path, new_entry);
272  return;
273  }
274 
275  PathString parent_path = GetParentPath(path);
276  new_entry.parent = shash::Md5(parent_path.GetChars(),
277  parent_path.GetLength());
278  Insert(new_entry.parent, parent_path);
279 
280  const uint16_t name_length = path.GetLength() - parent_path.GetLength() - 1;
281  const char *name_str = path.GetChars() + parent_path.GetLength() + 1;
282  new_entry.name = string_heap_->AddString(name_length, name_str);
283  map_.Insert(md5path, new_entry);
284  }
285 
286  bool Lookup(const shash::Md5 &md5path, PathString *path) {
287  PathInfo info;
288  bool retval = map_.Lookup(md5path, &info);
289  if (!retval)
290  return false;
291 
292  if (info.parent.IsNull())
293  return true;
294 
295  retval = Lookup(info.parent, path);
296  assert(retval);
297  path->Append("/", 1);
298  path->Append(info.name.data(), info.name.length());
299  return true;
300  }
301 
302  void Erase(const shash::Md5 &md5path) {
303  PathInfo info;
304  bool found = map_.Lookup(md5path, &info);
305  if (!found)
306  return;
307 
308  info.refcnt--;
309  if (info.refcnt == 0) {
310  map_.Erase(md5path);
312  if (string_heap_->GetUsage() < 0.75) {
313  StringHeap *new_string_heap = new StringHeap(string_heap_->used());
314  shash::Md5 empty_path = map_.empty_key();
315  for (unsigned i = 0; i < map_.capacity(); ++i) {
316  if (map_.keys()[i] != empty_path) {
317  (map_.values() + i)->name =
318  new_string_heap->AddString(map_.values()[i].name.length(),
319  map_.values()[i].name.data());
320  }
321  }
322  delete string_heap_;
323  string_heap_ = new_string_heap;
324  }
325  Erase(info.parent);
326  } else {
327  map_.Insert(md5path, info);
328  }
329  }
330 
331  void Clear() {
332  map_.Clear();
333  delete string_heap_;
334  string_heap_ = new StringHeap();
335  }
336 
338  return Cursor();
339  }
340 
341  bool Next(Cursor *cursor, shash::Md5 *parent, StringRef *name) {
342  shash::Md5 empty_key = map_.empty_key();
343  while (cursor->idx < map_.capacity()) {
344  if (map_.keys()[cursor->idx] == empty_key) {
345  cursor->idx++;
346  continue;
347  }
348  *parent = map_.values()[cursor->idx].parent;
349  *name = map_.values()[cursor->idx].name;
350  cursor->idx++;
351  return true;
352  }
353  return false;
354  }
355 
356  private:
357  struct PathInfo {
359  refcnt = 1;
360  }
362  uint32_t refcnt;
364  };
365 
366  void CopyFrom(const PathStore &other);
367 
370 };
371 
372 
373 //------------------------------------------------------------------------------
374 
375 
383 class StatStore {
384  public:
385  int32_t Add(const struct stat &info) {
386  // We don't support more that 2B open files
387  assert(store_.size() < (1LU << 31));
388  int32_t index = static_cast<int>(store_.size());
389  store_.PushBack(info);
390  return index;
391  }
392 
393  // Note that that if the last element is removed, no swap has taken place
394  uint64_t Erase(int32_t index) {
395  struct stat info_back = store_.At(store_.size() - 1);
396  store_.Replace(index, info_back);
397  store_.SetSize(store_.size() - 1);
399  return info_back.st_ino;
400  }
401 
402  struct stat Get(int32_t index) const { return store_.At(index); }
403 
404  private:
406 };
407 
408 
409 //------------------------------------------------------------------------------
410 
411 
412 class PathMap {
413  public:
416  }
417 
418  bool LookupPath(const shash::Md5 &md5path, PathString *path) {
419  bool found = path_store_.Lookup(md5path, path);
420  return found;
421  }
422 
423  uint64_t LookupInodeByPath(const PathString &path) {
424  uint64_t inode;
425  bool found = map_.Lookup(shash::Md5(path.GetChars(), path.GetLength()),
426  &inode);
427  if (found) return inode;
428  return 0;
429  }
430 
431  uint64_t LookupInodeByMd5Path(const shash::Md5 &md5path) {
432  uint64_t inode;
433  bool found = map_.Lookup(md5path, &inode);
434  if (found) return inode;
435  return 0;
436  }
437 
438  shash::Md5 Insert(const PathString &path, const uint64_t inode) {
439  shash::Md5 md5path(path.GetChars(), path.GetLength());
440  if (!map_.Contains(md5path)) {
441  path_store_.Insert(md5path, path);
442  map_.Insert(md5path, inode);
443  }
444  return md5path;
445  }
446 
447  void Erase(const shash::Md5 &md5path) {
448  bool found = map_.Contains(md5path);
449  if (found) {
450  path_store_.Erase(md5path);
451  map_.Erase(md5path);
452  }
453  }
454 
455  void Replace(const shash::Md5 &md5path, uint64_t new_inode) {
456  map_.Insert(md5path, new_inode);
457  }
458 
459  void Clear() {
460  map_.Clear();
461  path_store_.Clear();
462  }
463 
464  // For enumerating
466 
467  private:
470 };
471 
472 
473 //------------------------------------------------------------------------------
474 
475 
480 class InodeExMap {
481  public:
483  map_.Init(16, InodeEx(), hasher_inode_ex);
484  }
485 
486  bool LookupMd5Path(InodeEx *inode_ex, shash::Md5 *md5path) {
487  bool found = map_.LookupEx(inode_ex, md5path);
488  return found;
489  }
490 
491  void Insert(const InodeEx inode_ex, const shash::Md5 &md5path) {
492  map_.Insert(inode_ex, md5path);
493  }
494 
495  void Erase(const uint64_t inode) {
496  map_.Erase(InodeEx(inode, InodeEx::kUnknownType));
497  }
498 
499  void Clear() { map_.Clear(); }
500 
501  private:
503 };
504 
505 
506 //------------------------------------------------------------------------------
507 
508 
510  public:
514  struct Cursor {
515  Cursor() : idx(0) { }
516  uint32_t idx;
517  };
518 
520  map_.Init(16, 0, hasher_inode);
521  }
522 
523  bool Get(const uint64_t inode, const uint32_t by) {
524  uint32_t refcounter = 0;
525  const bool found = map_.Lookup(inode, &refcounter);
526  const bool new_inode = !found;
527  refcounter += by; // This is 0 if the inode is not found
528  map_.Insert(inode, refcounter);
529  return new_inode;
530  }
531 
532  bool Put(const uint64_t inode, const uint32_t by) {
533  uint32_t refcounter;
534  bool found = map_.Lookup(inode, &refcounter);
535  if (!found) {
536  // May happen if a retired inode is cleared, i.e. if a file with
537  // outdated content is closed
538  return false;
539  }
540 
541  if (refcounter < by) {
543  "inode tracker refcount mismatch, inode % " PRIu64
544  ", refcounts %u / %u", inode, refcounter, by);
545  }
546 
547  if (refcounter == by) {
548  map_.Erase(inode);
549  return true;
550  }
551  refcounter -= by;
552  map_.Insert(inode, refcounter);
553  return false;
554  }
555 
556  void Replace(const uint64_t old_inode, const uint64_t new_inode) {
557  map_.Erase(old_inode);
558  map_.Insert(new_inode, 0);
559  }
560 
561  void Clear() {
562  map_.Clear();
563  }
564 
566  return Cursor();
567  }
568 
569  bool Next(Cursor *cursor, uint64_t *inode) {
570  uint64_t empty_key = map_.empty_key();
571  while (cursor->idx < map_.capacity()) {
572  if (map_.keys()[cursor->idx] == empty_key) {
573  cursor->idx++;
574  continue;
575  }
576  *inode = map_.keys()[cursor->idx];
577  cursor->idx++;
578  return true;
579  }
580  return false;
581  }
582 
583  private:
585 };
586 
587 
588 //------------------------------------------------------------------------------
589 
590 
595  public:
599  struct Cursor {
600  explicit Cursor(
601  const PathStore::Cursor &p,
602  const InodeReferences::Cursor &i)
603  : csr_paths(p)
604  , csr_inos(i)
605  { }
608  };
609 
616  class VfsPutRaii {
617  public:
618  explicit VfsPutRaii(InodeTracker *t) : tracker_(t) {
619  tracker_->Lock();
620  }
622 
623  bool VfsPut(const uint64_t inode, const uint32_t by) {
624  bool removed = tracker_->inode_references_.Put(inode, by);
625  if (removed) {
626  // TODO(jblomer): pop operation (Lookup+Erase)
627  shash::Md5 md5path;
628  InodeEx inode_ex(inode, InodeEx::kUnknownType);
629  bool found = tracker_->inode_ex_map_.LookupMd5Path(&inode_ex, &md5path);
630  if (!found) {
632  "inode tracker ref map and path map out of sync: %" PRIu64,
633  inode);
634  }
635  tracker_->inode_ex_map_.Erase(inode);
636  tracker_->path_map_.Erase(md5path);
637  atomic_inc64(&tracker_->statistics_.num_removes);
638  }
639  atomic_xadd64(&tracker_->statistics_.num_references, -int32_t(by));
640  return removed;
641  }
642 
643  private:
645  };
646 
647  // Cannot be moved to the statistics manager because it has to survive
648  // reloads. Added manually in the fuse module initialization and in talk.cc.
649  struct Statistics {
651  atomic_init64(&num_inserts);
652  atomic_init64(&num_removes);
653  atomic_init64(&num_references);
654  atomic_init64(&num_hits_inode);
655  atomic_init64(&num_hits_path);
656  atomic_init64(&num_misses_path);
657  }
658  std::string Print() {
659  return
660  "inserts: " + StringifyInt(atomic_read64(&num_inserts)) +
661  " removes: " + StringifyInt(atomic_read64(&num_removes)) +
662  " references: " + StringifyInt(atomic_read64(&num_references)) +
663  " hits(inode): " + StringifyInt(atomic_read64(&num_hits_inode)) +
664  " hits(path): " + StringifyInt(atomic_read64(&num_hits_path)) +
665  " misses(path): " + StringifyInt(atomic_read64(&num_misses_path));
666  }
673  };
675 
676  InodeTracker();
677  explicit InodeTracker(const InodeTracker &other);
678  InodeTracker &operator= (const InodeTracker &other);
679  ~InodeTracker();
680 
681  void VfsGetBy(const InodeEx inode_ex, const uint32_t by,
682  const PathString &path)
683  {
684  uint64_t inode = inode_ex.GetInode();
685  Lock();
686  bool is_new_inode = inode_references_.Get(inode, by);
687  shash::Md5 md5path = path_map_.Insert(path, inode);
688  inode_ex_map_.Insert(inode_ex, md5path);
689  Unlock();
690 
691  atomic_xadd64(&statistics_.num_references, by);
692  if (is_new_inode) atomic_inc64(&statistics_.num_inserts);
693  }
694 
695  void VfsGet(const InodeEx inode_ex, const PathString &path) {
696  VfsGetBy(inode_ex, 1, path);
697  }
698 
699  VfsPutRaii GetVfsPutRaii() { return VfsPutRaii(this); }
700 
701  bool FindPath(InodeEx *inode_ex, PathString *path) {
702  Lock();
703  shash::Md5 md5path;
704  bool found = inode_ex_map_.LookupMd5Path(inode_ex, &md5path);
705  if (found) {
706  found = path_map_.LookupPath(md5path, path);
707  assert(found);
708  }
709  Unlock();
710 
711  if (found) {
712  atomic_inc64(&statistics_.num_hits_path);
713  } else {
714  atomic_inc64(&statistics_.num_misses_path);
715  }
716  return found;
717  }
718 
719  uint64_t FindInode(const PathString &path) {
720  Lock();
721  uint64_t inode = path_map_.LookupInodeByPath(path);
722  Unlock();
723  atomic_inc64(&statistics_.num_hits_inode);
724  return inode;
725  }
726 
727  bool FindDentry(uint64_t ino, uint64_t *parent_ino, NameString *name) {
728  PathString path;
729  InodeEx inodex(ino, InodeEx::kUnknownType);
730  shash::Md5 md5path;
731 
732  Lock();
733  bool found = inode_ex_map_.LookupMd5Path(&inodex, &md5path);
734  if (found) {
735  found = path_map_.LookupPath(md5path, &path);
736  assert(found);
737  *name = GetFileName(path);
738  path = GetParentPath(path);
739  *parent_ino = path_map_.LookupInodeByPath(path);
740  }
741  Unlock();
742  return found;
743  }
744 
749  bool ReplaceInode(uint64_t old_inode, const InodeEx &new_inode) {
750  shash::Md5 md5path;
751  InodeEx old_inode_ex(old_inode, InodeEx::kUnknownType);
752  Lock();
753  bool found = inode_ex_map_.LookupMd5Path(&old_inode_ex, &md5path);
754  if (found) {
755  inode_references_.Replace(old_inode, new_inode.GetInode());
756  path_map_.Replace(md5path, new_inode.GetInode());
757  inode_ex_map_.Erase(old_inode);
758  inode_ex_map_.Insert(new_inode, md5path);
759  }
760  Unlock();
761  return found;
762  }
763 
765  Lock();
768  }
769 
770  bool NextEntry(Cursor *cursor, uint64_t *inode_parent, NameString *name) {
771  shash::Md5 parent_md5;
772  StringRef name_ref;
773  bool result = path_map_.path_store()->Next(
774  &(cursor->csr_paths), &parent_md5, &name_ref);
775  if (!result)
776  return false;
777  if (parent_md5.IsNull())
778  *inode_parent = 0;
779  else
780  *inode_parent = path_map_.LookupInodeByMd5Path(parent_md5);
781  name->Assign(name_ref.data(), name_ref.length());
782  return true;
783  }
784 
785  bool NextInode(Cursor *cursor, uint64_t *inode) {
786  return inode_references_.Next(&(cursor->csr_inos), inode);
787  }
788 
789  void EndEnumerate(Cursor *cursor) {
790  Unlock();
791  }
792 
793  private:
794  static const unsigned kVersion = 4;
795 
796  void InitLock();
797  void CopyFrom(const InodeTracker &other);
798  inline void Lock() const {
799  int retval = pthread_mutex_lock(lock_);
800  assert(retval == 0);
801  }
802  inline void Unlock() const {
803  int retval = pthread_mutex_unlock(lock_);
804  assert(retval == 0);
805  }
806 
807  unsigned version_;
808  pthread_mutex_t *lock_;
813 }; // class InodeTracker
814 
815 
821  FRIEND_TEST(T_GlueBuffer, DentryTracker);
822 
823  private:
824  struct Entry {
825  Entry() : expiry(0), inode_parent(0) {}
826  Entry(uint64_t e, uint64_t p, const char *n)
827  : expiry(e)
828  , inode_parent(p)
829  , name(n, strlen(n))
830  {}
831  uint64_t expiry;
832  uint64_t inode_parent;
834  };
835 
836  public:
837  struct Cursor {
838  explicit Cursor(Entry *h) : head(h), pos(0) {}
840  size_t pos;
841  };
842 
843  // Cannot be moved to the statistics manager because it has to survive
844  // reloads. Added manually in the fuse module initialization and in talk.cc.
845  struct Statistics {
847  int64_t num_insert;
848  int64_t num_remove;
849  int64_t num_prune;
850  };
852 
853  static void *MainCleaner(void *data);
854 
855  DentryTracker();
856  DentryTracker(const DentryTracker &other);
857  DentryTracker &operator= (const DentryTracker &other);
858  ~DentryTracker();
859 
863  DentryTracker *Move();
864 
865  void Add(const uint64_t inode_parent, const char *name, uint64_t timeout_s) {
866  if (!is_active_) return;
867  if (timeout_s == 0) return;
868 
869  uint64_t now = platform_monotonic_time();
870  Lock();
871  entries_.PushBack(Entry(now + timeout_s, inode_parent, name));
873  DoPrune(now);
874  Unlock();
875  }
876 
877  void Prune();
882  void Disable() { is_active_ = false; }
883  bool is_active() const { return is_active_; }
884 
885  void SpawnCleaner(unsigned interval_s);
886 
887  Cursor BeginEnumerate();
888  bool NextEntry(Cursor *cursor, uint64_t *inode_parent, NameString *name);
889  void EndEnumerate(Cursor *cursor);
890 
891  private:
892  static const unsigned kVersion = 0;
893 
894  void CopyFrom(const DentryTracker &other);
895 
896  void InitLock();
897  inline void Lock() const {
898  int retval = pthread_mutex_lock(lock_);
899  assert(retval == 0);
900  }
901  inline void Unlock() const {
902  int retval = pthread_mutex_unlock(lock_);
903  assert(retval == 0);
904  }
905 
906  void DoPrune(uint64_t now) {
907  Entry *entry;
908  while (entries_.Peek(&entry)) {
909  if (entry->expiry >= now)
910  break;
911  entries_.PopFront();
913  }
915  }
916 
917  pthread_mutex_t *lock_;
918  unsigned version_;
922 
925  pthread_t thread_cleaner_;
926 }; // class DentryTracker
927 
933  private:
934  struct Entry {
935  Entry() : nopen(0), idx_stat(-1) {}
936  Entry(int32_t n, int32_t i, const shash::Any &h)
937  : nopen(n), idx_stat(i), hash(h) {}
943  int32_t nopen;
947  int32_t idx_stat;
953  };
954 
955  public:
961  static const unsigned int kBitDirectIo = 62;
962 
966  struct OpenDirectives {
979  bool direct_io;
980 
981  // Defaults to the old (pre v2.10) behavior: always flush the cache, never
982  // use direct I/O.
983  OpenDirectives() : keep_cache(false), direct_io(false) {}
984 
985  OpenDirectives(bool k, bool d) : keep_cache(k), direct_io(d) {}
986  };
987 
994  class EvictRaii {
995  public:
996  explicit EvictRaii(PageCacheTracker *t);
997  ~EvictRaii();
998  void Evict(uint64_t inode);
999 
1000  private:
1002  };
1003 
1004  // Cannot be moved to the statistics manager because it has to survive
1005  // reloads. Added manually in the fuse module initialization and in talk.cc.
1006  struct Statistics {
1008  : n_insert(0)
1009  , n_remove(0)
1010  , n_open_direct(0)
1011  , n_open_flush(0)
1012  , n_open_cached(0)
1013  {}
1014  uint64_t n_insert;
1015  uint64_t n_remove;
1016  uint64_t n_open_direct;
1017  uint64_t n_open_flush;
1018  uint64_t n_open_cached;
1019  };
1021 
1022  PageCacheTracker();
1023  explicit PageCacheTracker(const PageCacheTracker &other);
1026 
1027  OpenDirectives Open(uint64_t inode, const shash::Any &hash,
1028  const struct stat &info);
1033  OpenDirectives OpenDirect();
1034  void Close(uint64_t inode);
1035 
1036  bool GetInfoIfOpen(uint64_t inode, shash::Any *hash, struct stat *info)
1037  {
1038  MutexLockGuard guard(lock_);
1039  Entry entry;
1040  bool retval = map_.Lookup(inode, &entry);
1041  if (retval && (entry.nopen != 0)) {
1042  assert(entry.idx_stat >= 0);
1043  *hash = entry.hash;
1044  if (info != NULL)
1045  *info = stat_store_.Get(entry.idx_stat);
1046  return true;
1047  }
1048  return false;
1049  }
1050 
1057  bool IsStale(const catalog::DirectoryEntry &dirent) {
1058  Entry entry;
1059  const MutexLockGuard guard(lock_);
1060 
1061  const bool retval = map_.Lookup(dirent.inode(), &entry);
1062  if (!retval)
1063  return false;
1064  if (entry.hash.IsNull()) {
1065  // A previous call to IsStale() returned true (see below)
1066  return true;
1067  }
1068  if (entry.nopen == 0)
1069  return false;
1070  if (entry.hash == dirent.checksum())
1071  return false;
1072 
1073  bool is_stale = true;
1074  if (dirent.IsChunkedFile()) {
1075  // Shortcut for chunked files: go by last modified timestamp
1076  is_stale =
1077  stat_store_.Get(entry.idx_stat).st_mtime != dirent.mtime();
1078  }
1079  if (is_stale) {
1080  // We mark that inode as "stale" by setting its hash to NULL.
1081  // When we check next time IsStale(), it is returned stale even
1082  // if it is not open.
1083  // The call to GetInfoIfOpen() will from now on return the null hash.
1084  // That works, the caller will still assume that the version in the
1085  // page cache tracker is different from any inode in the catalogs.
1086  entry.hash = shash::Any();
1087  map_.Insert(dirent.inode(), entry);
1088  }
1089  return is_stale;
1090  }
1091 
1092  EvictRaii GetEvictRaii() { return EvictRaii(this); }
1093 
1094  // Used in RestoreState to prevent using the page cache tracker from a
1095  // previous version after hotpatch
1096  void Disable() { is_active_ = false; }
1097 
1098  private:
1099  static const unsigned kVersion = 0;
1100 
1101  void InitLock();
1102  void CopyFrom(const PageCacheTracker &other);
1103 
1104  pthread_mutex_t *lock_;
1105  unsigned version_;
1115 };
1116 
1117 
1118 } // namespace glue
1119 
1120 #endif // CVMFS_GLUE_BUFFER_H_
bool GetInfoIfOpen(uint64_t inode, shash::Any *hash, struct stat *info)
Definition: glue_buffer.h:1036
BigVector< void * > bins_
Definition: glue_buffer.h:229
VfsPutRaii GetVfsPutRaii()
Definition: glue_buffer.h:699
uint64_t inode_parent
Definition: glue_buffer.h:832
InodeReferences inode_references_
Definition: glue_buffer.h:811
bool IsNull() const
Definition: hash.h:383
InodeReferences::Cursor csr_inos
Definition: glue_buffer.h:607
void Lock() const
Definition: glue_buffer.h:798
void Unlock() const
Definition: glue_buffer.h:802
int64_t atomic_int64
Definition: atomic.h:18
static uint32_t hasher_md5(const shash::Md5 &key)
Definition: glue_buffer.h:96
Cursor(const PathStore::Cursor &p, const InodeReferences::Cursor &i)
Definition: glue_buffer.h:600
Item At(const size_t index) const
Definition: bigvector.h:50
bool ReplaceInode(uint64_t old_inode, const InodeEx &new_inode)
Definition: glue_buffer.h:749
time_t mtime() const
struct stat Get(int32_t index) const
Definition: glue_buffer.h:402
FRIEND_TEST(T_GlueBuffer, DentryTracker)
Statistics GetStatistics()
Definition: glue_buffer.h:674
NameString GetFileName(const PathString &path)
Definition: shortstring.cc:29
pthread_mutex_t * lock_
Definition: glue_buffer.h:1104
bool IsChunkedFile() const
void DoPrune(uint64_t now)
Definition: glue_buffer.h:906
NameString name
Definition: glue_buffer.h:833
PageCacheTracker & operator=(const PageCacheTracker &other)
Definition: glue_buffer.cc:287
uint64_t GetInode() const
Definition: glue_buffer.h:75
Definition: glue_buffer.h:934
#define PANIC(...)
Definition: exception.h:29
InodeEx(uint64_t inode, unsigned mode)
Definition: glue_buffer.h:71
Definition: glue_buffer.h:824
void Assign(const char *chars, const unsigned length)
Definition: shortstring.h:61
Cursor BeginEnumerate()
Definition: glue_buffer.h:764
double GetUsage() const
Definition: glue_buffer.h:199
PathStore::Cursor csr_paths
Definition: glue_buffer.h:606
void CopyFrom(const DentryTracker &other)
Definition: glue_buffer.cc:150
bool FindDentry(uint64_t ino, uint64_t *parent_ino, NameString *name)
Definition: glue_buffer.h:727
PathStore path_store_
Definition: glue_buffer.h:469
bool Put(const uint64_t inode, const uint32_t by)
Definition: glue_buffer.h:532
static uint16_t size(const uint16_t length)
Definition: glue_buffer.h:124
pthread_t thread_cleaner_
Definition: glue_buffer.h:925
void Unlock() const
Definition: glue_buffer.h:901
void Insert(const InodeEx inode_ex, const shash::Md5 &md5path)
Definition: glue_buffer.h:491
inode_t inode() const
assert((mem||(size==0))&&"Out Of Memory")
bool LookupMd5Path(InodeEx *inode_ex, shash::Md5 *md5path)
Definition: glue_buffer.h:486
SmallHashDynamic< uint64_t, uint32_t > map_
Definition: glue_buffer.h:584
BigVector< struct stat > store_
Definition: glue_buffer.h:405
bool NextEntry(Cursor *cursor, uint64_t *inode_parent, NameString *name)
Definition: glue_buffer.cc:246
StringHeap * string_heap_
Definition: glue_buffer.h:369
void EndEnumerate(Cursor *cursor)
Definition: glue_buffer.h:789
shash::Any checksum() const
bool VfsPut(const uint64_t inode, const uint32_t by)
Definition: glue_buffer.h:623
uint64_t bin_used_
Definition: glue_buffer.h:228
char * data() const
Definition: glue_buffer.h:127
uint64_t LookupInodeByMd5Path(const shash::Md5 &md5path)
Definition: glue_buffer.h:431
unsigned char digest[digest_size_]
Definition: hash.h:124
PathStore * path_store()
Definition: glue_buffer.h:465
void Lock() const
Definition: glue_buffer.h:897
uint16_t * length_
Definition: glue_buffer.h:139
bool IsStale(const catalog::DirectoryEntry &dirent)
Definition: glue_buffer.h:1057
uint64_t LookupInodeByPath(const PathString &path)
Definition: glue_buffer.h:423
bool operator==(const InodeEx &other) const
Definition: glue_buffer.h:80
int32_t nopen
Definition: glue_buffer.h:943
static uint32_t hasher_inode_ex(const InodeEx &inode_ex)
Definition: glue_buffer.h:105
void Insert(const shash::Md5 &md5path, const PathString &path)
Definition: glue_buffer.h:259
uint64_t inode_ex_
Definition: glue_buffer.h:93
shash::Md5 Insert(const PathString &path, const uint64_t inode)
Definition: glue_buffer.h:438
static uint32_t hasher_inode(const uint64_t &inode)
Definition: glue_buffer.h:101
bool Next(Cursor *cursor, shash::Md5 *parent, StringRef *name)
Definition: glue_buffer.h:341
uint64_t GetSizeAlloc() const
Definition: glue_buffer.h:207
void EndEnumerate(Cursor *cursor)
Definition: glue_buffer.cc:261
bool is_active() const
Definition: glue_buffer.h:883
SmallHashDynamic< InodeEx, shash::Md5 > map_
Definition: glue_buffer.h:502
uint16_t size() const
Definition: glue_buffer.h:123
void CopyFrom(const InodeTracker &other)
Definition: glue_buffer.cc:73
uint64_t FindInode(const PathString &path)
Definition: glue_buffer.h:719
InodeTracker & operator=(const InodeTracker &other)
Definition: glue_buffer.cc:95
uint32_t capacity() const
Definition: smallhash.h:301
Statistics GetStatistics()
Definition: glue_buffer.h:1020
uint64_t bin_size_
Definition: glue_buffer.h:227
Statistics GetStatistics()
Definition: glue_buffer.h:851
void Add(const uint64_t inode_parent, const char *name, uint64_t timeout_s)
Definition: glue_buffer.h:865
void Replace(const uint64_t old_inode, const uint64_t new_inode)
Definition: glue_buffer.h:556
bool NextEntry(Cursor *cursor, uint64_t *inode_parent, NameString *name)
Definition: glue_buffer.h:770
void SetSize(const size_t new_size)
Definition: bigvector.h:116
StringHeap(const uint64_t minimum_size)
Definition: glue_buffer.h:157
InodeEx(uint64_t inode, EFileType type)
Definition: glue_buffer.h:68
Statistics statistics_
Definition: glue_buffer.h:812
bool NextInode(Cursor *cursor, uint64_t *inode)
Definition: glue_buffer.h:785
static StringRef Place(const uint16_t length, const char *str, void *addr)
Definition: glue_buffer.h:128
void SpawnCleaner(unsigned interval_s)
Definition: glue_buffer.cc:170
uint16_t length() const
Definition: glue_buffer.h:122
int32_t idx_stat
Definition: glue_buffer.h:947
bool Lookup(const shash::Md5 &md5path, PathString *path)
Definition: glue_buffer.h:286
void Insert(const Key &key, const Value &value)
Definition: smallhash.h:109
int32_t Add(const struct stat &info)
Definition: glue_buffer.h:385
Key * keys() const
Definition: smallhash.h:154
void ShrinkIfOversized()
Definition: bigvector.h:99
DentryTracker * Move()
Definition: glue_buffer.cc:160
Entry(uint64_t e, uint64_t p, const char *n)
Definition: glue_buffer.h:826
bool Next(Cursor *cursor, uint64_t *inode)
Definition: glue_buffer.h:569
static const unsigned kVersion
Definition: glue_buffer.h:1099
void Append(const char *chars, const unsigned length)
Definition: shortstring.h:80
string StringifyInt(const int64_t value)
Definition: string.cc:78
void VfsGetBy(const InodeEx inode_ex, const uint32_t by, const PathString &path)
Definition: glue_buffer.h:681
pthread_mutex_t * lock_
Definition: glue_buffer.h:808
void Erase(const uint64_t inode)
Definition: glue_buffer.h:495
uint64_t platform_monotonic_time()
Entry(int32_t n, int32_t i, const shash::Any &h)
Definition: glue_buffer.h:936
static const unsigned kVersion
Definition: glue_buffer.h:794
InodeExMap inode_ex_map_
Definition: glue_buffer.h:810
SmallHashDynamic< shash::Md5, PathInfo > map_
Definition: glue_buffer.h:368
uint64_t used() const
Definition: glue_buffer.h:204
EFileType GetFileType() const
Definition: glue_buffer.h:76
SmallHashDynamic< shash::Md5, uint64_t > map_
Definition: glue_buffer.h:468
Entry()
Definition: glue_buffer.h:825
bool Contains(const Key &key) const
Definition: smallhash.h:102
PathStore & operator=(const PathStore &other)
Definition: glue_buffer.cc:32
OpenDirectives Open(uint64_t inode, const shash::Any &hash, const struct stat &info)
Definition: glue_buffer.cc:317
void PushBack(const Item &item)
Definition: bigvector.h:60
bool IsEmpty() const
Definition: shortstring.h:137
void Clear()
Definition: smallhash.h:134
uint64_t expiry
Definition: glue_buffer.h:831
bool IsCompatibleFileType(unsigned mode) const
Definition: glue_buffer.h:87
void Close(uint64_t inode)
Definition: glue_buffer.cc:406
OpenDirectives OpenDirect()
Definition: glue_buffer.cc:395
EvictRaii(PageCacheTracker *t)
Definition: glue_buffer.cc:457
void RemoveString(const StringRef str_ref)
Definition: glue_buffer.h:195
Definition: mutex.h:42
Entry()
Definition: glue_buffer.h:935
void Replace(size_t index, const Item &item)
Definition: bigvector.h:67
void Erase(const shash::Md5 &md5path)
Definition: glue_buffer.h:447
PathString GetParentPath(const PathString &path)
Definition: shortstring.cc:15
void CopyFrom(const PathStore &other)
Definition: glue_buffer.cc:47
uint64_t Erase(int32_t index)
Definition: glue_buffer.h:394
EvictRaii GetEvictRaii()
Definition: glue_buffer.h:1092
bool Erase(const Key &key)
Definition: smallhash.h:115
static uint64_t ShiftMode(unsigned mode)
Definition: glue_buffer.h:53
bool LookupPath(const shash::Md5 &md5path, PathString *path)
Definition: glue_buffer.h:418
DentryTracker & operator=(const DentryTracker &other)
Definition: glue_buffer.cc:139
void CopyFrom(const PageCacheTracker &other)
Definition: glue_buffer.cc:297
void Erase(const shash::Md5 &md5path)
Definition: glue_buffer.h:302
BigQueue< Entry > entries_
Definition: glue_buffer.h:921
unsigned GetLength() const
Definition: shortstring.h:131
bool Lookup(const Key &key, Value *value) const
Definition: smallhash.h:73
static void * MainCleaner(void *data)
Definition: glue_buffer.cc:180
static const unsigned kVersion
Definition: glue_buffer.h:892
void Init(uint32_t expected_size, Key empty, uint32_t(*hasher)(const Key &key))
Definition: smallhash.h:60
void Replace(const shash::Md5 &md5path, uint64_t new_inode)
Definition: glue_buffer.h:455
static const unsigned int kBitDirectIo
Definition: glue_buffer.h:961
const char * GetChars() const
Definition: shortstring.h:123
void Init(const uint64_t minimum_size)
Definition: glue_buffer.h:161
void AddBin(const uint64_t size)
Definition: glue_buffer.h:218
bool Get(const uint64_t inode, const uint32_t by)
Definition: glue_buffer.h:523
StringRef AddString(const uint16_t length, const char *str)
Definition: glue_buffer.h:178
static void size_t size
Definition: smalloc.h:54
Key empty_key() const
Definition: smallhash.h:153
pthread_mutex_t * lock_
Definition: glue_buffer.h:917
SmallHashDynamic< uint64_t, Entry > map_
Definition: glue_buffer.h:1113
void VfsGet(const InodeEx inode_ex, const PathString &path)
Definition: glue_buffer.h:695
shash::Any hash
Definition: glue_buffer.h:952
bool FindPath(InodeEx *inode_ex, PathString *path)
Definition: glue_buffer.h:701
Cursor BeginEnumerate()
Definition: glue_buffer.h:337
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:83
size_t size() const
Definition: bigvector.h:121
Statistics statistics_
Definition: glue_buffer.h:919