Rewrite lbatview_elem_realloc

* Only touch the data we need to touch.
 * Handle failure gracefully by reverting to old data.
 * Adjust libcbd check_one_lblk to ignore unused allocs.
This commit is contained in:
Tom Marshall 2019-11-02 09:35:49 -07:00
parent 5be74b3346
commit 7a743a7e65
2 changed files with 76 additions and 73 deletions

View File

@ -315,56 +315,64 @@ int
lbatview_elem_realloc(struct lbatview* lv, u64 lblk, u32 len)
{
int ret = 0;
u32 req_nalloc;
int err;
u32 elem_off;
u32 elem_len;
u32 req_nalloc;
u32 cur_nalloc;
u32 old_nalloc;
u32 off;
u32 n;
u64 pblk;
u32 elem_lelen;
u64 elem_lepblk;
if (len == CBD_UNCOMPRESSED) {
req_nalloc = lblk_per_pblk(lv->params);
}
else {
req_nalloc = DIV_ROUND_UP(len, PBLK_SIZE);
}
mutex_lock(&lv->lock);
elem_off = lbatview_elem_off(lv, lblk);
elem_lelen = __cpu_to_le32(len);
lbatview_wmem(lv, elem_off, lba_elem_len_bytes(lv->params), &elem_lelen);
off = elem_off + lba_elem_len_bytes(lv->params);
for (n = 0; n < lblk_per_pblk(lv->params); ++n, off += lba_elem_pblk_bytes(lv->params)) {
req_nalloc = (len == CBD_UNCOMPRESSED) ?
lblk_per_pblk(lv->params) :
DIV_ROUND_UP(len, PBLK_SIZE);
elem_lelen = 0;
lbatview_rmem(lv, elem_off, lba_elem_len_bytes(lv->params), &elem_lelen);
elem_len = __le32_to_cpu(elem_lelen);
cur_nalloc = (elem_len == CBD_UNCOMPRESSED) ?
lblk_per_pblk(lv->params) :
DIV_ROUND_UP(elem_len, PBLK_SIZE);
old_nalloc = cur_nalloc;
while (cur_nalloc < req_nalloc) {
off = elem_off + lba_elem_len_bytes(lv->params) +
cur_nalloc * lba_elem_pblk_bytes(lv->params);
pblk = lbatview_alloc_pblk(lv);
if (pblk == PBLK_NONE) {
printk(KERN_ERR "%s: lbatview_alloc_pblk failed\n", __func__);
ret = -ENOSPC;
req_nalloc = old_nalloc;
goto do_free;
}
elem_lepblk = __cpu_to_le64(pblk);
lbatview_wmem(lv, off, lba_elem_pblk_bytes(lv->params), &elem_lepblk);
++cur_nalloc;
}
do_free:
while (cur_nalloc > req_nalloc) {
--cur_nalloc;
off = elem_off + lba_elem_len_bytes(lv->params) +
cur_nalloc * lba_elem_pblk_bytes(lv->params);
elem_lepblk = 0;
lbatview_rmem(lv, off, lba_elem_pblk_bytes(lv->params), &elem_lepblk);
pblk = __le64_to_cpu(elem_lepblk);
if (pblk == 0) {
if (n >= req_nalloc) {
break;
}
pblk = lbatview_alloc_pblk(lv);
if (pblk == PBLK_NONE) {
printk(KERN_ERR "%s: lbatview_alloc_pblk failed\n", __func__);
ret = -ENOSPC;
goto out; /* XXX: undo */
}
elem_lepblk = __cpu_to_le64(pblk);
lbatview_wmem(lv, off, lba_elem_pblk_bytes(lv->params), &elem_lepblk);
}
else {
if (n >= req_nalloc) {
elem_lepblk = 0;
lbatview_wmem(lv, off, lba_elem_pblk_bytes(lv->params), &elem_lepblk);
ret = lbatview_free_pblk(lv, pblk);
if (ret) {
printk(KERN_ERR "%s: lbatview_free_pblk failed\n", __func__);
goto out; /* XXX: undo */
}
}
err = lbatview_free_pblk(lv, pblk);
if (err) {
printk(KERN_ERR "%s: lbatview_free_pblk failed\n", __func__);
ret = err;
}
}
out:
if (!ret) {
elem_lelen = __cpu_to_le32(len);
lbatview_wmem(lv, elem_off, lba_elem_len_bytes(lv->params), &elem_lelen);
}
mutex_unlock(&lv->lock);
return ret;
}

View File

@ -65,7 +65,7 @@ check_one_lblk(const struct cbd_params* params,
{
struct lba* lba;
u8* lba_buf;
u32 c_len;
u32 n_alloc;
u32 n;
u64 pblk;
@ -87,45 +87,40 @@ check_one_lblk(const struct cbd_params* params,
printf(" :E: Length out of bounds\n");
return;
}
c_len = (lba->len == CBD_UNCOMPRESSED) ? PBLK_SIZE * lblk_per_pblk(params) : lba->len;
for (n = 0; n < lblk_per_pblk(params); ++n) {
n_alloc = (lba->len == CBD_UNCOMPRESSED) ?
lblk_per_pblk(params) :
DIV_ROUND_UP(lba->len, PBLK_SIZE);
for (n = 0; n < n_alloc; ++n) {
u32 pblk_zone;
u32 rel_pblk;
pblk = lba->pblk[n];
if (c_len > PBLK_SIZE * n) {
u32 pblk_zone;
u32 rel_pblk;
if (pblk < CBD_HEADER_BLOCKS) {
printf(" [%u] :E: Alloc in header: %lu\n", n, pblk);
continue;
}
pblk_zone = (pblk - CBD_HEADER_BLOCKS) / zone_len(params);
if (pblk_zone >= params->nr_zones) {
printf(" [%u] :E: Alloc beyond end: %lu\n", n, pblk);
continue;
}
if (pblk < zone_data_off(params, pblk_zone)) {
printf(" [%u] :E: Alloc in metadata: %lu\n", n, pblk);
continue;
}
rel_pblk = pblk - zone_data_off(params, pblk_zone);
/* XXX: Cannot happen? */
if (rel_pblk >= pbat_len(params) * PBLK_SIZE_BITS) {
printf(" [%u] :E: Alloc out of zone: %lu\n", n, pblk);
continue;
}
printf(" [%u] pblk=%lu pblk_zone=%u rel_pblk=%u\n", n,
(unsigned long)pblk, pblk_zone, rel_pblk);
if (pblk_used[pblk_zone][rel_pblk/8] & (1 << (rel_pblk % 8))) {
printf(" [%u] :E: Duplicate allocation for zone %u block %u\n",
n, pblk_zone, rel_pblk);
continue;
}
pblk_used[pblk_zone][rel_pblk/8] |= (1 << (rel_pblk % 8));
if (pblk < CBD_HEADER_BLOCKS) {
printf(" [%u] :E: Alloc in header: %lu\n", n, pblk);
continue;
}
else {
if (pblk) {
printf(" [%u] :E: Unexpected pblk alloc: %lu\n", n, pblk);
}
pblk_zone = (pblk - CBD_HEADER_BLOCKS) / zone_len(params);
if (pblk_zone >= params->nr_zones) {
printf(" [%u] :E: Alloc beyond end: %lu\n", n, pblk);
continue;
}
if (pblk < zone_data_off(params, pblk_zone)) {
printf(" [%u] :E: Alloc in metadata: %lu\n", n, pblk);
continue;
}
rel_pblk = pblk - zone_data_off(params, pblk_zone);
/* XXX: Cannot happen? */
if (rel_pblk >= pbat_len(params) * PBLK_SIZE_BITS) {
printf(" [%u] :E: Alloc out of zone: %lu\n", n, pblk);
continue;
}
printf(" [%u] pblk=%lu pblk_zone=%u rel_pblk=%u\n", n,
(unsigned long)pblk, pblk_zone, rel_pblk);
if (pblk_used[pblk_zone][rel_pblk/8] & (1 << (rel_pblk % 8))) {
printf(" [%u] :E: Duplicate allocation for zone %u block %u\n",
n, pblk_zone, rel_pblk);
continue;
}
pblk_used[pblk_zone][rel_pblk/8] |= (1 << (rel_pblk % 8));
}
free(lba);