WIP: Implement zone caches

This commit is contained in:
Tom Marshall 2019-10-10 15:07:39 -07:00
parent 07f4221a5f
commit 24401f9a68
1 changed files with 312 additions and 179 deletions

View File

@ -50,13 +50,11 @@ struct dm_compress_io {
struct work_struct work;
};
struct dm_compress
{
struct dm_dev* dev;
bool io_failed;
struct cbd_params params;
struct zone_cache {
u32 zone;
struct mutex lock;
/* Compression working memory */
u8* lz4_wrkmem;
u8* lz4_cbuf;
@ -78,11 +76,26 @@ struct dm_compress
void* lblk;
};
struct dm_compress
{
struct dm_dev* dev;
bool io_failed;
struct cbd_params params;
struct mutex zc_lock;
unsigned int nr_zc;
struct zone_cache* zcache;
/* Queueing stuff */
struct workqueue_struct* io_queue;
struct mutex io_lock;
};
/* Forward decls */
static struct zone_cache* zone_cache_get(struct dm_compress*, u32);
static int zone_cache_put(struct dm_compress*, struct zone_cache*);
static inline int
memcmpz(const void* buf, size_t len)
{
@ -252,68 +265,65 @@ blkdev_pblk_write(struct dm_compress* dc, u64 pblk, u32 count, void *data)
**************************************/
static int
pblk_alloc_write(struct dm_compress* dc)
pblk_alloc_write(struct dm_compress* dc, struct zone_cache* zc)
{
u64 pblk;
u32 count;
void* pg;
BUG_ON(dc->pblk_alloc_idx == ZONE_NONE);
pblk = pblk_alloc_off(&dc->params, dc->pblk_alloc_idx);
BUG_ON(zc->pblk_alloc_idx == ZONE_NONE);
pblk = pblk_alloc_off(&dc->params, zc->pblk_alloc_idx);
count = pblk_alloc_len(&dc->params);
pg = compress_alloc_pages(PBLK_SIZE);
if (!pg) {
return -ENOMEM;
}
memcpy(pg, dc->pblk_alloc, count * PBLK_SIZE);
memcpy(pg, zc->pblk_alloc, count * PBLK_SIZE);
blkdev_pblk_write(dc, pblk, count, pg);
dc->pblk_alloc_dirty = false;
zc->pblk_alloc_dirty = false;
return 0;
}
static int
pblk_alloc_flush(struct dm_compress* dc)
pblk_alloc_flush(struct dm_compress* dc, struct zone_cache* zc)
{
int ret;
int ret = 0;
if (dc->pblk_alloc_dirty) {
ret = pblk_alloc_write(dc);
if (ret) {
return ret;
}
if (zc->pblk_alloc_dirty) {
ret = pblk_alloc_write(dc, zc);
}
return 0;
return ret;
}
/* Read zone physical block alloc bitmap */
static int
pblk_alloc_read(struct dm_compress* dc, u32 idx)
pblk_alloc_read(struct dm_compress* dc, struct zone_cache* zc)
{
int ret;
u64 pblk;
u32 count;
if (dc->pblk_alloc_idx == idx) {
if (zc->pblk_alloc_idx == zc->zone) {
return 0;
}
ret = pblk_alloc_flush(dc);
if (ret != 0) {
return ret;
}
pblk = pblk_alloc_off(&dc->params, idx);
count = pblk_alloc_len(&dc->params);
ret = blkdev_pblk_read(dc, pblk, count, dc->pblk_alloc);
ret = pblk_alloc_flush(dc, zc);
if (ret) {
return ret;
}
dc->pblk_alloc_idx = idx;
pblk = pblk_alloc_off(&dc->params, zc->zone);
count = pblk_alloc_len(&dc->params);
ret = blkdev_pblk_read(dc, pblk, count, zc->pblk_alloc);
if (ret) {
return ret;
}
zc->pblk_alloc_idx = zc->zone;
return 0;
}
@ -323,30 +333,36 @@ pblk_alloc_read(struct dm_compress* dc, u32 idx)
* XXX: get rid of this function and use pblk_alloc directly in lblk_write().
*/
static u64
pblk_alloc_get(struct dm_compress* dc, u32 zone_hint)
pblk_alloc_get(struct dm_compress* dc, struct zone_cache* zc_hint)
{
u32 zone_pblk_count = pblk_alloc_len(&dc->params) * PBLK_SIZE_BITS;
struct zone_cache* zc;
u32 zone;
u32 idx;
zone = zone_hint;
zc = zc_hint;
zone = zc->zone;
/* XXX: check both forward and backward */
do {
if (pblk_alloc_read(dc, zone) != 0) {
if (pblk_alloc_read(dc, zc) != 0) {
printk(KERN_ERR " pblk_alloc_read failed\n");
return 0;
}
idx = cbd_bitmap_alloc(dc->pblk_alloc, zone_pblk_count);
idx = cbd_bitmap_alloc(zc->pblk_alloc, zone_pblk_count);
if (idx != zone_pblk_count) {
dc->pblk_alloc_dirty = true;
zc->pblk_alloc_dirty = true;
if (zc != zc_hint) {
zone_cache_put(dc, zc);
}
return zone_data_off(&dc->params, zone) + idx;
}
++zone;
if (zone == dc->params.nr_zones) {
zone = 0;
}
zc = zone_cache_get(dc, zone);
}
while (zone != zone_hint);
while (zc != zc_hint);
printk(KERN_ERR "%s: fail, all zones full\n", __func__);
return 0;
@ -357,9 +373,10 @@ pblk_alloc_get(struct dm_compress* dc, u32 zone_hint)
* XXX: get rid of this function and use pblk_free directly in lblk_write().
*/
static int
pblk_alloc_put(struct dm_compress* dc, u64 pblk)
pblk_alloc_put(struct dm_compress* dc, struct zone_cache* zc, u64 pblk)
{
u32 zone_pblk_count = pblk_alloc_len(&dc->params) * PBLK_SIZE_BITS;
bool put_zone = false;
u32 zone;
u32 idx;
int ret;
@ -382,14 +399,25 @@ pblk_alloc_put(struct dm_compress* dc, u64 pblk)
printk(KERN_ERR "%s: pblk index out of bounds\n", __func__);
return -EINVAL;
}
ret = pblk_alloc_read(dc, zone);
if (ret != 0) {
return ret;
}
cbd_bitmap_free(dc->pblk_alloc, idx);
dc->pblk_alloc_dirty = true;
return 0;
if (zone != zc->zone) {
zc = zone_cache_get(dc, zone);
put_zone = true;
}
ret = pblk_alloc_read(dc, zc);
if (ret) {
goto out_put;
}
cbd_bitmap_free(zc->pblk_alloc, idx);
zc->pblk_alloc_dirty = true;
out_put:
if (put_zone) {
zone_cache_put(dc, zc);
}
return ret;
}
/**************************************
@ -397,7 +425,7 @@ pblk_alloc_put(struct dm_compress* dc, u64 pblk)
**************************************/
static int
lblk_alloc_elem_write(struct dm_compress* dc)
lblk_alloc_elem_write(struct dm_compress* dc, struct zone_cache* zc)
{
u32 zone;
u32 zone_lblk;
@ -409,32 +437,32 @@ lblk_alloc_elem_write(struct dm_compress* dc)
u8* buf;
void* pg;
BUG_ON(dc->lblk_alloc_elem_lblk == LBLK_NONE);
BUG_ON(dc->lblk_alloc_pblk == PBLK_NONE);
BUG_ON(dc->lblk_alloc_len == 0);
BUG_ON(zc->lblk_alloc_elem_lblk == LBLK_NONE);
BUG_ON(zc->lblk_alloc_pblk == PBLK_NONE);
BUG_ON(zc->lblk_alloc_len == 0);
zone = dc->lblk_alloc_elem_lblk / dc->params.lblk_per_zone;
zone_lblk = dc->lblk_alloc_elem_lblk - (zone * dc->params.lblk_per_zone);
zone = zc->lblk_alloc_elem_lblk / dc->params.lblk_per_zone;
zone_lblk = zc->lblk_alloc_elem_lblk - (zc->zone * dc->params.lblk_per_zone);
elem_off = lblk_alloc_elem_len(&dc->params) * zone_lblk;
elem_end = elem_off + lblk_alloc_elem_len(&dc->params);
rel_pblk = elem_off / PBLK_SIZE;
count = dc->lblk_alloc_len;
pblk = dc->lblk_alloc_pblk;
buf = dc->lblk_alloc + (elem_off - rel_pblk * PBLK_SIZE);
lblk_alloc_elem_put(&dc->params, buf, dc->lblk_alloc_elem);
count = zc->lblk_alloc_len;
pblk = zc->lblk_alloc_pblk;
buf = zc->lblk_alloc + (elem_off - rel_pblk * PBLK_SIZE);
lblk_alloc_elem_put(&dc->params, buf, zc->lblk_alloc_elem);
pg = compress_alloc_pages(count * PBLK_SIZE);
if (!pg) {
return -ENOMEM;
}
memcpy(pg, dc->lblk_alloc, count * PBLK_SIZE);
memcpy(pg, zc->lblk_alloc, count * PBLK_SIZE);
blkdev_pblk_write(dc, pblk, count, pg);
return 0;
}
static int
lblk_alloc_elem_read(struct dm_compress* dc, u64 lblk)
lblk_alloc_elem_read(struct dm_compress* dc, struct zone_cache* zc, u64 lblk)
{
int ret;
u32 zone;
@ -446,7 +474,7 @@ lblk_alloc_elem_read(struct dm_compress* dc, u64 lblk)
u64 pblk;
u8* buf;
if (dc->lblk_alloc_elem_lblk == lblk) {
if (zc->lblk_alloc_elem_lblk == lblk) {
return 0;
}
@ -457,17 +485,17 @@ lblk_alloc_elem_read(struct dm_compress* dc, u64 lblk)
rel_pblk = elem_off / PBLK_SIZE;
count = 1 + (elem_end - 1) / PBLK_SIZE - (elem_off / PBLK_SIZE);
pblk = lblk_alloc_off(&dc->params, zone) + rel_pblk;
if (dc->lblk_alloc_pblk != pblk || dc->lblk_alloc_len < count) {
ret = blkdev_pblk_read(dc, pblk, count, dc->lblk_alloc);
if (zc->lblk_alloc_pblk != pblk || zc->lblk_alloc_len < count) {
ret = blkdev_pblk_read(dc, pblk, count, zc->lblk_alloc);
if (ret != 0) {
return ret;
}
dc->lblk_alloc_pblk = pblk;
dc->lblk_alloc_len = count;
zc->lblk_alloc_pblk = pblk;
zc->lblk_alloc_len = count;
}
buf = dc->lblk_alloc + (elem_off - rel_pblk * PBLK_SIZE);
lblk_alloc_elem_get(&dc->params, buf, dc->lblk_alloc_elem);
dc->lblk_alloc_elem_lblk = lblk;
buf = zc->lblk_alloc + (elem_off - rel_pblk * PBLK_SIZE);
lblk_alloc_elem_get(&dc->params, buf, zc->lblk_alloc_elem);
zc->lblk_alloc_elem_lblk = lblk;
return 0;
}
@ -482,15 +510,15 @@ lblk_alloc_elem_read(struct dm_compress* dc, u64 lblk)
* Returns number of bytes in cbuf or 0 for failure.
*/
static size_t
lblk_compress(struct dm_compress* dc)
lblk_compress(struct cbd_params* params, struct zone_cache* zc)
{
int ret;
void *dbuf = dc->lblk;
u32 dlen = PBLK_SIZE * lblk_per_pblk(&dc->params);
void *cbuf = dc->lz4_cbuf;
u32 clen = PBLK_SIZE * lblk_per_pblk(&dc->params);
void *dbuf = zc->lblk;
u32 dlen = PBLK_SIZE * lblk_per_pblk(params);
void *cbuf = zc->lz4_cbuf;
u32 clen = PBLK_SIZE * lblk_per_pblk(params);
ret = LZ4_compress_default(dbuf, cbuf, dlen, clen, dc->lz4_wrkmem);
ret = LZ4_compress_default(dbuf, cbuf, dlen, clen, zc->lz4_wrkmem);
if (ret <= 0) {
return 0;
}
@ -504,12 +532,12 @@ lblk_compress(struct dm_compress* dc)
* Returns 0 for success, <0 for failure.
*/
static int
lblk_decompress(struct dm_compress* dc, u32 clen)
lblk_decompress(struct cbd_params* params, struct zone_cache* zc, u32 clen)
{
int ret;
void *cbuf = dc->lz4_cbuf;
void *dbuf = dc->lblk;
u32 dlen = PBLK_SIZE * lblk_per_pblk(&dc->params);
void *cbuf = zc->lz4_cbuf;
void *dbuf = zc->lblk;
u32 dlen = PBLK_SIZE * lblk_per_pblk(params);
ret = LZ4_decompress_safe(cbuf, dbuf, clen, dlen);
if (ret != dlen) {
@ -521,7 +549,7 @@ lblk_decompress(struct dm_compress* dc, u32 clen)
}
static int
lblk_write(struct dm_compress* dc)
lblk_write(struct dm_compress* dc, struct zone_cache* zc)
{
int ret;
u32 zone;
@ -533,51 +561,51 @@ lblk_write(struct dm_compress* dc)
u32 n;
u64 pblk;
zone = dc->lblk_num / dc->params.lblk_per_zone;
zone_lblk = dc->lblk_num - (zone * dc->params.lblk_per_zone);
elem_buf = dc->lblk_alloc + zone_lblk * lblk_alloc_elem_len(&dc->params);
zone = zc->zone;
zone_lblk = zc->lblk_num - (zone * dc->params.lblk_per_zone);
elem_buf = zc->lblk_alloc + zone_lblk * lblk_alloc_elem_len(&dc->params);
/* We must have a cached lblk elem */
BUG_ON(dc->lblk_alloc_elem_lblk == LBLK_NONE);
BUG_ON(zc->lblk_alloc_elem_lblk == LBLK_NONE);
d_len = PBLK_SIZE * lblk_per_pblk(&dc->params);
#ifdef CBD_DETECT_ZERO_BLOCKS
if (memcmpz(dc->lblk, d_len) == 0) {
if (memcmpz(zc->lblk, d_len) == 0) {
#else
if (0) {
#endif
c_len = 0;
c_buf = NULL;
dc->lblk_alloc_elem->len = 0;
zc->lblk_alloc_elem->len = 0;
}
else {
c_len = lblk_compress(dc);
c_len = lblk_compress(&dc->params, zc);
if (c_len > 0) {
size_t c_blkrem = c_len % PBLK_SIZE;
if (c_blkrem) {
memset(dc->lz4_cbuf + c_len, 0, c_blkrem);
memset(zc->lz4_cbuf + c_len, 0, c_blkrem);
}
c_buf = dc->lz4_cbuf;
dc->lblk_alloc_elem->len = c_len;
c_buf = zc->lz4_cbuf;
zc->lblk_alloc_elem->len = c_len;
}
else {
c_len = d_len;
c_buf = dc->lblk;
dc->lblk_alloc_elem->len = CBD_UNCOMPRESSED;
c_buf = zc->lblk;
zc->lblk_alloc_elem->len = CBD_UNCOMPRESSED;
}
}
for (n = 0; n < lblk_per_pblk(&dc->params); ++n) {
if (c_len > PBLK_SIZE * n) {
void* pg;
pblk = dc->lblk_alloc_elem->pblk[n];
pblk = zc->lblk_alloc_elem->pblk[n];
if (!pblk) {
pblk = pblk_alloc_get(dc, zone);
pblk = pblk_alloc_get(dc, zc);
if (pblk == 0) {
printk(KERN_ERR " pblk_alloc_get failed\n");
return -ENOSPC;
}
dc->lblk_alloc_elem->pblk[n] = pblk;
zc->lblk_alloc_elem->pblk[n] = pblk;
}
pg = compress_alloc_pages(PBLK_SIZE);
if (!pg) {
@ -588,10 +616,10 @@ lblk_write(struct dm_compress* dc)
c_buf += PBLK_SIZE;
}
else {
pblk = dc->lblk_alloc_elem->pblk[n];
pblk = zc->lblk_alloc_elem->pblk[n];
if (pblk) {
dc->lblk_alloc_elem->pblk[n] = 0;
ret = pblk_alloc_put(dc, pblk);
zc->lblk_alloc_elem->pblk[n] = 0;
ret = pblk_alloc_put(dc, zc, pblk);
if (ret != 0) {
printk(KERN_ERR " pblk_alloc_put failed\n");
return ret;
@ -600,29 +628,29 @@ lblk_write(struct dm_compress* dc)
}
}
ret = lblk_alloc_elem_write(dc);
ret = lblk_alloc_elem_write(dc, zc);
if (ret != 0) {
printk(KERN_ERR " lblk_alloc_elem_write failed\n");
return ret;
}
ret = pblk_alloc_flush(dc);
ret = pblk_alloc_flush(dc, zc);
if (ret != 0) {
printk(KERN_ERR " pblk_alloc_flush failed\n");
return ret;
}
dc->lblk_dirty = false;
zc->lblk_dirty = false;
return 0;
}
static int
lblk_flush(struct dm_compress* dc)
lblk_flush(struct dm_compress* dc, struct zone_cache* zc)
{
int ret;
if (dc->lblk_dirty) {
ret = lblk_write(dc);
if (zc->lblk_dirty) {
ret = lblk_write(dc, zc);
if (ret) {
return ret;
}
@ -632,7 +660,7 @@ lblk_flush(struct dm_compress* dc)
}
static int
lblk_read(struct dm_compress* dc, u64 idx)
lblk_read(struct dm_compress* dc, struct zone_cache* zc, u64 idx)
{
int ret;
u32 zone;
@ -641,27 +669,27 @@ lblk_read(struct dm_compress* dc, u64 idx)
u32 c_len;
u64 pblk;
if (dc->lblk_num == idx) {
if (zc->lblk_num == idx) {
return 0;
}
ret = lblk_flush(dc);
ret = lblk_flush(dc, zc);
if (ret) {
return ret;
}
zone = idx / dc->params.lblk_per_zone;
zone_lblk = idx - (zone * dc->params.lblk_per_zone);
elem_buf = dc->lblk_alloc + zone_lblk * lblk_alloc_elem_len(&dc->params);
elem_buf = zc->lblk_alloc + zone_lblk * lblk_alloc_elem_len(&dc->params);
ret = lblk_alloc_elem_read(dc, idx);
ret = lblk_alloc_elem_read(dc, zc, idx);
if (ret != 0) {
printk(KERN_ERR " lblk_alloc_elem_read failed\n");
return ret;
}
c_len = dc->lblk_alloc_elem->len;
c_len = zc->lblk_alloc_elem->len;
if (c_len == 0) {
memset(dc->lblk, 0, PBLK_SIZE * lblk_per_pblk(&dc->params));
memset(zc->lblk, 0, PBLK_SIZE * lblk_per_pblk(&dc->params));
}
else {
bool is_compressed = true;
@ -673,9 +701,9 @@ lblk_read(struct dm_compress* dc, u64 idx)
is_compressed = false;
c_len = d_len;
}
p = dc->lz4_cbuf;
p = zc->lz4_cbuf;
for (n = 0; n * PBLK_SIZE < c_len; ++n, p += PBLK_SIZE) {
pblk = dc->lblk_alloc_elem->pblk[n];
pblk = zc->lblk_alloc_elem->pblk[n];
BUG_ON(pblk == 0);
ret = blkdev_pblk_read(dc, pblk, 1, p);
if (ret != 0) {
@ -683,89 +711,169 @@ lblk_read(struct dm_compress* dc, u64 idx)
}
}
if (is_compressed) {
if (lblk_decompress(dc, c_len) != 0) {
if (lblk_decompress(&dc->params, zc, c_len) != 0) {
printk(KERN_ERR " decompress failed\n");
return -1;
}
}
else {
memcpy(dc->lblk, dc->lz4_cbuf, d_len);
memcpy(zc->lblk, zc->lz4_cbuf, d_len);
}
}
dc->lblk_num = idx;
zc->lblk_num = idx;
return 0;
}
/**************************************
* Main functions
* Zone cache functions
**************************************/
static void
compress_free_buffers(struct dm_compress* dc)
zone_cache_reset(struct zone_cache* zc, u32 zone)
{
compress_free_pages(dc->lblk, PBLK_SIZE * lblk_per_pblk(&dc->params));
dc->lblk = NULL;
kfree(dc->lblk_alloc_elem);
dc->lblk_alloc_elem = NULL;
compress_free_pages(dc->lblk_alloc, PBLK_SIZE * 2);
dc->lblk_alloc = NULL;
compress_free_pages(dc->pblk_alloc, PBLK_SIZE * pblk_alloc_len(&dc->params));
dc->pblk_alloc = NULL;
compress_free_pages(dc->lz4_cbuf, PBLK_SIZE * lblk_per_pblk(&dc->params));
dc->lz4_cbuf = NULL;
kfree(dc->lz4_wrkmem);
dc->lz4_wrkmem = NULL;
zc->zone = zone;
zc->pblk_alloc_idx = ZONE_NONE;
zc->pblk_alloc_dirty = false;
zc->lblk_alloc_pblk = PBLK_NONE;
zc->lblk_alloc_len = 0;
zc->lblk_alloc_elem_lblk = LBLK_NONE;
zc->lblk_num = LBLK_NONE;
zc->lblk_dirty = false;
}
/*
* XXX: Many of the below (all except lz4 buffers) are used in bio operations
* and should be page aligned. We always get page aligned buffers because of
* the way kmalloc() works, but that is technically not guaranteed.
*/
static int
compress_alloc_buffers(struct dm_compress* dc)
zone_cache_flush(struct dm_compress* dc, struct zone_cache* zc)
{
dc->lz4_wrkmem = kmalloc(LZ4_compressBound(PBLK_SIZE * lblk_per_pblk(&dc->params)), GFP_KERNEL);
if (!dc->lz4_wrkmem) {
int ret;
ret = lblk_flush(dc, zc);
if (ret) {
return ret;
}
ret = pblk_alloc_flush(dc, zc);
if (ret) {
return ret;
}
return 0;
}
static struct zone_cache*
zone_cache_get(struct dm_compress* dc, u32 zone)
{
struct zone_cache* zc;
u32 idx;
//printk(KERN_INFO "%s: zone=%u\n", __func__, (unsigned int)zone);
mutex_lock(&dc->zc_lock);
for (idx = 0; idx < dc->nr_zc; ++idx) {
zc = &dc->zcache[idx];
if (zc->zone == zone) {
mutex_lock(&zc->lock);
goto out;
}
}
for (idx = 0; idx < dc->nr_zc; ++idx) {
zc = &dc->zcache[idx];
if (zc->zone == ZONE_NONE) {
zone_cache_reset(zc, zone);
mutex_lock(&zc->lock);
goto out;
}
}
for (idx = 0; idx < dc->nr_zc; ++idx) {
zc = &dc->zcache[idx];
if (mutex_trylock(&zc->lock) == 1) {
zone_cache_reset(zc, zone);
goto out;
}
}
printk(KERN_ERR "%s: Cannot get zone %u\n", __func__, (unsigned int)zone);
zc = NULL;
out:
mutex_unlock(&dc->zc_lock);
return zc;
}
static int
zone_cache_put(struct dm_compress* dc, struct zone_cache* zc)
{
int ret;
//printk(KERN_INFO "%s: zone=%u\n", __func__, (unsigned int)zc->zone);
ret = zone_cache_flush(dc, zc);
mutex_unlock(&zc->lock);
return ret;
}
static void
zone_cache_dtr(struct dm_compress* dc, struct zone_cache* zc)
{
compress_free_pages(zc->lblk, PBLK_SIZE * lblk_per_pblk(&dc->params));
zc->lblk = NULL;
kfree(zc->lblk_alloc_elem);
zc->lblk_alloc_elem = NULL;
compress_free_pages(zc->lblk_alloc, PBLK_SIZE * 2);
zc->lblk_alloc = NULL;
compress_free_pages(zc->pblk_alloc, PBLK_SIZE * pblk_alloc_len(&dc->params));
zc->pblk_alloc = NULL;
compress_free_pages(zc->lz4_cbuf, PBLK_SIZE * lblk_per_pblk(&dc->params));
zc->lz4_cbuf = NULL;
kfree(zc->lz4_wrkmem);
zc->lz4_wrkmem = NULL;
}
static int
zone_cache_ctr(struct dm_compress* dc, struct zone_cache* zc)
{
zc->zone = ZONE_NONE;
mutex_init(&zc->lock);
zc->lz4_wrkmem = kmalloc(LZ4_compressBound(PBLK_SIZE * lblk_per_pblk(&dc->params)), GFP_KERNEL);
if (!zc->lz4_wrkmem) {
printk(KERN_ERR "%s: Failed to alloc lz4_wrkmem\n", __func__);
goto out_nomem;
}
dc->lz4_cbuf = compress_alloc_pages(PBLK_SIZE * lblk_per_pblk(&dc->params));
if (!dc->lz4_cbuf) {
zc->lz4_cbuf = compress_alloc_pages(PBLK_SIZE * lblk_per_pblk(&dc->params));
if (!zc->lz4_cbuf) {
printk(KERN_ERR "%s: Failed to alloc lz4_cmem\n", __func__);
goto out_nomem;
}
dc->pblk_alloc_idx = ZONE_NONE;
dc->pblk_alloc_dirty = false;
dc->pblk_alloc = compress_alloc_pages(PBLK_SIZE * pblk_alloc_len(&dc->params));
if (!dc->pblk_alloc) {
zc->pblk_alloc_idx = ZONE_NONE;
zc->pblk_alloc_dirty = false;
zc->pblk_alloc = compress_alloc_pages(PBLK_SIZE * pblk_alloc_len(&dc->params));
if (!zc->pblk_alloc) {
printk(KERN_ERR "%s: Failed to alloc pblk_alloc\n", __func__);
goto out_nomem;
}
dc->lblk_alloc_pblk = PBLK_NONE;
dc->lblk_alloc_len = 0;
dc->lblk_alloc = compress_alloc_pages(PBLK_SIZE * 2);
if (!dc->lblk_alloc) {
zc->lblk_alloc_pblk = PBLK_NONE;
zc->lblk_alloc_len = 0;
zc->lblk_alloc = compress_alloc_pages(PBLK_SIZE * 2);
if (!zc->lblk_alloc) {
printk(KERN_ERR "%s: Failed to alloc lblk_alloc\n", __func__);
goto out_nomem;
}
dc->lblk_alloc_elem_lblk = LBLK_NONE;
dc->lblk_alloc_elem = kmalloc(offsetof(struct lblk_alloc_elem, pblk[lblk_per_pblk(&dc->params)]), GFP_KERNEL);
if (!dc->lblk_alloc_elem) {
zc->lblk_alloc_elem_lblk = LBLK_NONE;
zc->lblk_alloc_elem = kmalloc(offsetof(struct lblk_alloc_elem, pblk[lblk_per_pblk(&dc->params)]), GFP_KERNEL);
if (!zc->lblk_alloc_elem) {
printk(KERN_ERR "%s: Failed to alloc lblk_alloc_elem\n", __func__);
goto out_nomem;
}
dc->lblk_num = LBLK_NONE;
dc->lblk_dirty = false;
dc->lblk = compress_alloc_pages(PBLK_SIZE * lblk_per_pblk(&dc->params));
if (!dc->lblk) {
zc->lblk_num = LBLK_NONE;
zc->lblk_dirty = false;
zc->lblk = compress_alloc_pages(PBLK_SIZE * lblk_per_pblk(&dc->params));
if (!zc->lblk) {
printk(KERN_ERR "%s: Failed to alloc lblk\n", __func__);
goto out_nomem;
}
@ -773,10 +881,14 @@ compress_alloc_buffers(struct dm_compress* dc)
return 0;
out_nomem:
compress_free_buffers(dc);
zone_cache_dtr(dc, zc);
return -ENOMEM;
}
/**************************************
* Main functions
**************************************/
static int
compress_open(struct dm_compress* dc, u64 dev_nr_pblks)
{
@ -784,6 +896,7 @@ compress_open(struct dm_compress* dc, u64 dev_nr_pblks)
u8 *pblkbuf;
struct cbd_header header;
u64 max_nr_zones;
unsigned int n;
pblkbuf = kmalloc(PBLK_SIZE, GFP_KERNEL);
if (!pblkbuf) {
@ -846,22 +959,30 @@ compress_open(struct dm_compress* dc, u64 dev_nr_pblks)
memcpy(&dc->params, &header.params, sizeof(header.params));
err = compress_alloc_buffers(dc);
if (err) {
printk(KERN_ERR "%s: failed to alloc buffers\n", __func__);
mutex_init(&dc->zc_lock);
dc->nr_zc = min(2 * num_online_cpus(), dc->params.nr_zones);
dc->zcache = kmalloc(dc->nr_zc * sizeof(struct zone_cache), GFP_KERNEL);
if (!dc->zcache) {
printk(KERN_ERR "%s: out of memory\n", __func__);
goto out;
}
for (n = 0; n < dc->nr_zc; ++n) {
err = zone_cache_ctr(dc, &dc->zcache[n]);
if (err) {
printk(KERN_ERR "%s: failed to init zone cache\n", __func__);
goto out;
}
}
dc->io_queue = alloc_workqueue("kcompress_io", WQ_HIGHPRI | WQ_MEM_RECLAIM, 1);
if (!dc->io_queue) {
printk(KERN_ERR "%s: failed to alloc io_queue\n", __func__);
compress_free_buffers(dc);
return -ENOMEM;
err = -ENOMEM;
goto out;
}
mutex_init(&dc->io_lock);
out:
/* XXX: cleanup on error */
kfree(pblkbuf);
return err;
@ -879,22 +1000,29 @@ compress_read(struct dm_compress *dc, struct bio *bio)
bio_for_each_segment(bv, bio, iter) {
sector_t lblk = iter.bi_sector / lblk_per_sector;
u32 lblk_off = (iter.bi_sector - lblk * lblk_per_sector) * SECTOR_SIZE;
u32 zone = lblk / dc->params.lblk_per_zone;
struct zone_cache* zc = NULL;
unsigned long flags;
char* data;
zc = zone_cache_get(dc, zone);
/* Ensure the data is within the logical block */
if (lblk_off + bv.bv_len > lblk_len) {
printk(KERN_ERR "%s: logical block bounds exceeded\n", __func__);
return -EIO;
}
/* BUG_ON(lblk_off + bv.bv_offset + bv.bv_len > PBLK_SIZE + lblk_per_pblk(dc)); */
ret = lblk_read(dc, lblk);
ret = lblk_read(dc, zc, lblk);
if (ret) {
zone_cache_put(dc, zc);
return ret;
}
data = bvec_kmap_irq(&bv, &flags);
memcpy(data, dc->lblk + lblk_off, bv.bv_len);
memcpy(data, zc->lblk + lblk_off, bv.bv_len);
bvec_kunmap_irq(data, &flags);
zone_cache_put(dc, zc);
}
return 0;
@ -912,9 +1040,13 @@ compress_write(struct dm_compress *dc, struct bio *bio)
bio_for_each_segment(bv, bio, iter) {
sector_t lblk = iter.bi_sector / lblk_per_sector;
u32 lblk_off = (iter.bi_sector - lblk * lblk_per_sector) * SECTOR_SIZE;
u32 zone = lblk / dc->params.lblk_per_zone;
struct zone_cache* zc = NULL;
unsigned long flags;
char* data;
zc = zone_cache_get(dc, zone);
/* Ensure the data is within the logical block */
if (lblk_off + bv.bv_len > lblk_len) {
printk(KERN_ERR "%s logical block bounds exceeded\n", __func__);
@ -924,18 +1056,17 @@ compress_write(struct dm_compress *dc, struct bio *bio)
return -EIO;
}
/* BUG_ON(lblk_off + bv.bv_offset + bv.bv_len > PBLK_SIZE + lblk_per_pblk(dc)); */
ret = lblk_read(dc, lblk);
ret = lblk_read(dc, zc, lblk);
if (ret) {
zone_cache_put(dc, zc);
return ret;
}
data = bvec_kmap_irq(&bv, &flags);
memcpy(dc->lblk + lblk_off, data, bv.bv_len);
memcpy(zc->lblk + lblk_off, data, bv.bv_len);
bvec_kunmap_irq(data, &flags);
dc->lblk_dirty = true;
}
ret = lblk_flush(dc);
if (ret) {
return ret;
zc->lblk_dirty = true;
zone_cache_put(dc, zc);
}
return 0;
@ -947,8 +1078,6 @@ static void compress_io(struct dm_compress_io* io)
struct dm_compress* dc = io->dc;
struct bio* bio = io->bio;
mutex_lock(&dc->io_lock);
switch (bio_op(bio)) {
case REQ_OP_READ:
ret = compress_read(dc, bio);
@ -964,8 +1093,6 @@ static void compress_io(struct dm_compress_io* io)
printk(KERN_ERR "%s: failed, ret=%d\n", __func__, ret);
}
mutex_unlock(&dc->io_lock);
bio->bi_status = (ret == 0 ? BLK_STS_OK : BLK_STS_IOERR); /* XXX */
bio_endio(bio);
}
@ -1072,11 +1199,17 @@ static void
compress_dtr(struct dm_target *ti)
{
struct dm_compress *dc;
unsigned int n;
printk(KERN_INFO "%s: enter\n", __func__);
dc = (struct dm_compress *)ti->private;
compress_free_buffers(dc);
if (dc->zcache) {
for (n = 0; n < dc->nr_zc; ++n) {
zone_cache_dtr(dc, &dc->zcache[n]);
}
kfree(dc->zcache);
}
if (dc->io_queue) {
destroy_workqueue(dc->io_queue);
}