summaryrefslogtreecommitdiff
path: root/drivers/lightnvm/pblk-core.c
diff options
context:
space:
mode:
authorJavier González <jg@lightnvm.io>2017-04-22 01:32:49 +0200
committerJens Axboe <axboe@fb.com>2017-04-23 16:57:52 -0600
commita44f53faf4674d84cba79f7ee574584e18ab8744 (patch)
tree92183bdbb213a0b9604bbb387bc4749c091919cc /drivers/lightnvm/pblk-core.c
parentbe388d9fbd4e09582e31c3ee82a022e368208ae3 (diff)
lightnvm: pblk: fix erase counters on error fail
When block erases fail, these blocks are marked bad. The number of valid blocks in the line was not updated, which could cause an infinite loop on the erase path. Fix this atomic counter and, in order to avoid taking an irq lock on the interrupt context, make the erase counters atomic too. Also, in the case that a significant number of blocks become bad in a line, the result is the double shared metadata buffer (emeta) to stop the pipeline until all metadata is flushed to the media. Increase the number of metadata lines from 2 to 4 to avoid this case. Fixes: a4bd217b4326 "lightnvm: physical block device (pblk) target" Signed-off-by: Javier González <javier@cnexlabs.com> Reviewed-by: Matias Bjørling <matias@cnexlabs.com> Signed-off-by: Jens Axboe <axboe@fb.com>
Diffstat (limited to 'drivers/lightnvm/pblk-core.c')
-rw-r--r--drivers/lightnvm/pblk-core.c28
1 files changed, 19 insertions, 9 deletions
diff --git a/drivers/lightnvm/pblk-core.c b/drivers/lightnvm/pblk-core.c
index ac3742b85f2e..5e44768ccffa 100644
--- a/drivers/lightnvm/pblk-core.c
+++ b/drivers/lightnvm/pblk-core.c
@@ -29,6 +29,7 @@ static void pblk_mark_bb(struct pblk *pblk, struct pblk_line *line,
pr_debug("pblk: erase failed: line:%d, pos:%d\n", line->id, pos);
atomic_long_inc(&pblk->erase_failed);
+ atomic_dec(&line->blk_in_line);
if (test_and_set_bit(pos, line->blk_bitmap))
pr_err("pblk: attempted to erase bb: line:%d, pos:%d\n",
line->id, pos);
@@ -832,21 +833,28 @@ int pblk_line_erase(struct pblk *pblk, struct pblk_line *line)
struct ppa_addr ppa;
int bit = -1;
- /* Erase one block at the time and only erase good blocks */
- while ((bit = find_next_zero_bit(line->erase_bitmap, lm->blk_per_line,
- bit + 1)) < lm->blk_per_line) {
+ /* Erase only good blocks, one at a time */
+ do {
+ spin_lock(&line->lock);
+ bit = find_next_zero_bit(line->erase_bitmap, lm->blk_per_line,
+ bit + 1);
+ if (bit >= lm->blk_per_line) {
+ spin_unlock(&line->lock);
+ break;
+ }
+
ppa = pblk->luns[bit].bppa; /* set ch and lun */
ppa.g.blk = line->id;
- /* If the erase fails, the block is bad and should be marked */
- line->left_eblks--;
+ atomic_dec(&line->left_eblks);
WARN_ON(test_and_set_bit(bit, line->erase_bitmap));
+ spin_unlock(&line->lock);
if (pblk_blk_erase_sync(pblk, ppa)) {
pr_err("pblk: failed to erase line %d\n", line->id);
return -ENOMEM;
}
- }
+ } while (1);
return 0;
}
@@ -1007,6 +1015,7 @@ retry_smeta:
static int pblk_line_prepare(struct pblk *pblk, struct pblk_line *line)
{
struct pblk_line_meta *lm = &pblk->lm;
+ int blk_in_line = atomic_read(&line->blk_in_line);
line->map_bitmap = mempool_alloc(pblk->line_meta_pool, GFP_ATOMIC);
if (!line->map_bitmap)
@@ -1030,12 +1039,13 @@ static int pblk_line_prepare(struct pblk *pblk, struct pblk_line *line)
return -EINTR;
}
line->state = PBLK_LINESTATE_OPEN;
+
+ atomic_set(&line->left_eblks, blk_in_line);
+ atomic_set(&line->left_seblks, blk_in_line);
spin_unlock(&line->lock);
/* Bad blocks do not need to be erased */
bitmap_copy(line->erase_bitmap, line->blk_bitmap, lm->blk_per_line);
- line->left_eblks = line->blk_in_line;
- atomic_set(&line->left_seblks, line->left_eblks);
kref_init(&line->ref);
@@ -1231,7 +1241,7 @@ retry_line:
left_seblks = atomic_read(&new->left_seblks);
if (left_seblks) {
/* If line is not fully erased, erase it */
- if (new->left_eblks) {
+ if (atomic_read(&new->left_eblks)) {
if (pblk_line_erase(pblk, new))
return NULL;
} else {