Skip to content

Commit aff3fb1

Browse files
MatiasBjorlingaxboe
authored andcommitted
lightnvm: move bad block and chunk state logic to core
pblk implements two data paths for recovery line state. One for 1.2 and another for 2.0, instead of having pblk implement these, combine them in the core to reduce complexity and make available to other targets. The new interface will adhere to the 2.0 chunk definition, including managing open chunks with an active write pointer. To provide this interface, a 1.2 device recovers the state of the chunks by manually detecting if a chunk is either free/open/close/offline, and if open, scanning the flash pages sequentially to find the next writeable page. This process takes on average ~10 seconds on a device with 64 dies, 1024 blocks and 60us read access time. The process can be parallelized but is left out for maintenance simplicity, as the 1.2 specification is deprecated. For 2.0 devices, the logic is maintained internally in the drive and retrieved through the 2.0 interface. Signed-off-by: Matias Bjørling <[email protected]> Signed-off-by: Jens Axboe <[email protected]>
1 parent d8adaa3 commit aff3fb1

File tree

6 files changed

+265
-187
lines changed

6 files changed

+265
-187
lines changed

drivers/lightnvm/core.c

Lines changed: 251 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -717,46 +717,6 @@ static void nvm_free_rqd_ppalist(struct nvm_tgt_dev *tgt_dev,
717717
nvm_dev_dma_free(tgt_dev->parent, rqd->ppa_list, rqd->dma_ppa_list);
718718
}
719719

720-
int nvm_get_chunk_meta(struct nvm_tgt_dev *tgt_dev, struct nvm_chk_meta *meta,
721-
struct ppa_addr ppa, int nchks)
722-
{
723-
struct nvm_dev *dev = tgt_dev->parent;
724-
725-
nvm_ppa_tgt_to_dev(tgt_dev, &ppa, 1);
726-
727-
return dev->ops->get_chk_meta(tgt_dev->parent, meta,
728-
(sector_t)ppa.ppa, nchks);
729-
}
730-
EXPORT_SYMBOL(nvm_get_chunk_meta);
731-
732-
int nvm_set_tgt_bb_tbl(struct nvm_tgt_dev *tgt_dev, struct ppa_addr *ppas,
733-
int nr_ppas, int type)
734-
{
735-
struct nvm_dev *dev = tgt_dev->parent;
736-
struct nvm_rq rqd;
737-
int ret;
738-
739-
if (nr_ppas > NVM_MAX_VLBA) {
740-
pr_err("nvm: unable to update all blocks atomically\n");
741-
return -EINVAL;
742-
}
743-
744-
memset(&rqd, 0, sizeof(struct nvm_rq));
745-
746-
nvm_set_rqd_ppalist(tgt_dev, &rqd, ppas, nr_ppas);
747-
nvm_rq_tgt_to_dev(tgt_dev, &rqd);
748-
749-
ret = dev->ops->set_bb_tbl(dev, &rqd.ppa_addr, rqd.nr_ppas, type);
750-
nvm_free_rqd_ppalist(tgt_dev, &rqd);
751-
if (ret) {
752-
pr_err("nvm: failed bb mark\n");
753-
return -EINVAL;
754-
}
755-
756-
return 0;
757-
}
758-
EXPORT_SYMBOL(nvm_set_tgt_bb_tbl);
759-
760720
static int nvm_set_flags(struct nvm_geo *geo, struct nvm_rq *rqd)
761721
{
762722
int flags = 0;
@@ -830,27 +790,159 @@ void nvm_end_io(struct nvm_rq *rqd)
830790
}
831791
EXPORT_SYMBOL(nvm_end_io);
832792

