GCC Code Coverage Report


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