diff options
Diffstat (limited to 'drivers/lightnvm/pblk-init.c')
-rw-r--r-- | drivers/lightnvm/pblk-init.c | 222 |
1 files changed, 158 insertions, 64 deletions
diff --git a/drivers/lightnvm/pblk-init.c b/drivers/lightnvm/pblk-init.c index 5b381700ef30..27b4974930b4 100644 --- a/drivers/lightnvm/pblk-init.c +++ b/drivers/lightnvm/pblk-init.c | |||
@@ -451,6 +451,7 @@ static void pblk_line_meta_free(struct pblk_line *line) | |||
451 | { | 451 | { |
452 | kfree(line->blk_bitmap); | 452 | kfree(line->blk_bitmap); |
453 | kfree(line->erase_bitmap); | 453 | kfree(line->erase_bitmap); |
454 | kfree(line->chks); | ||
454 | } | 455 | } |
455 | 456 | ||
456 | static void pblk_lines_free(struct pblk *pblk) | 457 | static void pblk_lines_free(struct pblk *pblk) |
@@ -495,55 +496,44 @@ static int pblk_bb_get_tbl(struct nvm_tgt_dev *dev, struct pblk_lun *rlun, | |||
495 | return 0; | 496 | return 0; |
496 | } | 497 | } |
497 | 498 | ||
498 | static void *pblk_bb_get_log(struct pblk *pblk) | 499 | static void *pblk_bb_get_meta(struct pblk *pblk) |
499 | { | 500 | { |
500 | struct nvm_tgt_dev *dev = pblk->dev; | 501 | struct nvm_tgt_dev *dev = pblk->dev; |
501 | struct nvm_geo *geo = &dev->geo; | 502 | struct nvm_geo *geo = &dev->geo; |
502 | u8 *log; | 503 | u8 *meta; |
503 | int i, nr_blks, blk_per_lun; | 504 | int i, nr_blks, blk_per_lun; |
504 | int ret; | 505 | int ret; |
505 | 506 | ||
506 | blk_per_lun = geo->num_chk * geo->pln_mode; | 507 | blk_per_lun = geo->num_chk * geo->pln_mode; |
507 | nr_blks = blk_per_lun * geo->all_luns; | 508 | nr_blks = blk_per_lun * geo->all_luns; |
508 | 509 | ||
509 | log = kmalloc(nr_blks, GFP_KERNEL); | 510 | meta = kmalloc(nr_blks, GFP_KERNEL); |
510 | if (!log) | 511 | if (!meta) |
511 | return ERR_PTR(-ENOMEM); | 512 | return ERR_PTR(-ENOMEM); |
512 | 513 | ||
513 | for (i = 0; i < geo->all_luns; i++) { | 514 | for (i = 0; i < geo->all_luns; i++) { |
514 | struct pblk_lun *rlun = &pblk->luns[i]; | 515 | struct pblk_lun *rlun = &pblk->luns[i]; |
515 | u8 *log_pos = log + i * blk_per_lun; | 516 | u8 *meta_pos = meta + i * blk_per_lun; |
516 | 517 | ||
517 | ret = pblk_bb_get_tbl(dev, rlun, log_pos, blk_per_lun); | 518 | ret = pblk_bb_get_tbl(dev, rlun, meta_pos, blk_per_lun); |
518 | if (ret) { | 519 | if (ret) { |
519 | kfree(log); | 520 | kfree(meta); |
520 | return ERR_PTR(-EIO); | 521 | return ERR_PTR(-EIO); |
521 | } | 522 | } |
522 | } | 523 | } |
523 | 524 | ||
524 | return log; | 525 | return meta; |
525 | } | 526 | } |
526 | 527 | ||
527 | static int pblk_bb_line(struct pblk *pblk, struct pblk_line *line, | 528 | static void *pblk_chunk_get_meta(struct pblk *pblk) |
528 | u8 *bb_log, int blk_per_line) | ||
529 | { | 529 | { |
530 | struct nvm_tgt_dev *dev = pblk->dev; | 530 | struct nvm_tgt_dev *dev = pblk->dev; |
531 | struct nvm_geo *geo = &dev->geo; | 531 | struct nvm_geo *geo = &dev->geo; |
532 | int i, bb_cnt = 0; | ||
533 | int blk_per_lun = geo->num_chk * geo->pln_mode; | ||
534 | 532 | ||
535 | for (i = 0; i < blk_per_line; i++) { | 533 | if (geo->version == NVM_OCSSD_SPEC_12) |
536 | struct pblk_lun *rlun = &pblk->luns[i]; | 534 | return pblk_bb_get_meta(pblk); |
537 | u8 *lun_bb_log = bb_log + i * blk_per_lun; | 535 | else |
538 | 536 | return pblk_chunk_get_info(pblk); | |
539 | if (lun_bb_log[line->id] == NVM_BLK_T_FREE) | ||
540 | continue; | ||
541 | |||
542 | set_bit(pblk_ppa_to_pos(geo, rlun->bppa), line->blk_bitmap); | ||
543 | bb_cnt++; | ||
544 | } | ||
545 | |||
546 | return bb_cnt; | ||
547 | } | 537 | } |
548 | 538 | ||
549 | static int pblk_luns_init(struct pblk *pblk) | 539 | static int pblk_luns_init(struct pblk *pblk) |
@@ -644,8 +634,131 @@ static void pblk_set_provision(struct pblk *pblk, long nr_free_blks) | |||
644 | atomic_set(&pblk->rl.free_user_blocks, nr_free_blks); | 634 | atomic_set(&pblk->rl.free_user_blocks, nr_free_blks); |
645 | } | 635 | } |
646 | 636 | ||
647 | static int pblk_setup_line_meta(struct pblk *pblk, struct pblk_line *line, | 637 | static int pblk_setup_line_meta_12(struct pblk *pblk, struct pblk_line *line, |
648 | void *chunk_log, long *nr_bad_blks) | 638 | void *chunk_meta) |
639 | { | ||
640 | struct nvm_tgt_dev *dev = pblk->dev; | ||
641 | struct nvm_geo *geo = &dev->geo; | ||
642 | struct pblk_line_meta *lm = &pblk->lm; | ||
643 | int i, chk_per_lun, nr_bad_chks = 0; | ||
644 | |||
645 | chk_per_lun = geo->num_chk * geo->pln_mode; | ||
646 | |||
647 | for (i = 0; i < lm->blk_per_line; i++) { | ||
648 | struct pblk_lun *rlun = &pblk->luns[i]; | ||
649 | struct nvm_chk_meta *chunk; | ||
650 | int pos = pblk_ppa_to_pos(geo, rlun->bppa); | ||
651 | u8 *lun_bb_meta = chunk_meta + pos * chk_per_lun; | ||
652 | |||
653 | chunk = &line->chks[pos]; | ||
654 | |||
655 | /* | ||
656 | * In 1.2 spec. chunk state is not persisted by the device. Thus | ||
657 | * some of the values are reset each time pblk is instantiated. | ||
658 | */ | ||
659 | if (lun_bb_meta[line->id] == NVM_BLK_T_FREE) | ||
660 | chunk->state = NVM_CHK_ST_FREE; | ||
661 | else | ||
662 | chunk->state = NVM_CHK_ST_OFFLINE; | ||
663 | |||
664 | chunk->type = NVM_CHK_TP_W_SEQ; | ||
665 | chunk->wi = 0; | ||
666 | chunk->slba = -1; | ||
667 | chunk->cnlb = geo->clba; | ||
668 | chunk->wp = 0; | ||
669 | |||
670 | if (!(chunk->state & NVM_CHK_ST_OFFLINE)) | ||
671 | continue; | ||
672 | |||
673 | set_bit(pos, line->blk_bitmap); | ||
674 | nr_bad_chks++; | ||
675 | } | ||
676 | |||
677 | return nr_bad_chks; | ||
678 | } | ||
679 | |||
680 | static int pblk_setup_line_meta_20(struct pblk *pblk, struct pblk_line *line, | ||
681 | struct nvm_chk_meta *meta) | ||
682 | { | ||
683 | struct nvm_tgt_dev *dev = pblk->dev; | ||
684 | struct nvm_geo *geo = &dev->geo; | ||
685 | struct pblk_line_meta *lm = &pblk->lm; | ||
686 | int i, nr_bad_chks = 0; | ||
687 | |||
688 | for (i = 0; i < lm->blk_per_line; i++) { | ||
689 | struct pblk_lun *rlun = &pblk->luns[i]; | ||
690 | struct nvm_chk_meta *chunk; | ||
691 | struct nvm_chk_meta *chunk_meta; | ||
692 | struct ppa_addr ppa; | ||
693 | int pos; | ||
694 | |||
695 | ppa = rlun->bppa; | ||
696 | pos = pblk_ppa_to_pos(geo, ppa); | ||
697 | chunk = &line->chks[pos]; | ||
698 | |||
699 | ppa.m.chk = line->id; | ||
700 | chunk_meta = pblk_chunk_get_off(pblk, meta, ppa); | ||
701 | |||
702 | chunk->state = chunk_meta->state; | ||
703 | chunk->type = chunk_meta->type; | ||
704 | chunk->wi = chunk_meta->wi; | ||
705 | chunk->slba = chunk_meta->slba; | ||
706 | chunk->cnlb = chunk_meta->cnlb; | ||
707 | chunk->wp = chunk_meta->wp; | ||
708 | |||
709 | if (!(chunk->state & NVM_CHK_ST_OFFLINE)) | ||
710 | continue; | ||
711 | |||
712 | if (chunk->type & NVM_CHK_TP_SZ_SPEC) { | ||
713 | WARN_ONCE(1, "pblk: custom-sized chunks unsupported\n"); | ||
714 | continue; | ||
715 | } | ||
716 | |||
717 | set_bit(pos, line->blk_bitmap); | ||
718 | nr_bad_chks++; | ||
719 | } | ||
720 | |||
721 | return nr_bad_chks; | ||
722 | } | ||
723 | |||
724 | static long pblk_setup_line_meta(struct pblk *pblk, struct pblk_line *line, | ||
725 | void *chunk_meta, int line_id) | ||
726 | { | ||
727 | struct nvm_tgt_dev *dev = pblk->dev; | ||
728 | struct nvm_geo *geo = &dev->geo; | ||
729 | struct pblk_line_mgmt *l_mg = &pblk->l_mg; | ||
730 | struct pblk_line_meta *lm = &pblk->lm; | ||
731 | long nr_bad_chks, chk_in_line; | ||
732 | |||
733 | line->pblk = pblk; | ||
734 | line->id = line_id; | ||
735 | line->type = PBLK_LINETYPE_FREE; | ||
736 | line->state = PBLK_LINESTATE_NEW; | ||
737 | line->gc_group = PBLK_LINEGC_NONE; | ||
738 | line->vsc = &l_mg->vsc_list[line_id]; | ||
739 | spin_lock_init(&line->lock); | ||
740 | |||
741 | if (geo->version == NVM_OCSSD_SPEC_12) | ||
742 | nr_bad_chks = pblk_setup_line_meta_12(pblk, line, chunk_meta); | ||
743 | else | ||
744 | nr_bad_chks = pblk_setup_line_meta_20(pblk, line, chunk_meta); | ||
745 | |||
746 | chk_in_line = lm->blk_per_line - nr_bad_chks; | ||
747 | if (nr_bad_chks < 0 || nr_bad_chks > lm->blk_per_line || | ||
748 | chk_in_line < lm->min_blk_line) { | ||
749 | line->state = PBLK_LINESTATE_BAD; | ||
750 | list_add_tail(&line->list, &l_mg->bad_list); | ||
751 | return 0; | ||
752 | } | ||
753 | |||
754 | atomic_set(&line->blk_in_line, chk_in_line); | ||
755 | list_add_tail(&line->list, &l_mg->free_list); | ||
756 | l_mg->nr_free_lines++; | ||
757 | |||
758 | return chk_in_line; | ||
759 | } | ||
760 | |||
761 | static int pblk_alloc_line_meta(struct pblk *pblk, struct pblk_line *line) | ||
649 | { | 762 | { |
650 | struct pblk_line_meta *lm = &pblk->lm; | 763 | struct pblk_line_meta *lm = &pblk->lm; |
651 | 764 | ||
@@ -659,7 +772,13 @@ static int pblk_setup_line_meta(struct pblk *pblk, struct pblk_line *line, | |||
659 | return -ENOMEM; | 772 | return -ENOMEM; |
660 | } | 773 | } |
661 | 774 | ||
662 | *nr_bad_blks = pblk_bb_line(pblk, line, chunk_log, lm->blk_per_line); | 775 | line->chks = kmalloc(lm->blk_per_line * sizeof(struct nvm_chk_meta), |
776 | GFP_KERNEL); | ||
777 | if (!line->chks) { | ||
778 | kfree(line->erase_bitmap); | ||
779 | kfree(line->blk_bitmap); | ||
780 | return -ENOMEM; | ||
781 | } | ||
663 | 782 | ||
664 | return 0; | 783 | return 0; |
665 | } | 784 | } |
@@ -846,10 +965,9 @@ add_emeta_page: | |||
846 | static int pblk_lines_init(struct pblk *pblk) | 965 | static int pblk_lines_init(struct pblk *pblk) |
847 | { | 966 | { |
848 | struct pblk_line_mgmt *l_mg = &pblk->l_mg; | 967 | struct pblk_line_mgmt *l_mg = &pblk->l_mg; |
849 | struct pblk_line_meta *lm = &pblk->lm; | ||
850 | struct pblk_line *line; | 968 | struct pblk_line *line; |
851 | void *chunk_log; | 969 | void *chunk_meta; |
852 | long nr_bad_blks = 0, nr_free_blks = 0; | 970 | long nr_free_chks = 0; |
853 | int i, ret; | 971 | int i, ret; |
854 | 972 | ||
855 | ret = pblk_line_meta_init(pblk); | 973 | ret = pblk_line_meta_init(pblk); |
@@ -864,11 +982,9 @@ static int pblk_lines_init(struct pblk *pblk) | |||
864 | if (ret) | 982 | if (ret) |
865 | goto fail_free_meta; | 983 | goto fail_free_meta; |
866 | 984 | ||
867 | chunk_log = pblk_bb_get_log(pblk); | 985 | chunk_meta = pblk_chunk_get_meta(pblk); |
868 | if (IS_ERR(chunk_log)) { | 986 | if (IS_ERR(chunk_meta)) { |
869 | pr_err("pblk: could not get bad block log (%lu)\n", | 987 | ret = PTR_ERR(chunk_meta); |
870 | PTR_ERR(chunk_log)); | ||
871 | ret = PTR_ERR(chunk_log); | ||
872 | goto fail_free_luns; | 988 | goto fail_free_luns; |
873 | } | 989 | } |
874 | 990 | ||
@@ -876,52 +992,30 @@ static int pblk_lines_init(struct pblk *pblk) | |||
876 | GFP_KERNEL); | 992 | GFP_KERNEL); |
877 | if (!pblk->lines) { | 993 | if (!pblk->lines) { |
878 | ret = -ENOMEM; | 994 | ret = -ENOMEM; |
879 | goto fail_free_chunk_log; | 995 | goto fail_free_chunk_meta; |
880 | } | 996 | } |
881 | 997 | ||
882 | for (i = 0; i < l_mg->nr_lines; i++) { | 998 | for (i = 0; i < l_mg->nr_lines; i++) { |
883 | int chk_in_line; | ||
884 | |||
885 | line = &pblk->lines[i]; | 999 | line = &pblk->lines[i]; |
886 | 1000 | ||
887 | line->pblk = pblk; | 1001 | ret = pblk_alloc_line_meta(pblk, line); |
888 | line->id = i; | ||
889 | line->type = PBLK_LINETYPE_FREE; | ||
890 | line->state = PBLK_LINESTATE_FREE; | ||
891 | line->gc_group = PBLK_LINEGC_NONE; | ||
892 | line->vsc = &l_mg->vsc_list[i]; | ||
893 | spin_lock_init(&line->lock); | ||
894 | |||
895 | ret = pblk_setup_line_meta(pblk, line, chunk_log, &nr_bad_blks); | ||
896 | if (ret) | 1002 | if (ret) |
897 | goto fail_free_lines; | 1003 | goto fail_free_lines; |
898 | 1004 | ||
899 | chk_in_line = lm->blk_per_line - nr_bad_blks; | 1005 | nr_free_chks += pblk_setup_line_meta(pblk, line, chunk_meta, i); |
900 | if (nr_bad_blks < 0 || nr_bad_blks > lm->blk_per_line || | ||
901 | chk_in_line < lm->min_blk_line) { | ||
902 | line->state = PBLK_LINESTATE_BAD; | ||
903 | list_add_tail(&line->list, &l_mg->bad_list); | ||
904 | continue; | ||
905 | } | ||
906 | |||
907 | nr_free_blks += chk_in_line; | ||
908 | atomic_set(&line->blk_in_line, chk_in_line); | ||
909 | |||
910 | l_mg->nr_free_lines++; | ||
911 | list_add_tail(&line->list, &l_mg->free_list); | ||
912 | } | 1006 | } |
913 | 1007 | ||
914 | pblk_set_provision(pblk, nr_free_blks); | 1008 | pblk_set_provision(pblk, nr_free_chks); |
915 | 1009 | ||
916 | kfree(chunk_log); | 1010 | kfree(chunk_meta); |
917 | return 0; | 1011 | return 0; |
918 | 1012 | ||
919 | fail_free_lines: | 1013 | fail_free_lines: |
920 | while (--i >= 0) | 1014 | while (--i >= 0) |
921 | pblk_line_meta_free(&pblk->lines[i]); | 1015 | pblk_line_meta_free(&pblk->lines[i]); |
922 | kfree(pblk->lines); | 1016 | kfree(pblk->lines); |
923 | fail_free_chunk_log: | 1017 | fail_free_chunk_meta: |
924 | kfree(chunk_log); | 1018 | kfree(chunk_meta); |
925 | fail_free_luns: | 1019 | fail_free_luns: |
926 | kfree(pblk->luns); | 1020 | kfree(pblk->luns); |
927 | fail_free_meta: | 1021 | fail_free_meta: |