GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/cache_ram.h
Date: 2025-07-06 02:35:01
Exec Total Coverage
Lines: 32 33 97.0%
Branches: 53 104 51.0%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 */
4 #ifndef CVMFS_CACHE_RAM_H_
5 #define CVMFS_CACHE_RAM_H_
6
7 #include <pthread.h>
8
9 #include <cassert>
10 #include <cstdlib>
11 #include <string>
12 #include <vector>
13
14 #include "cache.h"
15 #include "crypto/hash.h"
16 #include "fd_table.h"
17 #include "kvstore.h"
18 #include "statistics.h"
19 #include "util/pointer.h"
20
21
22 /**
23 * The @p RamCacheManager class provides a cache backend that operates
24 * entirely from the host's RAM. This backend does not require any
25 * additional privileges on the host such as filesystem access.
26 * This cache resides in a single process' memory, and does not
27 * support sharing or persistence. This cache backend is a good
28 * choice in highly restrictive environments, e.g. HPC resources,
29 * where it is not possible to make use of a shared/local filesystem.
30 *
31 * To use this cache backend, set @p CVMFS_CACHE_PRIMARY=ram. There
32 * are not many knobs to configure; at present only the size to be
33 * used for cached objects. @p CVMFS_CACHE_RAM_SIZE sets the amount
34 * of memory to use (in MB) for caching objects. If the size ends
35 * with a percent sign, the cache size will be set to that
36 * percentage of the system memory. If no size is specified, the
37 * RAM cache size defaults to ~3% of the system memory.
38 * The minimum cache size is 200 MB.
39 *
40 * RamCacheManager uses a custom heap allocator rather than
41 * the system's libc @p malloc(). To switch to libc malloc, set
42 * @p CVMFS_CACHE_RAM_MALLOC=libc
43 */
44 class RamCacheManager : public CacheManager {
45 public:
46 struct Counters {
47 perf::Counter *n_getsize;
48 perf::Counter *n_close;
49 perf::Counter *n_pread;
50 perf::Counter *n_dup;
51 perf::Counter *n_readahead;
52 perf::Counter *n_starttxn;
53 perf::Counter *n_write;
54 perf::Counter *n_reset;
55 perf::Counter *n_aborttxn;
56 perf::Counter *n_committxn;
57 perf::Counter *n_enfile;
58 perf::Counter *n_openregular;
59 perf::Counter *n_openvolatile;
60 perf::Counter *n_openmiss;
61 perf::Counter *n_overrun;
62 perf::Counter *n_full;
63 perf::Counter *n_realloc;
64
65 610 explicit Counters(perf::StatisticsTemplate statistics) {
66
3/6
✓ Branch 2 taken 610 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 610 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 610 times.
✗ Branch 10 not taken.
610 n_getsize = statistics.RegisterTemplated("n_getsize",
67 "Number of GetSize calls");
68
3/6
✓ Branch 2 taken 610 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 610 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 610 times.
✗ Branch 10 not taken.
610 n_close = statistics.RegisterTemplated("n_close",
69 "Number of Close calls");
70
3/6
✓ Branch 2 taken 610 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 610 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 610 times.
✗ Branch 10 not taken.
610 n_pread = statistics.RegisterTemplated("n_pread",
71 "Number of Pread calls");
72
3/6
✓ Branch 2 taken 610 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 610 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 610 times.
✗ Branch 10 not taken.
610 n_dup = statistics.RegisterTemplated("n_dup", "Number of Dup calls");
73
3/6
✓ Branch 2 taken 610 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 610 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 610 times.
✗ Branch 10 not taken.
610 n_readahead = statistics.RegisterTemplated("n_readahead",
74 "Number of ReadAhead calls");
75
3/6
✓ Branch 2 taken 610 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 610 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 610 times.
✗ Branch 10 not taken.
610 n_starttxn = statistics.RegisterTemplated("n_starttxn",
76 "Number of StartTxn calls");
77
3/6
✓ Branch 2 taken 610 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 610 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 610 times.
✗ Branch 10 not taken.
610 n_write = statistics.RegisterTemplated("n_write",
78 "Number of Write calls");
79
3/6
✓ Branch 2 taken 610 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 610 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 610 times.
✗ Branch 10 not taken.
610 n_reset = statistics.RegisterTemplated("n_reset",
80 "Number of Reset calls");
81
3/6
✓ Branch 2 taken 610 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 610 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 610 times.
✗ Branch 10 not taken.
610 n_aborttxn = statistics.RegisterTemplated("n_aborttxn",
82 "Number of AbortTxn calls");
83
3/6
✓ Branch 2 taken 610 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 610 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 610 times.
✗ Branch 10 not taken.
610 n_committxn = statistics.RegisterTemplated("n_committxn",
84 "Number of Commit calls");
85
3/6
✓ Branch 2 taken 610 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 610 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 610 times.
✗ Branch 10 not taken.
610 n_enfile = statistics.RegisterTemplated(
86 "n_enfile", "Number of times the limit on handles was reached");
87
3/6
✓ Branch 2 taken 610 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 610 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 610 times.
✗ Branch 10 not taken.
610 n_openregular = statistics.RegisterTemplated(
88 "n_openregular", "Number of opens from the regular cache");
89
3/6
✓ Branch 2 taken 610 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 610 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 610 times.
✗ Branch 10 not taken.
610 n_openvolatile = statistics.RegisterTemplated(
90 "n_openvolatile", "Number of opens from the volatile cache");
91
3/6
✓ Branch 2 taken 610 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 610 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 610 times.
✗ Branch 10 not taken.
610 n_openmiss = statistics.RegisterTemplated("n_openmiss",
92 "Number of missed opens");
93
3/6
✓ Branch 2 taken 610 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 610 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 610 times.
✗ Branch 10 not taken.
610 n_realloc = statistics.RegisterTemplated("n_realloc",
94 "Number of reallocs");
95
3/6
✓ Branch 2 taken 610 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 610 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 610 times.
✗ Branch 10 not taken.
610 n_overrun = statistics.RegisterTemplated(
96 "n_overrun", "Number of cache limit overruns");
97
3/6
✓ Branch 2 taken 610 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 610 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 610 times.
✗ Branch 10 not taken.
610 n_full = statistics.RegisterTemplated(
98 "n_full", "Number of overruns that could not be resolved");
99 610 }
100 };
101
102 120 virtual CacheManagerIds id() { return kRamCacheManager; }
103 virtual std::string Describe();
104
105 RamCacheManager(uint64_t max_size,
106 unsigned max_entries,
107 MemoryKvStore::MemoryAllocator alloc,
108 perf::StatisticsTemplate statistics);
109
110 virtual ~RamCacheManager();
111
112 virtual bool AcquireQuotaManager(QuotaManager *quota_mgr);
113
114 /**
115 * Open a new file descriptor into the cache. Note that opening entries
116 * effectively pins them in the cache, so it may be necessary to close
117 * unneeded file descriptors to allow eviction to make room in the cache
118 * @param object The tagged hash key
119 * @returns A nonnegative integer file descriptor
120 * @retval -ENOENT The entry is not in the cache
121 * @retval -ENFILE No handles are available
122 */
123 virtual int Open(const LabeledObject &object);
124
125 /**
126 * Look up the size in bytes of the open cache entry
127 * @param id The hash key
128 * @returns The size of the entry
129 * @retval -EBADF @p fd is not valid
130 */
131 virtual int64_t GetSize(int fd);
132
133 /**
134 * Close the descriptor in the cache. Entries not marked as pinned will become
135 * subject to eviction once closed
136 * @param id The hash key
137 * @retval -EBADF @p fd is not valid
138 */
139 virtual int Close(int fd);
140
141 /**
142 * Read a section from the cache entry. See pread(3) for a discussion of the
143 * arguments
144 * @param id The hash key
145 * @returns The number of bytes copied
146 * @retval -EBADF @p fd is not valid
147 */
148 virtual int64_t Pread(int fd, void *buf, uint64_t size, uint64_t offset);
149
150 /**
151 * Duplicates the open file descriptor, allowing the original and the new one
152 * to be used independently
153 * @param id The hash key
154 * @returns A new, nonnegative integer fd
155 * @retval -EBADF @p fd is not valid
156 * @retval -ENFILE No handles are available
157 */
158 virtual int Dup(int fd);
159
160 /**
161 * No effect for in-memory caches
162 * @param id The hash key
163 * @retval -EBADF @p fd is not valid
164 */
165 virtual int Readahead(int fd);
166
167
168 /**
169 * Get the amount of space to be allocated for a call to @ref StartTxn
170 */
171 815 virtual uint32_t SizeOfTxn() { return sizeof(Transaction); }
172
173
174 /**
175 * Start a new transaction, allocating the required memory and initializing
176 * the transaction parameters
177 * @param id The hash key
178 * @param size The total size of the new cache entry
179 * @param txn A pointer to space allocated for storing the transaction details
180 */
181 virtual int StartTxn(const shash::Any &id, uint64_t size, void *txn);
182
183 /**
184 * Set the transaction parameters. At present, only @ref type is used for
185 * pinned, volatile, etc.
186 * @param description Unused
187 * @param type The type of the entry, e.g. pinned
188 * @param flags Unused
189 * @param txn A pointer to space allocated for storing the transaction details
190 */
191 virtual void CtrlTxn(const Label &label, const int flags, void *txn);
192
193 /**
194 * Copy the given memory region into the transaction buffer. Copying starts at
195 * the transaction's current offset
196 * @param buf The source address
197 * @param size The number of bytes to copy
198 * @param txn A pointer to space allocated for storing the transaction details
199 */
200 virtual int64_t Write(const void *buf, uint64_t size, void *txn);
201
202 /**
203 * Seek to the beginning of the transaction buffer
204 * @param txn A pointer to space allocated for storing the transaction details
205 */
206 virtual int Reset(void *txn);
207
208 /**
209 * Commit a transaction and open the resulting cache entry. This is necessary
210 * to avoid a race condition in which the cache entry is evicted between the
211 * calls to CommitTxn and Open.
212 * @param txn A pointer to space allocated for storing the transaction details
213 * @returns A file descriptor to the new cache entry, or a negative error from
214 * @ref CommitTxn
215 */
216 virtual int OpenFromTxn(void *txn);
217
218 /**
219 * Free the resources allocated for the pending transaction
220 * @param txn A pointer to space allocated for storing the transaction details
221 */
222 virtual int AbortTxn(void *txn);
223
224 /**
225 * Commit a transaction to the cache. If there is not enough free space in the
226 * cache, first try to make room by evicting volatile entries. If there is
227 * still not enough room, try evicting regular entries. If there is *still*
228 * not enough space, give up an return failure.
229 * @param txn A pointer to space allocated for storing the transaction details
230 * @retval -ENOSPC The transaction would exceed the size of the cache
231 * @retval -ENFILE No handles are available
232 * @retval -EEXIST An entry with nonzero reference count already exists
233 */
234 virtual int CommitTxn(void *txn);
235
236 virtual void Spawn() { }
237
238 private:
239 // The null hash (hashed output is all null bytes) serves as a marker for
240 // an invalid handle
241 static const shash::Any kInvalidHandle;
242
243 struct ReadOnlyHandle {
244 610 ReadOnlyHandle() : handle(kInvalidHandle), is_volatile(false) { }
245 310 ReadOnlyHandle(const shash::Any &h, bool v) : handle(h), is_volatile(v) { }
246 551845 bool operator==(const ReadOnlyHandle &other) const {
247 551845 return this->handle == other.handle;
248 }
249 1102425 bool operator!=(const ReadOnlyHandle &other) const {
250 1102425 return this->handle != other.handle;
251 }
252
253 shash::Any handle;
254 bool is_volatile;
255 };
256
257 struct Transaction {
258 785 Transaction() : buffer(), expected_size(0), pos(0) { }
259 MemoryBuffer buffer;
260 uint64_t expected_size;
261 uint64_t pos;
262 std::string description;
263 };
264
265 551800 inline MemoryKvStore *GetStore(const ReadOnlyHandle &fd) {
266
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 551770 times.
551800 if (fd.is_volatile) {
267 30 return &volatile_entries_;
268 } else {
269 551770 return &regular_entries_;
270 }
271 }
272
273 inline MemoryKvStore *GetTransactionStore(Transaction *txn) {
274 if (txn->buffer.object_flags & CacheManager::kLabelVolatile) {
275 return &volatile_entries_;
276 } else {
277 return &regular_entries_;
278 }
279 }
280
281 int AddFd(const ReadOnlyHandle &handle);
282 int64_t CommitToKvStore(Transaction *transaction);
283 virtual int DoOpen(const shash::Any &id);
284
285 uint64_t max_size_;
286 FdTable<ReadOnlyHandle> fd_table_;
287 pthread_rwlock_t rwlock_;
288 MemoryKvStore regular_entries_;
289 MemoryKvStore volatile_entries_;
290 Counters counters_;
291 }; // class RamCacheManager
292
293 #endif // CVMFS_CACHE_RAM_H_
294