GCC Code Coverage Report
Directory: cvmfs/ Exec Total Coverage
File: cvmfs/glue_buffer.h Lines: 187 289 64.7 %
Date: 2019-02-03 02:48:13 Branches: 29 78 37.2 %

Line Branch Exec Source
1
/**
2
 * This file is part of the CernVM File System.
3
 *
4
 * This module provides the inode tracker in order to remember inodes
5
 * and their parents that are in use by the kernel.
6
 *
7
 * These objects have to survive reloading of the library, so no virtual
8
 * functions.
9
 */
10
11
#include <pthread.h>
12
#include <sched.h>
13
#include <stdint.h>
14
15
#include <cassert>
16
#include <cstring>
17
#include <map>
18
#include <string>
19
#include <vector>
20
21
#include "atomic.h"
22
#include "bigvector.h"
23
#include "hash.h"
24
#include "shortstring.h"
25
#include "smallhash.h"
26
#include "smalloc.h"
27
#include "util/posix.h"
28
#include "util/string.h"
29
30
#ifndef CVMFS_GLUE_BUFFER_H_
31
#define CVMFS_GLUE_BUFFER_H_
32
33
namespace glue {
34
35
10172
static inline uint32_t hasher_md5(const shash::Md5 &key) {
36
  // Don't start with the first bytes, because == is using them as well
37
10172
  return (uint32_t) *(reinterpret_cast<const uint32_t *>(key.digest) + 1);
38
}
39
40
41
7093
static inline uint32_t hasher_inode(const uint64_t &inode) {
42
7093
  return MurmurHash2(&inode, sizeof(inode), 0x07387a4f);
43
}
44
45
46
//------------------------------------------------------------------------------
47
48
49
/**
50
 * Pointer to a 2 byte length information followed by the characters
51
 */
52
class StringRef {
53
 public:
54
10202
  StringRef() {
55
10202
    length_ = NULL;
56
10202
  }
57
58
3
  uint16_t length() const { return *length_; }
59
  uint16_t size() const { return sizeof(uint16_t) + *length_; }
60
1027
  static uint16_t size(const uint16_t length) {
61
1027
    return sizeof(uint16_t) + length;
62
  }
63
3
  char *data() const { return reinterpret_cast<char *>(length_ + 1); }
64
1027
  static StringRef Place(const uint16_t length, const char *str,
65
                         void *addr)
66
  {
67
1027
    StringRef result;
68
1027
    result.length_ = reinterpret_cast<uint16_t *>(addr);
69
1027
    *result.length_ = length;
70
1027
    if (length > 0)
71
1025
      memcpy(result.length_ + 1, str, length);
72
1027
    return result;
73
  }
74
 private:
75
  uint16_t *length_;
76
};
77
78
79
//------------------------------------------------------------------------------
80
81
82
/**
83
 * Manages memory bins with immutable strings (deleting is a no-op).
84
 * When the fraction of garbage is too large, the user of the StringHeap
85
 * can copy the entire contents to a new heap.
86
 */
87
class StringHeap : public SingleCopy {
88
 public:
89
36
  StringHeap() {
90
36
    Init(128*1024);  // 128kB (should be >= 64kB+2B which is largest string)
91
  }
92
93
  explicit StringHeap(const uint32_t minimum_size) {
94
    Init(minimum_size);
95
  }
96
97
36
  void Init(const uint32_t minimum_size) {
98
36
    size_ = 0;
99
36
    used_ = 0;
100
101
    // Initial bin: 128kB or smallest power of 2 >= minimum size
102
36
    uint32_t pow2_size = minimum_size < 128*1024 ? 128*1024 : minimum_size;
103
36
    pow2_size--;
104
36
    pow2_size |= pow2_size >> 1;
105
36
    pow2_size |= pow2_size >> 2;
106
36
    pow2_size |= pow2_size >> 4;
107
36
    pow2_size |= pow2_size >> 8;
108
36
    pow2_size |= pow2_size >> 16;
109
36
    pow2_size++;
110
36
    AddBin(pow2_size);
111
36
  }
112
113
36
  ~StringHeap() {
114
72
    for (unsigned i = 0; i < bins_.size(); ++i) {
115
36
      smunmap(bins_.At(i));
116
    }
117
  }
118
119
1027
  StringRef AddString(const uint16_t length, const char *str) {
120
1027
    const uint16_t str_size = StringRef::size(length);
121
1027
    const uint64_t remaining_bin_size = bin_size_ - bin_used_;
122
    // May require opening of new bin
123
1027
    if (remaining_bin_size < str_size) {
124
      size_ += remaining_bin_size;
125
      AddBin(2*bin_size_);
126
    }
127
    StringRef result =
128
      StringRef::Place(length, str,
129
1027
                       static_cast<char *>(bins_.At(bins_.size()-1))+bin_used_);
130
1027
    size_ += str_size;
131
1027
    used_ += str_size;
132
1027
    bin_used_ += str_size;
133
1027
    return result;
134
  }
135
136
  void RemoveString(const StringRef str_ref) {
137
    used_ -= str_ref.size();
138
  }
139
140
  double GetUsage() const {
141
    if (size_ == 0) return 1.0;
142
    return static_cast<double>(used_) / static_cast<double>(size_);
143
  }
144
145
  uint64_t used() const { return used_; }
146
147
 private:
148
36
  void AddBin(const uint64_t size) {
149
36
    void *bin = smmap(size);
150
36
    bins_.PushBack(bin);
151
36
    bin_size_ = size;
152
36
    bin_used_ = 0;
153
36
  }
154
155
  uint64_t size_;
156
  uint64_t used_;
157
  uint64_t bin_size_;
158
  uint64_t bin_used_;
159
  BigVector<void *> bins_;
160
};
161
162
163
//------------------------------------------------------------------------------
164
165
166
class PathStore {
167
 public:
168
  /**
169
   * Used to enumerate all paths
170
   */
171
  struct Cursor {
172
5
    Cursor() : idx(0) { }
173
    uint32_t idx;
174
  };
175
176
177
36
  PathStore() {
178
36
    map_.Init(16, shash::Md5(shash::AsciiPtr("!")), hasher_md5);
179
36
    string_heap_ = new StringHeap();
180
36
  }
181
182
36
  ~PathStore() {
183
36
    delete string_heap_;
184
  }
185
186
  explicit PathStore(const PathStore &other);
187
  PathStore &operator= (const PathStore &other);
188
189
2052
  void Insert(const shash::Md5 &md5path, const PathString &path) {
190
2052
    PathInfo info;
191
2052
    bool found = map_.Lookup(md5path, &info);
192
2052
    if (found) {
193
1025
      info.refcnt++;
194
1025
      map_.Insert(md5path, info);
195
1025
      return;
196
    }
197
198
1027
    PathInfo new_entry;
199
1027
    if (path.IsEmpty()) {
200
2
      new_entry.name = string_heap_->AddString(0, "");
201
2
      map_.Insert(md5path, new_entry);
202
2
      return;
203
    }
204
205
1025
    PathString parent_path = GetParentPath(path);
206
    new_entry.parent = shash::Md5(parent_path.GetChars(),
207
1025
                                  parent_path.GetLength());
208
1025
    Insert(new_entry.parent, parent_path);
209
210
1025
    const uint16_t name_length = path.GetLength() - parent_path.GetLength() - 1;
211
1025
    const char *name_str = path.GetChars() + parent_path.GetLength() + 1;
212
1025
    new_entry.name = string_heap_->AddString(name_length, name_str);
213
1025
    map_.Insert(md5path, new_entry);
214
  }
215
216
  bool Lookup(const shash::Md5 &md5path, PathString *path) {
217
    PathInfo info;
218
    bool retval = map_.Lookup(md5path, &info);
219
    if (!retval)
220
      return false;
221
222
    if (info.parent.IsNull())
223
      return true;
224
225
    retval = Lookup(info.parent, path);
226
    assert(retval);
227
    path->Append("/", 1);
228
    path->Append(info.name.data(), info.name.length());
229
    return true;
230
  }
231
232
  void Erase(const shash::Md5 &md5path) {
233
    PathInfo info;
234
    bool found = map_.Lookup(md5path, &info);
235
    if (!found)
236
      return;
237
238
    info.refcnt--;
239
    if (info.refcnt == 0) {
240
      map_.Erase(md5path);
241
      string_heap_->RemoveString(info.name);
242
      if (string_heap_->GetUsage() < 0.75) {
243
        StringHeap *new_string_heap = new StringHeap(string_heap_->used());
244
        shash::Md5 empty_path = map_.empty_key();
245
        for (unsigned i = 0; i < map_.capacity(); ++i) {
246
          if (map_.keys()[i] != empty_path) {
247
            (map_.values() + i)->name =
248
              new_string_heap->AddString(map_.values()[i].name.length(),
249
                                         map_.values()[i].name.data());
250
          }
251
        }
252
        delete string_heap_;
253
        string_heap_ = new_string_heap;
254
      }
255
      Erase(info.parent);
256
    } else {
257
      map_.Insert(md5path, info);
258
    }
259
  }
260
261
  void Clear() {
262
    map_.Clear();
263
    delete string_heap_;
264
    string_heap_ = new StringHeap();
265
  }
266
267
5
  Cursor BeginEnumerate() {
268
5
    return Cursor();
269
  }
270
271
6
  bool Next(Cursor *cursor, shash::Md5 *parent, StringRef *name) {
272
6
    shash::Md5 empty_key = map_.empty_key();
273
6
    while (cursor->idx < map_.capacity()) {
274
42
      if (map_.keys()[cursor->idx] == empty_key) {
275
39
        cursor->idx++;
276
39
        continue;
277
      }
278
3
      *parent = map_.values()[cursor->idx].parent;
279
3
      *name = map_.values()[cursor->idx].name;
280
3
      cursor->idx++;
281
3
      return true;
282
    }
283
3
    return false;
284
  }
285
286
 private:
287
6090
  struct PathInfo {
288
9169
    PathInfo() {
289
9169
      refcnt = 1;
290
9169
    }
291
    shash::Md5 parent;
292
    uint32_t refcnt;
293
    StringRef name;
294
  };
295
296
  void CopyFrom(const PathStore &other);
297
298
  SmallHashDynamic<shash::Md5, PathInfo> map_;
299
  StringHeap *string_heap_;
300
};
301
302
303
//------------------------------------------------------------------------------
304
305
306
36
class PathMap {
307
 public:
308
36
  PathMap() {
309
36
    map_.Init(16, shash::Md5(shash::AsciiPtr("!")), hasher_md5);
310
36
  }
311
312
  bool LookupPath(const shash::Md5 &md5path, PathString *path) {
313
    bool found = path_store_.Lookup(md5path, path);
314
    return found;
315
  }
316
317
  uint64_t LookupInodeByPath(const PathString &path) {
318
    uint64_t inode;
319
    bool found = map_.Lookup(shash::Md5(path.GetChars(), path.GetLength()),
320
                             &inode);
321
    if (found) return inode;
322
    return 0;
323
  }
324
325
2
  uint64_t LookupInodeByMd5Path(const shash::Md5 &md5path) {
326
    uint64_t inode;
327
2
    bool found = map_.Lookup(md5path, &inode);
328
2
    if (found) return inode;
329
    return 0;
330
  }
331
332
1027
  shash::Md5 Insert(const PathString &path, const uint64_t inode) {
333
1027
    shash::Md5 md5path(path.GetChars(), path.GetLength());
334
1027
    if (!map_.Contains(md5path)) {
335
1027
      path_store_.Insert(md5path, path);
336
1027
      map_.Insert(md5path, inode);
337
    }
338
1027
    return md5path;
339
  }
340
341
  void Erase(const shash::Md5 &md5path) {
342
    bool found = map_.Contains(md5path);
343
    if (found) {
344
      path_store_.Erase(md5path);
345
      map_.Erase(md5path);
346
    }
347
  }
348
349
  void Clear() {
350
    map_.Clear();
351
    path_store_.Clear();
352
  }
353
354
  // For enumerating
355
11
  PathStore *path_store() { return &path_store_; }
356
357
 private:
358
  SmallHashDynamic<shash::Md5, uint64_t> map_;
359
  PathStore path_store_;
360
};
361
362
363
//------------------------------------------------------------------------------
364
365
366
36
class InodeMap {
367
 public:
368
36
  InodeMap() {
369
36
    map_.Init(16, 0, hasher_inode);
370
  }
371
372
  bool LookupMd5Path(const uint64_t inode, shash::Md5 *md5path) {
373
    bool found = map_.Lookup(inode, md5path);
374
    return found;
375
  }
376
377
1027
  void Insert(const uint64_t inode, const shash::Md5 &md5path) {
378
1027
    map_.Insert(inode, md5path);
379
1027
  }
380
381
  void Erase(const uint64_t inode) {
382
    map_.Erase(inode);
383
  }
384
385
  void Clear() { map_.Clear(); }
386
387
 private:
388
  SmallHashDynamic<uint64_t, shash::Md5> map_;
389
};
390
391
392
//------------------------------------------------------------------------------
393
394
395
36
class InodeReferences {
396
 public:
397
  /**
398
   * Used to enumerate all inodes
399
   */
400
  struct Cursor {
401
5
    Cursor() : idx(0) { }
402
    uint32_t idx;
403
  };
404
405
36
  InodeReferences() {
406
36
    map_.Init(16, 0, hasher_inode);
407
  }
408
409
1027
  bool Get(const uint64_t inode, const uint32_t by) {
410
1027
    uint32_t refcounter = 0;
411
1027
    const bool found = map_.Lookup(inode, &refcounter);
412
1027
    const bool new_inode = !found;
413
1027
    refcounter += by;  // This is 0 if the inode is not found
414
1027
    map_.Insert(inode, refcounter);
415
1027
    return new_inode;
416
  }
417
418
  bool Put(const uint64_t inode, const uint32_t by) {
419
    uint32_t refcounter;
420
    bool found = map_.Lookup(inode, &refcounter);
421
    assert(found);
422
    assert(refcounter >= by);
423
    if (refcounter == by) {
424
      map_.Erase(inode);
425
      return true;
426
    }
427
    refcounter -= by;
428
    map_.Insert(inode, refcounter);
429
    return false;
430
  }
431
432
  void Clear() {
433
    map_.Clear();
434
  }
435
436
5
  Cursor BeginEnumerate() {
437
5
    return Cursor();
438
  }
439
440
3080
  bool Next(Cursor *cursor, uint64_t *inode) {
441
3080
    uint64_t empty_key = map_.empty_key();
442
11191
    while (cursor->idx < map_.capacity()) {
443
8106
      if (map_.keys()[cursor->idx] == empty_key) {
444
5031
        cursor->idx++;
445
5031
        continue;
446
      }
447
3075
      *inode = map_.keys()[cursor->idx];
448
3075
      cursor->idx++;
449
3075
      return true;
450
    }
451
5
    return false;
452
  }
453
454
 private:
455
  SmallHashDynamic<uint64_t, uint32_t> map_;
456
};
457
458
459
//------------------------------------------------------------------------------
460
461
462
/**
463
 * Tracks inode reference counters as given by Fuse.
464
 */
465
class InodeTracker {
466
 public:
467
  /**
468
   * Used to actively evict all known paths from kernel caches
469
   */
470
  struct Cursor {
471
5
    explicit Cursor(
472
      const PathStore::Cursor &p,
473
      const InodeReferences::Cursor &i)
474
      : csr_paths(p)
475
5
      , csr_inos(i)
476
5
    { }
477
    PathStore::Cursor csr_paths;
478
    InodeReferences::Cursor csr_inos;
479
  };
480
481
  // Cannot be moved to the statistics manager because it has to survive
482
  // reloads.  Added manually in the fuse module initialization and in talk.cc.
483
  struct Statistics {
484
36
    Statistics() {
485
36
      atomic_init64(&num_inserts);
486
36
      atomic_init64(&num_removes);
487
36
      atomic_init64(&num_references);
488
36
      atomic_init64(&num_hits_inode);
489
36
      atomic_init64(&num_hits_path);
490
36
      atomic_init64(&num_misses_path);
491
36
    }
492
    std::string Print() {
493
      return
494
      "inserts: " + StringifyInt(atomic_read64(&num_inserts)) +
495
      "  removes: " + StringifyInt(atomic_read64(&num_removes)) +
496
      "  references: " + StringifyInt(atomic_read64(&num_references)) +
497
      "  hits(inode): " + StringifyInt(atomic_read64(&num_hits_inode)) +
498
      "  hits(path): " + StringifyInt(atomic_read64(&num_hits_path)) +
499
      "  misses(path): " + StringifyInt(atomic_read64(&num_misses_path));
500
    }
501
    atomic_int64 num_inserts;
502
    atomic_int64 num_removes;
503
    atomic_int64 num_references;
504
    atomic_int64 num_hits_inode;
505
    atomic_int64 num_hits_path;
506
    atomic_int64 num_misses_path;
507
  };
508
  Statistics GetStatistics() { return statistics_; }
509
510
  InodeTracker();
511
  explicit InodeTracker(const InodeTracker &other);
512
  InodeTracker &operator= (const InodeTracker &other);
513
  ~InodeTracker();
514
515
1027
  void VfsGetBy(const uint64_t inode, const uint32_t by, const PathString &path)
516
  {
517
1027
    Lock();
518
1027
    bool new_inode = inode_references_.Get(inode, by);
519
1027
    shash::Md5 md5path = path_map_.Insert(path, inode);
520
1027
    inode_map_.Insert(inode, md5path);
521
1027
    Unlock();
522
523
1027
    atomic_xadd64(&statistics_.num_references, by);
524
1027
    if (new_inode) atomic_inc64(&statistics_.num_inserts);
525
1027
  }
526
527
1027
  void VfsGet(const uint64_t inode, const PathString &path) {
528
1027
    VfsGetBy(inode, 1, path);
529
1027
  }
530
531
  void VfsPut(const uint64_t inode, const uint32_t by) {
532
    Lock();
533
    bool removed = inode_references_.Put(inode, by);
534
    if (removed) {
535
      // TODO(jblomer): pop operation (Lookup+Erase)
536
      shash::Md5 md5path;
537
      bool found = inode_map_.LookupMd5Path(inode, &md5path);
538
      assert(found);
539
      inode_map_.Erase(inode);
540
      path_map_.Erase(md5path);
541
      atomic_inc64(&statistics_.num_removes);
542
    }
543
    Unlock();
544
    atomic_xadd64(&statistics_.num_references, -int32_t(by));
545
  }
546
547
  bool FindPath(const uint64_t inode, PathString *path) {
548
    Lock();
549
    shash::Md5 md5path;
550
    bool found = inode_map_.LookupMd5Path(inode, &md5path);
551
    if (found) {
552
      found = path_map_.LookupPath(md5path, path);
553
      assert(found);
554
    }
555
    Unlock();
556
557
    if (found) {
558
      atomic_inc64(&statistics_.num_hits_path);
559
    } else {
560
      atomic_inc64(&statistics_.num_misses_path);
561
    }
562
    return found;
563
  }
564
565
  uint64_t FindInode(const PathString &path) {
566
    Lock();
567
    uint64_t inode = path_map_.LookupInodeByPath(path);
568
    Unlock();
569
    atomic_inc64(&statistics_.num_hits_inode);
570
    return inode;
571
  }
572
573
5
  Cursor BeginEnumerate() {
574
5
    Lock();
575
    return Cursor(path_map_.path_store()->BeginEnumerate(),
576
5
                  inode_references_.BeginEnumerate());
577
  }
578
579
6
  bool NextEntry(Cursor *cursor, uint64_t *inode_parent, NameString *name) {
580
6
    shash::Md5 parent_md5;
581
6
    StringRef name_ref;
582
    bool result = path_map_.path_store()->Next(
583
6
      &(cursor->csr_paths), &parent_md5, &name_ref);
584
6
    if (!result)
585
3
      return false;
586
3
    if (parent_md5.IsNull())
587
1
      *inode_parent = 0;
588
    else
589
2
      *inode_parent = path_map_.LookupInodeByMd5Path(parent_md5);
590
3
    name->Assign(name_ref.data(), name_ref.length());
591
3
    return true;
592
  }
593
594
3080
  bool NextInode(Cursor *cursor, uint64_t *inode) {
595
3080
    return inode_references_.Next(&(cursor->csr_inos), inode);
596
  }
597
598
5
  void EndEnumerate(Cursor *cursor) {
599
5
    Unlock();
600
5
  }
601
602
 private:
603
  static const unsigned kVersion = 4;
604
605
  void InitLock();
606
  void CopyFrom(const InodeTracker &other);
607
1032
  inline void Lock() const {
608
1032
    int retval = pthread_mutex_lock(lock_);
609
1032
    assert(retval == 0);
610
1032
  }
611
1032
  inline void Unlock() const {
612
1032
    int retval = pthread_mutex_unlock(lock_);
613
1032
    assert(retval == 0);
614
1032
  }
615
616
  unsigned version_;
617
  pthread_mutex_t *lock_;
618
  PathMap path_map_;
619
  InodeMap inode_map_;
620
  InodeReferences inode_references_;
621
  Statistics statistics_;
622
};  // class InodeTracker
623
624
625
}  // namespace glue
626
627
#endif  // CVMFS_GLUE_BUFFER_H_