Branch data Line data Source code
1 : : /* SPDX-FileCopyrightText: Copyright 2026 Cozens Software Solutions Limited
2 : : * SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 OR LicenseRef-PolyForm-Internal-Use-1.0.0 OR LicenseRef-COSOSO-Commercial
3 : : */
4 : :
5 : : #include "SolidSyslogBlockSequencePrivate.h"
6 : :
7 : : #include "SolidSyslogBlockDevice.h"
8 : : #include "SolidSyslogBlockStore.h"
9 : : #include "SolidSyslogBlockStoreErrors.h"
10 : : #include "SolidSyslogBlockStorePrivate.h"
11 : : #include "SolidSyslogPrival.h"
12 : : #include "SolidSyslogStoreCategories.h"
13 : :
14 : : #include <stdbool.h>
15 : : #include <stddef.h>
16 : : #include <stdint.h>
17 : :
18 : : struct SolidSyslogBlockDevice;
19 : :
20 : : enum
21 : : {
22 : : MIN_MAX_BLOCKS = 2U,
23 : : /* Two short of the sequence space: a failed eviction leaves one block over
24 : : * the ceiling, and a resume still needs an absent number to find the run. */
25 : : MAX_MAX_BLOCKS = BLOCK_SEQUENCE_MODULUS - 2U
26 : : };
27 : :
28 : 29282 : static inline uint8_t BlockSequence_NextSequence(uint8_t current)
29 : : {
30 : 29282 : return (uint8_t) ((current + 1U) % BLOCK_SEQUENCE_MODULUS);
31 : : }
32 : :
33 : 33670 : static inline size_t BlockSequence_BlockCount(const struct SolidSyslogBlockSequence* blockSequence)
34 : : {
35 : 33670 : return (size_t) ((blockSequence->WriteSequence - blockSequence->OldestSequence + BLOCK_SEQUENCE_MODULUS) %
36 : 33670 : BLOCK_SEQUENCE_MODULUS) +
37 : : 1U;
38 : : }
39 : :
40 : : // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) -- value, min, max have distinct semantics
41 : 19219 : static inline size_t BlockSequence_ClampToRange(size_t value, size_t min, size_t max)
42 : : {
43 : 19219 : size_t result = value;
44 : :
45 [ + + ]: 19219 : if (result < min)
46 : : {
47 : 9 : result = min;
48 : : }
49 : :
50 [ + + ]: 19219 : if (result > max)
51 : : {
52 : 27 : result = max;
53 : : }
54 : :
55 : 19219 : return result;
56 : : }
57 : :
58 : : static void BlockSequence_NullThresholdCrossed(void* context);
59 : :
60 : 19219 : void SolidSyslogBlockSequence_Initialise(
61 : : struct SolidSyslogBlockSequence* blockSequence,
62 : : const struct SolidSyslogBlockSequenceConfig* config
63 : : )
64 : : {
65 : 19219 : blockSequence->BlockDevice = config->BlockDevice;
66 : 19219 : blockSequence->MaxBlockSize = config->MaxBlockSize;
67 : 19219 : blockSequence->MaxBlocks = BlockSequence_ClampToRange(config->MaxBlocks, MIN_MAX_BLOCKS, MAX_MAX_BLOCKS);
68 : 19219 : blockSequence->GetDiscardPolicy =
69 [ + + ]: 19219 : (config->GetDiscardPolicy != NULL) ? config->GetDiscardPolicy : SolidSyslogDiscardPolicy_Default;
70 : 19219 : blockSequence->DiscardPolicyContext = config->DiscardPolicyContext;
71 : 19219 : blockSequence->GetCapacityThreshold = config->GetCapacityThreshold;
72 : 19219 : blockSequence->OnThresholdCrossed =
73 [ + + ]: 19219 : (config->OnThresholdCrossed != NULL) ? config->OnThresholdCrossed : BlockSequence_NullThresholdCrossed;
74 : 19219 : blockSequence->ThresholdContext = config->ThresholdContext;
75 : 19219 : blockSequence->AtCapacity = false;
76 : 19219 : blockSequence->ThresholdCrossed = false;
77 : 19219 : blockSequence->OldestSequence = 0;
78 : 19219 : blockSequence->ReadSequence = 0;
79 : 19219 : blockSequence->WriteSequence = 0;
80 : 19219 : blockSequence->ReadCursor = 0;
81 : 19219 : blockSequence->ReadBlockSize = 0;
82 : 19219 : blockSequence->ReadBlockSizeKnown = false;
83 : 19219 : blockSequence->WritePosition = 0;
84 : 19219 : blockSequence->WriteBlockCorrupt = false;
85 : 19219 : blockSequence->DeviceFailed = false;
86 : 19219 : blockSequence->EvictedUnsent = false;
87 : 19219 : blockSequence->FailedOperation = SOLIDSYSLOG_BLOCK_STORE_ERROR_MAX;
88 : :
89 [ + + ]: 1941119 : for (size_t seq = 0; seq < BLOCK_SEQUENCE_MODULUS; seq++)
90 : : {
91 : 1921900 : blockSequence->BlockPresent[seq] = false;
92 : : }
93 : 19219 : }
94 : :
95 : : /* A threshold with no callback is still raised as an event. */
96 : 1 : static void BlockSequence_NullThresholdCrossed(void* context)
97 : : {
98 : : (void) context;
99 : 1 : }
100 : :
101 : 19219 : void SolidSyslogBlockSequence_Cleanup(struct SolidSyslogBlockSequence* blockSequence)
102 : : {
103 : : /* No owned resources to release. The BlockDevice pointer is caller-owned
104 : : * (integrator-supplied via BlockSequenceConfig.BlockDevice); the next
105 : : * SolidSyslogBlockSequence_Initialise overwrites every field, so clearing here would be churn. */
106 : : (void) blockSequence;
107 : 19219 : }
108 : :
109 : : static bool BlockSequence_ScanForExistingBlocks(struct SolidSyslogBlockSequence* blockSequence);
110 : : static inline bool BlockSequence_AcquireBlock(struct SolidSyslogBlockSequence* blockSequence, size_t blockIndex);
111 : : static inline bool BlockSequence_DisposeBlock(struct SolidSyslogBlockSequence* blockSequence, size_t blockIndex);
112 : : static inline uint8_t BlockSequence_NextPresentSequence(
113 : : const struct SolidSyslogBlockSequence* blockSequence,
114 : : uint8_t current
115 : : );
116 : : static inline void BlockSequence_NoteDeviceFailure(
117 : : struct SolidSyslogBlockSequence* blockSequence,
118 : : enum SolidSyslogBlockStoreErrors failedOperation
119 : : );
120 : : static inline void BlockSequence_NotifyThresholdCrossed(struct SolidSyslogBlockSequence* blockSequence);
121 : :
122 : 19209 : bool SolidSyslogBlockSequence_Open(struct SolidSyslogBlockSequence* blockSequence)
123 : : {
124 : 19209 : bool foundExistingBlocks = BlockSequence_ScanForExistingBlocks(blockSequence);
125 : 19209 : bool ready = false;
126 : :
127 [ + + ]: 19209 : if (foundExistingBlocks)
128 : : {
129 : 12288 : size_t writePosition = 0U;
130 : 12288 : ready = SolidSyslogBlockDevice_Size(blockSequence->BlockDevice, blockSequence->WriteSequence, &writePosition);
131 : 12288 : blockSequence->WritePosition = writePosition;
132 : :
133 [ + + ]: 12288 : if (!ready)
134 : : {
135 : 320 : BlockSequence_NoteDeviceFailure(blockSequence, SOLIDSYSLOG_BLOCK_STORE_ERROR_SIZE_FAILED);
136 : : }
137 : : }
138 : : else
139 : : {
140 : 6921 : ready = BlockSequence_AcquireBlock(blockSequence, 0);
141 : :
142 [ + + ]: 6921 : if (!ready)
143 : : {
144 : 4 : BlockSequence_NoteDeviceFailure(blockSequence, SOLIDSYSLOG_BLOCK_STORE_ERROR_OPEN_FAILED);
145 : : }
146 : : }
147 : :
148 [ + + ]: 19209 : if (ready)
149 : : {
150 : 18885 : BlockSequence_NotifyThresholdCrossed(blockSequence); /* fire on resume if usage already at-or-above threshold */
151 : : }
152 : :
153 : 19209 : return ready;
154 : : }
155 : :
156 : : /* The map follows the device: a block it acquired is present... */
157 : 22244 : static inline bool BlockSequence_AcquireBlock(struct SolidSyslogBlockSequence* blockSequence, size_t blockIndex)
158 : : {
159 : 22244 : bool acquired = SolidSyslogBlockDevice_Acquire(blockSequence->BlockDevice, blockIndex);
160 : :
161 [ + + ]: 22244 : if (acquired)
162 : : {
163 : 22237 : blockSequence->BlockPresent[blockIndex] = true;
164 : : }
165 : :
166 : 22244 : return acquired;
167 : : }
168 : :
169 : 443 : static inline void BlockSequence_NoteDeviceFailure(
170 : : struct SolidSyslogBlockSequence* blockSequence,
171 : : enum SolidSyslogBlockStoreErrors failedOperation
172 : : )
173 : : {
174 [ + + ]: 443 : if (!blockSequence->DeviceFailed)
175 : : {
176 : 442 : blockSequence->DeviceFailed = true;
177 : 442 : blockSequence->FailedOperation = failedOperation;
178 : : }
179 : 443 : }
180 : :
181 : : /* A run of absent sequence numbers: where it starts and how many it spans. */
182 : : struct Gap
183 : : {
184 : : int Start;
185 : : int Length;
186 : : };
187 : :
188 : : static inline int BlockSequence_CircularPrev(int index);
189 : : static inline int BlockSequence_CircularNext(int index);
190 : : static int BlockSequence_ScanForBlockPresence(struct SolidSyslogBlockSequence* blockSequence);
191 : : static void BlockSequence_LocateRunBoundaries(
192 : : const struct SolidSyslogBlockSequence* blockSequence,
193 : : int lastPresent,
194 : : int* oldest, // NOLINT(bugprone-easily-swappable-parameters) -- oldest / newest are positional run endpoints
195 : : int* newest
196 : : );
197 : :
198 : : static inline void BlockSequence_StartReadingBlock(
199 : : struct SolidSyslogBlockSequence* blockSequence,
200 : : uint8_t readSequence
201 : : );
202 : :
203 : : /* The on-disk block set is one contiguous run in the circular sequence space
204 : : * [0, BLOCK_SEQUENCE_MODULUS), which after enough rotations straddles the
205 : : * 99 -> 00 boundary (e.g. {98, 99, 0, 1}). A block missing from inside the
206 : : * run - a filesystem check removed it - splits the set with a second, smaller
207 : : * gap. So the run is read as the complement of the largest gap: oldest sits
208 : : * one past its end, write one before its start, and the hole stays inside the
209 : : * run, counting towards the block ceiling exactly as the block it replaces
210 : : * would have. That holds while the hole is shorter than the free gap: with
211 : : * the store at its ceiling the free gap is two slots, and a hole of two or
212 : : * more consecutive blocks is then read as the gap instead. */
213 : 19209 : static bool BlockSequence_ScanForExistingBlocks(struct SolidSyslogBlockSequence* blockSequence)
214 : : {
215 : 19209 : int lastPresent = BlockSequence_ScanForBlockPresence(blockSequence);
216 : 19209 : bool foundAny = lastPresent >= 0;
217 : :
218 [ + + ]: 19209 : if (foundAny)
219 : : {
220 : 12288 : int oldest = 0;
221 : 12288 : int newest = BLOCK_SEQUENCE_MODULUS - 1;
222 : 12288 : BlockSequence_LocateRunBoundaries(blockSequence, lastPresent, &oldest, &newest);
223 : :
224 : 12288 : blockSequence->OldestSequence = (uint8_t) oldest;
225 : 12288 : BlockSequence_StartReadingBlock(blockSequence, (uint8_t) oldest);
226 : 12288 : blockSequence->WriteSequence = (uint8_t) newest;
227 : : }
228 : :
229 : 19209 : return foundAny;
230 : : }
231 : :
232 : : /* The cursor goes to the start of the block, and a size cached for the block
233 : : * it leaves is forgotten. */
234 : 26219 : static inline void BlockSequence_StartReadingBlock(struct SolidSyslogBlockSequence* blockSequence, uint8_t readSequence)
235 : : {
236 : 26219 : blockSequence->ReadSequence = readSequence;
237 : 26219 : blockSequence->ReadCursor = 0;
238 : 26219 : blockSequence->ReadBlockSizeKnown = false;
239 : 26219 : }
240 : :
241 : : /* Fills the presence map, and says which sequence number holds the last
242 : : * present block - or -1 when there is none. */
243 : 19209 : static int BlockSequence_ScanForBlockPresence(struct SolidSyslogBlockSequence* blockSequence)
244 : : {
245 : 19209 : int lastPresent = -1;
246 : :
247 [ + + ]: 1940109 : for (int seq = 0; seq < BLOCK_SEQUENCE_MODULUS; seq++)
248 : : {
249 : 1920900 : blockSequence->BlockPresent[seq] = SolidSyslogBlockDevice_Exists(blockSequence->BlockDevice, (size_t) seq);
250 : :
251 [ + + ]: 1920900 : if (blockSequence->BlockPresent[seq])
252 : : {
253 : 19810 : lastPresent = seq;
254 : : }
255 : : }
256 : :
257 : 19209 : return lastPresent;
258 : : }
259 : :
260 : : /* Walks once round the sequence space from just after a present block, so
261 : : * every gap is met whole, and keeps the largest. Nothing changes when every
262 : : * block is present: maxBlocks is clamped to BLOCK_SEQUENCE_MODULUS - 2, so
263 : : * that cannot arise from the library's own rotation, and the caller's
264 : : * defaults stand. */
265 : 12288 : static void BlockSequence_LocateRunBoundaries(
266 : : const struct SolidSyslogBlockSequence* blockSequence,
267 : : int lastPresent,
268 : : int* oldest, // NOLINT(bugprone-easily-swappable-parameters) -- oldest / newest are positional run endpoints
269 : : int* newest
270 : : )
271 : : {
272 : 12288 : struct Gap largest = {0, 0};
273 : 12288 : struct Gap current = {0, 0};
274 : 12288 : int index = lastPresent;
275 : :
276 [ + + ]: 1241088 : for (int step = 0; step < BLOCK_SEQUENCE_MODULUS; step++)
277 : : {
278 : 1228800 : index = BlockSequence_CircularNext(index);
279 : :
280 [ + + ]: 1228800 : if (blockSequence->BlockPresent[index])
281 : : {
282 : 19810 : current.Length = 0;
283 : : }
284 : : else
285 : : {
286 [ + + ]: 1208990 : if (current.Length == 0)
287 : : {
288 : 12316 : current.Start = index;
289 : : }
290 : :
291 : 1208990 : current.Length++;
292 : :
293 [ + + ]: 1208990 : if (current.Length > largest.Length)
294 : : {
295 : 1208961 : largest = current;
296 : : }
297 : : }
298 : : }
299 : :
300 [ + + ]: 12288 : if (largest.Length > 0)
301 : : {
302 : 12287 : *oldest = (largest.Start + largest.Length) % BLOCK_SEQUENCE_MODULUS;
303 : 12287 : *newest = BlockSequence_CircularPrev(largest.Start);
304 : : }
305 : 12288 : }
306 : :
307 : 1228800 : static inline int BlockSequence_CircularNext(int index)
308 : : {
309 : 1228800 : return (index + 1) % BLOCK_SEQUENCE_MODULUS;
310 : : }
311 : :
312 : 12287 : static inline int BlockSequence_CircularPrev(int index)
313 : : {
314 : 12287 : return (index + BLOCK_SEQUENCE_MODULUS - 1) % BLOCK_SEQUENCE_MODULUS;
315 : : }
316 : :
317 : : static inline bool BlockSequence_ThresholdEnabled(const struct SolidSyslogBlockSequence* blockSequence);
318 : : static inline bool BlockSequence_IsAboveThreshold(
319 : : const struct SolidSyslogBlockSequence* blockSequence,
320 : : size_t threshold
321 : : );
322 : :
323 : 293452 : static inline void BlockSequence_NotifyThresholdCrossed(struct SolidSyslogBlockSequence* blockSequence)
324 : : {
325 [ + + ]: 293452 : if (BlockSequence_ThresholdEnabled(blockSequence))
326 : : {
327 : 3035 : size_t threshold = blockSequence->GetCapacityThreshold(blockSequence->ThresholdContext);
328 : :
329 [ + + ]: 3035 : if (!BlockSequence_IsAboveThreshold(blockSequence, threshold))
330 : : {
331 : 1579 : blockSequence->ThresholdCrossed = false;
332 : : }
333 [ + + ]: 1456 : else if (!blockSequence->ThresholdCrossed)
334 : : {
335 : 52 : blockSequence->ThresholdCrossed = true;
336 : 52 : BlockStore_Report(
337 : : SOLIDSYSLOG_SEVERITY_WARNING,
338 : : SOLIDSYSLOG_CAT_STORE_THRESHOLD_CROSSED,
339 : : SOLIDSYSLOG_BLOCK_STORE_ERROR_THRESHOLD_CROSSED
340 : : );
341 : 52 : blockSequence->OnThresholdCrossed(blockSequence->ThresholdContext);
342 : : }
343 : : else
344 : : {
345 : : /* still above threshold and already notified - no edge to report */
346 : : }
347 : : }
348 : 293452 : }
349 : :
350 : 293452 : static inline bool BlockSequence_ThresholdEnabled(const struct SolidSyslogBlockSequence* blockSequence)
351 : : {
352 : 293452 : return blockSequence->GetCapacityThreshold != NULL;
353 : : }
354 : :
355 : 3035 : static inline bool BlockSequence_IsAboveThreshold(
356 : : const struct SolidSyslogBlockSequence* blockSequence,
357 : : size_t threshold
358 : : )
359 : : {
360 [ + + + + ]: 3035 : return (threshold != 0U) && (SolidSyslogBlockSequence_UsedBytes(blockSequence) >= threshold);
361 : : }
362 : :
363 : : static inline bool BlockSequence_BlockIsFull(const struct SolidSyslogBlockSequence* blockSequence, size_t recordSize);
364 : : static inline bool BlockSequence_StoreIsFull(const struct SolidSyslogBlockSequence* blockSequence);
365 : : static inline enum SolidSyslogDiscardPolicy BlockSequence_DiscardPolicy(
366 : : const struct SolidSyslogBlockSequence* blockSequence
367 : : );
368 : : static bool BlockSequence_RotateToNextBlock(struct SolidSyslogBlockSequence* blockSequence, bool* readBlockChanged);
369 : :
370 : 261515 : bool SolidSyslogBlockSequence_PrepareForWrite(
371 : : struct SolidSyslogBlockSequence* blockSequence,
372 : : size_t recordSize,
373 : : bool* readBlockChanged
374 : : )
375 : : {
376 : 261515 : bool blockFull = BlockSequence_BlockIsFull(blockSequence, recordSize);
377 : 261515 : bool spaceAvailable = true;
378 : 261515 : *readBlockChanged = false;
379 : :
380 [ + + + + ]: 261515 : if (blockFull && BlockSequence_StoreIsFull(blockSequence))
381 : : {
382 : : /* Fixes UsedBytes at total while the store is full and refusing, so a
383 : : * threshold at 100% fires on the refusal even with slack in the blocks.
384 : : * Cleared once a block drains or a record is accepted. */
385 : 253 : blockSequence->AtCapacity = true;
386 : 253 : BlockSequence_NotifyThresholdCrossed(blockSequence); /* ahead of the refusal the store reports next */
387 : 253 : spaceAvailable = false;
388 : : }
389 [ + + ]: 261262 : else if (blockFull)
390 : : {
391 : 15325 : spaceAvailable = BlockSequence_RotateToNextBlock(blockSequence, readBlockChanged);
392 : : }
393 : : else
394 : : {
395 : : /* current block has room - leave spaceAvailable=true */
396 : : }
397 : :
398 : 261515 : return spaceAvailable;
399 : : }
400 : :
401 : 261515 : static inline bool BlockSequence_BlockIsFull(const struct SolidSyslogBlockSequence* blockSequence, size_t recordSize)
402 : : {
403 [ + + ]: 521728 : return (blockSequence->WriteBlockCorrupt) ||
404 [ + + ]: 260213 : ((blockSequence->WritePosition + recordSize) > blockSequence->MaxBlockSize);
405 : : }
406 : :
407 : : /* The policy is asked only once the store is at its block ceiling, so an
408 : : * integrator's function answers at the moment it has to decide, and nothing
409 : : * earlier. */
410 : 15578 : static inline bool BlockSequence_StoreIsFull(const struct SolidSyslogBlockSequence* blockSequence)
411 : : {
412 [ + + + + ]: 15899 : return (BlockSequence_BlockCount(blockSequence) >= blockSequence->MaxBlocks) &&
413 : 321 : (BlockSequence_DiscardPolicy(blockSequence) != SOLIDSYSLOG_DISCARD_POLICY_OLDEST);
414 : : }
415 : :
416 : 321 : static inline enum SolidSyslogDiscardPolicy BlockSequence_DiscardPolicy(
417 : : const struct SolidSyslogBlockSequence* blockSequence
418 : : )
419 : : {
420 : 321 : return blockSequence->GetDiscardPolicy(blockSequence->DiscardPolicyContext);
421 : : }
422 : :
423 : : static bool BlockSequence_DiscardOldestBlock(struct SolidSyslogBlockSequence* blockSequence);
424 : : static void BlockSequence_ResetReadToOldest(struct SolidSyslogBlockSequence* blockSequence);
425 : :
426 : : static inline void BlockSequence_AdvanceWriteToNewBlock(
427 : : struct SolidSyslogBlockSequence* blockSequence,
428 : : uint8_t nextSequence
429 : : );
430 : : static inline bool BlockSequence_ExceedsMaxBlocks(const struct SolidSyslogBlockSequence* blockSequence);
431 : : static inline bool BlockSequence_AcquireEmptyBlock(struct SolidSyslogBlockSequence* blockSequence, size_t blockIndex);
432 : :
433 : 15325 : static bool BlockSequence_RotateToNextBlock(struct SolidSyslogBlockSequence* blockSequence, bool* readBlockChanged)
434 : : {
435 : 15325 : uint8_t nextSequence = BlockSequence_NextSequence(blockSequence->WriteSequence);
436 : 15325 : bool acquired = BlockSequence_AcquireEmptyBlock(blockSequence, nextSequence);
437 : :
438 : 15325 : *readBlockChanged = false;
439 : :
440 [ + + ]: 15325 : if (acquired)
441 : : {
442 : 15320 : BlockSequence_AdvanceWriteToNewBlock(blockSequence, nextSequence);
443 : 15320 : blockSequence->EvictedUnsent = false;
444 : :
445 : : /* Sealing the prior write block can re-arm dispose-on-empty: drain that
446 : : * fully MarkSent the block before rotation could not fire here because
447 : : * BlockSequence_IsReadBlockActiveWrite was true at MarkSent time. It is
448 : : * re-evaluated before any eviction, so a block with nothing left to send
449 : : * is disposed rather than evicted, and an eviction always costs records. */
450 : 15320 : SolidSyslogBlockSequence_DisposeReadBlockIfDrained(blockSequence, readBlockChanged);
451 : :
452 : : /* A failed device is not called again. */
453 [ + + + + ]: 15320 : if (!blockSequence->DeviceFailed && BlockSequence_ExceedsMaxBlocks(blockSequence))
454 : : {
455 [ + + - + ]: 67 : *readBlockChanged = BlockSequence_DiscardOldestBlock(blockSequence) || *readBlockChanged;
456 : : }
457 : : }
458 : : else
459 : : {
460 : : /* Only a device failure gets here, and AcquireEmptyBlock has noted it: a
461 : : * full store that keeps what it has never rotates. */
462 : : }
463 : :
464 : 15325 : return acquired;
465 : : }
466 : :
467 : : /* Dispose-then-Acquire enforces the BlockDevice contract that an Acquired block
468 : : * starts empty. Stale content can be left by a crash mid-Append on a previous
469 : : * run, or by a Dispose that succeeded after our state had already advanced
470 : : * past it. Flash drivers depend on this - Acquire = erase, and writing into
471 : : * a non-erased block corrupts data on most flash families.
472 : : *
473 : : * If Dispose fails, Acquire is not tried: a "verify-and-use" flash driver would
474 : : * reject the still-stale block anyway. The failure is noted as the operation
475 : : * that failed, so a stale block that will not go is a dispose failure. */
476 : 15325 : static inline bool BlockSequence_AcquireEmptyBlock(struct SolidSyslogBlockSequence* blockSequence, size_t blockIndex)
477 : : {
478 : 15325 : bool ready = true;
479 : :
480 [ + + ]: 15325 : if (SolidSyslogBlockDevice_Exists(blockSequence->BlockDevice, blockIndex))
481 : : {
482 : 3 : ready = BlockSequence_DisposeBlock(blockSequence, blockIndex);
483 : :
484 [ + + ]: 3 : if (!ready)
485 : : {
486 : 2 : BlockSequence_NoteDeviceFailure(blockSequence, SOLIDSYSLOG_BLOCK_STORE_ERROR_DISPOSE_FAILED);
487 : : }
488 : : }
489 : :
490 [ + + ]: 15325 : if (ready)
491 : : {
492 : 15323 : ready = BlockSequence_AcquireBlock(blockSequence, blockIndex);
493 : :
494 [ + + ]: 15323 : if (!ready)
495 : : {
496 : 3 : BlockSequence_NoteDeviceFailure(blockSequence, SOLIDSYSLOG_BLOCK_STORE_ERROR_ROTATE_FAILED);
497 : : }
498 : : }
499 : :
500 : 15325 : return ready;
501 : : }
502 : :
503 : : /* ...and one it disposed of is not. */
504 : 13946 : static inline bool BlockSequence_DisposeBlock(struct SolidSyslogBlockSequence* blockSequence, size_t blockIndex)
505 : : {
506 : 13946 : bool disposed = SolidSyslogBlockDevice_Dispose(blockSequence->BlockDevice, blockIndex);
507 : :
508 [ + + ]: 13946 : if (disposed)
509 : : {
510 : 13932 : blockSequence->BlockPresent[blockIndex] = false;
511 : : }
512 : :
513 : 13946 : return disposed;
514 : : }
515 : :
516 : 15320 : static inline void BlockSequence_AdvanceWriteToNewBlock(
517 : : struct SolidSyslogBlockSequence* blockSequence,
518 : : uint8_t nextSequence
519 : : )
520 : : {
521 : 15320 : blockSequence->WriteSequence = nextSequence;
522 : 15320 : blockSequence->WritePosition = 0;
523 : 15320 : blockSequence->WriteBlockCorrupt = false;
524 : 15320 : }
525 : :
526 : 15316 : static inline bool BlockSequence_ExceedsMaxBlocks(const struct SolidSyslogBlockSequence* blockSequence)
527 : : {
528 : 15316 : return BlockSequence_BlockCount(blockSequence) > blockSequence->MaxBlocks;
529 : : }
530 : :
531 : : /* The oldest block is the read block - a block the read has finished with is
532 : : * disposed of as the read leaves it - so an eviction always costs records
533 : : * still to send, and the read starts again on the block after it. */
534 : 67 : static bool BlockSequence_DiscardOldestBlock(struct SolidSyslogBlockSequence* blockSequence)
535 : : {
536 : 67 : bool readBlockChanged = false;
537 : :
538 [ + + ]: 67 : if (BlockSequence_DisposeBlock(blockSequence, blockSequence->OldestSequence))
539 : : {
540 : 63 : blockSequence->EvictedUnsent = true;
541 : 63 : blockSequence->OldestSequence = BlockSequence_NextPresentSequence(blockSequence, blockSequence->OldestSequence);
542 : 63 : BlockSequence_ResetReadToOldest(blockSequence);
543 : 63 : readBlockChanged = true;
544 : : }
545 : : else
546 : : {
547 : 4 : BlockSequence_NoteDeviceFailure(blockSequence, SOLIDSYSLOG_BLOCK_STORE_ERROR_DISPOSE_FAILED);
548 : : }
549 : :
550 : 67 : return readBlockChanged;
551 : : }
552 : :
553 : 63 : static void BlockSequence_ResetReadToOldest(struct SolidSyslogBlockSequence* blockSequence)
554 : : {
555 : 63 : BlockSequence_StartReadingBlock(blockSequence, blockSequence->OldestSequence);
556 : 63 : }
557 : :
558 : : /* Steps over a hole in the run. Terminates because the write block is always
559 : : * present: the scan ends the run on one, and a rotation marks the one it
560 : : * acquires. */
561 : 13931 : static inline uint8_t BlockSequence_NextPresentSequence(
562 : : const struct SolidSyslogBlockSequence* blockSequence,
563 : : uint8_t current
564 : : )
565 : : {
566 : 13931 : uint8_t next = BlockSequence_NextSequence(current);
567 : :
568 [ + + ]: 13957 : while (!blockSequence->BlockPresent[next])
569 : : {
570 : 26 : next = BlockSequence_NextSequence(next);
571 : : }
572 : :
573 : 13931 : return next;
574 : : }
575 : :
576 : 769972 : struct SolidSyslogBlockDevice* SolidSyslogBlockSequence_BlockDevice(const struct SolidSyslogBlockSequence* blockSequence
577 : : )
578 : : {
579 : 769972 : return blockSequence->BlockDevice;
580 : : }
581 : :
582 : 279930 : size_t SolidSyslogBlockSequence_WriteSequence(const struct SolidSyslogBlockSequence* blockSequence)
583 : : {
584 : 279930 : return blockSequence->WriteSequence;
585 : : }
586 : :
587 : 20092 : size_t SolidSyslogBlockSequence_WritePosition(const struct SolidSyslogBlockSequence* blockSequence)
588 : : {
589 : 20092 : return blockSequence->WritePosition;
590 : : }
591 : :
592 : 260446 : void SolidSyslogBlockSequence_NoteRecordWritten(struct SolidSyslogBlockSequence* blockSequence, size_t recordSize)
593 : : {
594 : 260446 : blockSequence->WritePosition += recordSize;
595 : 260446 : blockSequence->AtCapacity = false;
596 : 260446 : BlockSequence_NotifyThresholdCrossed(blockSequence);
597 : 260446 : }
598 : :
599 : 2650 : void SolidSyslogBlockSequence_MarkWriteBlockCorrupt(struct SolidSyslogBlockSequence* blockSequence)
600 : : {
601 : 2650 : blockSequence->WriteBlockCorrupt = true;
602 : 2650 : }
603 : :
604 : 259006 : size_t SolidSyslogBlockSequence_ReadSequence(const struct SolidSyslogBlockSequence* blockSequence)
605 : : {
606 : 259006 : return blockSequence->ReadSequence;
607 : : }
608 : :
609 : 254278 : size_t SolidSyslogBlockSequence_ReadCursor(const struct SolidSyslogBlockSequence* blockSequence)
610 : : {
611 : 254278 : return blockSequence->ReadCursor;
612 : : }
613 : :
614 : 255155 : void SolidSyslogBlockSequence_SetReadCursor(struct SolidSyslogBlockSequence* blockSequence, size_t cursor)
615 : : {
616 : 255155 : blockSequence->ReadCursor = cursor;
617 : 255155 : }
618 : :
619 : : static inline bool BlockSequence_IsReadBlockActiveWrite(const struct SolidSyslogBlockSequence* blockSequence);
620 : :
621 : : /* A size the device could not read is a failed device, never an empty block:
622 : : * read as empty, a block still holding unsent records would look drained. A
623 : : * block behind the write block no longer grows, so its size is read once. */
624 : 440831 : bool SolidSyslogBlockSequence_ReadBlockSize(struct SolidSyslogBlockSequence* blockSequence, size_t* size)
625 : : {
626 : 440831 : bool known = true;
627 : :
628 [ + + ]: 440831 : if (BlockSequence_IsReadBlockActiveWrite(blockSequence))
629 : : {
630 : 84359 : *size = blockSequence->WritePosition;
631 : : }
632 [ + + ]: 356472 : else if (blockSequence->ReadBlockSizeKnown)
633 : : {
634 : 337056 : *size = blockSequence->ReadBlockSize;
635 : : }
636 : : else
637 : : {
638 : 19416 : known = SolidSyslogBlockDevice_Size(blockSequence->BlockDevice, blockSequence->ReadSequence, size);
639 : 19416 : blockSequence->ReadBlockSize = *size;
640 : 19416 : blockSequence->ReadBlockSizeKnown = known;
641 : :
642 [ + + ]: 19416 : if (!known)
643 : : {
644 : 102 : BlockSequence_NoteDeviceFailure(blockSequence, SOLIDSYSLOG_BLOCK_STORE_ERROR_SIZE_FAILED);
645 : : }
646 : : }
647 : :
648 : 440831 : return known;
649 : : }
650 : :
651 : 687188 : static inline bool BlockSequence_IsReadBlockActiveWrite(const struct SolidSyslogBlockSequence* blockSequence)
652 : : {
653 : 687188 : return blockSequence->ReadSequence == blockSequence->WriteSequence;
654 : : }
655 : :
656 : : static inline bool BlockSequence_IsReadBlockFullyDrained(
657 : : const struct SolidSyslogBlockSequence* blockSequence,
658 : : size_t readBlockSize
659 : : );
660 : : static inline void BlockSequence_AdvancePastDisposedReadBlock(struct SolidSyslogBlockSequence* blockSequence);
661 : : static inline bool BlockSequence_DisposeReadBlock(struct SolidSyslogBlockSequence* blockSequence);
662 : :
663 : : /* The read block is the oldest: every block before it was disposed of or
664 : : * evicted as the read left it, so disposing it leaves no hole in the run. The
665 : : * caller has checked the read is behind the write, so a next block exists. */
666 : 1235 : void SolidSyslogBlockSequence_LeaveReadBlock(struct SolidSyslogBlockSequence* blockSequence)
667 : : {
668 : 1235 : (void) BlockSequence_DisposeReadBlock(blockSequence);
669 : 1235 : }
670 : :
671 : 13876 : static inline bool BlockSequence_DisposeReadBlock(struct SolidSyslogBlockSequence* blockSequence)
672 : : {
673 : 13876 : bool disposed = BlockSequence_DisposeBlock(blockSequence, blockSequence->ReadSequence);
674 : :
675 [ + + ]: 13876 : if (disposed)
676 : : {
677 : 13868 : BlockSequence_AdvancePastDisposedReadBlock(blockSequence);
678 : : }
679 : : else
680 : : {
681 : 8 : BlockSequence_NoteDeviceFailure(blockSequence, SOLIDSYSLOG_BLOCK_STORE_ERROR_DISPOSE_FAILED);
682 : : }
683 : :
684 : 13876 : return disposed;
685 : : }
686 : :
687 : 13868 : static inline void BlockSequence_AdvancePastDisposedReadBlock(struct SolidSyslogBlockSequence* blockSequence)
688 : : {
689 : 13868 : blockSequence->OldestSequence = BlockSequence_NextPresentSequence(blockSequence, blockSequence->OldestSequence);
690 : 13868 : BlockSequence_StartReadingBlock(blockSequence, blockSequence->OldestSequence);
691 : 13868 : blockSequence->AtCapacity = false;
692 : 13868 : BlockSequence_NotifyThresholdCrossed(blockSequence);
693 : 13868 : }
694 : :
695 : 246357 : void SolidSyslogBlockSequence_DisposeReadBlockIfDrained(
696 : : struct SolidSyslogBlockSequence* blockSequence,
697 : : bool* readBlockChanged
698 : : )
699 : : {
700 : 246357 : *readBlockChanged = false;
701 : :
702 [ + + ]: 246357 : if (!BlockSequence_IsReadBlockActiveWrite(blockSequence))
703 : : {
704 : 181737 : size_t readBlockSize = 0U;
705 : :
706 [ + + + + ]: 363471 : if (SolidSyslogBlockSequence_ReadBlockSize(blockSequence, &readBlockSize) &&
707 : 181734 : BlockSequence_IsReadBlockFullyDrained(blockSequence, readBlockSize))
708 : : {
709 : 12641 : *readBlockChanged = BlockSequence_DisposeReadBlock(blockSequence);
710 : : }
711 : : }
712 : 246357 : }
713 : :
714 : 181734 : static inline bool BlockSequence_IsReadBlockFullyDrained(
715 : : const struct SolidSyslogBlockSequence* blockSequence,
716 : : size_t readBlockSize
717 : : )
718 : : {
719 : 181734 : return blockSequence->ReadCursor >= readBlockSize;
720 : : }
721 : :
722 : 506873 : bool SolidSyslogBlockSequence_ReadIsBehindWrite(const struct SolidSyslogBlockSequence* blockSequence)
723 : : {
724 : 506873 : return blockSequence->ReadSequence != blockSequence->WriteSequence;
725 : : }
726 : :
727 : 516559 : bool SolidSyslogBlockSequence_DeviceFailed(const struct SolidSyslogBlockSequence* blockSequence)
728 : : {
729 : 516559 : return blockSequence->DeviceFailed;
730 : : }
731 : :
732 : 261243 : bool SolidSyslogBlockSequence_EvictedUnsent(const struct SolidSyslogBlockSequence* blockSequence)
733 : : {
734 : 261243 : return blockSequence->EvictedUnsent;
735 : : }
736 : :
737 : 378 : enum SolidSyslogBlockStoreErrors SolidSyslogBlockSequence_FailedOperation(
738 : : const struct SolidSyslogBlockSequence* blockSequence
739 : : )
740 : : {
741 : 378 : return blockSequence->FailedOperation;
742 : : }
743 : :
744 : 484911 : bool SolidSyslogBlockSequence_HasUnsent(const struct SolidSyslogBlockSequence* blockSequence)
745 : : {
746 [ + + ]: 638892 : return SolidSyslogBlockSequence_ReadIsBehindWrite(blockSequence) ||
747 [ + + ]: 153981 : (blockSequence->ReadCursor < blockSequence->WritePosition);
748 : : }
749 : :
750 : 246 : size_t SolidSyslogBlockSequence_TotalBytes(const struct SolidSyslogBlockSequence* blockSequence)
751 : : {
752 : 246 : return blockSequence->MaxBlocks * blockSequence->MaxBlockSize;
753 : : }
754 : :
755 : 3013 : size_t SolidSyslogBlockSequence_UsedBytes(const struct SolidSyslogBlockSequence* blockSequence)
756 : : {
757 : 3013 : size_t used = 0U;
758 : :
759 [ + + ]: 3013 : if (blockSequence->AtCapacity)
760 : : {
761 : 237 : used = SolidSyslogBlockSequence_TotalBytes(blockSequence);
762 : : }
763 : : else
764 : : {
765 : 2776 : size_t closedBlocks = BlockSequence_BlockCount(blockSequence) - 1U;
766 : 2776 : used = (closedBlocks * blockSequence->MaxBlockSize) + blockSequence->WritePosition;
767 : : }
768 : :
769 : 3013 : return used;
770 : : }
|