aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJavier González <jg@lightnvm.io>2017-04-21 19:32:49 -0400
committerJens Axboe <axboe@fb.com>2017-04-23 18:57:52 -0400
commita44f53faf4674d84cba79f7ee574584e18ab8744 (patch)
tree92183bdbb213a0b9604bbb387bc4749c091919cc
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>
-rw-r--r--drivers/lightnvm/pblk-core.c28
-rw-r--r--drivers/lightnvm/pblk-gc.c2
-rw-r--r--drivers/lightnvm/pblk-init.c9
-rw-r--r--drivers/lightnvm/pblk-map.c4
-rw-r--r--drivers/lightnvm/pblk-rl.c6
-rw-r--r--drivers/lightnvm/pblk-write.c4
-rw-r--r--drivers/lightnvm/pblk.h6
7 files changed, 37 insertions, 22 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,
29 pr_debug("pblk: erase failed: line:%d, pos:%d\n", line->id, pos); 29 pr_debug("pblk: erase failed: line:%d, pos:%d\n", line->id, pos);
30 atomic_long_inc(&pblk->erase_failed); 30 atomic_long_inc(&pblk->erase_failed);
31 31
32 atomic_dec(&line->blk_in_line);
32 if (test_and_set_bit(pos, line->blk_bitmap)) 33 if (test_and_set_bit(pos, line->blk_bitmap))
33 pr_err("pblk: attempted to erase bb: line:%d, pos:%d\n", 34 pr_err("pblk: attempted to erase bb: line:%d, pos:%d\n",
34 line->id, pos); 35 line->id, pos);
@@ -832,21 +833,28 @@ int pblk_line_erase(struct pblk *pblk, struct pblk_line *line)
832 struct ppa_addr ppa; 833 struct ppa_addr ppa;
833 int bit = -1; 834 int bit = -1;
834 835
835 /* Erase one block at the time and only erase good blocks */ 836 /* Erase only good blocks, one at a time */
836 while ((bit = find_next_zero_bit(line->erase_bitmap, lm->blk_per_line, 837 do {
837 bit + 1)) < lm->blk_per_line) { 838 spin_lock(&line->lock);
839 bit = find_next_zero_bit(line->erase_bitmap, lm->blk_per_line,
840 bit + 1);
841 if (bit >= lm->blk_per_line) {
842 spin_unlock(&line->lock);
843 break;
844 }
845
838 ppa = pblk->luns[bit].bppa; /* set ch and lun */ 846 ppa = pblk->luns[bit].bppa; /* set ch and lun */
839 ppa.g.blk = line->id; 847 ppa.g.blk = line->id;
840 848
841 /* If the erase fails, the block is bad and should be marked */ 849 atomic_dec(&line->left_eblks);
842 line->left_eblks--;
843 WARN_ON(test_and_set_bit(bit, line->erase_bitmap)); 850 WARN_ON(test_and_set_bit(bit, line->erase_bitmap));
851 spin_unlock(&line->lock);
844 852
845 if (pblk_blk_erase_sync(pblk, ppa)) { 853 if (pblk_blk_erase_sync(pblk, ppa)) {
846 pr_err("pblk: failed to erase line %d\n", line->id); 854 pr_err("pblk: failed to erase line %d\n", line->id);
847 return -ENOMEM; 855 return -ENOMEM;
848 } 856 }
849 } 857 } while (1);
850 858
851 return 0; 859 return 0;
852} 860}
@@ -1007,6 +1015,7 @@ retry_smeta:
1007static int pblk_line_prepare(struct pblk *pblk, struct pblk_line *line) 1015static int pblk_line_prepare(struct pblk *pblk, struct pblk_line *line)
1008{ 1016{
1009 struct pblk_line_meta *lm = &pblk->lm; 1017 struct pblk_line_meta *lm = &pblk->lm;
1018 int blk_in_line = atomic_read(&line->blk_in_line);
1010 1019
1011 line->map_bitmap = mempool_alloc(pblk->line_meta_pool, GFP_ATOMIC); 1020 line->map_bitmap = mempool_alloc(pblk->line_meta_pool, GFP_ATOMIC);
1012 if (!line->map_bitmap) 1021 if (!line->map_bitmap)
@@ -1030,12 +1039,13 @@ static int pblk_line_prepare(struct pblk *pblk, struct pblk_line *line)
1030 return -EINTR; 1039 return -EINTR;
1031 } 1040 }
1032 line->state = PBLK_LINESTATE_OPEN; 1041 line->state = PBLK_LINESTATE_OPEN;
1042
1043 atomic_set(&line->left_eblks, blk_in_line);
1044 atomic_set(&line->left_seblks, blk_in_line);
1033 spin_unlock(&line->lock); 1045 spin_unlock(&line->lock);
1034 1046
1035 /* Bad blocks do not need to be erased */ 1047 /* Bad blocks do not need to be erased */
1036 bitmap_copy(line->erase_bitmap, line->blk_bitmap, lm->blk_per_line); 1048 bitmap_copy(line->erase_bitmap, line->blk_bitmap, lm->blk_per_line);
1037 line->left_eblks = line->blk_in_line;
1038 atomic_set(&line->left_seblks, line->left_eblks);
1039 1049
1040 kref_init(&line->ref); 1050 kref_init(&line->ref);
1041 1051
@@ -1231,7 +1241,7 @@ retry_line:
1231 left_seblks = atomic_read(&new->left_seblks); 1241 left_seblks = atomic_read(&new->left_seblks);
1232 if (left_seblks) { 1242 if (left_seblks) {
1233 /* If line is not fully erased, erase it */ 1243 /* If line is not fully erased, erase it */
1234 if (new->left_eblks) { 1244 if (atomic_read(&new->left_eblks)) {
1235 if (pblk_line_erase(pblk, new)) 1245 if (pblk_line_erase(pblk, new))
1236 return NULL; 1246 return NULL;
1237 } else { 1247 } else {
diff --git a/drivers/lightnvm/pblk-gc.c b/drivers/lightnvm/pblk-gc.c
index f173fd4ea947..eaf479c6b63c 100644
--- a/drivers/lightnvm/pblk-gc.c
+++ b/drivers/lightnvm/pblk-gc.c
@@ -332,7 +332,7 @@ next_gc_group:
332 } 332 }
333 333
334 line = list_first_entry(group_list, struct pblk_line, list); 334 line = list_first_entry(group_list, struct pblk_line, list);
335 nr_blocks_free += line->blk_in_line; 335 nr_blocks_free += atomic_read(&line->blk_in_line);
336 336
337 spin_lock(&line->lock); 337 spin_lock(&line->lock);
338 WARN_ON(line->state != PBLK_LINESTATE_CLOSED); 338 WARN_ON(line->state != PBLK_LINESTATE_CLOSED);
diff --git a/drivers/lightnvm/pblk-init.c b/drivers/lightnvm/pblk-init.c
index 3996e4b8fb0e..15b2787c3ddc 100644
--- a/drivers/lightnvm/pblk-init.c
+++ b/drivers/lightnvm/pblk-init.c
@@ -678,6 +678,8 @@ add_emeta_page:
678 678
679 nr_free_blks = 0; 679 nr_free_blks = 0;
680 for (i = 0; i < l_mg->nr_lines; i++) { 680 for (i = 0; i < l_mg->nr_lines; i++) {
681 int blk_in_line;
682
681 line = &pblk->lines[i]; 683 line = &pblk->lines[i];
682 684
683 line->pblk = pblk; 685 line->pblk = pblk;
@@ -693,14 +695,15 @@ add_emeta_page:
693 goto fail_free_lines; 695 goto fail_free_lines;
694 } 696 }
695 697
696 line->blk_in_line = lm->blk_per_line - nr_bad_blks; 698 blk_in_line = lm->blk_per_line - nr_bad_blks;
697 if (line->blk_in_line < lm->min_blk_line) { 699 if (blk_in_line < lm->min_blk_line) {
698 line->state = PBLK_LINESTATE_BAD; 700 line->state = PBLK_LINESTATE_BAD;
699 list_add_tail(&line->list, &l_mg->bad_list); 701 list_add_tail(&line->list, &l_mg->bad_list);
700 continue; 702 continue;
701 } 703 }
702 704
703 nr_free_blks += line->blk_in_line; 705 nr_free_blks += blk_in_line;
706 atomic_set(&line->blk_in_line, blk_in_line);
704 707
705 l_mg->nr_free_lines++; 708 l_mg->nr_free_lines++;
706 list_add_tail(&line->list, &l_mg->free_list); 709 list_add_tail(&line->list, &l_mg->free_list);
diff --git a/drivers/lightnvm/pblk-map.c b/drivers/lightnvm/pblk-map.c
index 3f8bab4c4d5c..17c16955284d 100644
--- a/drivers/lightnvm/pblk-map.c
+++ b/drivers/lightnvm/pblk-map.c
@@ -110,7 +110,7 @@ void pblk_map_erase_rq(struct pblk *pblk, struct nvm_rq *rqd,
110 continue; 110 continue;
111 111
112 set_bit(erase_lun, e_line->erase_bitmap); 112 set_bit(erase_lun, e_line->erase_bitmap);
113 e_line->left_eblks--; 113 atomic_dec(&e_line->left_eblks);
114 *erase_ppa = rqd->ppa_list[i]; 114 *erase_ppa = rqd->ppa_list[i];
115 erase_ppa->g.blk = e_line->id; 115 erase_ppa->g.blk = e_line->id;
116 116
@@ -129,7 +129,7 @@ void pblk_map_erase_rq(struct pblk *pblk, struct nvm_rq *rqd,
129 return; 129 return;
130 130
131 set_bit(i, e_line->erase_bitmap); 131 set_bit(i, e_line->erase_bitmap);
132 e_line->left_eblks--; 132 atomic_dec(&e_line->left_eblks);
133 *erase_ppa = pblk->luns[i].bppa; /* set ch and lun */ 133 *erase_ppa = pblk->luns[i].bppa; /* set ch and lun */
134 erase_ppa->g.blk = e_line->id; 134 erase_ppa->g.blk = e_line->id;
135 } 135 }
diff --git a/drivers/lightnvm/pblk-rl.c b/drivers/lightnvm/pblk-rl.c
index 4042162ec9bc..ab7cbb144f3f 100644
--- a/drivers/lightnvm/pblk-rl.c
+++ b/drivers/lightnvm/pblk-rl.c
@@ -107,9 +107,10 @@ void pblk_rl_set_gc_rsc(struct pblk_rl *rl, int rsv)
107void pblk_rl_free_lines_inc(struct pblk_rl *rl, struct pblk_line *line) 107void pblk_rl_free_lines_inc(struct pblk_rl *rl, struct pblk_line *line)
108{ 108{
109 struct pblk *pblk = container_of(rl, struct pblk, rl); 109 struct pblk *pblk = container_of(rl, struct pblk, rl);
110 int blk_in_line = atomic_read(&line->blk_in_line);
110 int ret; 111 int ret;
111 112
112 atomic_add(line->blk_in_line, &rl->free_blocks); 113 atomic_add(blk_in_line, &rl->free_blocks);
113 /* Rates will not change that often - no need to lock update */ 114 /* Rates will not change that often - no need to lock update */
114 ret = pblk_rl_update_rates(rl, rl->rb_budget); 115 ret = pblk_rl_update_rates(rl, rl->rb_budget);
115 116
@@ -122,9 +123,10 @@ void pblk_rl_free_lines_inc(struct pblk_rl *rl, struct pblk_line *line)
122void pblk_rl_free_lines_dec(struct pblk_rl *rl, struct pblk_line *line) 123void pblk_rl_free_lines_dec(struct pblk_rl *rl, struct pblk_line *line)
123{ 124{
124 struct pblk *pblk = container_of(rl, struct pblk, rl); 125 struct pblk *pblk = container_of(rl, struct pblk, rl);
126 int blk_in_line = atomic_read(&line->blk_in_line);
125 int ret; 127 int ret;
126 128
127 atomic_sub(line->blk_in_line, &rl->free_blocks); 129 atomic_sub(blk_in_line, &rl->free_blocks);
128 130
129 /* Rates will not change that often - no need to lock update */ 131 /* Rates will not change that often - no need to lock update */
130 ret = pblk_rl_update_rates(rl, rl->rb_budget); 132 ret = pblk_rl_update_rates(rl, rl->rb_budget);
diff --git a/drivers/lightnvm/pblk-write.c b/drivers/lightnvm/pblk-write.c
index a896190c82f0..aef6fd7c4a0c 100644
--- a/drivers/lightnvm/pblk-write.c
+++ b/drivers/lightnvm/pblk-write.c
@@ -244,7 +244,7 @@ static int pblk_setup_w_rq(struct pblk *pblk, struct nvm_rq *rqd,
244 } 244 }
245 245
246 ppa_set_empty(&erase_ppa); 246 ppa_set_empty(&erase_ppa);
247 if (likely(!e_line || !e_line->left_eblks)) 247 if (likely(!e_line || !atomic_read(&e_line->left_eblks)))
248 pblk_map_rq(pblk, rqd, c_ctx->sentry, lun_bitmap, valid, 0); 248 pblk_map_rq(pblk, rqd, c_ctx->sentry, lun_bitmap, valid, 0);
249 else 249 else
250 pblk_map_erase_rq(pblk, rqd, c_ctx->sentry, lun_bitmap, 250 pblk_map_erase_rq(pblk, rqd, c_ctx->sentry, lun_bitmap,
@@ -257,7 +257,7 @@ out:
257 struct nvm_geo *geo = &dev->geo; 257 struct nvm_geo *geo = &dev->geo;
258 int bit; 258 int bit;
259 259
260 e_line->left_eblks++; 260 atomic_inc(&e_line->left_eblks);
261 bit = erase_ppa.g.lun * geo->nr_chnls + erase_ppa.g.ch; 261 bit = erase_ppa.g.lun * geo->nr_chnls + erase_ppa.g.ch;
262 WARN_ON(!test_and_clear_bit(bit, e_line->erase_bitmap)); 262 WARN_ON(!test_and_clear_bit(bit, e_line->erase_bitmap));
263 up(&pblk->erase_sem); 263 up(&pblk->erase_sem);
diff --git a/drivers/lightnvm/pblk.h b/drivers/lightnvm/pblk.h
index 11ed7d83f572..99f3186b5288 100644
--- a/drivers/lightnvm/pblk.h
+++ b/drivers/lightnvm/pblk.h
@@ -363,14 +363,14 @@ struct pblk_line {
363 363
364 unsigned int sec_in_line; /* Number of usable secs in line */ 364 unsigned int sec_in_line; /* Number of usable secs in line */
365 365
366 unsigned int blk_in_line; /* Number of good blocks in line */ 366 atomic_t blk_in_line; /* Number of good blocks in line */
367 unsigned long *blk_bitmap; /* Bitmap for valid/invalid blocks */ 367 unsigned long *blk_bitmap; /* Bitmap for valid/invalid blocks */
368 unsigned long *erase_bitmap; /* Bitmap for erased blocks */ 368 unsigned long *erase_bitmap; /* Bitmap for erased blocks */
369 369
370 unsigned long *map_bitmap; /* Bitmap for mapped sectors in line */ 370 unsigned long *map_bitmap; /* Bitmap for mapped sectors in line */
371 unsigned long *invalid_bitmap; /* Bitmap for invalid sectors in line */ 371 unsigned long *invalid_bitmap; /* Bitmap for invalid sectors in line */
372 372
373 int left_eblks; /* Blocks left for erasing */ 373 atomic_t left_eblks; /* Blocks left for erasing */
374 atomic_t left_seblks; /* Blocks left for sync erasing */ 374 atomic_t left_seblks; /* Blocks left for sync erasing */
375 375
376 int left_msecs; /* Sectors left for mapping */ 376 int left_msecs; /* Sectors left for mapping */
@@ -383,7 +383,7 @@ struct pblk_line {
383 spinlock_t lock; /* Necessary for invalid_bitmap only */ 383 spinlock_t lock; /* Necessary for invalid_bitmap only */
384}; 384};
385 385
386#define PBLK_DATA_LINES 2 386#define PBLK_DATA_LINES 4
387 387
388enum{ 388enum{
389 PBLK_KMALLOC_META = 1, 389 PBLK_KMALLOC_META = 1,