GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/cache_ram.h
Date: 2024-04-28 02:33:07
Exec Total Coverage
Lines: 41 42 97.6%
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 29 explicit Counters(perf::StatisticsTemplate statistics) {
66
3/6
✓ Branch 2 taken 29 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 29 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 29 times.
✗ Branch 10 not taken.
29 n_getsize = statistics.RegisterTemplated("n_getsize",
67 "Number of GetSize calls");
68
3/6
✓ Branch 2 taken 29 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 29 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 29 times.
✗ Branch 10 not taken.
29 n_close = statistics.RegisterTemplated("n_close",
69 "Number of Close calls");
70
3/6
✓ Branch 2 taken 29 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 29 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 29 times.
✗ Branch 10 not taken.
29 n_pread = statistics.RegisterTemplated("n_pread",
71 "Number of Pread calls");
72
3/6
✓ Branch 2 taken 29 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 29 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 29 times.
✗ Branch 10 not taken.
29 n_dup = statistics.RegisterTemplated("n_dup",
73 "Number of Dup calls");
74
3/6
✓ Branch 2 taken 29 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 29 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 29 times.
✗ Branch 10 not taken.
29 n_readahead = statistics.RegisterTemplated("n_readahead",
75 "Number of ReadAhead calls");
76
3/6
✓ Branch 2 taken 29 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 29 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 29 times.
✗ Branch 10 not taken.
29 n_starttxn = statistics.RegisterTemplated("n_starttxn",
77 "Number of StartTxn calls");
78
3/6
✓ Branch 2 taken 29 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 29 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 29 times.
✗ Branch 10 not taken.
29 n_write = statistics.RegisterTemplated("n_write",
79 "Number of Write calls");
80
3/6
✓ Branch 2 taken 29 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 29 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 29 times.
✗ Branch 10 not taken.
29 n_reset = statistics.RegisterTemplated("n_reset",
81 "Number of Reset calls");
82
3/6
✓ Branch 2 taken 29 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 29 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 29 times.
✗ Branch 10 not taken.
29 n_aborttxn = statistics.RegisterTemplated("n_aborttxn",
83 "Number of AbortTxn calls");
84
3/6
✓ Branch 2 taken 29 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 29 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 29 times.
✗ Branch 10 not taken.
29 n_committxn = statistics.RegisterTemplated("n_committxn",
85 "Number of Commit calls");
86
3/6
✓ Branch 2 taken 29 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 29 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 29 times.
✗ Branch 10 not taken.
29 n_enfile = statistics.RegisterTemplated("n_enfile",
87 "Number of times the limit on handles was reached");
88
3/6
✓ Branch 2 taken 29 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 29 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 29 times.
✗ Branch 10 not taken.
29 n_openregular = statistics.RegisterTemplated("n_openregular",
89 "Number of opens from the regular cache");
90
3/6
✓ Branch 2 taken 29 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 29 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 29 times.
✗ Branch 10 not taken.
29 n_openvolatile = statistics.RegisterTemplated("n_openvolatile",
91 "Number of opens from the volatile cache");
92
3/6
✓ Branch 2 taken 29 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 29 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 29 times.
✗ Branch 10 not taken.
29 n_openmiss = statistics.RegisterTemplated("n_openmiss",
93 "Number of missed opens");
94
3/6
✓ Branch 2 taken 29 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 29 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 29 times.
✗ Branch 10 not taken.
29 n_realloc = statistics.RegisterTemplated("n_realloc",
95 "Number of reallocs");
96
3/6
✓ Branch 2 taken 29 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 29 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 29 times.
✗ Branch 10 not taken.
29 n_overrun = statistics.RegisterTemplated("n_overrun",
97 "Number of cache limit overruns");
98
3/6
✓ Branch 2 taken 29 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 29 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 29 times.
✗ Branch 10 not taken.
29 n_full = statistics.RegisterTemplated("n_full",
99 "Number of overruns that could not be resolved");
100 29 }
101 };
102
103 4 virtual CacheManagerIds id() { return kRamCacheManager; }
104 virtual std::string Describe();
105
106 RamCacheManager(
107 uint64_t max_size,
108 unsigned max_entries,
109 MemoryKvStore::MemoryAllocator alloc,
110 perf::StatisticsTemplate statistics);
111
112 virtual ~RamCacheManager();
113
114 virtual bool AcquireQuotaManager(QuotaManager *quota_mgr);
115
116 /**
117 * Open a new file descriptor into the cache. Note that opening entries
118 * effectively pins them in the cache, so it may be necessary to close
119 * unneeded file descriptors to allow eviction to make room in the cache
120 * @param object The tagged hash key
121 * @returns A nonnegative integer file descriptor
122 * @retval -ENOENT The entry is not in the cache
123 * @retval -ENFILE No handles are available
124 */
125 virtual int Open(const LabeledObject &object);
126
127 /**
128 * Look up the size in bytes of the open cache entry
129 * @param id The hash key
130 * @returns The size of the entry
131 * @retval -EBADF @p fd is not valid
132 */
133 virtual int64_t GetSize(int fd);
134
135 /**
136 * Close the descriptor in the cache. Entries not marked as pinned will become
137 * subject to eviction once closed
138 * @param id The hash key
139 * @retval -EBADF @p fd is not valid
140 */
141 virtual int Close(int fd);
142
143 /**
144 * Read a section from the cache entry. See pread(3) for a discussion of the
145 * arguments
146 * @param id The hash key
147 * @returns The number of bytes copied
148 * @retval -EBADF @p fd is not valid
149 */
150 virtual int64_t Pread(int fd, void *buf, uint64_t size, uint64_t offset);
151
152 /**
153 * Duplicates the open file descriptor, allowing the original and the new one
154 * to be used independently
155 * @param id The hash key
156 * @returns A new, nonnegative integer fd
157 * @retval -EBADF @p fd is not valid
158 * @retval -ENFILE No handles are available
159 */
160 virtual int Dup(int fd);
161
162 /**
163 * No effect for in-memory caches
164 * @param id The hash key
165 * @retval -EBADF @p fd is not valid
166 */
167 virtual int Readahead(int fd);
168
169
170 /**
171 * Get the amount of space to be allocated for a call to @ref StartTxn
172 */
173 43 virtual uint32_t SizeOfTxn() { return sizeof(Transaction); }
174
175
176 /**
177 * Start a new transaction, allocating the required memory and initializing
178 * the transaction parameters
179 * @param id The hash key
180 * @param size The total size of the new cache entry
181 * @param txn A pointer to space allocated for storing the transaction details
182 */
183 virtual int StartTxn(const shash::Any &id, uint64_t size, void *txn);
184
185 /**
186 * Set the transaction parameters. At present, only @ref type is used for
187 * pinned, volatile, etc.
188 * @param description Unused
189 * @param type The type of the entry, e.g. pinned
190 * @param flags Unused
191 * @param txn A pointer to space allocated for storing the transaction details
192 */
193 virtual void CtrlTxn(const Label &label, const int flags, void *txn);
194
195 /**
196 * Copy the given memory region into the transaction buffer. Copying starts at
197 * the transaction's current offset
198 * @param buf The source address
199 * @param size The number of bytes to copy
200 * @param txn A pointer to space allocated for storing the transaction details
201 */
202 virtual int64_t Write(const void *buf, uint64_t size, void *txn);
203
204 /**
205 * Seek to the beginning of the transaction buffer
206 * @param txn A pointer to space allocated for storing the transaction details
207 */
208 virtual int Reset(void *txn);
209
210 /**
211 * Commit a transaction and open the resulting cache entry. This is necessary
212 * to avoid a race condition in which the cache entry is evicted between the
213 * calls to CommitTxn and Open.
214 * @param txn A pointer to space allocated for storing the transaction details
215 * @returns A file descriptor to the new cache entry, or a negative error from
216 * @ref CommitTxn
217 */
218 virtual int OpenFromTxn(void *txn);
219
220 /**
221 * Free the resources allocated for the pending transaction
222 * @param txn A pointer to space allocated for storing the transaction details
223 */
224 virtual int AbortTxn(void *txn);
225
226 /**
227 * Commit a transaction to the cache. If there is not enough free space in the
228 * cache, first try to make room by evicting volatile entries. If there is
229 * still not enough room, try evicting regular entries. If there is *still*
230 * not enough space, give up an return failure.
231 * @param txn A pointer to space allocated for storing the transaction details
232 * @retval -ENOSPC The transaction would exceed the size of the cache
233 * @retval -ENFILE No handles are available
234 * @retval -EEXIST An entry with nonzero reference count already exists
235 */
236 virtual int CommitTxn(void *txn);
237
238 virtual void Spawn() { }
239
240 private:
241 // The null hash (hashed output is all null bytes) serves as a marker for
242 // an invalid handle
243 static const shash::Any kInvalidHandle;
244
245 struct ReadOnlyHandle {
246 29 ReadOnlyHandle()
247 29 : handle(kInvalidHandle)
248 29 , is_volatile(false)
249 29 { }
250 18 ReadOnlyHandle(const shash::Any &h, bool v)
251 18 : handle(h)
252 18 , is_volatile(v)
253 18 { }
254 22085 bool operator ==(const ReadOnlyHandle &other) const {
255 22085 return this->handle == other.handle;
256 }
257 44115 bool operator !=(const ReadOnlyHandle &other) const {
258 44115 return this->handle != other.handle;
259 }
260
261 shash::Any handle;
262 bool is_volatile;
263 };
264
265 struct Transaction {
266 37 Transaction()
267 37 : buffer()
268 37 , expected_size(0)
269 37 , pos(0) { }
270 MemoryBuffer buffer;
271 uint64_t expected_size;
272 uint64_t pos;
273 std::string description;
274 };
275
276 22088 inline MemoryKvStore *GetStore(const ReadOnlyHandle &fd) {
277
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 22082 times.
22088 if (fd.is_volatile) {
278 6 return &volatile_entries_;
279 } else {
280 22082 return &regular_entries_;
281 }
282 }
283
284 inline MemoryKvStore *GetTransactionStore(Transaction *txn) {
285 if (txn->buffer.object_flags & CacheManager::kLabelVolatile) {
286 return &volatile_entries_;
287 } else {
288 return &regular_entries_;
289 }
290 }
291
292 int AddFd(const ReadOnlyHandle &handle);
293 int64_t CommitToKvStore(Transaction *transaction);
294 virtual int DoOpen(const shash::Any &id);
295
296 uint64_t max_size_;
297 FdTable<ReadOnlyHandle> fd_table_;
298 pthread_rwlock_t rwlock_;
299 MemoryKvStore regular_entries_;
300 MemoryKvStore volatile_entries_;
301 Counters counters_;
302 }; // class RamCacheManager
303
304 #endif // CVMFS_CACHE_RAM_H_
305