563 lines
14 KiB
C
563 lines
14 KiB
C
#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;
|
|
|
|
#define CBD_FLAG_DIRTY 0x01
|
|
|
|
enum cbd_alg {
|
|
CBD_ALG_NONE,
|
|
CBD_ALG_LZ4,
|
|
CBD_ALG_ZLIB,
|
|
/* lzo, zstd, ... */
|
|
CBD_ALG_MAX
|
|
};
|
|
|
|
struct cbd_params {
|
|
u16 flags;
|
|
u8 algorithm; /* enum cbd_alg */
|
|
u8 compression; /* 0..9 */
|
|
u16 pbat_len;
|
|
u16 lblk_shift;
|
|
u64 nr_pblk;
|
|
u32 nr_zones;
|
|
u32 lblk_per_zone;
|
|
void* priv;
|
|
};
|
|
|
|
struct cbd_stats {
|
|
u64 pblk_alloc;
|
|
};
|
|
|
|
struct cbd_header {
|
|
u8 magic[4];
|
|
u16 version_major;
|
|
u16 version_minor;
|
|
struct cbd_params params;
|
|
struct cbd_stats stats;
|
|
};
|
|
|
|
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 hint)
|
|
{
|
|
u32 off;
|
|
u32 bit;
|
|
|
|
for (off = hint / BITS_PER_BYTE; off < bitsize / BITS_PER_BYTE; ++off) {
|
|
if (buf[off] != 0xff) {
|
|
bit = 0;
|
|
while (buf[off] & (1 << bit)) {
|
|
++bit;
|
|
}
|
|
buf[off] |= (1 << bit);
|
|
return off * BITS_PER_BYTE + bit;
|
|
}
|
|
}
|
|
for (off = 0; off < hint / BITS_PER_BYTE; ++off) {
|
|
if (buf[off] != 0xff) {
|
|
bit = 0;
|
|
while (buf[off] & (1 << bit)) {
|
|
++bit;
|
|
}
|
|
buf[off] |= (1 << bit);
|
|
return off * BITS_PER_BYTE + bit;
|
|
}
|
|
}
|
|
|
|
return bitsize;
|
|
}
|
|
|
|
/* 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 params->pbat_len;
|
|
}
|
|
|
|
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.flags = get16_le(&buf);
|
|
header->params.algorithm = get_byte(&buf);
|
|
header->params.compression = get_byte(&buf);
|
|
header->params.pbat_len = get16_le(&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);
|
|
buf += 32; /* Reserved */
|
|
header->stats.pblk_alloc = get64_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);
|
|
put16_le(&buf, header->params.flags);
|
|
put_byte(&buf, header->params.algorithm);
|
|
put_byte(&buf, header->params.compression);
|
|
put16_le(&buf, header->params.pbat_len);
|
|
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);
|
|
buf += 32; /* Reserved */
|
|
put64_le(&buf, header->stats.pblk_alloc);
|
|
}
|
|
|
|
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_write_wait(struct block_device* bdev,
|
|
u64 pblk, u32 count, struct page** pagev);
|
|
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, u32 cache_pages);
|
|
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, u32 cache_pages);
|
|
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, struct cbd_stats* stats);
|
|
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, u32 cache_pages);
|
|
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;
|
|
u64 lbd_lblk(struct lbd* 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, u32 cache_pages);
|
|
void lbdcache_dtr(struct lbdcache* lc);
|
|
struct lbd*
|
|
lbdcache_get(struct lbdcache* lc, u64 lblk);
|
|
int lbdcache_put(struct lbdcache* lc, struct lbd* lbd, struct cbd_stats* stats);
|
|
|
|
#endif
|
|
|
|
#endif /* _LINUX_DM_COMPRESS_H */
|