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 */
29 struct 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 */
pcache_meta_crc(struct pcache_meta_header * header,u32 meta_size)43 static 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 */
pcache_meta_seq_after(u8 seq1,u8 seq2)64 static 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 */
pcache_meta_find_latest(struct pcache_meta_header * header,u32 meta_size,u32 meta_max_size,void * meta_ret)78 static 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 */
118