793+
static int nvm_submit_io_sync_raw(struct nvm_dev *dev, struct nvm_rq *rqd)
794+
{
795+
if (!dev->ops->submit_io_sync)
796+
return -ENODEV;
797+
798+
rqd->flags = nvm_set_flags(&dev->geo, rqd);
799+
800+
return dev->ops->submit_io_sync(dev, rqd);
801+
}
802+
803+
static int nvm_bb_chunk_sense(struct nvm_dev *dev, struct ppa_addr ppa)
804+
{
805+
struct nvm_rq rqd = { NULL };
806+
struct bio bio;
807+
struct bio_vec bio_vec;
808+
struct page *page;
809+
int ret;
810+
811+
page = alloc_page(GFP_KERNEL);
812+
if (!page)
813+
return -ENOMEM;
814+
815+
bio_init(&bio, &bio_vec, 1);
816+
bio_add_page(&bio, page, PAGE_SIZE, 0);
817+
bio_set_op_attrs(&bio, REQ_OP_READ, 0);
818+
819+
rqd.bio = &bio;
820+
rqd.opcode = NVM_OP_PREAD;
821+
rqd.is_seq = 1;
822+
rqd.nr_ppas = 1;
823+
rqd.ppa_addr = generic_to_dev_addr(dev, ppa);
824+
825+
ret = nvm_submit_io_sync_raw(dev, &rqd);
826+
if (ret)
827+
return ret;
828+
829+
__free_page(page);
830+
831+
return rqd.error;
832+
}
833+
833834
/*
834-
* folds a bad block list from its plane representation to its virtual
835-
* block representation. The fold is done in place and reduced size is
836-
* returned.
837-
*
838-
* If any of the planes status are bad or grown bad block, the virtual block
839-
* is marked bad. If not bad, the first plane state acts as the block state.
835+
* Scans a 1.2 chunk first and last page to determine if its state.
836+
* If the chunk is found to be open, also scan it to update the write
837+
* pointer.
840838
*/
841-
int nvm_bb_tbl_fold(struct nvm_dev *dev, u8 *blks, int nr_blks)
839+
static int nvm_bb_chunk_scan(struct nvm_dev *dev, struct ppa_addr ppa,
840+
struct nvm_chk_meta *meta)
842841
{
843842
struct nvm_geo *geo = &dev->geo;
844-
int blk, offset, pl, blktype;
843+
int ret, pg, pl;
845844

846-
if (nr_blks != geo->num_chk * geo->pln_mode)
847-
return -EINVAL;
845+
/* sense first page */
846+
ret = nvm_bb_chunk_sense(dev, ppa);
847+
if (ret < 0) /* io error */
848+
return ret;
849+
else if (ret == 0) /* valid data */
850+
meta->state = NVM_CHK_ST_OPEN;
851+
else if (ret > 0) {
852+
/*
853+
* If empty page, the chunk is free, else it is an
854+
* actual io error. In that case, mark it offline.
855+
*/
856+
switch (ret) {
857+
case NVM_RSP_ERR_EMPTYPAGE:
858+
meta->state = NVM_CHK_ST_FREE;
859+
return 0;
860+
case NVM_RSP_ERR_FAILCRC:
861+
case NVM_RSP_ERR_FAILECC:
862+
case NVM_RSP_WARN_HIGHECC:
863+
meta->state = NVM_CHK_ST_OPEN;
864+
goto scan;
865+
default:
866+
return -ret; /* other io error */
867+
}
868+
}
869+
870+
/* sense last page */
871+
ppa.g.pg = geo->num_pg - 1;
872+
ppa.g.pl = geo->num_pln - 1;
873+
874+
ret = nvm_bb_chunk_sense(dev, ppa);
875+
if (ret < 0) /* io error */
876+
return ret;
877+
else if (ret == 0) { /* Chunk fully written */
878+
meta->state = NVM_CHK_ST_CLOSED;
879+
meta->wp = geo->clba;
880+
return 0;
881+
} else if (ret > 0) {
882+
switch (ret) {
883+
case NVM_RSP_ERR_EMPTYPAGE:
884+
case NVM_RSP_ERR_FAILCRC:
885+
case NVM_RSP_ERR_FAILECC:
886+
case NVM_RSP_WARN_HIGHECC:
887+
meta->state = NVM_CHK_ST_OPEN;
888+
break;
889+
default:
890+
return -ret; /* other io error */
891+
}
892+
}
893+
894+
scan:
895+
/*
896+
* chunk is open, we scan sequentially to update the write pointer.
897+
* We make the assumption that targets write data across all planes
898+
* before moving to the next page.
899+
*/
900+
for (pg = 0; pg < geo->num_pg; pg++) {
901+
for (pl = 0; pl < geo->num_pln; pl++) {
902+
ppa.g.pg = pg;
903+
ppa.g.pl = pl;
904+
905+
ret = nvm_bb_chunk_sense(dev, ppa);
906+
if (ret < 0) /* io error */
907+
return ret;
908+
else if (ret == 0) {
909+
meta->wp += geo->ws_min;
910+
} else if (ret > 0) {
911+
switch (ret) {
912+
case NVM_RSP_ERR_EMPTYPAGE:
913+
return 0;
914+
case NVM_RSP_ERR_FAILCRC:
915+
case NVM_RSP_ERR_FAILECC:
916+
case NVM_RSP_WARN_HIGHECC:
917+
meta->wp += geo->ws_min;
918+
break;
919+
default:
920+
return -ret; /* other io error */
921+
}
922+
}
923+
}
924+
}
925+
926+
return 0;
927+
}
928+
929+
/*
930+
* folds a bad block list from its plane representation to its
931+
* chunk representation.
932+
*
933+
* If any of the planes status are bad or grown bad, the chunk is marked
934+
* offline. If not bad, the first plane state acts as the chunk state.
935+
*/
936+
static int nvm_bb_to_chunk(struct nvm_dev *dev, struct ppa_addr ppa,
937+
u8 *blks, int nr_blks, struct nvm_chk_meta *meta)
938+
{
939+
struct nvm_geo *geo = &dev->geo;
940+
int ret, blk, pl, offset, blktype;
848941

849942
for (blk = 0; blk < geo->num_chk; blk++) {
850943
offset = blk * geo->pln_mode;
851944
blktype = blks[offset];
852945

853-
/* Bad blocks on any planes take precedence over other types */
854946
for (pl = 0; pl < geo->pln_mode; pl++) {
855947
if (blks[offset + pl] &
856948
(NVM_BLK_T_BAD|NVM_BLK_T_GRWN_BAD)) {
@@ -859,23 +951,124 @@ int nvm_bb_tbl_fold(struct nvm_dev *dev, u8 *blks, int nr_blks)
859951
}
860952
}
861953

862-
blks[blk] = blktype;
954+
ppa.g.blk = blk;
955+
956+
meta->wp = 0;
957+
meta->type = NVM_CHK_TP_W_SEQ;
958+
meta->wi = 0;
959+
meta->slba = generic_to_dev_addr(dev, ppa).ppa;
960+
meta->cnlb = dev->geo.clba;
961+
962+
if (blktype == NVM_BLK_T_FREE) {
963+
ret = nvm_bb_chunk_scan(dev, ppa, meta);
964+
if (ret)
965+
return ret;
966+
} else {
967+
meta->state = NVM_CHK_ST_OFFLINE;
968+
}
969+
970+
meta++;
863971
}
864972

865-
return geo->num_chk;
973+
return 0;
974+
}
975+
976+
static int nvm_get_bb_meta(struct nvm_dev *dev, sector_t slba,
977+
int nchks, struct nvm_chk_meta *meta)
978+
{
979+
struct nvm_geo *geo = &dev->geo;
980+
struct ppa_addr ppa;
981+
u8 *blks;
982+
int ch, lun, nr_blks;
983+
int ret;
984+
985+
ppa.ppa = slba;
986+
ppa = dev_to_generic_addr(dev, ppa);
987+
988+
if (ppa.g.blk != 0)
989+
return -EINVAL;
990+
991+
if ((nchks % geo->num_chk) != 0)
992+
return -EINVAL;
993+
994+
nr_blks = geo->num_chk * geo->pln_mode;
995+
996+
blks = kmalloc(nr_blks, GFP_KERNEL);
997+
if (!blks)
998+
return -ENOMEM;
999+
1000+
for (ch = ppa.g.ch; ch < geo->num_ch; ch++) {
1001+
for (lun = ppa.g.lun; lun < geo->num_lun; lun++) {
1002+
struct ppa_addr ppa_gen, ppa_dev;
1003+
1004+
if (!nchks)
1005+
goto done;
1006+
1007+
ppa_gen.ppa = 0;
1008+
ppa_gen.g.ch = ch;
1009+
ppa_gen.g.lun = lun;
1010+
ppa_dev = generic_to_dev_addr(dev, ppa_gen);
1011+
1012+
ret = dev->ops->get_bb_tbl(dev, ppa_dev, blks);
1013+
if (ret)
1014+
goto done;
1015+
1016+
ret = nvm_bb_to_chunk(dev, ppa_gen, blks, nr_blks,
1017+
meta);
1018+
if (ret)
1019+
goto done;
1020+
1021+
meta += geo->num_chk;
1022+
nchks -= geo->num_chk;
1023+
}
1024+
}
1025+
done:
1026+
kfree(blks);
1027+
return ret;
8661028
}
867-
EXPORT_SYMBOL(nvm_bb_tbl_fold);
8681029

