#ifndef _LINUX_DM_COMPRESS_H #define _LINUX_DM_COMPRESS_H #ifndef SECTOR_SHIFT #define SECTOR_SHIFT 9 #endif #ifndef SECTOR_SIZE #define SECTOR_SIZE (1 << SECTOR_SHIFT) #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 (20 - PBLK_SHIFT) #define ZONE_NONE (u32)(~0) #define PBLK_NONE (u64)(~0) #define LBLK_NONE (u64)(~0) #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; /* enum cbd_alg */ u8 compression; /* 0..9 */ u16 lblk_shift; u64 nr_pblk; u32 nr_zones; u32 lblk_per_zone; void* priv; }; struct cbd_header { u8 magic[4]; u16 version_major; u16 version_minor; struct cbd_params params; }; struct lba { 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); } /* XXX: Use kernel bit functions */ 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: Use kernel bit functions */ 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 u32 lblk_per_pblk(const struct cbd_params* params) { return (1 << params->lblk_shift); } static inline u32 pbat_len(const struct cbd_params* params) { return 1; } static inline u32 lba_elem_len_bytes(const struct cbd_params* params) { return (params->lblk_shift + PBLK_SHIFT > 16) ? 4 : 2; } static inline u32 lba_elem_pblk_bytes(const struct cbd_params* params) { return (params->nr_pblk <= 0xffff ? 2 : (params->nr_pblk <= 0xffffffff ? 4 : 6)); } static inline u32 lba_len(const struct cbd_params* params) { return lba_elem_len_bytes(params) + lba_elem_pblk_bytes(params) * (1 << params->lblk_shift); } static inline u32 lbat_len(const struct cbd_params* params) { return DIV_ROUND_UP(params->lblk_per_zone * lba_len(params), PBLK_SIZE); } static inline u32 zone_metadata_len(const struct cbd_params* params) { return pbat_len(params) + lbat_len(params); } static inline u32 zone_data_len(const struct cbd_params* params) { return pbat_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 pbat_off(const struct cbd_params* params, u32 idx) { return zone_off(params, idx) + 0; } static inline u64 lbat_off(const struct cbd_params* params, u32 idx) { return zone_off(params, idx) + pbat_len(params); } static inline u64 zone_data_off(const struct cbd_params* params, u32 idx) { return zone_off(params, idx) + pbat_len(params) + lbat_len(params); } static inline u32 zone_for_pblk(const struct cbd_params* params, u64 pblk) { if (pblk < CBD_HEADER_BLOCKS) { return ZONE_NONE; } return (pblk - CBD_HEADER_BLOCKS) / zone_len(params); } static inline u32 zone_for_lblk(const struct cbd_params* params, u64 lblk) { return (lblk / params->lblk_per_zone); } 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); } static inline u32 lba_len_get(const struct cbd_params* params, const u8* buf) { if (lba_elem_len_bytes(params) == 2) { return get16_le(&buf); } else { return get32_le(&buf); } } static inline void lba_len_put(const struct cbd_params* params, u8* buf, u32 val) { if (lba_elem_len_bytes(params) == 2) { put16_le(&buf, val); } else { put32_le(&buf, val); } } static inline u64 lba_pblk_get(const struct cbd_params* params, const u8* buf, u32 idx) { const u8* p = buf; u32 len_bytes = lba_elem_len_bytes(params); u32 pblk_bytes = lba_elem_pblk_bytes(params); if (pblk_bytes == 2) { p += len_bytes + 2 * idx; return get16_le(&p); } else if (pblk_bytes == 4) { p += len_bytes + 4 * idx; return get32_le(&p); } else { p += len_bytes + 6 * idx; return get48_le(&p); } } static inline void lba_pblk_put(const struct cbd_params* params, u8* buf, u32 idx, u64 val) { u8* p = buf; u32 len_bytes = lba_elem_len_bytes(params); u32 pblk_bytes = lba_elem_pblk_bytes(params); if (pblk_bytes == 2) { p += len_bytes + 2 * idx; put16_le(&p, val); } else if (pblk_bytes == 4) { p += len_bytes + 4 * idx; put32_le(&p, val); } else { p += len_bytes + 6 * idx; put48_le(&p, val); } } static inline void lba_get(const struct cbd_params* params, const u8* buf, struct lba* lba) { u32 n; u32 len_bytes = lba_elem_len_bytes(params); u32 pblk_bytes = lba_elem_pblk_bytes(params); if (len_bytes == 2) { lba->len = get16_le(&buf); } else { lba->len = get32_le(&buf); } if (pblk_bytes == 2) { for (n = 0; n < lblk_per_pblk(params); ++n) { lba->pblk[n] = get16_le(&buf); } } else if (pblk_bytes == 4) { for (n = 0; n < lblk_per_pblk(params); ++n) { lba->pblk[n] = get32_le(&buf); } } else { for (n = 0; n < lblk_per_pblk(params); ++n) { lba->pblk[n] = get48_le(&buf); } } } static inline void lba_put(const struct cbd_params* params, u8* buf, const struct lba* lba) { u32 n; u32 len_bytes = lba_elem_len_bytes(params); u32 pblk_bytes = lba_elem_pblk_bytes(params); if (len_bytes == 2) { put16_le(&buf, lba->len); } else { put32_le(&buf, lba->len); } if (pblk_bytes == 2) { for (n = 0; n < lblk_per_pblk(params); ++n) { put16_le(&buf, lba->pblk[n]); } } else if (pblk_bytes == 4) { for (n = 0; n < lblk_per_pblk(params); ++n) { put32_le(&buf, lba->pblk[n]); } } else { for (n = 0; n < lblk_per_pblk(params); ++n) { put48_le(&buf, lba->pblk[n]); } } } #ifdef __KERNEL__ #if defined(CONFIG_LZ4_COMPRESS) || defined(CONFIG_LZ4_COMPRESS_MODULE) #define COMPRESS_HAVE_LZ4 1 #endif #if defined(CONFIG_ZLIB_INFLATE) || defined(CONFIG_ZLIB_INFLATE_MODULE) #define COMPRESS_HAVE_ZLIB 1 #endif typedef void (*pblk_endio_t)(struct bio*); /* Single page allocator */ struct page* cbd_alloc_page(void); void cbd_free_page(struct page* page); /* Multiple page allocator */ struct page* cbd_alloc_pages(size_t len); void cbd_free_pages(struct page* pages, size_t len); /* Vector page allocator */ bool cbd_alloc_pagev(struct page** pagev, size_t len); void cbd_free_pagev(struct page** pagev, size_t len); /* Core low-level I/O */ int pblk_read_wait(struct block_device* bdev, u64 pblk, u32 count, struct page** pagev); int pblk_read(struct block_device* bdev, u64 pblk, u32 count, struct page** pagev, pblk_endio_t endio, void* endio_priv); void pblk_write(struct block_device* bdev, u64 pblk, u32 count, struct page** pagev); struct pbat; u32 pbat_zone(struct pbat* pbat); u64 pbat_alloc(struct pbat* pbat); int pbat_free(struct pbat* pbat, u64 pblk); struct pbatcache; size_t pbatcache_size(void); bool pbatcache_ctr(struct pbatcache* pbatcache, struct cbd_params* params); void pbatcache_dtr(struct pbatcache* pbatcache); struct pbat* pbatcache_get(struct pbatcache* pbatcache, u32 zone); int pbatcache_put(struct pbatcache* pbatcache, struct pbat* pbat); struct lbatpage; u8* lbatpage_get_buf(struct lbatpage* lp, bool rw); void lbatpage_put_buf(struct lbatpage* lp); struct lbatpagecache; size_t lbatpagecache_size(void); bool lbatpagecache_ctr(struct lbatpagecache* lpc, struct cbd_params* params); void lbatpagecache_dtr(struct lbatpagecache* lpc); struct lbatpage* lbatpagecache_get(struct lbatpagecache* lpc, u64 pblk); int lbatpagecache_put(struct lbatpagecache* lpc, struct lbatpage* lpi); struct lbatview; int lbatview_elem_realloc(struct lbatview* lv, u64 lblk, u32 len); u32 lbatview_elem_len(struct lbatview* lv, u64 lblk); u64 lbatview_elem_pblk(struct lbatview* lv, u64 lblk, u32 idx); struct lbatviewcache; size_t lbatviewcache_size(void); bool lbatviewcache_ctr(struct lbatviewcache* lvc, struct cbd_params* params); void lbatviewcache_dtr(struct lbatviewcache* lvc); struct lbatview* lbatviewcache_get(struct lbatviewcache* lvc, u64 lblk); int lbatviewcache_put(struct lbatviewcache* lvc, struct lbatview* lbv); struct lbd; void lbd_data_read(struct lbd* lbd, u32 off, u32 len, u8* buf); void lbd_data_write(struct lbd* lbd, u32 off, u32 len, const u8* buf); struct lbdcache; size_t lbdcache_size(void); bool lbdcache_ctr(struct lbdcache* lc, struct cbd_params* params); void lbdcache_dtr(struct lbdcache* lc); struct lbd* lbdcache_get(struct lbdcache* lc, u64 lblk); int lbdcache_put(struct lbdcache* lc, struct lbd* lbd); #endif #endif /* _LINUX_DM_COMPRESS_H */