xref: /linux/drivers/md/dm-pcache/pcache_internal.h (revision 55a42f78ffd386e01a5404419f8c5ded7db70a21)
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  */
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  */
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  */
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 = (void *)header + (i * meta_max_size);
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