869-
int nvm_get_tgt_bb_tbl(struct nvm_tgt_dev *tgt_dev, struct ppa_addr ppa,
870-
u8 *blks)
1030+
int nvm_get_chunk_meta(struct nvm_tgt_dev *tgt_dev, struct ppa_addr ppa,
1031+
int nchks, struct nvm_chk_meta *meta)
8711032
{
8721033
struct nvm_dev *dev = tgt_dev->parent;
8731034

8741035
nvm_ppa_tgt_to_dev(tgt_dev, &ppa, 1);
8751036

876-
return dev->ops->get_bb_tbl(dev, ppa, blks);
1037+
if (dev->geo.version == NVM_OCSSD_SPEC_12)
1038+
return nvm_get_bb_meta(dev, (sector_t)ppa.ppa, nchks, meta);
1039+
1040+
return dev->ops->get_chk_meta(dev, (sector_t)ppa.ppa, nchks, meta);
1041+
}
1042+
EXPORT_SYMBOL_GPL(nvm_get_chunk_meta);
1043+
1044+
int nvm_set_chunk_meta(struct nvm_tgt_dev *tgt_dev, struct ppa_addr *ppas,
1045+
int nr_ppas, int type)
1046+
{
1047+
struct nvm_dev *dev = tgt_dev->parent;
1048+
struct nvm_rq rqd;
1049+
int ret;
1050+
1051+
if (dev->geo.version == NVM_OCSSD_SPEC_20)
1052+
return 0;
1053+
1054+
if (nr_ppas > NVM_MAX_VLBA) {
1055+
pr_err("nvm: unable to update all blocks atomically\n");
1056+
return -EINVAL;
1057+
}
1058+
1059+
memset(&rqd, 0, sizeof(struct nvm_rq));
1060+
1061+
nvm_set_rqd_ppalist(tgt_dev, &rqd, ppas, nr_ppas);
1062+
nvm_rq_tgt_to_dev(tgt_dev, &rqd);
1063+
1064+
ret = dev->ops->set_bb_tbl(dev, &rqd.ppa_addr, rqd.nr_ppas, type);
1065+
nvm_free_rqd_ppalist(tgt_dev, &rqd);
1066+
if (ret)
1067+
return -EINVAL;
1068+
1069+
return 0;
8771070
}
878-
EXPORT_SYMBOL(nvm_get_tgt_bb_tbl);
1071+
EXPORT_SYMBOL_GPL(nvm_set_chunk_meta);
8791072

