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