GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/cache_ram.cc
Date: 2025-09-28 02:35:26
Exec Total Coverage
Lines: 191 229 83.4%
Branches: 100 208 48.1%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 */
4
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
22 const shash::Any RamCacheManager::kInvalidHandle;
23
24 string RamCacheManager::Describe() {
25 return "Internal in-memory cache manager (size "
26 + StringifyInt(max_size_ / (1024 * 1024)) + "MB)\n";
27 }
28
29
30 1007 RamCacheManager::RamCacheManager(uint64_t max_size,
31 unsigned max_entries,
32 MemoryKvStore::MemoryAllocator alloc,
33 1007 perf::StatisticsTemplate statistics)
34 1007 : max_size_(max_size)
35
1/2
✓ Branch 1 taken 1007 times.
✗ Branch 2 not taken.
1007 , 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
1/2
✓ Branch 1 taken 1007 times.
✗ Branch 2 not taken.
1007 , regular_entries_(max_entries,
39 alloc,
40 max_size,
41
2/4
✓ Branch 2 taken 1007 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1007 times.
✗ Branch 6 not taken.
2014 perf::StatisticsTemplate("kv.regular", statistics))
42
1/2
✓ Branch 1 taken 1007 times.
✗ Branch 2 not taken.
1007 , volatile_entries_(max_entries,
43 alloc,
44 max_size,
45
2/4
✓ Branch 2 taken 1007 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1007 times.
✗ Branch 6 not taken.
2014 perf::StatisticsTemplate("kv.volatile", statistics))
46
2/4
✓ Branch 3 taken 1007 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1007 times.
✗ Branch 7 not taken.
2014 , counters_(statistics) {
47 1007 const int retval = pthread_rwlock_init(&rwlock_, NULL);
48
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1007 times.
1007 assert(retval == 0);
49
1/2
✓ Branch 1 taken 1007 times.
✗ Branch 2 not taken.
1007 LogCvmfs(kLogCache, kLogDebug, "max %lu B, %u entries", max_size,
50 max_entries);
51
1/2
✓ Branch 1 taken 1007 times.
✗ Branch 2 not taken.
1007 LogCvmfs(kLogCache, kLogDebug | kLogSyslogWarn,
52 "DEPRECATION WARNING: The RAM cache manager is depcreated and "
53 "will be removed from future releases.");
54 1007 }
55
56
57 2852 RamCacheManager::~RamCacheManager() { pthread_rwlock_destroy(&rwlock_); }
58
59
60 541107 int RamCacheManager::AddFd(const ReadOnlyHandle &handle) {
61 541107 const int result = fd_table_.OpenFd(handle);
62
2/2
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 541058 times.
541107 if (result == -ENFILE) {
63 49 LogCvmfs(kLogCache, kLogDebug, "too many open files");
64 49 perf::Inc(counters_.n_enfile);
65 }
66 541107 return result;
67 }
68
69
70 27 bool RamCacheManager::AcquireQuotaManager(QuotaManager *quota_mgr) {
71
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 27 times.
27 assert(quota_mgr != NULL);
72 27 quota_mgr_ = quota_mgr;
73 27 LogCvmfs(kLogCache, kLogDebug, "set quota manager");
74 27 return true;
75 }
76
77
78 1127 int RamCacheManager::Open(const LabeledObject &object) {
79 1127 const WriteLockGuard guard(rwlock_);
80
1/2
✓ Branch 1 taken 1127 times.
✗ Branch 2 not taken.
2254 return DoOpen(object.id);
81 1127 }
82
83
84 1568 int RamCacheManager::DoOpen(const shash::Any &id) {
85 bool ok;
86 bool is_volatile;
87
1/2
✓ Branch 1 taken 1568 times.
✗ Branch 2 not taken.
1568 const MemoryBuffer buf;
88
89
3/4
✓ Branch 1 taken 1568 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 784 times.
✓ Branch 4 taken 784 times.
1568 if (regular_entries_.Contains(id)) {
90 784 is_volatile = false;
91
3/4
✓ Branch 1 taken 784 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 98 times.
✓ Branch 4 taken 686 times.
784 } else if (volatile_entries_.Contains(id)) {
92 98 is_volatile = true;
93 } else {
94
2/4
✓ Branch 1 taken 686 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 686 times.
✗ Branch 6 not taken.
686 LogCvmfs(kLogCache, kLogDebug, "miss for %s", id.ToString().c_str());
95 686 perf::Inc(counters_.n_openmiss);
96 686 return -ENOENT;
97 }
98 882 const ReadOnlyHandle generic_handle(id, is_volatile);
99
1/2
✓ Branch 1 taken 882 times.
✗ Branch 2 not taken.
882 const int fd = AddFd(generic_handle);
100
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 882 times.
882 if (fd < 0) {
101 LogCvmfs(kLogCache, kLogDebug, "error while opening %s: %s",
102 id.ToString().c_str(), strerror(-fd));
103 return fd;
104 }
105
2/2
✓ Branch 0 taken 98 times.
✓ Branch 1 taken 784 times.
882 if (is_volatile) {
106
1/2
✓ Branch 2 taken 98 times.
✗ Branch 3 not taken.
98 LogCvmfs(kLogCache, kLogDebug, "hit in volatile entries for %s",
107
1/2
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
196 id.ToString().c_str());
108 98 perf::Inc(counters_.n_openvolatile);
109 } else {
110
1/2
✓ Branch 2 taken 784 times.
✗ Branch 3 not taken.
784 LogCvmfs(kLogCache, kLogDebug, "hit in regular entries for %s",
111
1/2
✓ Branch 1 taken 784 times.
✗ Branch 2 not taken.
1568 id.ToString().c_str());
112 784 perf::Inc(counters_.n_openregular);
113 }
114
1/2
✓ Branch 2 taken 882 times.
✗ Branch 3 not taken.
882 ok = GetStore(generic_handle)->IncRef(id);
115
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 882 times.
882 assert(ok);
116 882 return fd;
117 }
118
119
120 294 int64_t RamCacheManager::GetSize(int fd) {
121 294 const ReadLockGuard guard(rwlock_);
122
1/2
✓ Branch 1 taken 294 times.
✗ Branch 2 not taken.
294 const ReadOnlyHandle generic_handle = fd_table_.GetHandle(fd);
123
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 294 times.
294 if (generic_handle.handle == kInvalidHandle) {
124 LogCvmfs(kLogCache, kLogDebug, "bad fd %d on GetSize", fd);
125 return -EBADF;
126 }
127 294 perf::Inc(counters_.n_getsize);
128
1/2
✓ Branch 2 taken 294 times.
✗ Branch 3 not taken.
294 return GetStore(generic_handle)->GetSize(generic_handle.handle);
129 294 }
130
131
132 540764 int RamCacheManager::Close(int fd) {
133 bool rc;
134
135 540764 const WriteLockGuard guard(rwlock_);
136
1/2
✓ Branch 1 taken 540764 times.
✗ Branch 2 not taken.
540764 const ReadOnlyHandle generic_handle = fd_table_.GetHandle(fd);
137
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 540764 times.
540764 if (generic_handle.handle == kInvalidHandle) {
138 LogCvmfs(kLogCache, kLogDebug, "bad fd %d on Close", fd);
139 return -EBADF;
140 }
141
1/2
✓ Branch 2 taken 540764 times.
✗ Branch 3 not taken.
540764 rc = GetStore(generic_handle)->Unref(generic_handle.handle);
142
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 540764 times.
540764 assert(rc);
143
144
1/2
✓ Branch 1 taken 540764 times.
✗ Branch 2 not taken.
540764 const int rc_int = fd_table_.CloseFd(fd);
145
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 540764 times.
540764 assert(rc_int == 0);
146
1/2
✓ Branch 1 taken 540764 times.
✗ Branch 2 not taken.
540764 LogCvmfs(kLogCache, kLogDebug, "closed fd %d", fd);
147 540764 perf::Inc(counters_.n_close);
148 540764 return 0;
149 540764 }
150
151
152 196 int64_t RamCacheManager::Pread(int fd,
153 void *buf,
154 uint64_t size,
155 uint64_t offset) {
156 196 const ReadLockGuard guard(rwlock_);
157
1/2
✓ Branch 1 taken 196 times.
✗ Branch 2 not taken.
196 const ReadOnlyHandle generic_handle = fd_table_.GetHandle(fd);
158
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 196 times.
196 if (generic_handle.handle == kInvalidHandle) {
159 LogCvmfs(kLogCache, kLogDebug, "bad fd %d on Pread", fd);
160 return -EBADF;
161 }
162 196 perf::Inc(counters_.n_pread);
163 return GetStore(generic_handle)
164
1/2
✓ Branch 2 taken 196 times.
✗ Branch 3 not taken.
196 ->Read(generic_handle.handle, buf, size, offset);
165 196 }
166
167
168 540274 int RamCacheManager::Dup(int fd) {
169 bool ok;
170 int rc;
171 540274 const WriteLockGuard guard(rwlock_);
172
1/2
✓ Branch 1 taken 540274 times.
✗ Branch 2 not taken.
540274 const ReadOnlyHandle generic_handle = fd_table_.GetHandle(fd);
173
2/2
✓ Branch 1 taken 49 times.
✓ Branch 2 taken 540225 times.
540274 if (generic_handle.handle == kInvalidHandle) {
174
1/2
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
49 LogCvmfs(kLogCache, kLogDebug, "bad fd %d on Dup", fd);
175 49 return -EBADF;
176 }
177
1/2
✓ Branch 1 taken 540225 times.
✗ Branch 2 not taken.
540225 rc = AddFd(generic_handle);
178
2/2
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 540176 times.
540225 if (rc < 0)
179 49 return rc;
180
1/2
✓ Branch 2 taken 540176 times.
✗ Branch 3 not taken.
540176 ok = GetStore(generic_handle)->IncRef(generic_handle.handle);
181
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 540176 times.
540176 assert(ok);
182
1/2
✓ Branch 1 taken 540176 times.
✗ Branch 2 not taken.
540176 LogCvmfs(kLogCache, kLogDebug, "dup fd %d", fd);
183 540176 perf::Inc(counters_.n_dup);
184 540176 return rc;
185 540274 }
186
187
188 /**
189 * For a RAM cache, read-ahead is a no-op.
190 */
191 int RamCacheManager::Readahead(int fd) {
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);
199 perf::Inc(counters_.n_readahead);
200 return 0;
201 }
202
203
204 1813 int RamCacheManager::StartTxn(const shash::Any &id, uint64_t size, void *txn) {
205
1/2
✓ Branch 2 taken 1813 times.
✗ Branch 3 not taken.
1813 LogCvmfs(kLogCache, kLogDebug, "new transaction with id %s",
206 3626 id.ToString().c_str());
207 1813 Transaction *transaction = new (txn) Transaction();
208 1813 transaction->buffer.id = id;
209 1813 transaction->pos = 0;
210 1813 transaction->expected_size = size;
211
1/2
✓ Branch 0 taken 1813 times.
✗ Branch 1 not taken.
1813 transaction->buffer.size = (size == kSizeUnknown) ? kPageSize : size;
212 1813 transaction->buffer.address = malloc(transaction->buffer.size);
213
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1813 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1813 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 }
218 1813 perf::Inc(counters_.n_starttxn);
219 1813 return 0;
220 }
221
222
223 392 void RamCacheManager::CtrlTxn(const Label &label, const int /* flags */,
224 void *txn) {
225 392 Transaction *transaction = reinterpret_cast<Transaction *>(txn);
226 392 transaction->description = label.GetDescription();
227 392 transaction->buffer.object_flags = label.flags;
228
1/2
✓ Branch 2 taken 392 times.
✗ Branch 3 not taken.
392 LogCvmfs(kLogCache, kLogDebug, "modified transaction %s",
229 784 transaction->buffer.id.ToString().c_str());
230 392 }
231
232
233 1862 int64_t RamCacheManager::Write(const void *buf, uint64_t size, void *txn) {
234 1862 Transaction *transaction = reinterpret_cast<Transaction *>(txn);
235
236
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1862 times.
1862 assert(transaction->pos <= transaction->buffer.size);
237
2/2
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 1813 times.
1862 if (transaction->pos + size > transaction->buffer.size) {
238
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 49 times.
49 if (transaction->expected_size == kSizeUnknown) {
239 perf::Inc(counters_.n_realloc);
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 {
254 49 LogCvmfs(kLogCache, kLogDebug,
255 "attempted to write more than requested (%lu>%zu)", size,
256 transaction->buffer.size);
257 49 return -EFBIG;
258 }
259 }
260
261
2/4
✓ Branch 0 taken 1813 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1813 times.
✗ Branch 3 not taken.
1813 if (transaction->buffer.address && buf) {
262 // LogCvmfs(kLogCache, kLogDebug, "copy %u bytes of transaction %s",
263 // size, transaction->id.ToString().c_str());
264 1813 memcpy(static_cast<char *>(transaction->buffer.address) + transaction->pos,
265 buf, size);
266 }
267 1813 transaction->pos += size;
268 1813 perf::Inc(counters_.n_write);
269 1813 return size;
270 }
271
272
273 98 int RamCacheManager::Reset(void *txn) {
274 98 Transaction *transaction = reinterpret_cast<Transaction *>(txn);
275 98 transaction->pos = 0;
276
1/2
✓ Branch 2 taken 98 times.
✗ Branch 3 not taken.
98 LogCvmfs(kLogCache, kLogDebug, "reset transaction %s",
277 196 transaction->buffer.id.ToString().c_str());
278 98 perf::Inc(counters_.n_reset);
279 98 return 0;
280 }
281
282
283 490 int RamCacheManager::OpenFromTxn(void *txn) {
284 490 const WriteLockGuard guard(rwlock_);
285 490 Transaction *transaction = reinterpret_cast<Transaction *>(txn);
286
1/2
✓ Branch 1 taken 490 times.
✗ Branch 2 not taken.
490 const int64_t retval = CommitToKvStore(transaction);
287
2/2
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 441 times.
490 if (retval < 0) {
288
1/2
✓ Branch 3 taken 49 times.
✗ Branch 4 not taken.
98 LogCvmfs(kLogCache, kLogDebug,
289 "error while committing transaction on %s: %s",
290
1/2
✓ Branch 1 taken 49 times.
✗ Branch 2 not taken.
98 transaction->buffer.id.ToString().c_str(), strerror(-retval));
291 49 return retval;
292 }
293
1/2
✓ Branch 2 taken 441 times.
✗ Branch 3 not taken.
441 LogCvmfs(kLogCache, kLogDebug, "open pending transaction for %s",
294
1/2
✓ Branch 1 taken 441 times.
✗ Branch 2 not taken.
882 transaction->buffer.id.ToString().c_str());
295 441 perf::Inc(counters_.n_committxn);
296
1/2
✓ Branch 1 taken 441 times.
✗ Branch 2 not taken.
441 return DoOpen(transaction->buffer.id);
297 490 }
298
299
300 98 int RamCacheManager::AbortTxn(void *txn) {
301 98 Transaction *transaction = reinterpret_cast<Transaction *>(txn);
302 98 free(transaction->buffer.address);
303
1/2
✓ Branch 2 taken 98 times.
✗ Branch 3 not taken.
98 LogCvmfs(kLogCache, kLogDebug, "abort transaction %s",
304 196 transaction->buffer.id.ToString().c_str());
305 98 perf::Inc(counters_.n_aborttxn);
306 98 return 0;
307 }
308
309
310 1421 int RamCacheManager::CommitTxn(void *txn) {
311 1421 const WriteLockGuard guard(rwlock_);
312 1421 Transaction *transaction = reinterpret_cast<Transaction *>(txn);
313 1421 perf::Inc(counters_.n_committxn);
314
1/2
✓ Branch 1 taken 1421 times.
✗ Branch 2 not taken.
1421 const int64_t rc = CommitToKvStore(transaction);
315
2/2
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 1372 times.
1421 if (rc < 0)
316 49 return rc;
317 1372 free(transaction->buffer.address);
318 1372 return rc;
319 1421 }
320
321
322 1911 int64_t RamCacheManager::CommitToKvStore(Transaction *transaction) {
323 MemoryKvStore *store;
324
325
2/2
✓ Branch 0 taken 147 times.
✓ Branch 1 taken 1764 times.
1911 if (transaction->buffer.object_flags & CacheManager::kLabelVolatile) {
326 147 store = &volatile_entries_;
327 } else {
328 1764 store = &regular_entries_;
329 }
330
2/2
✓ Branch 0 taken 1862 times.
✓ Branch 1 taken 49 times.
1911 if ((transaction->buffer.object_flags & CacheManager::kLabelPinned)
331
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1862 times.
1862 || (transaction->buffer.object_flags & CacheManager::kLabelCatalog)) {
332 49 transaction->buffer.refcount = 1;
333 } else {
334 1862 transaction->buffer.refcount = 0;
335 }
336
337 1911 const int64_t regular_size = regular_entries_.GetUsed();
338 1911 const int64_t volatile_size = volatile_entries_.GetUsed();
339 1911 int64_t overrun = regular_size + volatile_size + transaction->buffer.size
340 1911 - max_size_;
341
342
2/2
✓ Branch 0 taken 294 times.
✓ Branch 1 taken 1617 times.
1911 if (overrun > 0) {
343 // if we're going to clean the cache, try to remove at least 25%
344 294 overrun = max(overrun, (int64_t)max_size_ >> 2);
345 294 perf::Inc(counters_.n_overrun);
346
1/2
✓ Branch 2 taken 294 times.
✗ Branch 3 not taken.
294 volatile_entries_.ShrinkTo(max((int64_t)0, volatile_size - overrun));
347 }
348 1911 overrun -= volatile_size - volatile_entries_.GetUsed();
349
2/2
✓ Branch 0 taken 245 times.
✓ Branch 1 taken 1666 times.
1911 if (overrun > 0) {
350
1/2
✓ Branch 2 taken 245 times.
✗ Branch 3 not taken.
245 regular_entries_.ShrinkTo(max((int64_t)0, regular_size - overrun));
351 }
352 1911 overrun -= regular_size - regular_entries_.GetUsed();
353
2/2
✓ Branch 0 taken 98 times.
✓ Branch 1 taken 1813 times.
1911 if (overrun > 0) {
354
1/2
✓ Branch 2 taken 98 times.
✗ Branch 3 not taken.
98 LogCvmfs(kLogCache, kLogDebug,
355 "transaction for %s would overrun the cache limit by %ld",
356
1/2
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
196 transaction->buffer.id.ToString().c_str(), overrun);
357 98 perf::Inc(counters_.n_full);
358 98 return -ENOSPC;
359 }
360
361
1/2
✓ Branch 1 taken 1813 times.
✗ Branch 2 not taken.
1813 const int rc = store->Commit(transaction->buffer);
362
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1813 times.
1813 if (rc < 0) {
363 LogCvmfs(kLogCache, kLogDebug, "commit on %s failed",
364 transaction->buffer.id.ToString().c_str());
365 return rc;
366 }
367
1/2
✓ Branch 2 taken 1813 times.
✗ Branch 3 not taken.
1813 LogCvmfs(kLogCache, kLogDebug, "committed %s to cache",
368
1/2
✓ Branch 1 taken 1813 times.
✗ Branch 2 not taken.
3626 transaction->buffer.id.ToString().c_str());
369 1813 return 0;
370 }
371