]> git.neil.brown.name Git - linux.git/commitdiff
lightnvm: pblk: recheck for bad lines at runtime
authorJavier González <javier@javigon.com>
Fri, 1 Jun 2018 13:04:16 +0000 (15:04 +0200)
committerJens Axboe <axboe@kernel.dk>
Fri, 1 Jun 2018 13:43:53 +0000 (07:43 -0600)
Bad blocks can grow at runtime. Check that the number of valid blocks in
a line are within the sanity threshold before allocating the line for
new writes.

Signed-off-by: Javier González <javier@cnexlabs.com>
Signed-off-by: Matias Bjørling <mb@lightnvm.io>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
drivers/lightnvm/pblk-core.c
drivers/lightnvm/pblk-init.c

index 934341b10493cefd05a09739e9f402b67de63b0e..f34ce522348e77b31b5b6ccdd7973c3886f36ea0 100644 (file)
@@ -1174,7 +1174,8 @@ static int pblk_prepare_new_line(struct pblk *pblk, struct pblk_line *line)
 static int pblk_line_prepare(struct pblk *pblk, struct pblk_line *line)
 {
        struct pblk_line_meta *lm = &pblk->lm;
-       int blk_to_erase;
+       int blk_in_line = atomic_read(&line->blk_in_line);
+       int blk_to_erase, ret;
 
        line->map_bitmap = kzalloc(lm->sec_bitmap_len, GFP_ATOMIC);
        if (!line->map_bitmap)
@@ -1183,8 +1184,8 @@ static int pblk_line_prepare(struct pblk *pblk, struct pblk_line *line)
        /* will be initialized using bb info from map_bitmap */
        line->invalid_bitmap = kmalloc(lm->sec_bitmap_len, GFP_ATOMIC);
        if (!line->invalid_bitmap) {
-               kfree(line->map_bitmap);
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto fail_free_map_bitmap;
        }
 
        /* Bad blocks do not need to be erased */
@@ -1199,16 +1200,19 @@ static int pblk_line_prepare(struct pblk *pblk, struct pblk_line *line)
                blk_to_erase = pblk_prepare_new_line(pblk, line);
                line->state = PBLK_LINESTATE_FREE;
        } else {
-               blk_to_erase = atomic_read(&line->blk_in_line);
+               blk_to_erase = blk_in_line;
+       }
+
+       if (blk_in_line < lm->min_blk_line) {
+               ret = -EAGAIN;
+               goto fail_free_invalid_bitmap;
        }
 
        if (line->state != PBLK_LINESTATE_FREE) {
-               kfree(line->map_bitmap);
-               kfree(line->invalid_bitmap);
-               spin_unlock(&line->lock);
                WARN(1, "pblk: corrupted line %d, state %d\n",
                                                        line->id, line->state);
-               return -EAGAIN;
+               ret = -EINTR;
+               goto fail_free_invalid_bitmap;
        }
 
        line->state = PBLK_LINESTATE_OPEN;
@@ -1222,6 +1226,16 @@ static int pblk_line_prepare(struct pblk *pblk, struct pblk_line *line)
        kref_init(&line->ref);
 
        return 0;
+
+fail_free_invalid_bitmap:
+       spin_unlock(&line->lock);
+       kfree(line->invalid_bitmap);
+       line->invalid_bitmap = NULL;
+fail_free_map_bitmap:
+       kfree(line->map_bitmap);
+       line->map_bitmap = NULL;
+
+       return ret;
 }
 
 int pblk_line_recov_alloc(struct pblk *pblk, struct pblk_line *line)
@@ -1292,10 +1306,14 @@ struct pblk_line *pblk_line_get(struct pblk *pblk)
 
        ret = pblk_line_prepare(pblk, line);
        if (ret) {
-               if (ret == -EAGAIN) {
+               switch (ret) {
+               case -EAGAIN:
+                       list_add(&line->list, &l_mg->bad_list);
+                       goto retry;
+               case -EINTR:
                        list_add(&line->list, &l_mg->corrupt_list);
                        goto retry;
-               } else {
+               default:
                        pr_err("pblk: failed to prepare line %d\n", line->id);
                        list_add(&line->list, &l_mg->free_list);
                        l_mg->nr_free_lines++;
index 44f9ec8d4c2a884f9b98d9c68011772fede04c1d..fe501e6d45fc439ba318891aab1b6fd37163638f 100644 (file)
@@ -127,10 +127,8 @@ static int pblk_l2p_recover(struct pblk *pblk, bool factory_init)
        if (!line) {
                /* Configure next line for user data */
                line = pblk_line_get_first_data(pblk);
-               if (!line) {
-                       pr_err("pblk: line list corrupted\n");
+               if (!line)
                        return -EFAULT;
-               }
        }
 
        return 0;
@@ -141,6 +139,7 @@ static int pblk_l2p_init(struct pblk *pblk, bool factory_init)
        sector_t i;
        struct ppa_addr ppa;
        size_t map_size;
+       int ret = 0;
 
        map_size = pblk_trans_map_size(pblk);
        pblk->trans_map = vmalloc(map_size);
@@ -152,7 +151,11 @@ static int pblk_l2p_init(struct pblk *pblk, bool factory_init)
        for (i = 0; i < pblk->rl.nr_secs; i++)
                pblk_trans_map_set(pblk, i, ppa);
 
-       return pblk_l2p_recover(pblk, factory_init);
+       ret = pblk_l2p_recover(pblk, factory_init);
+       if (ret)
+               vfree(pblk->trans_map);
+
+       return ret;
 }
 
 static void pblk_rwb_free(struct pblk *pblk)