682 lines
18 KiB
C
682 lines
18 KiB
C
/*
|
|
* Copyright (c) 2019 Tom Marshall <tdm.code@gmail.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
* 02110-1301, USA.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/init.h>
|
|
#include <linux/bio.h>
|
|
#include <linux/device-mapper.h>
|
|
#include <linux/workqueue.h>
|
|
#include <linux/mutex.h>
|
|
|
|
#include <linux/lz4.h>
|
|
|
|
#include <linux/dm-compress.h>
|
|
|
|
struct lbatview {
|
|
u64 pblk;
|
|
struct mutex reflock;
|
|
unsigned int ref;
|
|
|
|
struct mutex lock;
|
|
enum cache_state state;
|
|
struct cbd_params* params;
|
|
struct pbatcache* pbatcache;
|
|
struct pbat* pbat;
|
|
struct lbatpagecache* lpc;
|
|
struct lbatpage* pages[2];
|
|
};
|
|
|
|
bool
|
|
lbatview_ctr(struct lbatview* lv,
|
|
struct cbd_params* params,
|
|
struct pbatcache* pbatcache,
|
|
struct lbatpagecache* lpc)
|
|
{
|
|
memset(lv, 0, sizeof(struct lbatview));
|
|
lv->pblk = PBLK_NONE;
|
|
mutex_init(&lv->reflock);
|
|
lv->ref = 0;
|
|
mutex_init(&lv->lock);
|
|
lv->state = CACHE_STATE_UNCACHED;
|
|
lv->params = params;
|
|
lv->pbatcache = pbatcache;
|
|
lv->pbat = NULL;
|
|
lv->lpc = lpc;
|
|
lv->pages[0] = lv->pages[1] = NULL;
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
lbatview_dtr(struct lbatview* lv)
|
|
{
|
|
if (pbatcache_put(lv->pbatcache, lv->pbat) != 0) {
|
|
printk(KERN_ERR "%s: pbatcache_put failed\n", __func__);
|
|
}
|
|
lv->pbat = NULL;
|
|
lbatpagecache_put(lv->lpc, lv->pages[0]);
|
|
lbatpagecache_put(lv->lpc, lv->pages[1]);
|
|
lv->pages[0] = lv->pages[1] = NULL;
|
|
lv->lpc = NULL;
|
|
}
|
|
|
|
int
|
|
lbatview_flush(struct lbatview* lv)
|
|
{
|
|
int ret = 0;
|
|
|
|
mutex_lock(&lv->lock);
|
|
if (lv->state != CACHE_STATE_DIRTY) {
|
|
goto out;
|
|
}
|
|
BUG_ON(!lv->pages[0]);
|
|
BUG_ON(lv->pblk == PBLK_NONE);
|
|
if (lv->pages[0]) {
|
|
ret = lbatpage_flush(lv->pages[0]);
|
|
if (ret) {
|
|
goto out;
|
|
}
|
|
}
|
|
if (lv->pages[1]) {
|
|
ret = lbatpage_flush(lv->pages[1]);
|
|
if (ret) {
|
|
goto out;
|
|
}
|
|
}
|
|
lv->state = CACHE_STATE_CLEAN;
|
|
|
|
out:
|
|
mutex_unlock(&lv->lock);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
lbatview_read(struct lbatview* lv)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = lbatview_flush(lv);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
mutex_lock(&lv->lock);
|
|
if (lv->pages[0]) {
|
|
ret = lbatpage_read(lv->pages[0]);
|
|
if (ret) {
|
|
goto out;
|
|
}
|
|
}
|
|
if (lv->pages[1]) {
|
|
ret = lbatpage_read(lv->pages[1]);
|
|
if (ret) {
|
|
goto out;
|
|
}
|
|
}
|
|
lv->state = CACHE_STATE_CLEAN;
|
|
|
|
out:
|
|
mutex_unlock(&lv->lock);
|
|
return ret;
|
|
}
|
|
|
|
bool
|
|
lbatview_reset(struct lbatview* lv, u64 pblk, u32 count)
|
|
{
|
|
bool ret = true;
|
|
u32 zone = (pblk - CBD_HEADER_BLOCKS) / zone_len(lv->params);
|
|
|
|
BUG_ON(lv->pblk == pblk);
|
|
lv->pblk = pblk;
|
|
lv->state = CACHE_STATE_UNCACHED;
|
|
if (pbatcache_put(lv->pbatcache, lv->pbat) != 0) {
|
|
printk(KERN_ERR "%s: pbatcache_put failed\n", __func__);
|
|
ret = false;
|
|
}
|
|
lv->pbat = pbatcache_get(lv->pbatcache, zone);
|
|
if (!lv->pbat) {
|
|
ret = false;
|
|
}
|
|
if (lbatpagecache_put(lv->lpc, lv->pages[0]) != 0) {
|
|
ret = false;
|
|
}
|
|
lv->pages[0] = NULL;
|
|
if (lbatpagecache_put(lv->lpc, lv->pages[1]) != 0) {
|
|
ret = false;
|
|
}
|
|
lv->pages[1] = NULL;
|
|
if (count > 0) {
|
|
lv->pages[0] = lbatpagecache_get(lv->lpc, pblk + 0);
|
|
}
|
|
if (count > 1) {
|
|
lv->pages[1] = lbatpagecache_get(lv->lpc, pblk + 1);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static u64
|
|
lbatview_alloc_pblk(struct lbatview* lv)
|
|
{
|
|
int ret = 0;
|
|
u32 zone = (lv->pblk - CBD_HEADER_BLOCKS) / zone_len(lv->params);
|
|
u64 pblk;
|
|
u32 zone_off;
|
|
struct pbat* pbat;
|
|
|
|
if (!lv->pbat) {
|
|
printk(KERN_ERR "%s: *** lv->pbat is NULL\n", __func__);
|
|
return PBLK_NONE;
|
|
}
|
|
|
|
pblk = pbat_alloc(lv->pbat);
|
|
if (pblk != PBLK_NONE) {
|
|
return pblk;
|
|
}
|
|
printk(KERN_INFO "%s: alloc failed for current zone\n", __func__);
|
|
ret = pbatcache_put(lv->pbatcache, lv->pbat);
|
|
if (ret) {
|
|
printk(KERN_ERR "%s: pbatcache_put failed\n", __func__);
|
|
return PBLK_NONE;
|
|
}
|
|
lv->pbat = NULL;
|
|
for (zone_off = 1;
|
|
zone_off <= zone || zone + zone_off < lv->params->nr_zones;
|
|
++zone_off) {
|
|
if (zone_off <= zone) {
|
|
pbat = pbatcache_get(lv->pbatcache, zone - zone_off);
|
|
if (!pbat) {
|
|
printk(KERN_ERR "%s: pbatcache_get failed\n", __func__);
|
|
return PBLK_NONE;
|
|
}
|
|
if (pbat_read(pbat) != 0) {
|
|
printk(KERN_ERR "%s: pbat_read failed\n", __func__);
|
|
return PBLK_NONE;
|
|
}
|
|
pblk = pbat_alloc(pbat);
|
|
if (pblk != PBLK_NONE) {
|
|
printk(KERN_INFO "%s: using zone %u, alloc=%lu\n", __func__, (zone - zone_off), (unsigned long)pblk);
|
|
lv->pbat = pbat;
|
|
return pblk;
|
|
}
|
|
ret = pbatcache_put(lv->pbatcache, pbat);
|
|
if (ret) {
|
|
printk(KERN_ERR "%s: pbatcache_put failed\n", __func__);
|
|
return PBLK_NONE;
|
|
}
|
|
}
|
|
if (zone + zone_off < lv->params->nr_zones) {
|
|
pbat = pbatcache_get(lv->pbatcache, zone + zone_off);
|
|
if (!pbat) {
|
|
printk(KERN_ERR "%s: pbatcache_get failed\n", __func__);
|
|
return PBLK_NONE;
|
|
}
|
|
if (pbat_read(pbat) != 0) {
|
|
printk(KERN_ERR "%s: pbat_read failed\n", __func__);
|
|
return PBLK_NONE;
|
|
}
|
|
pblk = pbat_alloc(pbat);
|
|
if (pblk != PBLK_NONE) {
|
|
printk(KERN_INFO "%s: using zone %u, alloc=%lu\n", __func__, (zone + zone_off), (unsigned long)pblk);
|
|
lv->pbat = pbat;
|
|
return pblk;
|
|
}
|
|
ret = pbatcache_put(lv->pbatcache, pbat);
|
|
if (ret) {
|
|
printk(KERN_ERR "%s: pbatcache_put failed\n", __func__);
|
|
return PBLK_NONE;
|
|
}
|
|
}
|
|
}
|
|
printk(KERN_ERR "%s: fail, all zones full\n", __func__);
|
|
|
|
return PBLK_NONE;
|
|
}
|
|
|
|
static int
|
|
lbatview_free_pblk(struct lbatview* lv, u64 pblk)
|
|
{
|
|
int ret = 0;
|
|
u32 zone = (lv->pblk - CBD_HEADER_BLOCKS) / zone_len(lv->params);
|
|
u32 pblk_zone;
|
|
struct pbat* pbat;
|
|
|
|
if (!lv->pbat) {
|
|
printk(KERN_ERR "%s: *** lv->pbat is NULL\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (pblk < CBD_HEADER_BLOCKS) {
|
|
printk(KERN_ERR "%s: pblk index is in header\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
pblk_zone = (pblk - CBD_HEADER_BLOCKS) / zone_len(lv->params);
|
|
if (pblk_zone >= lv->params->nr_zones) {
|
|
printk(KERN_ERR "%s: pblk zone out of bounds\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
pbat = pbatcache_get(lv->pbatcache, pblk_zone);
|
|
if (!pbat) {
|
|
printk(KERN_ERR "%s: pbatcache_get failed\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
ret = pbat_read(pbat);
|
|
if (ret != 0) {
|
|
printk(KERN_ERR "%s: pbat_read failed\n", __func__);
|
|
return ret;
|
|
}
|
|
ret = pbat_free(pbat, pblk);
|
|
if (pblk_zone == zone && pbat_zone(lv->pbat) != zone) {
|
|
printk(KERN_INFO "%s: freed block %lu in zone %u switching back\n", __func__, (unsigned long)pblk, zone);
|
|
ret = pbatcache_put(lv->pbatcache, lv->pbat);
|
|
if (ret) {
|
|
printk(KERN_ERR "%s: pbatcache_put failed\n", __func__);
|
|
}
|
|
lv->pbat = pbat;
|
|
}
|
|
else {
|
|
ret = pbatcache_put(lv->pbatcache, pbat);
|
|
if (ret) {
|
|
printk(KERN_ERR "%s: pbatcache_put failed\n", __func__);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static u32
|
|
lbatview_elem_off(struct lbatview* lv, u64 lblk)
|
|
{
|
|
u32 lv_zone = (lv->pblk - CBD_HEADER_BLOCKS) / zone_len(lv->params);
|
|
/* The relative lblk in the zone. */
|
|
u32 zone_rel_lblk = lblk - (lv_zone * lv->params->lblk_per_zone);
|
|
/* The offset of the element in the (full) lbat. */
|
|
u32 lbat_elem_off = zone_rel_lblk * lba_len(lv->params);
|
|
/* The offset of the first view pblk. */
|
|
u32 lbatview_off = PBLK_SIZE * (lv->pblk - lbat_off(lv->params, lv_zone));
|
|
|
|
return lbat_elem_off - lbatview_off;
|
|
}
|
|
|
|
static void
|
|
lbatview_rmem(struct lbatview* lv, u32 off, u32 len, void* buf)
|
|
{
|
|
/* XXX: Convert below to a BUG_ON */
|
|
if (off + len > 2 * PAGE_SIZE) {
|
|
printk(KERN_ERR "%s: *** out of bounds\n", __func__);
|
|
return;
|
|
}
|
|
if (off < PAGE_SIZE) {
|
|
if (!lv->pages[0]) {
|
|
printk(KERN_ERR "%s *** no page0\n", __func__);
|
|
return;
|
|
}
|
|
}
|
|
if (off + len > PAGE_SIZE) {
|
|
if (!lv->pages[1]) {
|
|
printk(KERN_ERR "%s *** no page1\n", __func__);
|
|
return;
|
|
}
|
|
}
|
|
if (off < PAGE_SIZE && off + len > PAGE_SIZE) {
|
|
u32 len0 = PAGE_SIZE - off;
|
|
u8* pagebuf0 = lbatpage_get_buf(lv->pages[0], false);
|
|
u8* pagebuf1 = lbatpage_get_buf(lv->pages[1], false);
|
|
memcpy(buf, pagebuf0 + off, len0);
|
|
memcpy(buf + len0, pagebuf1, len - len0);
|
|
lbatpage_put_buf(lv->pages[1]);
|
|
lbatpage_put_buf(lv->pages[0]);
|
|
}
|
|
else {
|
|
u32 bufidx = off / PAGE_SIZE;
|
|
u32 bufoff = off % PAGE_SIZE;
|
|
u8* pagebuf = lbatpage_get_buf(lv->pages[bufidx], false);
|
|
memcpy(buf, pagebuf + bufoff, len);
|
|
lbatpage_put_buf(lv->pages[bufidx]);
|
|
}
|
|
}
|
|
|
|
static void
|
|
lbatview_wmem(struct lbatview* lv, u32 off, u32 len, void* buf)
|
|
{
|
|
/* XXX: Convert below to a BUG_ON */
|
|
if (off + len > 2 * PAGE_SIZE) {
|
|
printk(KERN_ERR "%s: *** out of bounds\n", __func__);
|
|
return;
|
|
}
|
|
if (off < PAGE_SIZE) {
|
|
if (!lv->pages[0]) {
|
|
printk(KERN_ERR "%s *** no page0\n", __func__);
|
|
return;
|
|
}
|
|
}
|
|
if (off + len > PAGE_SIZE) {
|
|
if (!lv->pages[1]) {
|
|
printk(KERN_ERR "%s *** no page1\n", __func__);
|
|
return;
|
|
}
|
|
}
|
|
if (off < PAGE_SIZE && off + len > PAGE_SIZE) {
|
|
u32 len0 = PAGE_SIZE - off;
|
|
u8* pagebuf0 = lbatpage_get_buf(lv->pages[0], true);
|
|
u8* pagebuf1 = lbatpage_get_buf(lv->pages[1], true);
|
|
memcpy(pagebuf0 + off, buf, len0);
|
|
memcpy(pagebuf1, buf + len0, len - len0);
|
|
lbatpage_put_buf(lv->pages[1]);
|
|
lbatpage_put_buf(lv->pages[0]);
|
|
}
|
|
else {
|
|
u32 bufidx = off / PAGE_SIZE;
|
|
u32 bufoff = off % PAGE_SIZE;
|
|
u8* pagebuf = lbatpage_get_buf(lv->pages[bufidx], true);
|
|
memcpy(pagebuf + bufoff, buf, len);
|
|
lbatpage_put_buf(lv->pages[bufidx]);
|
|
}
|
|
lv->state = CACHE_STATE_DIRTY;
|
|
}
|
|
|
|
int
|
|
lbatview_elem_realloc(struct lbatview* lv, u64 lblk, u32 len)
|
|
{
|
|
int ret = 0;
|
|
u32 off;
|
|
u32 n;
|
|
u64 pblk;
|
|
u32 elem_len_size = (lv->params->lblk_shift + PBLK_SHIFT > 16) ? 4 : 2;
|
|
u32 elem_pblk_size = (lv->params->nr_pblk <= 0xffff ? 2 :
|
|
(lv->params->nr_pblk <= 0xffffffff ? 4 : 6));
|
|
u32 elem_lelen;
|
|
u64 elem_lepblk;
|
|
|
|
mutex_lock(&lv->lock);
|
|
off = lbatview_elem_off(lv, lblk);
|
|
elem_lelen = __cpu_to_le32(len);
|
|
lbatview_wmem(lv, off, elem_len_size, &elem_lelen);
|
|
off += elem_len_size;
|
|
if (len == CBD_UNCOMPRESSED) {
|
|
len = PBLK_SIZE * lblk_per_pblk(lv->params);
|
|
}
|
|
for (n = 0; n < lblk_per_pblk(lv->params); ++n, off += elem_pblk_size) {
|
|
elem_lepblk = 0;
|
|
lbatview_rmem(lv, off, elem_pblk_size, &elem_lepblk);
|
|
pblk = __le64_to_cpu(elem_lepblk);
|
|
if (len > PBLK_SIZE * n) {
|
|
if (pblk == 0) {
|
|
pblk = lbatview_alloc_pblk(lv);
|
|
if (pblk == PBLK_NONE) {
|
|
printk(KERN_ERR " lbat_alloc_pblk failed\n");
|
|
ret = -ENOSPC;
|
|
goto out; /* XXX: undo */
|
|
}
|
|
elem_lepblk = __cpu_to_le64(pblk);
|
|
lbatview_wmem(lv, off, elem_pblk_size, &elem_lepblk);
|
|
}
|
|
}
|
|
else {
|
|
if (pblk != 0) {
|
|
elem_lepblk = 0;
|
|
lbatview_wmem(lv, off, elem_pblk_size, &elem_lepblk);
|
|
ret = lbatview_free_pblk(lv, pblk);
|
|
if (ret) {
|
|
printk(KERN_ERR " lbat_free_pblk failed\n");
|
|
goto out; /* XXX: undo */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
out:
|
|
mutex_unlock(&lv->lock);
|
|
return ret;
|
|
}
|
|
|
|
u32
|
|
lbatview_elem_len(struct lbatview* lv, u64 lblk)
|
|
{
|
|
u32 off;
|
|
u32 elem_len_size = (lv->params->lblk_shift + PBLK_SHIFT > 16) ? 4 : 2;
|
|
u32 elem_lelen;
|
|
|
|
mutex_lock(&lv->lock);
|
|
off = lbatview_elem_off(lv, lblk);
|
|
elem_lelen = 0;
|
|
lbatview_rmem(lv, off, elem_len_size, &elem_lelen);
|
|
mutex_unlock(&lv->lock);
|
|
|
|
return __le32_to_cpu(elem_lelen);
|
|
}
|
|
|
|
u64
|
|
lbatview_elem_pblk(struct lbatview* lv, u64 lblk, u32 idx)
|
|
{
|
|
u32 off;
|
|
u32 elem_len_size = (lv->params->lblk_shift + PBLK_SHIFT > 16) ? 4 : 2;
|
|
u32 elem_pblk_size = (lv->params->nr_pblk <= 0xffff ? 2 :
|
|
(lv->params->nr_pblk <= 0xffffffff ? 4 : 6));
|
|
u64 elem_lepblk;
|
|
|
|
mutex_lock(&lv->lock);
|
|
off = lbatview_elem_off(lv, lblk) +
|
|
elem_len_size + idx * elem_pblk_size;
|
|
elem_lepblk = 0;
|
|
lbatview_rmem(lv, off, elem_pblk_size, &elem_lepblk);
|
|
mutex_unlock(&lv->lock);
|
|
|
|
return __le64_to_cpu(elem_lepblk);
|
|
}
|
|
|
|
struct lbatviewcache {
|
|
struct mutex lock;
|
|
struct cbd_params* params;
|
|
struct pbatcache* pc;
|
|
struct lbatpagecache* lpc;
|
|
unsigned int len;
|
|
struct lbatview** cache;
|
|
};
|
|
|
|
size_t
|
|
lbatviewcache_size(void)
|
|
{
|
|
return sizeof(struct lbatviewcache);
|
|
}
|
|
|
|
static bool
|
|
lbatviewcache_realloc(struct lbatviewcache* lvc, unsigned int len)
|
|
{
|
|
struct lbatview** cache;
|
|
unsigned int n;
|
|
struct lbatview* lv;
|
|
|
|
cache = kzalloc(len * sizeof(struct lbatview*), GFP_KERNEL);
|
|
if (!cache) {
|
|
return false;
|
|
}
|
|
n = 0;
|
|
if (lvc->len) {
|
|
memcpy(cache, lvc->cache, lvc->len * sizeof(struct lbatview*));
|
|
n = lvc->len;
|
|
kfree(lvc->cache);
|
|
}
|
|
lvc->len = len;
|
|
lvc->cache = cache;
|
|
while (n < len) {
|
|
lv = kmalloc(sizeof(struct lbatview), GFP_KERNEL);
|
|
if (!lv) {
|
|
return false;
|
|
}
|
|
cache[n++] = lv;
|
|
if (!lbatview_ctr(lv, lvc->params, lvc->pc, lvc->lpc)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
lbatviewcache_ctr(struct lbatviewcache* lvc,
|
|
struct cbd_params* params)
|
|
{
|
|
memset(lvc, 0, sizeof(struct lbatviewcache));
|
|
mutex_init(&lvc->lock);
|
|
lvc->params = params;
|
|
lvc->pc = kmalloc(pbatcache_size(), GFP_KERNEL);
|
|
if (!lvc->pc) {
|
|
return false;
|
|
}
|
|
if (!pbatcache_ctr(lvc->pc, params)) {
|
|
return false;
|
|
}
|
|
lvc->lpc = kmalloc(lbatpagecache_size(), GFP_KERNEL);
|
|
if (!lvc->lpc) {
|
|
return false;
|
|
}
|
|
if (!lbatpagecache_ctr(lvc->lpc, params)) {
|
|
return false;
|
|
}
|
|
|
|
return lbatviewcache_realloc(lvc, 1);
|
|
}
|
|
|
|
void
|
|
lbatviewcache_dtr(struct lbatviewcache* lvc)
|
|
{
|
|
unsigned int n;
|
|
struct lbatview* lv;
|
|
|
|
for (n = 0; n < lvc->len; ++n) {
|
|
lv = lvc->cache[n];
|
|
if (!lv) {
|
|
continue;
|
|
}
|
|
lbatview_dtr(lv);
|
|
if (lv->ref) {
|
|
printk(KERN_ERR "%s: lbatview ref leak: n=%u ref=%u\n", __func__, n, lv->ref);
|
|
}
|
|
kfree(lv);
|
|
}
|
|
kfree(lvc->cache);
|
|
lvc->cache = NULL;
|
|
lvc->len = 0;
|
|
lbatpagecache_dtr(lvc->lpc);
|
|
kfree(lvc->lpc);
|
|
lvc->lpc = NULL;
|
|
pbatcache_dtr(lvc->pc);
|
|
kfree(lvc->pc);
|
|
lvc->pc = NULL;
|
|
lvc->params = NULL;
|
|
}
|
|
|
|
struct lbatview*
|
|
lbatviewcache_get(struct lbatviewcache* lvc, u64 lblk)
|
|
{
|
|
u32 zone;
|
|
u64 zone_lbat_pblk;
|
|
u32 rel_lblk;
|
|
u32 lbat_offset;
|
|
u32 rel_pblk;
|
|
u64 pblk;
|
|
u32 count;
|
|
|
|
unsigned int n;
|
|
struct lbatview* lv;
|
|
|
|
zone = lblk / lvc->params->lblk_per_zone;
|
|
zone_lbat_pblk = lbat_off(lvc->params, zone);
|
|
rel_lblk = lblk - lvc->params->lblk_per_zone * zone;
|
|
lbat_offset = rel_lblk * lba_len(lvc->params);
|
|
rel_pblk = lbat_offset / PBLK_SIZE;
|
|
pblk = zone_lbat_pblk + rel_pblk;
|
|
count = (rel_pblk == lbat_len(lvc->params) - 1) ? 1 : 2;
|
|
|
|
mutex_lock(&lvc->lock);
|
|
for (n = 0; n < lvc->len; ++n) {
|
|
lv = lvc->cache[n];
|
|
mutex_lock(&lv->reflock);
|
|
if (lv->pblk == pblk) {
|
|
++lv->ref;
|
|
mutex_unlock(&lv->reflock);
|
|
goto out;
|
|
}
|
|
mutex_unlock(&lv->reflock);
|
|
}
|
|
for (n = 0; n < lvc->len; ++n) {
|
|
lv = lvc->cache[n];
|
|
mutex_lock(&lv->reflock);
|
|
if (lv->pblk == PBLK_NONE) {
|
|
goto found;
|
|
}
|
|
mutex_unlock(&lv->reflock);
|
|
}
|
|
for (n = 0; n < lvc->len; ++n) {
|
|
lv = lvc->cache[n];
|
|
mutex_lock(&lv->reflock);
|
|
if (lv->ref == 0) {
|
|
goto found;
|
|
}
|
|
mutex_unlock(&lv->reflock);
|
|
}
|
|
printk(KERN_INFO "%s: all objects in use, realloc...\n", __func__);
|
|
n = lvc->len;
|
|
if (!lbatviewcache_realloc(lvc, lvc->len * 2)) {
|
|
printk(KERN_ERR "%s: realloc failed\n", __func__);
|
|
lv = NULL;
|
|
goto out;
|
|
}
|
|
lv = lvc->cache[n];
|
|
mutex_lock(&lv->reflock);
|
|
|
|
found:
|
|
if (!lbatview_reset(lv, pblk, count)) {
|
|
mutex_unlock(&lv->reflock);
|
|
printk(KERN_ERR "%s: lbatview_reset failed\n", __func__);
|
|
lv = NULL;
|
|
goto out;
|
|
}
|
|
lv->ref = 1;
|
|
mutex_unlock(&lv->reflock);
|
|
|
|
out:
|
|
mutex_unlock(&lvc->lock);
|
|
|
|
return lv;
|
|
}
|
|
|
|
int
|
|
lbatviewcache_put(struct lbatviewcache* lvc, struct lbatview* lv)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (!lv) {
|
|
return 0;
|
|
}
|
|
mutex_lock(&lvc->lock);
|
|
mutex_lock(&lv->reflock);
|
|
if (--lv->ref == 0) {
|
|
ret = lbatview_flush(lv);
|
|
if (ret) {
|
|
printk(KERN_ERR "%s: lbatview_flush failed\n", __func__);
|
|
}
|
|
}
|
|
mutex_unlock(&lv->reflock);
|
|
mutex_unlock(&lvc->lock);
|
|
|
|
return ret;
|
|
}
|