CernVM-FS  2.13.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
cache_ram.cc
Go to the documentation of this file.
1 
5 #include "cache_ram.h"
6 
7 #include <errno.h>
8 
9 #include <algorithm>
10 #include <cassert>
11 #include <cstring>
12 #include <new>
13 
14 #include "kvstore.h"
15 #include "util/concurrency.h"
16 #include "util/logging.h"
17 #include "util/posix.h"
18 #include "util/string.h"
19 
20 using namespace std; // NOLINT
21 
23 
25  return "Internal in-memory cache manager (size "
26  + StringifyInt(max_size_ / (1024 * 1024)) + "MB)\n";
27 }
28 
29 
31  unsigned max_entries,
33  perf::StatisticsTemplate statistics)
34  : max_size_(max_size)
35  , fd_table_(max_entries, ReadOnlyHandle())
36  // TODO(jblomer): the number of slots in the kv-stores should _not_ be the
37  // number of open files.
38  , regular_entries_(max_entries,
39  alloc,
40  max_size,
41  perf::StatisticsTemplate("kv.regular", statistics))
42  , volatile_entries_(max_entries,
43  alloc,
44  max_size,
45  perf::StatisticsTemplate("kv.volatile", statistics))
46  , counters_(statistics) {
47  const int retval = pthread_rwlock_init(&rwlock_, NULL);
48  assert(retval == 0);
49  LogCvmfs(kLogCache, kLogDebug, "max %lu B, %u entries", max_size,
50  max_entries);
52  "DEPRECATION WARNING: The RAM cache manager is depcreated and "
53  "will be removed from future releases.");
54 }
55 
56 
57 RamCacheManager::~RamCacheManager() { pthread_rwlock_destroy(&rwlock_); }
58 
59 
61  const int result = fd_table_.OpenFd(handle);
62  if (result == -ENFILE) {
63  LogCvmfs(kLogCache, kLogDebug, "too many open files");
65  }
66  return result;
67 }
68 
69 
71  assert(quota_mgr != NULL);
73  LogCvmfs(kLogCache, kLogDebug, "set quota manager");
74  return true;
75 }
76 
77 
79  const WriteLockGuard guard(rwlock_);
80  return DoOpen(object.id);
81 }
82 
83 
85  bool ok;
86  bool is_volatile;
87  const MemoryBuffer buf;
88 
89  if (regular_entries_.Contains(id)) {
90  is_volatile = false;
91  } else if (volatile_entries_.Contains(id)) {
92  is_volatile = true;
93  } else {
94  LogCvmfs(kLogCache, kLogDebug, "miss for %s", id.ToString().c_str());
96  return -ENOENT;
97  }
98  const ReadOnlyHandle generic_handle(id, is_volatile);
99  const int fd = AddFd(generic_handle);
100  if (fd < 0) {
101  LogCvmfs(kLogCache, kLogDebug, "error while opening %s: %s",
102  id.ToString().c_str(), strerror(-fd));
103  return fd;
104  }
105  if (is_volatile) {
106  LogCvmfs(kLogCache, kLogDebug, "hit in volatile entries for %s",
107  id.ToString().c_str());
109  } else {
110  LogCvmfs(kLogCache, kLogDebug, "hit in regular entries for %s",
111  id.ToString().c_str());
113  }
114  ok = GetStore(generic_handle)->IncRef(id);
115  assert(ok);
116  return fd;
117 }
118 
119 
120 int64_t RamCacheManager::GetSize(int fd) {
121  const ReadLockGuard guard(rwlock_);
122  const ReadOnlyHandle generic_handle = fd_table_.GetHandle(fd);
123  if (generic_handle.handle == kInvalidHandle) {
124  LogCvmfs(kLogCache, kLogDebug, "bad fd %d on GetSize", fd);
125  return -EBADF;
126  }
128  return GetStore(generic_handle)->GetSize(generic_handle.handle);
129 }
130 
131 
133  bool rc;
134 
135  const WriteLockGuard guard(rwlock_);
136  const ReadOnlyHandle generic_handle = fd_table_.GetHandle(fd);
137  if (generic_handle.handle == kInvalidHandle) {
138  LogCvmfs(kLogCache, kLogDebug, "bad fd %d on Close", fd);
139  return -EBADF;
140  }
141  rc = GetStore(generic_handle)->Unref(generic_handle.handle);
142  assert(rc);
143 
144  const int rc_int = fd_table_.CloseFd(fd);
145  assert(rc_int == 0);
146  LogCvmfs(kLogCache, kLogDebug, "closed fd %d", fd);
148  return 0;
149 }
150 
151 
152 int64_t RamCacheManager::Pread(int fd,
153  void *buf,
154  uint64_t size,
155  uint64_t offset) {
156  const ReadLockGuard guard(rwlock_);
157  const ReadOnlyHandle generic_handle = fd_table_.GetHandle(fd);
158  if (generic_handle.handle == kInvalidHandle) {
159  LogCvmfs(kLogCache, kLogDebug, "bad fd %d on Pread", fd);
160  return -EBADF;
161  }
163  return GetStore(generic_handle)
164  ->Read(generic_handle.handle, buf, size, offset);
165 }
166 
167 
168 int RamCacheManager::Dup(int fd) {
169  bool ok;
170  int rc;
171  const WriteLockGuard guard(rwlock_);
172  const ReadOnlyHandle generic_handle = fd_table_.GetHandle(fd);
173  if (generic_handle.handle == kInvalidHandle) {
174  LogCvmfs(kLogCache, kLogDebug, "bad fd %d on Dup", fd);
175  return -EBADF;
176  }
177  rc = AddFd(generic_handle);
178  if (rc < 0)
179  return rc;
180  ok = GetStore(generic_handle)->IncRef(generic_handle.handle);
181  assert(ok);
182  LogCvmfs(kLogCache, kLogDebug, "dup fd %d", fd);
184  return rc;
185 }
186 
187 
192  const ReadLockGuard guard(rwlock_);
193  const ReadOnlyHandle generic_handle = fd_table_.GetHandle(fd);
194  if (generic_handle.handle == kInvalidHandle) {
195  LogCvmfs(kLogCache, kLogDebug, "bad fd %d on Readahead", fd);
196  return -EBADF;
197  }
198  LogCvmfs(kLogCache, kLogDebug, "readahead (no-op) on %d", fd);
200  return 0;
201 }
202 
203 
204 int RamCacheManager::StartTxn(const shash::Any &id, uint64_t size, void *txn) {
205  LogCvmfs(kLogCache, kLogDebug, "new transaction with id %s",
206  id.ToString().c_str());
207  Transaction *transaction = new (txn) Transaction();
208  transaction->buffer.id = id;
209  transaction->pos = 0;
210  transaction->expected_size = size;
211  transaction->buffer.size = (size == kSizeUnknown) ? kPageSize : size;
212  transaction->buffer.address = malloc(transaction->buffer.size);
213  if (!transaction->buffer.address && size > 0) {
214  LogCvmfs(kLogCache, kLogDebug, "failed to allocate %lu B for %s", size,
215  id.ToString().c_str());
216  return -errno;
217  }
219  return 0;
220 }
221 
222 
223 void RamCacheManager::CtrlTxn(const Label &label, const int /* flags */,
224  void *txn) {
225  Transaction *transaction = reinterpret_cast<Transaction *>(txn);
226  transaction->description = label.GetDescription();
227  transaction->buffer.object_flags = label.flags;
228  LogCvmfs(kLogCache, kLogDebug, "modified transaction %s",
229  transaction->buffer.id.ToString().c_str());
230 }
231 
232 
233 int64_t RamCacheManager::Write(const void *buf, uint64_t size, void *txn) {
234  Transaction *transaction = reinterpret_cast<Transaction *>(txn);
235 
236  assert(transaction->pos <= transaction->buffer.size);
237  if (transaction->pos + size > transaction->buffer.size) {
238  if (transaction->expected_size == kSizeUnknown) {
240  const size_t new_size = max(2 * transaction->buffer.size,
241  static_cast<size_t>(size + transaction->pos));
242  LogCvmfs(kLogCache, kLogDebug, "reallocate transaction for %s to %lu B",
243  transaction->buffer.id.ToString().c_str(),
244  transaction->buffer.size);
245  void *new_ptr = realloc(transaction->buffer.address, new_size);
246  if (!new_ptr) {
247  LogCvmfs(kLogCache, kLogDebug, "failed to allocate %lu B for %s",
248  new_size, transaction->buffer.id.ToString().c_str());
249  return -EIO;
250  }
251  transaction->buffer.address = new_ptr;
252  transaction->buffer.size = new_size;
253  } else {
255  "attempted to write more than requested (%lu>%zu)", size,
256  transaction->buffer.size);
257  return -EFBIG;
258  }
259  }
260 
261  if (transaction->buffer.address && buf) {
262  // LogCvmfs(kLogCache, kLogDebug, "copy %u bytes of transaction %s",
263  // size, transaction->id.ToString().c_str());
264  memcpy(static_cast<char *>(transaction->buffer.address) + transaction->pos,
265  buf, size);
266  }
267  transaction->pos += size;
269  return size;
270 }
271 
272 
273 int RamCacheManager::Reset(void *txn) {
274  Transaction *transaction = reinterpret_cast<Transaction *>(txn);
275  transaction->pos = 0;
276  LogCvmfs(kLogCache, kLogDebug, "reset transaction %s",
277  transaction->buffer.id.ToString().c_str());
279  return 0;
280 }
281 
282 
284  const WriteLockGuard guard(rwlock_);
285  Transaction *transaction = reinterpret_cast<Transaction *>(txn);
286  const int64_t retval = CommitToKvStore(transaction);
287  if (retval < 0) {
289  "error while committing transaction on %s: %s",
290  transaction->buffer.id.ToString().c_str(), strerror(-retval));
291  return retval;
292  }
293  LogCvmfs(kLogCache, kLogDebug, "open pending transaction for %s",
294  transaction->buffer.id.ToString().c_str());
296  return DoOpen(transaction->buffer.id);
297 }
298 
299 
301  Transaction *transaction = reinterpret_cast<Transaction *>(txn);
302  free(transaction->buffer.address);
303  LogCvmfs(kLogCache, kLogDebug, "abort transaction %s",
304  transaction->buffer.id.ToString().c_str());
306  return 0;
307 }
308 
309 
311  const WriteLockGuard guard(rwlock_);
312  Transaction *transaction = reinterpret_cast<Transaction *>(txn);
314  const int64_t rc = CommitToKvStore(transaction);
315  if (rc < 0)
316  return rc;
317  free(transaction->buffer.address);
318  return rc;
319 }
320 
321 
323  MemoryKvStore *store;
324 
325  if (transaction->buffer.object_flags & CacheManager::kLabelVolatile) {
326  store = &volatile_entries_;
327  } else {
328  store = &regular_entries_;
329  }
330  if ((transaction->buffer.object_flags & CacheManager::kLabelPinned)
331  || (transaction->buffer.object_flags & CacheManager::kLabelCatalog)) {
332  transaction->buffer.refcount = 1;
333  } else {
334  transaction->buffer.refcount = 0;
335  }
336 
337  const int64_t regular_size = regular_entries_.GetUsed();
338  const int64_t volatile_size = volatile_entries_.GetUsed();
339  int64_t overrun = regular_size + volatile_size + transaction->buffer.size
340  - max_size_;
341 
342  if (overrun > 0) {
343  // if we're going to clean the cache, try to remove at least 25%
344  overrun = max(overrun, (int64_t)max_size_ >> 2);
346  volatile_entries_.ShrinkTo(max((int64_t)0, volatile_size - overrun));
347  }
348  overrun -= volatile_size - volatile_entries_.GetUsed();
349  if (overrun > 0) {
350  regular_entries_.ShrinkTo(max((int64_t)0, regular_size - overrun));
351  }
352  overrun -= regular_size - regular_entries_.GetUsed();
353  if (overrun > 0) {
355  "transaction for %s would overrun the cache limit by %ld",
356  transaction->buffer.id.ToString().c_str(), overrun);
358  return -ENOSPC;
359  }
360 
361  const int rc = store->Commit(transaction->buffer);
362  if (rc < 0) {
363  LogCvmfs(kLogCache, kLogDebug, "commit on %s failed",
364  transaction->buffer.id.ToString().c_str());
365  return rc;
366  }
367  LogCvmfs(kLogCache, kLogDebug, "committed %s to cache",
368  transaction->buffer.id.ToString().c_str());
369  return 0;
370 }
perf::Counter * n_aborttxn
Definition: cache_ram.h:55
perf::Counter * n_openmiss
Definition: cache_ram.h:60
virtual CacheManagerIds id()
Definition: cache_ram.h:102
RamCacheManager(uint64_t max_size, unsigned max_entries, MemoryKvStore::MemoryAllocator alloc, perf::StatisticsTemplate statistics)
Definition: cache_ram.cc:30
perf::Counter * n_getsize
Definition: cache_ram.h:47
virtual int OpenFromTxn(void *txn)
Definition: cache_ram.cc:283
std::string GetDescription() const
Definition: cache.h:110
virtual int Open(const LabeledObject &object)
Definition: cache_ram.cc:78
perf::Counter * n_pread
Definition: cache_ram.h:49
static const shash::Any kInvalidHandle
Definition: cache_ram.h:241
static const int kLabelCatalog
Definition: cache.h:80
perf::Counter * n_write
Definition: cache_ram.h:53
bool Contains(const shash::Any &id)
Definition: kvstore.cc:85
std::string ToString(const bool with_suffix=false) const
Definition: hash.h:241
int64_t Read(const shash::Any &id, void *buf, size_t size, size_t offset)
Definition: kvstore.cc:235
perf::Counter * n_full
Definition: cache_ram.h:62
virtual int Close(int fd)
Definition: cache_ram.cc:132
perf::Counter * n_committxn
Definition: cache_ram.h:56
virtual int CommitTxn(void *txn)
Definition: cache_ram.cc:310
assert((mem||(size==0))&&"Out Of Memory")
MemoryKvStore * GetStore(const ReadOnlyHandle &fd)
Definition: cache_ram.h:265
unsigned int refcount
Definition: kvstore.h:45
virtual int AbortTxn(void *txn)
Definition: cache_ram.cc:300
perf::Counter * n_starttxn
Definition: cache_ram.h:52
virtual int Dup(int fd)
Definition: cache_ram.cc:168
virtual void CtrlTxn(const Label &label, const int flags, void *txn)
Definition: cache_ram.cc:223
virtual int StartTxn(const shash::Any &id, uint64_t size, void *txn)
Definition: cache_ram.cc:204
int Commit(const MemoryBuffer &buf)
Definition: kvstore.cc:260
void Transaction()
virtual int64_t Pread(int fd, void *buf, uint64_t size, uint64_t offset)
Definition: cache_ram.cc:152
virtual int Readahead(int fd)
Definition: cache_ram.cc:191
FdTable< ReadOnlyHandle > fd_table_
Definition: cache_ram.h:286
perf::Counter * n_readahead
Definition: cache_ram.h:51
perf::Counter * n_overrun
Definition: cache_ram.h:61
bool IncRef(const shash::Any &id)
Definition: kvstore.cc:198
perf::Counter * n_realloc
Definition: cache_ram.h:63
size_t GetUsed()
Definition: kvstore.h:193
int AddFd(const ReadOnlyHandle &handle)
Definition: cache_ram.cc:60
int64_t CommitToKvStore(Transaction *transaction)
Definition: cache_ram.cc:322
perf::Counter * n_openregular
Definition: cache_ram.h:58
bool Unref(const shash::Any &id)
Definition: kvstore.cc:217
static const int kLabelPinned
Definition: cache.h:81
perf::Counter * n_reset
Definition: cache_ram.h:54
shash::Any id
Definition: kvstore.h:47
string StringifyInt(const int64_t value)
Definition: string.cc:77
perf::Counter * n_dup
Definition: cache_ram.h:50
bool ShrinkTo(size_t size)
Definition: kvstore.cc:349
void Inc(class Counter *counter)
Definition: statistics.h:50
virtual int DoOpen(const shash::Any &id)
Definition: cache_ram.cc:84
int object_flags
Definition: kvstore.h:46
perf::Counter * n_close
Definition: cache_ram.h:48
perf::Counter * n_enfile
Definition: cache_ram.h:57
static const int kLabelVolatile
Definition: cache.h:82
perf::Counter * n_openvolatile
Definition: cache_ram.h:59
virtual std::string Describe()
Definition: cache_ram.cc:24
int64_t GetSize(const shash::Any &id)
Definition: kvstore.cc:166
Counters counters_
Definition: cache_ram.h:290
QuotaManager * quota_mgr()
Definition: cache.h:191
uint64_t max_size_
Definition: cache_ram.h:285
void * address
Definition: kvstore.h:43
size_t size
Definition: kvstore.h:44
const unsigned kPageSize
Definition: posix.h:30
pthread_rwlock_t rwlock_
Definition: cache_ram.h:287
QuotaManager * quota_mgr_
Definition: cache.h:233
Definition: mutex.h:42
virtual int64_t GetSize(int fd)
Definition: cache_ram.cc:120
virtual int Reset(void *txn)
Definition: cache_ram.cc:273
virtual ~RamCacheManager()
Definition: cache_ram.cc:57
MemoryKvStore regular_entries_
Definition: cache_ram.h:288
MemoryKvStore volatile_entries_
Definition: cache_ram.h:289
virtual int64_t Write(const void *buf, uint64_t size, void *txn)
Definition: cache_ram.cc:233
static void size_t size
Definition: smalloc.h:54
virtual bool AcquireQuotaManager(QuotaManager *quota_mgr)
Definition: cache_ram.cc:70
static const uint64_t kSizeUnknown
Definition: cache.h:74
CVMFS_EXPORT void LogCvmfs(const LogSource source, const int mask, const char *format,...)
Definition: logging.cc:545