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