Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1/* SPDX-License-Identifier: GPL-2.0-or-later */
2#ifndef _PCACHE_INTERNAL_H
3#define _PCACHE_INTERNAL_H
4
5#include <linux/delay.h>
6#include <linux/crc32c.h>
7
8#define pcache_err(fmt, ...) \
9 pr_err("dm-pcache: %s:%u " fmt, __func__, __LINE__, ##__VA_ARGS__)
10#define pcache_info(fmt, ...) \
11 pr_info("dm-pcache: %s:%u " fmt, __func__, __LINE__, ##__VA_ARGS__)
12#define pcache_debug(fmt, ...) \
13 pr_debug("dm-pcache: %s:%u " fmt, __func__, __LINE__, ##__VA_ARGS__)
14
15#define PCACHE_KB (1024ULL)
16#define PCACHE_MB (1024 * PCACHE_KB)
17
18/* Maximum number of metadata indices */
19#define PCACHE_META_INDEX_MAX 2
20
21#define PCACHE_CRC_SEED 0x3B15A
22/*
23 * struct pcache_meta_header - PCACHE metadata header structure
24 * @crc: CRC checksum for validating metadata integrity.
25 * @seq: Sequence number to track metadata updates.
26 * @version: Metadata version.
27 * @res: Reserved space for future use.
28 */
29struct pcache_meta_header {
30 __u32 crc;
31 __u8 seq;
32 __u8 version;
33 __u16 res;
34};
35
36/*
37 * pcache_meta_crc - Calculate CRC for the given metadata header.
38 * @header: Pointer to the metadata header.
39 * @meta_size: Size of the metadata structure.
40 *
41 * Returns the CRC checksum calculated by excluding the CRC field itself.
42 */
43static inline u32 pcache_meta_crc(struct pcache_meta_header *header, u32 meta_size)
44{
45 return crc32c(PCACHE_CRC_SEED, (void *)header + 4, meta_size - 4);
46}
47
48/*
49 * pcache_meta_seq_after - Check if a sequence number is more recent, accounting for overflow.
50 * @seq1: First sequence number.
51 * @seq2: Second sequence number.
52 *
53 * Determines if @seq1 is more recent than @seq2 by calculating the signed
54 * difference between them. This approach allows handling sequence number
55 * overflow correctly because the difference wraps naturally, and any value
56 * greater than zero indicates that @seq1 is "after" @seq2. This method
57 * assumes 8-bit unsigned sequence numbers, where the difference wraps
58 * around if seq1 overflows past seq2.
59 *
60 * Returns:
61 * - true if @seq1 is more recent than @seq2, indicating it comes "after"
62 * - false otherwise.
63 */
64static inline bool pcache_meta_seq_after(u8 seq1, u8 seq2)
65{
66 return (s8)(seq1 - seq2) > 0;
67}
68
69/*
70 * pcache_meta_find_latest - Find the latest valid metadata.
71 * @header: Pointer to the metadata header.
72 * @meta_size: Size of each metadata block.
73 *
74 * Finds the latest valid metadata by checking sequence numbers. If a
75 * valid entry with the highest sequence number is found, its pointer
76 * is returned. Returns NULL if no valid metadata is found.
77 */
78static inline void __must_check *pcache_meta_find_latest(struct pcache_meta_header *header,
79 u32 meta_size, u32 meta_max_size,
80 void *meta_ret)
81{
82 struct pcache_meta_header *meta, *latest = NULL;
83 u32 i, seq_latest = 0;
84 void *meta_addr;
85
86 meta = meta_ret;
87
88 for (i = 0; i < PCACHE_META_INDEX_MAX; i++) {
89 meta_addr = (void *)header + (i * meta_max_size);
90 if (copy_mc_to_kernel(meta, meta_addr, meta_size)) {
91 pcache_err("hardware memory error when copy meta");
92 return ERR_PTR(-EIO);
93 }
94
95 /* Skip if CRC check fails, which means corrupted */
96 if (meta->crc != pcache_meta_crc(meta, meta_size))
97 continue;
98
99 /* Update latest if a more recent sequence is found */
100 if (!latest || pcache_meta_seq_after(meta->seq, seq_latest)) {
101 seq_latest = meta->seq;
102 latest = meta_addr;
103 }
104 }
105
106 if (!latest)
107 return NULL;
108
109 if (copy_mc_to_kernel(meta_ret, latest, meta_size)) {
110 pcache_err("hardware memory error");
111 return ERR_PTR(-EIO);
112 }
113
114 return latest;
115}
116
117#endif /* _PCACHE_INTERNAL_H */