#ifndef _LINUX_DM_COMPRESS_H #define _LINUX_DM_COMPRESS_H #ifndef SECTOR_SHIFT #define SECTOR_SHIFT 9 #endif #define PBLK_SHIFT 12 #define PBLK_SIZE (1 << PBLK_SHIFT) #define PBLK_SIZE_BITS (PBLK_SIZE * BITS_PER_BYTE) #define PBLK_PER_SECTOR (1 << (PBLK_SHIFT - SECTOR_SHIFT)) #define LBLK_SHIFT_MIN 1 #define LBLK_SHIFT_MAX (30 - PBLK_SHIFT) #define CBD_HEADER_BLOCKS 1 #define CBD_UNCOMPRESSED 1 static const u8 CBD_MAGIC[] = { 'C', 'B', 'D', '\0' }; static const u16 CBD_VERSION_MAJOR = 1; static const u16 CBD_VERSION_MINOR = 1; enum cbd_alg { CBD_ALG_NONE, CBD_ALG_LZ4, CBD_ALG_ZLIB, /* lzo, zstd, ... */ CBD_ALG_MAX }; struct cbd_params { u8 algorithm; /* cbd_alg */ u8 compression; /* 0..9 */ u16 lblk_shift; u64 nr_pblk; u32 nr_zones; u32 lblk_per_zone; }; struct cbd_header { u8 magic[4]; u16 version_major; u16 version_minor; struct cbd_params params; }; struct lblk_alloc { u32 len; /* Compressed length */ u64 pblk[1]; /* Vector of physical blocks */ }; static inline void get_mem(const u8** raw, u8* buf, size_t len) { memcpy(buf, *raw, len); *raw += len; } static inline void put_mem(u8** raw, const u8* buf, size_t len) { memcpy(*raw, buf, len); *raw += len; } static inline u8 get_byte(const u8** raw) { u8 val = **raw; *raw += sizeof(u8); return val; } static inline void put_byte(u8** raw, u8 val) { **raw = val; *raw += sizeof(u8); } static inline u16 get16_le(const u8** raw) { u16 leval = 0; memcpy(&leval, *raw, sizeof(leval)); *raw += sizeof(leval); return __le16_to_cpu(leval); } static inline void put16_le(u8** raw, u16 val) { u16 leval = __cpu_to_le16(val); memcpy(*raw, &leval, sizeof(leval)); *raw += sizeof(leval); } static inline u32 get32_le(const u8** raw) { u32 leval = 0; memcpy(&leval, *raw, sizeof(leval)); *raw += sizeof(leval); return __le32_to_cpu(leval); } static inline void put32_le(u8** raw, u32 val) { u32 leval = __cpu_to_le32(val); memcpy(*raw, &leval, sizeof(leval)); *raw += sizeof(leval); } static inline u64 get48_le(const u8** raw) { u64 leval = 0; memcpy(&leval, *raw, 6); *raw += 6; return __le64_to_cpu(leval); } static inline void put48_le(u8** raw, u64 val) { u64 leval = __cpu_to_le64(val); memcpy(*raw, &leval, 6); *raw += 6; } static inline u64 get64_le(const u8** raw) { u64 leval = 0; memcpy(&leval, *raw, sizeof(leval)); *raw += sizeof(leval); return __le64_to_cpu(leval); } static inline void put64_le(u8** raw, u64 val) { u64 leval = __cpu_to_le64(val); memcpy(*raw, &leval, sizeof(leval)); *raw += sizeof(leval); } static inline u32 lblk_per_pblk(const struct cbd_params* params) { return (1 << params->lblk_shift); } static inline u32 zone_pblk_alloc_len(const struct cbd_params* params) { return 1; } static inline u32 zone_lblk_alloc_elem_len(const struct cbd_params* params) { u32 elem_len_bytes = (params->lblk_shift + PBLK_SHIFT > 16) ? 4 : 2; u32 elem_pblk_bytes = (params->nr_pblk <= 0xffff ? 2 : (params->nr_pblk <= 0xffffffff ? 4 : 6)); return elem_len_bytes + (1 << params->lblk_shift) * elem_pblk_bytes; } static inline u32 zone_lblk_alloc_len(const struct cbd_params* params) { return DIV_ROUND_UP(params->lblk_per_zone * zone_lblk_alloc_elem_len(params), PBLK_SIZE); } static inline u32 zone_metadata_len(const struct cbd_params* params) { return zone_pblk_alloc_len(params) + zone_lblk_alloc_len(params); } static inline u32 zone_data_len(const struct cbd_params* params) { return zone_pblk_alloc_len(params) * PBLK_SIZE * BITS_PER_BYTE; } static inline u32 zone_len(const struct cbd_params* params) { return zone_metadata_len(params) + zone_data_len(params); } static inline u64 zone_off(const struct cbd_params* params, u32 idx) { return CBD_HEADER_BLOCKS + idx * zone_len(params); } static inline u64 zone_pblk_alloc_off(const struct cbd_params* params, u32 idx) { return zone_off(params, idx) + 0; } static inline u64 zone_lblk_alloc_off(const struct cbd_params* params, u32 idx) { return zone_off(params, idx) + zone_pblk_alloc_len(params); } static inline u64 zone_data_off(const struct cbd_params* params, u32 idx) { return zone_off(params, idx) + zone_pblk_alloc_len(params) + zone_lblk_alloc_len(params); } static inline void cbd_header_get(const u8* buf, struct cbd_header* header) { get_mem(&buf, header->magic, sizeof(header->magic)); header->version_major = get16_le(&buf); header->version_minor = get16_le(&buf); header->params.algorithm = get_byte(&buf); header->params.compression = get_byte(&buf); header->params.lblk_shift = get16_le(&buf); header->params.nr_pblk = get64_le(&buf); header->params.nr_zones = get32_le(&buf); header->params.lblk_per_zone = get32_le(&buf); } static inline void cbd_header_put(u8* buf, const struct cbd_header* header) { put_mem(&buf, header->magic, sizeof(header->magic)); put16_le(&buf, header->version_major); put16_le(&buf, header->version_minor); put_byte(&buf, header->params.algorithm); put_byte(&buf, header->params.compression); put16_le(&buf, header->params.lblk_shift); put64_le(&buf, header->params.nr_pblk); put32_le(&buf, header->params.nr_zones); put32_le(&buf, header->params.lblk_per_zone); } /* * XXX: * nr_bits = zone_pblk_alloc_len(params) * PBLK_SIZE; * bit = find_next_zero_bit_le(buf, nr_bits); * if (bit < nr_bits) { * set_bit_le(bit, buf); * } * return bit; */ static inline u32 cbd_bitmap_alloc(u8* buf, u32 bitsize) { u32 off = 0; u32 bit = 0; for (off = 0; off < bitsize / BITS_PER_BYTE; ++off) { if (buf[off] != 0xff) { while (buf[off] & (1 << bit)) { ++bit; } buf[off] |= (1 << bit); break; } } return off * BITS_PER_BYTE + bit; } /* * XXX: * clear_bit_le(bit, buf); */ static inline void cbd_bitmap_free(u8* buf, u32 idx) { u32 off = idx / BITS_PER_BYTE; u32 bit = idx % BITS_PER_BYTE; buf[off] &= ~(1 << bit); } static inline void cbd_lblk_alloc_get(const struct cbd_params* params, const u8* buf, u32 idx, struct lblk_alloc* alloc) { const u8* raw = buf + idx * zone_lblk_alloc_elem_len(params); u32 n; if (params->lblk_shift + PBLK_SHIFT > 16) { alloc->len = get32_le(&raw); } else { alloc->len = get16_le(&raw); } if (params->nr_pblk <= 0xffff) { for (n = 0; n < lblk_per_pblk(params); ++n) { alloc->pblk[n] = get16_le(&raw); } } else if (params->nr_pblk <= 0xffffffff) { for (n = 0; n < lblk_per_pblk(params); ++n) { alloc->pblk[n] = get32_le(&raw); } } else { for (n = 0; n < lblk_per_pblk(params); ++n) { alloc->pblk[n] = get48_le(&raw); } } } static inline void cbd_lblk_alloc_put(const struct cbd_params* params, void* buf, u32 idx, const struct lblk_alloc* alloc) { u8* raw = buf + idx * zone_lblk_alloc_elem_len(params); u32 n; if (params->lblk_shift + PBLK_SHIFT > 16) { put32_le(&raw, alloc->len); } else { put16_le(&raw, alloc->len); } if (params->nr_pblk <= 0xffff) { for (n = 0; n < lblk_per_pblk(params); ++n) { put16_le(&raw, alloc->pblk[n]); } } else if (params->nr_pblk <= 0xffffffff) { for (n = 0; n < lblk_per_pblk(params); ++n) { put32_le(&raw, alloc->pblk[n]); } } else { for (n = 0; n < lblk_per_pblk(params); ++n) { put48_le(&raw, alloc->pblk[n]); } } } #endif /* _LINUX_DM_COMPRESS_H */