8801073
static int nvm_core_init(struct nvm_dev *dev)
8811074
{

drivers/lightnvm/pblk-core.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ static void pblk_line_mark_bb(struct work_struct *work)
2727
struct ppa_addr *ppa = line_ws->priv;
2828
int ret;
2929

30-
ret = nvm_set_tgt_bb_tbl(dev, ppa, 1, NVM_BLK_T_GRWN_BAD);
30+
ret = nvm_set_chunk_meta(dev, ppa, 1, NVM_BLK_T_GRWN_BAD);
3131
if (ret) {
3232
struct pblk_line *line;
3333
int pos;
@@ -110,7 +110,7 @@ static void pblk_end_io_erase(struct nvm_rq *rqd)
110110
*
111111
* The caller is responsible for freeing the returned structure
112112
*/
113-
struct nvm_chk_meta *pblk_chunk_get_info(struct pblk *pblk)
113+
struct nvm_chk_meta *pblk_get_chunk_meta(struct pblk *pblk)
114114
{
115115
struct nvm_tgt_dev *dev = pblk->dev;
116116
struct nvm_geo *geo = &dev->geo;
@@ -126,7 +126,7 @@ struct nvm_chk_meta *pblk_chunk_get_info(struct pblk *pblk)
126126
if (!meta)
127127
return ERR_PTR(-ENOMEM);
128128

129-
ret = nvm_get_chunk_meta(dev, meta, ppa, geo->all_chunks);
129+
ret = nvm_get_chunk_meta(dev, ppa, geo->all_chunks, meta);
130130
if (ret) {
131131
kfree(meta);
132132
return ERR_PTR(-EIO);

0 commit comments

Comments
 (0)