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