> On 29 Nov 2018, at 08.16, Igor Konopko <igor.j.konopko@xxxxxxxxx> wrote: > > In current pblk implementation, l2p mapping for not closed lines > is always stored only in OOB metadata and recovered from it. > > Such a solution does not provide data integrity when drives does > not have such a OOB metadata space. > > The goal of this patch is to add support for so called packed > metadata, which store l2p mapping for open lines in last sector > of every write unit. > > After this set of changes, drives with OOB size <16B will use > packed metadata, when >=16B will continue to use OOB metadata. > > Signed-off-by: Igor Konopko <igor.j.konopko@xxxxxxxxx> > --- > drivers/lightnvm/pblk-core.c | 48 ++++++++++++++++++++++++++++++++++++---- > drivers/lightnvm/pblk-init.c | 38 ++++++++++++++++++++++++++----- > drivers/lightnvm/pblk-map.c | 4 ++-- > drivers/lightnvm/pblk-rb.c | 3 +++ > drivers/lightnvm/pblk-read.c | 6 +++++ > drivers/lightnvm/pblk-recovery.c | 5 +++-- > drivers/lightnvm/pblk-sysfs.c | 7 ++++++ > drivers/lightnvm/pblk-write.c | 9 ++++---- > drivers/lightnvm/pblk.h | 10 ++++++++- > 9 files changed, 112 insertions(+), 18 deletions(-) > > diff --git a/drivers/lightnvm/pblk-core.c b/drivers/lightnvm/pblk-core.c > index 1347d1a93dd0..a95e18de5beb 100644 > --- a/drivers/lightnvm/pblk-core.c > +++ b/drivers/lightnvm/pblk-core.c > @@ -376,7 +376,7 @@ void pblk_write_should_kick(struct pblk *pblk) > { > unsigned int secs_avail = pblk_rb_read_count(&pblk->rwb); > > - if (secs_avail >= pblk->min_write_pgs) > + if (secs_avail >= pblk->min_write_pgs_data) > pblk_write_kick(pblk); > } > > @@ -407,7 +407,9 @@ struct list_head *pblk_line_gc_list(struct pblk *pblk, struct pblk_line *line) > struct pblk_line_meta *lm = &pblk->lm; > struct pblk_line_mgmt *l_mg = &pblk->l_mg; > struct list_head *move_list = NULL; > - int vsc = le32_to_cpu(*line->vsc); > + int packed_meta = (le32_to_cpu(*line->vsc) / pblk->min_write_pgs_data) > + * (pblk->min_write_pgs - pblk->min_write_pgs_data); > + int vsc = le32_to_cpu(*line->vsc) + packed_meta; > > lockdep_assert_held(&line->lock); > > @@ -620,12 +622,15 @@ struct bio *pblk_bio_map_addr(struct pblk *pblk, void *data, > } > > int pblk_calc_secs(struct pblk *pblk, unsigned long secs_avail, > - unsigned long secs_to_flush) > + unsigned long secs_to_flush, bool skip_meta) > { > int max = pblk->sec_per_write; > int min = pblk->min_write_pgs; > int secs_to_sync = 0; > > + if (skip_meta && pblk->min_write_pgs_data != pblk->min_write_pgs) > + min = max = pblk->min_write_pgs_data; > + > if (secs_avail >= max) > secs_to_sync = max; > else if (secs_avail >= min) > @@ -852,7 +857,7 @@ int pblk_line_emeta_read(struct pblk *pblk, struct pblk_line *line, > next_rq: > memset(&rqd, 0, sizeof(struct nvm_rq)); > > - rq_ppas = pblk_calc_secs(pblk, left_ppas, 0); > + rq_ppas = pblk_calc_secs(pblk, left_ppas, 0, false); > rq_len = rq_ppas * geo->csecs; > > bio = pblk_bio_map_addr(pblk, emeta_buf, rq_ppas, rq_len, > @@ -2161,3 +2166,38 @@ void pblk_lookup_l2p_rand(struct pblk *pblk, struct ppa_addr *ppas, > } > spin_unlock(&pblk->trans_lock); > } > + > +void *pblk_get_meta_for_writes(struct pblk *pblk, struct nvm_rq *rqd) > +{ > + void *buffer; > + > + if (pblk_is_oob_meta_supported(pblk)) { > + /* Just use OOB metadata buffer as always */ > + buffer = rqd->meta_list; > + } else { > + /* We need to reuse last page of request (packed metadata) > + * in similar way as traditional oob metadata > + */ > + buffer = page_to_virt( > + rqd->bio->bi_io_vec[rqd->bio->bi_vcnt - 1].bv_page); > + } > + > + return buffer; > +} > + > +void pblk_get_packed_meta(struct pblk *pblk, struct nvm_rq *rqd) > +{ > + void *meta_list = rqd->meta_list; > + void *page; > + int i = 0; > + > + if (pblk_is_oob_meta_supported(pblk)) > + return; > + > + page = page_to_virt(rqd->bio->bi_io_vec[rqd->bio->bi_vcnt - 1].bv_page); > + /* We need to fill oob meta buffer with data from packed metadata */ > + for (; i < rqd->nr_ppas; i++) > + memcpy(pblk_get_meta(pblk, meta_list, i), > + page + (i * sizeof(struct pblk_sec_meta)), > + sizeof(struct pblk_sec_meta)); > +} > diff --git a/drivers/lightnvm/pblk-init.c b/drivers/lightnvm/pblk-init.c > index a728f861edd6..830ebe3d098a 100644 > --- a/drivers/lightnvm/pblk-init.c > +++ b/drivers/lightnvm/pblk-init.c > @@ -403,6 +403,7 @@ static int pblk_core_init(struct pblk *pblk) > pblk->nr_flush_rst = 0; > > pblk->min_write_pgs = geo->ws_opt; > + pblk->min_write_pgs_data = pblk->min_write_pgs; > max_write_ppas = pblk->min_write_pgs * geo->all_luns; > pblk->max_write_pgs = min_t(int, max_write_ppas, NVM_MAX_VLBA); > pblk->max_write_pgs = min_t(int, pblk->max_write_pgs, > @@ -410,9 +411,35 @@ static int pblk_core_init(struct pblk *pblk) > pblk_set_sec_per_write(pblk, pblk->min_write_pgs); > > pblk->oob_meta_size = geo->sos; > - if (pblk->oob_meta_size < sizeof(struct pblk_sec_meta)) { > - pblk_err(pblk, "Unsupported metadata size\n"); > - return -EINVAL; > + if (!pblk_is_oob_meta_supported(pblk)) { > + /* For drives which does not have OOB metadata feature > + * in order to support recovery feature we need to use > + * so called packed metadata. Packed metada will store > + * the same information as OOB metadata (l2p table mapping, > + * but in the form of the single page at the end of > + * every write request. > + */ > + if (pblk->min_write_pgs > + * sizeof(struct pblk_sec_meta) > PAGE_SIZE) { > + /* We want to keep all the packed metadata on single > + * page per write requests. So we need to ensure that > + * it will fit. > + * > + * This is more like sanity check, since there is > + * no device with such a big minimal write size > + * (above 1 metabytes). > + */ > + pblk_err(pblk, "Not supported min write size\n"); > + return -EINVAL; > + } > + /* For packed meta approach we do some simplification. > + * On read path we always issue requests which size > + * equal to max_write_pgs, with all pages filled with > + * user payload except of last one page which will be > + * filled with packed metadata. > + */ > + pblk->max_write_pgs = pblk->min_write_pgs; > + pblk->min_write_pgs_data = pblk->min_write_pgs - 1; > } > > pblk->pad_dist = kcalloc(pblk->min_write_pgs - 1, sizeof(atomic64_t), > @@ -645,7 +672,7 @@ static int pblk_set_provision(struct pblk *pblk, int nr_free_chks) > struct pblk_line_meta *lm = &pblk->lm; > struct nvm_geo *geo = &dev->geo; > sector_t provisioned; > - int sec_meta, blk_meta; > + int sec_meta, blk_meta, clba; > int minimum; > > if (geo->op == NVM_TARGET_DEFAULT_OP) > @@ -686,7 +713,8 @@ static int pblk_set_provision(struct pblk *pblk, int nr_free_chks) > sec_meta = (lm->smeta_sec + lm->emeta_sec[0]) * l_mg->nr_free_lines; > blk_meta = DIV_ROUND_UP(sec_meta, geo->clba); > > - pblk->capacity = (provisioned - blk_meta) * geo->clba; > + clba = (geo->clba / pblk->min_write_pgs) * pblk->min_write_pgs_data; > + pblk->capacity = (provisioned - blk_meta) * clba; > > atomic_set(&pblk->rl.free_blocks, nr_free_chks); > atomic_set(&pblk->rl.free_user_blocks, nr_free_chks); > diff --git a/drivers/lightnvm/pblk-map.c b/drivers/lightnvm/pblk-map.c > index 81e503ec384e..79df583ea709 100644 > --- a/drivers/lightnvm/pblk-map.c > +++ b/drivers/lightnvm/pblk-map.c > @@ -96,7 +96,7 @@ int pblk_map_rq(struct pblk *pblk, struct nvm_rq *rqd, unsigned int sentry, > unsigned long *lun_bitmap, unsigned int valid_secs, > unsigned int off) > { > - void *meta_list = rqd->meta_list; > + void *meta_list = pblk_get_meta_for_writes(pblk, rqd); > void *meta_buffer; > struct ppa_addr *ppa_list = nvm_rq_to_ppa_list(rqd); > unsigned int map_secs; > @@ -125,7 +125,7 @@ int pblk_map_erase_rq(struct pblk *pblk, struct nvm_rq *rqd, > struct nvm_tgt_dev *dev = pblk->dev; > struct nvm_geo *geo = &dev->geo; > struct pblk_line_meta *lm = &pblk->lm; > - void *meta_list = rqd->meta_list; > + void *meta_list = pblk_get_meta_for_writes(pblk, rqd); > void *meta_buffer; > struct ppa_addr *ppa_list = nvm_rq_to_ppa_list(rqd); > struct pblk_line *e_line, *d_line; > diff --git a/drivers/lightnvm/pblk-rb.c b/drivers/lightnvm/pblk-rb.c > index 9f7fa0fe9c77..d4ca8c64ee0f 100644 > --- a/drivers/lightnvm/pblk-rb.c > +++ b/drivers/lightnvm/pblk-rb.c > @@ -552,6 +552,9 @@ unsigned int pblk_rb_read_to_bio(struct pblk_rb *rb, struct nvm_rq *rqd, > to_read = count; > } > > + /* Add space for packed metadata if in use*/ > + pad += (pblk->min_write_pgs - pblk->min_write_pgs_data); > + > c_ctx->sentry = pos; > c_ctx->nr_valid = to_read; > c_ctx->nr_padded = pad; > diff --git a/drivers/lightnvm/pblk-read.c b/drivers/lightnvm/pblk-read.c > index 6becd85ca4c6..3789185144da 100644 > --- a/drivers/lightnvm/pblk-read.c > +++ b/drivers/lightnvm/pblk-read.c > @@ -112,6 +112,9 @@ static void pblk_read_check_seq(struct pblk *pblk, struct nvm_rq *rqd, > int nr_lbas = rqd->nr_ppas; > int i; > > + if (!pblk_is_oob_meta_supported(pblk)) > + return; > + > for (i = 0; i < nr_lbas; i++) { > struct pblk_sec_meta *meta = pblk_get_meta(pblk, meta_list, i); > u64 lba = le64_to_cpu(meta->lba); > @@ -141,6 +144,9 @@ static void pblk_read_check_rand(struct pblk *pblk, struct nvm_rq *rqd, > void *meta_lba_list = rqd->meta_list; > int i, j; > > + if (!pblk_is_oob_meta_supported(pblk)) > + return; > + > for (i = 0, j = 0; i < nr_lbas; i++) { > struct pblk_sec_meta *meta = pblk_get_meta(pblk, > meta_lba_list, j); > diff --git a/drivers/lightnvm/pblk-recovery.c b/drivers/lightnvm/pblk-recovery.c > index 52cbe06e3ebc..d1960aa640d2 100644 > --- a/drivers/lightnvm/pblk-recovery.c > +++ b/drivers/lightnvm/pblk-recovery.c > @@ -188,7 +188,7 @@ static int pblk_recov_pad_line(struct pblk *pblk, struct pblk_line *line, > kref_init(&pad_rq->ref); > > next_pad_rq: > - rq_ppas = pblk_calc_secs(pblk, left_ppas, 0); > + rq_ppas = pblk_calc_secs(pblk, left_ppas, 0, false); > if (rq_ppas < pblk->min_write_pgs) { > pblk_err(pblk, "corrupted pad line %d\n", line->id); > goto fail_free_pad; > @@ -368,7 +368,7 @@ static int pblk_recov_scan_oob(struct pblk *pblk, struct pblk_line *line, > next_rq: > memset(rqd, 0, pblk_g_rq_size); > > - rq_ppas = pblk_calc_secs(pblk, left_ppas, 0); > + rq_ppas = pblk_calc_secs(pblk, left_ppas, 0, false); > if (!rq_ppas) > rq_ppas = pblk->min_write_pgs; > rq_len = rq_ppas * geo->csecs; > @@ -437,6 +437,7 @@ static int pblk_recov_scan_oob(struct pblk *pblk, struct pblk_line *line, > goto retry_rq; > } > > + pblk_get_packed_meta(pblk, rqd); > for (i = 0; i < rqd->nr_ppas; i++) { > struct pblk_sec_meta *meta = pblk_get_meta(pblk, meta_list, i); > u64 lba = le64_to_cpu(meta->lba); > diff --git a/drivers/lightnvm/pblk-sysfs.c b/drivers/lightnvm/pblk-sysfs.c > index 2d2818155aa8..7d8958df9472 100644 > --- a/drivers/lightnvm/pblk-sysfs.c > +++ b/drivers/lightnvm/pblk-sysfs.c > @@ -479,6 +479,13 @@ static ssize_t pblk_sysfs_set_sec_per_write(struct pblk *pblk, > if (kstrtouint(page, 0, &sec_per_write)) > return -EINVAL; > > + if (!pblk_is_oob_meta_supported(pblk)) { > + /* For packed metadata case it is > + * not allowed to change sec_per_write. > + */ > + return -EINVAL; > + } > + > if (sec_per_write < pblk->min_write_pgs > || sec_per_write > pblk->max_write_pgs > || sec_per_write % pblk->min_write_pgs != 0) > diff --git a/drivers/lightnvm/pblk-write.c b/drivers/lightnvm/pblk-write.c > index 2bf78f81862d..06d56deb645d 100644 > --- a/drivers/lightnvm/pblk-write.c > +++ b/drivers/lightnvm/pblk-write.c > @@ -348,7 +348,7 @@ static int pblk_calc_secs_to_sync(struct pblk *pblk, unsigned int secs_avail, > { > int secs_to_sync; > > - secs_to_sync = pblk_calc_secs(pblk, secs_avail, secs_to_flush); > + secs_to_sync = pblk_calc_secs(pblk, secs_avail, secs_to_flush, true); > > #ifdef CONFIG_NVM_PBLK_DEBUG > if ((!secs_to_sync && secs_to_flush) > @@ -569,7 +569,7 @@ static int pblk_submit_write(struct pblk *pblk, int *secs_left) > struct bio *bio; > struct nvm_rq *rqd; > unsigned int secs_avail, secs_to_sync, secs_to_com; > - unsigned int secs_to_flush; > + unsigned int secs_to_flush, packed_meta_pgs; > unsigned long pos; > unsigned int resubmit; > > @@ -607,7 +607,7 @@ static int pblk_submit_write(struct pblk *pblk, int *secs_left) > return 0; > > secs_to_flush = pblk_rb_flush_point_count(&pblk->rwb); > - if (!secs_to_flush && secs_avail < pblk->min_write_pgs) > + if (!secs_to_flush && secs_avail < pblk->min_write_pgs_data) > return 0; > > secs_to_sync = pblk_calc_secs_to_sync(pblk, secs_avail, > @@ -622,7 +622,8 @@ static int pblk_submit_write(struct pblk *pblk, int *secs_left) > pos = pblk_rb_read_commit(&pblk->rwb, secs_to_com); > } > > - bio = bio_alloc(GFP_KERNEL, secs_to_sync); > + packed_meta_pgs = (pblk->min_write_pgs - pblk->min_write_pgs_data); > + bio = bio_alloc(GFP_KERNEL, secs_to_sync + packed_meta_pgs); > > bio->bi_iter.bi_sector = 0; /* internal bio */ > bio_set_op_attrs(bio, REQ_OP_WRITE, 0); > diff --git a/drivers/lightnvm/pblk.h b/drivers/lightnvm/pblk.h > index 9087d53d5c25..bc40b1381ff6 100644 > --- a/drivers/lightnvm/pblk.h > +++ b/drivers/lightnvm/pblk.h > @@ -632,6 +632,7 @@ struct pblk { > int state; /* pblk line state */ > > int min_write_pgs; /* Minimum amount of pages required by controller */ > + int min_write_pgs_data; /* Minimum amount of payload pages */ > int max_write_pgs; /* Maximum amount of pages supported by controller */ > int oob_meta_size; /* Size of OOB sector metadata */ > > @@ -838,7 +839,7 @@ void pblk_dealloc_page(struct pblk *pblk, struct pblk_line *line, int nr_secs); > u64 pblk_alloc_page(struct pblk *pblk, struct pblk_line *line, int nr_secs); > u64 __pblk_alloc_page(struct pblk *pblk, struct pblk_line *line, int nr_secs); > int pblk_calc_secs(struct pblk *pblk, unsigned long secs_avail, > - unsigned long secs_to_flush); > + unsigned long secs_to_flush, bool skip_meta); > void pblk_down_rq(struct pblk *pblk, struct ppa_addr ppa, > unsigned long *lun_bitmap); > void pblk_down_chunk(struct pblk *pblk, struct ppa_addr ppa); > @@ -862,6 +863,8 @@ void pblk_lookup_l2p_rand(struct pblk *pblk, struct ppa_addr *ppas, > u64 *lba_list, int nr_secs); > void pblk_lookup_l2p_seq(struct pblk *pblk, struct ppa_addr *ppas, > sector_t blba, int nr_secs); > +void *pblk_get_meta_for_writes(struct pblk *pblk, struct nvm_rq *rqd); > +void pblk_get_packed_meta(struct pblk *pblk, struct nvm_rq *rqd); > > /* > * pblk user I/O write path > @@ -1392,4 +1395,9 @@ static inline int pblk_dma_meta_size(struct pblk *pblk) > { > return pblk->oob_meta_size * NVM_MAX_VLBA; > } > + > +static inline int pblk_is_oob_meta_supported(struct pblk *pblk) > +{ > + return pblk->oob_meta_size >= sizeof(struct pblk_sec_meta); > +} > #endif /* PBLK_H_ */ > -- > 2.14.5 Looks good to me. Reviewed-by: Javier González <javier@xxxxxxxxxxxx>
Attachment:
signature.asc
Description: Message signed with OpenPGP