[v2,2/2] lightnvm: add non-continuous lun target creation support
diff mbox

Message ID 56A89169.6030704@lightnvm.io
State New
Headers show

Commit Message

Matias Bjørling Jan. 27, 2016, 9:44 a.m. UTC
On 01/26/2016 01:33 PM, Wenwei Tao wrote:
> When create a target, we specify the begin lunid and
> the end lunid, and get the corresponding continuous
> luns from media manager, if one of the luns is not free,
> we failed to create the target, even if the device's
> total free luns are enough.
> 
> So add non-continuous lun target creation support,
> thus we can improve the backend device's space utilization.
> Signed-off-by: Wenwei Tao <ww.tao0320@gmail.com>
> ---
> Changes since v1:
> -use NVM_FIXED instead NVM_C_FIXED in gennvm_get_lun
> -add target creation flags check
> -rebase to v4.5-rc1
>  
>  drivers/lightnvm/core.c       |  36 ++++---
>  drivers/lightnvm/gennvm.c     |  42 ++++++++-
>  drivers/lightnvm/rrpc.c       | 215 +++++++++++++++++++++++++++---------------
>  drivers/lightnvm/rrpc.h       |   6 +-
>  include/linux/lightnvm.h      |  24 ++++-
>  include/uapi/linux/lightnvm.h |   3 +
>  6 files changed, 229 insertions(+), 97 deletions(-)
> 

Hi Wenwei,

I did some digging on the patch and changed the interface to a
reserve/release interface. I also removed the logic to dynamically
select another lun than the one requested.

A couple of questions:

 1. The rrpc_lun->rev_lock and rev_trans_map change; this might be for
another patch, and it isn't directly related to continuous mapping?
 2. Instead of dynamically assigning new luns when not available, what
about taking a list of lun ids instead?

I would only implement this in the lnvm ioctl interface. It would allow
a list of lun ids to be passed through the lnvm ioctl interface. This
way, the NVM_CONFIG_TYPE_SIMPLE can be extended with another
NVM_CONFIG_TYPE_LIST, or similar, which then parses the ioctl
appropriately. Would that be a better way to do it?

Here is the diff. It is also rebased on top of the two latest patches
that which are sent up for the next -rc.

Thanks

 typedef int (nvmm_get_area_fn)(struct nvm_dev *, sector_t *, sector_t);
@@ -488,6 +493,8 @@ struct nvmm_type {

 	/* Configuration management */
 	nvmm_get_lun_fn *get_lun;
+	nvmm_reserve_lun *reserve_lun;
+	nvmm_release_lun *release_lun;

 	/* Statistics */
 	nvmm_lun_info_print_fn *lun_info_print;

--
To unsubscribe from this list: send the line "unsubscribe linux-block" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Comments

Wenwei Tao Jan. 28, 2016, 8:50 a.m. UTC | #1
2016-01-27 17:44 GMT+08:00 Matias Bjørling <mb@lightnvm.io>:
> On 01/26/2016 01:33 PM, Wenwei Tao wrote:
>> When create a target, we specify the begin lunid and
>> the end lunid, and get the corresponding continuous
>> luns from media manager, if one of the luns is not free,
>> we failed to create the target, even if the device's
>> total free luns are enough.
>>
>> So add non-continuous lun target creation support,
>> thus we can improve the backend device's space utilization.
>> Signed-off-by: Wenwei Tao <ww.tao0320@gmail.com>
>> ---
>> Changes since v1:
>> -use NVM_FIXED instead NVM_C_FIXED in gennvm_get_lun
>> -add target creation flags check
>> -rebase to v4.5-rc1
>>
>>  drivers/lightnvm/core.c       |  36 ++++---
>>  drivers/lightnvm/gennvm.c     |  42 ++++++++-
>>  drivers/lightnvm/rrpc.c       | 215 +++++++++++++++++++++++++++---------------
>>  drivers/lightnvm/rrpc.h       |   6 +-
>>  include/linux/lightnvm.h      |  24 ++++-
>>  include/uapi/linux/lightnvm.h |   3 +
>>  6 files changed, 229 insertions(+), 97 deletions(-)
>>
>
> Hi Wenwei,
>
> I did some digging on the patch and changed the interface to a
> reserve/release interface. I also removed the logic to dynamically
> select another lun than the one requested.
>
> A couple of questions:
>
>  1. The rrpc_lun->rev_lock and rev_trans_map change; this might be for
> another patch, and it isn't directly related to continuous mapping?

rrpc_lun->rev_lock and rev_trans_map change is related to
non-continuous mapping, it's not directly related to continuous
mapping.
Put this change in  another patch along with non-continuous mapping
support and this patch would be only add reserve/release thing, is
that your suggestion?


>  2. Instead of dynamically assigning new luns when not available, what
> about taking a list of lun ids instead?
>

Seems you prefer user make the choice ?
But the target creation can still fail if one of the list lun ids is
not available although there may be enough free luns.

> I would only implement this in the lnvm ioctl interface. It would allow
> a list of lun ids to be passed through the lnvm ioctl interface. This
> way, the NVM_CONFIG_TYPE_SIMPLE can be extended with another
> NVM_CONFIG_TYPE_LIST, or similar, which then parses the ioctl
> appropriately. Would that be a better way to do it?
>
> Here is the diff. It is also rebased on top of the two latest patches
> that which are sent up for the next -rc.
>
> Thanks
>
> diff --git a/drivers/lightnvm/core.c b/drivers/lightnvm/core.c
> index 27a59e8..59a4bf9 100644
> --- a/drivers/lightnvm/core.c
> +++ b/drivers/lightnvm/core.c
> @@ -468,6 +468,11 @@ static int nvm_core_init(struct nvm_dev *dev)
>                                 dev->luns_per_chnl *
>                                 dev->nr_chnls;
>         dev->total_pages = dev->total_blocks * dev->pgs_per_blk;
> +       dev->lun_map = kcalloc(BITS_TO_LONGS(dev->nr_luns),
> +                       sizeof(unsigned long), GFP_KERNEL);
> +       if (!dev->lun_map)
> +               return -ENOMEM;
> +
>         INIT_LIST_HEAD(&dev->online_targets);
>         mutex_init(&dev->mlock);
>         spin_lock_init(&dev->lock);
> @@ -610,6 +615,7 @@ void nvm_unregister(char *disk_name)
>         up_write(&nvm_lock);
>
>         nvm_exit(dev);
> +       kfree(dev->lun_map);
>         kfree(dev);
>  }
>  EXPORT_SYMBOL(nvm_unregister);
> diff --git a/drivers/lightnvm/gennvm.c b/drivers/lightnvm/gennvm.c
> index 6e2685d..6419898 100644
> --- a/drivers/lightnvm/gennvm.c
> +++ b/drivers/lightnvm/gennvm.c
> @@ -188,6 +188,9 @@ static int gennvm_block_map(u64 slba, u32 nlb,
> __le64 *entries, void *private)
>                 lun_id = div_u64(pba, dev->sec_per_lun);
>                 lun = &gn->luns[lun_id];
>
> +               if (!test_bit(lun_id, dev->lun_map))
> +                       __set_bit(lun_id, dev->lun_map);
> +
>                 /* Calculate block offset into lun */
>                 pba = pba - (dev->sec_per_lun * lun_id);
>                 blk = &lun->vlun.blocks[div_u64(pba, dev->sec_per_blk)];
> @@ -478,10 +481,23 @@ static int gennvm_erase_blk(struct nvm_dev *dev,
> struct nvm_block *blk,
>         return nvm_erase_ppa(dev, &addr, 1);
>  }
>
> +static int gennvm_reserve_lun(struct nvm_dev *dev, int lunid)
> +{
> +       return test_and_set_bit(lunid, dev->lun_map);
> +}
> +
> +static void gennvm_release_lun(struct nvm_dev *dev, int lunid)
> +{
> +       WARN_ON(!test_and_clear_bit(lunid, dev->lun_map));
> +}
> +
>  static struct nvm_lun *gennvm_get_lun(struct nvm_dev *dev, int lunid)
>  {
>         struct gen_nvm *gn = dev->mp;
>
> +       if (unlikely(lunid >= dev->nr_luns))
> +               return NULL;
> +
>         return &gn->luns[lunid].vlun;
>  }
>
> @@ -523,6 +539,8 @@ static struct nvmm_type gennvm = {
>         .erase_blk              = gennvm_erase_blk,
>
>         .get_lun                = gennvm_get_lun,
> +       .reserve_lun            = gennvm_reserve_lun,
> +       .release_lun            = gennvm_release_lun,
>         .lun_info_print         = gennvm_lun_info_print,
>
>         .get_area               = gennvm_get_area,
> diff --git a/drivers/lightnvm/rrpc.c b/drivers/lightnvm/rrpc.c
> index 20afe1c..0a99ebc 100644
> --- a/drivers/lightnvm/rrpc.c
> +++ b/drivers/lightnvm/rrpc.c
> @@ -26,25 +26,32 @@ static int rrpc_submit_io(struct rrpc *rrpc, struct
> bio *bio,
>                 for ((i) = 0, rlun = &(rrpc)->luns[0]; \
>                         (i) < (rrpc)->nr_luns; (i)++, rlun = &(rrpc)->luns[(i)])
>
> +static inline u64 lun_poffset(struct nvm_dev *dev, struct nvm_lun *lun)
> +{
> +       return lun->id * dev->sec_per_lun;
> +}
> +
>  static void rrpc_page_invalidate(struct rrpc *rrpc, struct rrpc_addr *a)
>  {
>         struct rrpc_block *rblk = a->rblk;
> -       unsigned int pg_offset;
> +       struct rrpc_lun *rlun = rblk->rlun;
> +       u64 pg_offset;
>
> -       lockdep_assert_held(&rrpc->rev_lock);
> +       lockdep_assert_held(&rlun->rev_lock);
>
>         if (a->addr == ADDR_EMPTY || !rblk)
>                 return;
>
>         spin_lock(&rblk->lock);
>
> -       div_u64_rem(a->addr, rrpc->dev->pgs_per_blk, &pg_offset);
> +       div_u64_rem(a->addr, rrpc->dev->pgs_per_blk, (u32 *)&pg_offset);
>         WARN_ON(test_and_set_bit(pg_offset, rblk->invalid_pages));
>         rblk->nr_invalid_pages++;
>
>         spin_unlock(&rblk->lock);
>
> -       rrpc->rev_trans_map[a->addr - rrpc->poffset].addr = ADDR_EMPTY;
> +       pg_offset = lun_poffset(rrpc->dev, rlun->parent);
> +       rlun->rev_trans_map[a->addr - pg_offset].addr = ADDR_EMPTY;
>  }
>
>  static void rrpc_invalidate_range(struct rrpc *rrpc, sector_t slba,
> @@ -52,14 +59,15 @@ static void rrpc_invalidate_range(struct rrpc *rrpc,
> sector_t slba,
>  {
>         sector_t i;
>
> -       spin_lock(&rrpc->rev_lock);
>         for (i = slba; i < slba + len; i++) {
>                 struct rrpc_addr *gp = &rrpc->trans_map[i];
> +               struct rrpc_lun *rlun = gp->rblk->rlun;
>
> +               spin_lock(&rlun->rev_lock);
>                 rrpc_page_invalidate(rrpc, gp);
> +               spin_unlock(&rlun->rev_lock);
>                 gp->rblk = NULL;
>         }
> -       spin_unlock(&rrpc->rev_lock);
>  }
>
>  static struct nvm_rq *rrpc_inflight_laddr_acquire(struct rrpc *rrpc,
> @@ -281,13 +289,14 @@ static void rrpc_end_sync_bio(struct bio *bio)
>  static int rrpc_move_valid_pages(struct rrpc *rrpc, struct rrpc_block
> *rblk)
>  {
>         struct request_queue *q = rrpc->dev->q;
> +       struct rrpc_lun *rlun = rblk->rlun;
>         struct rrpc_rev_addr *rev;
>         struct nvm_rq *rqd;
>         struct bio *bio;
>         struct page *page;
>         int slot;
>         int nr_pgs_per_blk = rrpc->dev->pgs_per_blk;
> -       u64 phys_addr;
> +       u64 phys_addr, poffset;
>         DECLARE_COMPLETION_ONSTACK(wait);
>
>         if (bitmap_full(rblk->invalid_pages, nr_pgs_per_blk))
> @@ -303,6 +312,7 @@ static int rrpc_move_valid_pages(struct rrpc *rrpc,
> struct rrpc_block *rblk)
>         if (!page)
>                 return -ENOMEM;
>
> +       poffset = lun_poffset(rrpc->dev, rlun->parent);
>         while ((slot = find_first_zero_bit(rblk->invalid_pages,
>                                             nr_pgs_per_blk)) < nr_pgs_per_blk) {
>
> @@ -310,23 +320,23 @@ static int rrpc_move_valid_pages(struct rrpc
> *rrpc, struct rrpc_block *rblk)
>                 phys_addr = (rblk->parent->id * nr_pgs_per_blk) + slot;
>
>  try:
> -               spin_lock(&rrpc->rev_lock);
> +               spin_lock(&rlun->rev_lock);
>                 /* Get logical address from physical to logical table */
> -               rev = &rrpc->rev_trans_map[phys_addr - rrpc->poffset];
> +               rev = &rlun->rev_trans_map[phys_addr - poffset];
>                 /* already updated by previous regular write */
>                 if (rev->addr == ADDR_EMPTY) {
> -                       spin_unlock(&rrpc->rev_lock);
> +                       spin_unlock(&rlun->rev_lock);
>                         continue;
>                 }
>
>                 rqd = rrpc_inflight_laddr_acquire(rrpc, rev->addr, 1);
>                 if (IS_ERR_OR_NULL(rqd)) {
> -                       spin_unlock(&rrpc->rev_lock);
> +                       spin_unlock(&rlun->rev_lock);
>                         schedule();
>                         goto try;
>                 }
>
> -               spin_unlock(&rrpc->rev_lock);
> +               spin_unlock(&rlun->rev_lock);
>
>                 /* Perform read to do GC */
>                 bio->bi_iter.bi_sector = rrpc_get_sector(rev->addr);
> @@ -395,7 +405,7 @@ static void rrpc_block_gc(struct work_struct *work)
>         struct rrpc_block *rblk = gcb->rblk;
>         struct nvm_dev *dev = rrpc->dev;
>         struct nvm_lun *lun = rblk->parent->lun;
> -       struct rrpc_lun *rlun = &rrpc->luns[lun->id - rrpc->lun_offset];
> +       struct rrpc_lun *rlun = lun->private;
>
>         mempool_free(gcb, rrpc->gcb_pool);
>         pr_debug("nvm: block '%lu' being reclaimed\n", rblk->parent->id);
> @@ -496,9 +506,9 @@ static void rrpc_gc_queue(struct work_struct *work)
>                                                                         ws_gc);
>         struct rrpc *rrpc = gcb->rrpc;
>         struct rrpc_block *rblk = gcb->rblk;
> -       struct nvm_lun *lun = rblk->parent->lun;
>         struct nvm_block *blk = rblk->parent;
> -       struct rrpc_lun *rlun = &rrpc->luns[lun->id - rrpc->lun_offset];
> +       struct nvm_lun *lun = blk->lun;
> +       struct rrpc_lun *rlun = lun->private;
>
>         spin_lock(&rlun->lock);
>         list_add_tail(&rblk->prio, &rlun->prio_list);
> @@ -549,22 +559,24 @@ static struct rrpc_lun *rrpc_get_lun_rr(struct
> rrpc *rrpc, int is_gc)
>  static struct rrpc_addr *rrpc_update_map(struct rrpc *rrpc, sector_t laddr,
>                                         struct rrpc_block *rblk, u64 paddr)
>  {
> +       struct rrpc_lun *rlun = rblk->rlun;
>         struct rrpc_addr *gp;
>         struct rrpc_rev_addr *rev;
> +       u64 poffset = lun_poffset(rrpc->dev, rlun->parent);
>
>         BUG_ON(laddr >= rrpc->nr_pages);
>
>         gp = &rrpc->trans_map[laddr];
> -       spin_lock(&rrpc->rev_lock);
> +       spin_lock(&rlun->rev_lock);
>         if (gp->rblk)
>                 rrpc_page_invalidate(rrpc, gp);
>
>         gp->addr = paddr;
>         gp->rblk = rblk;
>
> -       rev = &rrpc->rev_trans_map[gp->addr - rrpc->poffset];
> +       rev = &rlun->rev_trans_map[gp->addr - poffset];
>         rev->addr = laddr;
> -       spin_unlock(&rrpc->rev_lock);
> +       spin_unlock(&rlun->rev_lock);
>
>         return gp;
>  }
> @@ -953,8 +965,6 @@ static void rrpc_requeue(struct work_struct *work)
>
>  static void rrpc_gc_free(struct rrpc *rrpc)
>  {
> -       struct rrpc_lun *rlun;
> -       int i;
>
>         if (rrpc->krqd_wq)
>                 destroy_workqueue(rrpc->krqd_wq);
> @@ -962,16 +972,6 @@ static void rrpc_gc_free(struct rrpc *rrpc)
>         if (rrpc->kgc_wq)
>                 destroy_workqueue(rrpc->kgc_wq);
>
> -       if (!rrpc->luns)
> -               return;
> -
> -       for (i = 0; i < rrpc->nr_luns; i++) {
> -               rlun = &rrpc->luns[i];
> -
> -               if (!rlun->blocks)
> -                       break;
> -               vfree(rlun->blocks);
> -       }
>  }
>
>  static int rrpc_gc_init(struct rrpc *rrpc)
> @@ -992,7 +992,6 @@ static int rrpc_gc_init(struct rrpc *rrpc)
>
>  static void rrpc_map_free(struct rrpc *rrpc)
>  {
> -       vfree(rrpc->rev_trans_map);
>         vfree(rrpc->trans_map);
>  }
>
> @@ -1000,19 +999,28 @@ static int rrpc_l2p_update(u64 slba, u32 nlb,
> __le64 *entries, void *private)
>  {
>         struct rrpc *rrpc = (struct rrpc *)private;
>         struct nvm_dev *dev = rrpc->dev;
> -       struct rrpc_addr *addr = rrpc->trans_map + slba;
> -       struct rrpc_rev_addr *raddr = rrpc->rev_trans_map;
> +       struct rrpc_addr *addr;
> +       struct rrpc_rev_addr *raddr;
>         sector_t max_pages = dev->total_pages * (dev->sec_size >> 9);
> -       u64 elba = slba + nlb;
> -       u64 i;
> +       int page_size = dev->sec_per_pg * dev->sec_size;
> +       u64 elba, i;
> +
> +       elba = slba + nlb;
>
>         if (unlikely(elba > dev->total_pages)) {
>                 pr_err("nvm: L2P data from device is out of bounds!\n");
>                 return -EINVAL;
>         }
>
> +       slba -= rrpc->soffset >> (ilog2(page_size) - 9);
> +       addr = rrpc->trans_map + slba;
>         for (i = 0; i < nlb; i++) {
> +               struct rrpc_lun *rlun;
> +               struct nvm_lun *lun;
>                 u64 pba = le64_to_cpu(entries[i]);
> +               u64 poffset;
> +               int lunid;
> +
>                 /* LNVM treats address-spaces as silos, LBA and PBA are
>                  * equally large and zero-indexed.
>                  */
> @@ -1028,8 +1036,15 @@ static int rrpc_l2p_update(u64 slba, u32 nlb,
> __le64 *entries, void *private)
>                 if (!pba)
>                         continue;
>
> +               lunid = div_u64(pba, dev->sec_per_lun);
> +               lun = dev->mt->get_lun(dev, lunid);
> +               if (unlikely(!lun))
> +                       return -EINVAL;
> +               rlun = lun->private;
> +               raddr = rlun->rev_trans_map;
> +               poffset = lun_poffset(dev, lun);
>                 addr[i].addr = pba;
> -               raddr[pba].addr = slba + i;
> +               raddr[pba - poffset].addr = slba + i;
>         }
>
>         return 0;
> @@ -1049,17 +1064,11 @@ static int rrpc_map_init(struct rrpc *rrpc)
>         if (!rrpc->trans_map)
>                 return -ENOMEM;
>
> -       rrpc->rev_trans_map = vmalloc(sizeof(struct rrpc_rev_addr)
> -                                                       * rrpc->nr_pages);
> -       if (!rrpc->rev_trans_map)
> -               return -ENOMEM;
>
>         for (i = 0; i < rrpc->nr_pages; i++) {
>                 struct rrpc_addr *p = &rrpc->trans_map[i];
> -               struct rrpc_rev_addr *r = &rrpc->rev_trans_map[i];
>
>                 p->addr = ADDR_EMPTY;
> -               r->addr = ADDR_EMPTY;
>         }
>
>         if (!dev->ops->get_l2p_tbl)
> @@ -1130,22 +1139,86 @@ static void rrpc_core_free(struct rrpc *rrpc)
>
>  static void rrpc_luns_free(struct rrpc *rrpc)
>  {
> +       struct nvm_dev *dev = rrpc->dev;
> +       struct rrpc_lun *rlun;
> +       struct nvm_lun *lun;
> +       int i;
> +
> +       if (!rrpc->luns)
> +               return;
> +
> +       for (i = 0; i < rrpc->nr_luns; i++) {
> +               rlun = &rrpc->luns[i];
> +               if (!rlun)
> +                       break;
> +               lun = rlun->parent;
> +               dev->mt->release_lun(dev, lun->id);
> +               vfree(rlun->rev_trans_map);
> +               vfree(rlun->blocks);
> +       }
>         kfree(rrpc->luns);
> +       rrpc->luns = NULL;
> +
> +}
> +
> +static int rrpc_lun_init(struct rrpc *rrpc, struct rrpc_lun *rlun,
> +                                                       struct nvm_lun *lun)
> +{
> +       struct nvm_dev *dev = rrpc->dev;
> +       int i;
> +
> +       rlun->rrpc = rrpc;
> +       rlun->parent = lun;
> +
> +       rlun->rev_trans_map = vmalloc(sizeof(struct rrpc_rev_addr) *
> +                                                       dev->sec_per_lun);
> +       if (!rlun->rev_trans_map)
> +               return -ENOMEM;
> +
> +       for (i = 0; i < dev->sec_per_lun; i++) {
> +               struct rrpc_rev_addr *r = &rlun->rev_trans_map[i];
> +
> +               r->addr = ADDR_EMPTY;
> +       }
> +
> +       rlun->blocks = vzalloc(sizeof(struct rrpc_block) * dev->blks_per_lun);
> +       if (!rlun->blocks) {
> +               vfree(rlun->rev_trans_map);
> +               return -ENOMEM;
> +       }
> +
> +       for (i = 0; i < dev->blks_per_lun; i++) {
> +               struct rrpc_block *rblk = &rlun->blocks[i];
> +               struct nvm_block *blk = &lun->blocks[i];
> +
> +               rblk->parent = blk;
> +               rblk->rlun = rlun;
> +               INIT_LIST_HEAD(&rblk->prio);
> +               spin_lock_init(&rblk->lock);
> +       }
> +
> +       lun->private = rlun;
> +       INIT_LIST_HEAD(&rlun->prio_list);
> +       INIT_LIST_HEAD(&rlun->open_list);
> +       INIT_LIST_HEAD(&rlun->closed_list);
> +       INIT_WORK(&rlun->ws_gc, rrpc_lun_gc);
> +       spin_lock_init(&rlun->lock);
> +       spin_lock_init(&rlun->rev_lock);
> +
> +       return 0;
>  }
>
>  static int rrpc_luns_init(struct rrpc *rrpc, int lun_begin, int lun_end)
>  {
>         struct nvm_dev *dev = rrpc->dev;
>         struct rrpc_lun *rlun;
> -       int i, j;
> +       int i, ret;
>
>         if (dev->pgs_per_blk > MAX_INVALID_PAGES_STORAGE * BITS_PER_LONG) {
>                 pr_err("rrpc: number of pages per block too high.");
>                 return -EINVAL;
>         }
>
> -       spin_lock_init(&rrpc->rev_lock);
> -
>         rrpc->luns = kcalloc(rrpc->nr_luns, sizeof(struct rrpc_lun),
>                                                                 GFP_KERNEL);
>         if (!rrpc->luns)
> @@ -1153,40 +1226,35 @@ static int rrpc_luns_init(struct rrpc *rrpc, int
> lun_begin, int lun_end)
>
>         /* 1:1 mapping */
>         for (i = 0; i < rrpc->nr_luns; i++) {
> -               struct nvm_lun *lun = dev->mt->get_lun(dev, lun_begin + i);
> +               int lunid = lun_begin + i;
> +               struct nvm_lun *lun;
> +
> +               if (dev->mt->reserve_lun(dev, lunid)) {
> +                       pr_err("rrpc: lun %u is already allocated\n", lunid);
> +                       ret = -EINVAL;
> +                       goto err;
> +               }
> +
> +               lun = dev->mt->get_lun(dev, lunid);
> +               if (!lun) {
> +                       ret = -EINVAL;
> +                       goto err;
> +               }
>
>                 rlun = &rrpc->luns[i];
> -               rlun->rrpc = rrpc;
> -               rlun->parent = lun;
> -               INIT_LIST_HEAD(&rlun->prio_list);
> -               INIT_LIST_HEAD(&rlun->open_list);
> -               INIT_LIST_HEAD(&rlun->closed_list);
> -
> -               INIT_WORK(&rlun->ws_gc, rrpc_lun_gc);
> -               spin_lock_init(&rlun->lock);
> +               ret = rrpc_lun_init(rrpc, rlun, lun);
> +               if (ret)
> +                       goto err;
>
>                 rrpc->total_blocks += dev->blks_per_lun;
>                 rrpc->nr_pages += dev->sec_per_lun;
> -
> -               rlun->blocks = vzalloc(sizeof(struct rrpc_block) *
> -                                               rrpc->dev->blks_per_lun);
> -               if (!rlun->blocks)
> -                       goto err;
> -
> -               for (j = 0; j < rrpc->dev->blks_per_lun; j++) {
> -                       struct rrpc_block *rblk = &rlun->blocks[j];
> -                       struct nvm_block *blk = &lun->blocks[j];
> -
> -                       rblk->parent = blk;
> -                       rblk->rlun = rlun;
> -                       INIT_LIST_HEAD(&rblk->prio);
> -                       spin_lock_init(&rblk->lock);
> -               }
>         }
>
>         return 0;
>  err:
> -       return -ENOMEM;
> +       rrpc_luns_free(rrpc);
> +       return ret;
> +
>  }
>
>  /* returns 0 on success and stores the beginning address in *begin */
> @@ -1258,14 +1326,16 @@ static sector_t rrpc_capacity(void *private)
>  static void rrpc_block_map_update(struct rrpc *rrpc, struct rrpc_block
> *rblk)
>  {
>         struct nvm_dev *dev = rrpc->dev;
> +       struct rrpc_lun *rlun = rblk->rlun;
>         int offset;
>         struct rrpc_addr *laddr;
> -       u64 paddr, pladdr;
> +       u64 paddr, pladdr, poffset;
>
> +       poffset = lun_poffset(dev, rlun->parent);
>         for (offset = 0; offset < dev->pgs_per_blk; offset++) {
>                 paddr = block_to_addr(rrpc, rblk) + offset;
>
> -               pladdr = rrpc->rev_trans_map[paddr].addr;
> +               pladdr = rlun->rev_trans_map[paddr - poffset].addr;
>                 if (pladdr == ADDR_EMPTY)
>                         continue;
>
> @@ -1374,9 +1444,6 @@ static void *rrpc_init(struct nvm_dev *dev, struct
> gendisk *tdisk,
>                 goto err;
>         }
>
> -       rrpc->poffset = dev->sec_per_lun * lun_begin;
> -       rrpc->lun_offset = lun_begin;
> -
>         ret = rrpc_core_init(rrpc);
>         if (ret) {
>                 pr_err("nvm: rrpc: could not initialize core\n");
> diff --git a/drivers/lightnvm/rrpc.h b/drivers/lightnvm/rrpc.h
> index 9380c68..4d756d8 100644
> --- a/drivers/lightnvm/rrpc.h
> +++ b/drivers/lightnvm/rrpc.h
> @@ -86,6 +86,9 @@ struct rrpc_lun {
>                                          */
>
>         struct work_struct ws_gc;
> +       /* store a reverse map for garbage collection */
> +       struct rrpc_rev_addr *rev_trans_map;
> +       spinlock_t rev_lock;
>
>         spinlock_t lock;
>  };
> @@ -124,9 +127,6 @@ struct rrpc {
>          * addresses are used when writing to the disk block device.
>          */
>         struct rrpc_addr *trans_map;
> -       /* also store a reverse map for garbage collection */
> -       struct rrpc_rev_addr *rev_trans_map;
> -       spinlock_t rev_lock;
>
>         struct rrpc_inflight inflights;
>
> diff --git a/include/linux/lightnvm.h b/include/linux/lightnvm.h
> index 18f1bb0..a33af4f 100644
> --- a/include/linux/lightnvm.h
> +++ b/include/linux/lightnvm.h
> @@ -271,6 +271,7 @@ struct nvm_lun {
>         spinlock_t lock;
>
>         struct nvm_block *blocks;
> +       void *private;
>  };
>
>  enum {
> @@ -342,6 +343,8 @@ struct nvm_dev {
>         int nr_luns;
>         unsigned max_pages_per_blk;
>
> +       unsigned long *lun_map;
> +
>         void *ppalist_pool;
>
>         struct nvm_id identity;
> @@ -462,6 +465,8 @@ typedef int (nvmm_submit_io_fn)(struct nvm_dev *,
> struct nvm_rq *);
>  typedef int (nvmm_erase_blk_fn)(struct nvm_dev *, struct nvm_block *,
>                                                                 unsigned long);
>  typedef struct nvm_lun *(nvmm_get_lun_fn)(struct nvm_dev *, int);
> +typedef int (nvmm_reserve_lun(struct nvm_dev *, int));
> +typedef void (nvmm_release_lun(struct nvm_dev *, int));
>  typedef void (nvmm_lun_info_print_fn)(struct nvm_dev *);
>
>  typedef int (nvmm_get_area_fn)(struct nvm_dev *, sector_t *, sector_t);
> @@ -488,6 +493,8 @@ struct nvmm_type {
>
>         /* Configuration management */
>         nvmm_get_lun_fn *get_lun;
> +       nvmm_reserve_lun *reserve_lun;
> +       nvmm_release_lun *release_lun;
>
>         /* Statistics */
>         nvmm_lun_info_print_fn *lun_info_print;
>
--
To unsubscribe from this list: send the line "unsubscribe linux-block" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Matias Bjørling Jan. 28, 2016, 9:09 a.m. UTC | #2
On 01/28/2016 09:50 AM, Wenwei Tao wrote:
> 2016-01-27 17:44 GMT+08:00 Matias Bjørling <mb@lightnvm.io>:
>> On 01/26/2016 01:33 PM, Wenwei Tao wrote:
>>> When create a target, we specify the begin lunid and
>>> the end lunid, and get the corresponding continuous
>>> luns from media manager, if one of the luns is not free,
>>> we failed to create the target, even if the device's
>>> total free luns are enough.
>>>
>>> So add non-continuous lun target creation support,
>>> thus we can improve the backend device's space utilization.
>>> Signed-off-by: Wenwei Tao <ww.tao0320@gmail.com>
>>> ---
>>> Changes since v1:
>>> -use NVM_FIXED instead NVM_C_FIXED in gennvm_get_lun
>>> -add target creation flags check
>>> -rebase to v4.5-rc1
>>>
>>>  drivers/lightnvm/core.c       |  36 ++++---
>>>  drivers/lightnvm/gennvm.c     |  42 ++++++++-
>>>  drivers/lightnvm/rrpc.c       | 215 +++++++++++++++++++++++++++---------------
>>>  drivers/lightnvm/rrpc.h       |   6 +-
>>>  include/linux/lightnvm.h      |  24 ++++-
>>>  include/uapi/linux/lightnvm.h |   3 +
>>>  6 files changed, 229 insertions(+), 97 deletions(-)
>>>
>>
>> Hi Wenwei,
>>
>> I did some digging on the patch and changed the interface to a
>> reserve/release interface. I also removed the logic to dynamically
>> select another lun than the one requested.
>>
>> A couple of questions:
>>
>>  1. The rrpc_lun->rev_lock and rev_trans_map change; this might be for
>> another patch, and it isn't directly related to continuous mapping?
> 
> rrpc_lun->rev_lock and rev_trans_map change is related to
> non-continuous mapping, it's not directly related to continuous
> mapping.
> Put this change in  another patch along with non-continuous mapping
> support and this patch would be only add reserve/release thing, is
> that your suggestion?

Yes, that would be great. Then we keep it separate. I'll like to do some
benchmarks with the patch on and off, to see the performance difference.

> 
>>  2. Instead of dynamically assigning new luns when not available, what
>> about taking a list of lun ids instead?
>>
> 
> Seems you prefer user make the choice ?

Yes, I want it to be deterministic. For example, if we do it
dynamically, the user might first allocate 2-4, and then allocate 1-3 ,
which will actually allocate 0,1,5. Then later, a user tries to allocate
on 0, and instead gets returned 6. It quickly makes it difficult to use.

> But the target creation can still fail if one of the list lun ids is
> not available although there may be enough free luns.

Agree, the user would have to look up the free luns and then resubmit
the target allocation.
--
To unsubscribe from this list: send the line "unsubscribe linux-block" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Wenwei Tao Jan. 28, 2016, 10:19 a.m. UTC | #3
OK, I see. Will include these changes in next version.

2016-01-28 17:09 GMT+08:00 Matias Bjørling <mb@lightnvm.io>:
> On 01/28/2016 09:50 AM, Wenwei Tao wrote:
>> 2016-01-27 17:44 GMT+08:00 Matias Bjørling <mb@lightnvm.io>:
>>> On 01/26/2016 01:33 PM, Wenwei Tao wrote:
>>>> When create a target, we specify the begin lunid and
>>>> the end lunid, and get the corresponding continuous
>>>> luns from media manager, if one of the luns is not free,
>>>> we failed to create the target, even if the device's
>>>> total free luns are enough.
>>>>
>>>> So add non-continuous lun target creation support,
>>>> thus we can improve the backend device's space utilization.
>>>> Signed-off-by: Wenwei Tao <ww.tao0320@gmail.com>
>>>> ---
>>>> Changes since v1:
>>>> -use NVM_FIXED instead NVM_C_FIXED in gennvm_get_lun
>>>> -add target creation flags check
>>>> -rebase to v4.5-rc1
>>>>
>>>>  drivers/lightnvm/core.c       |  36 ++++---
>>>>  drivers/lightnvm/gennvm.c     |  42 ++++++++-
>>>>  drivers/lightnvm/rrpc.c       | 215 +++++++++++++++++++++++++++---------------
>>>>  drivers/lightnvm/rrpc.h       |   6 +-
>>>>  include/linux/lightnvm.h      |  24 ++++-
>>>>  include/uapi/linux/lightnvm.h |   3 +
>>>>  6 files changed, 229 insertions(+), 97 deletions(-)
>>>>
>>>
>>> Hi Wenwei,
>>>
>>> I did some digging on the patch and changed the interface to a
>>> reserve/release interface. I also removed the logic to dynamically
>>> select another lun than the one requested.
>>>
>>> A couple of questions:
>>>
>>>  1. The rrpc_lun->rev_lock and rev_trans_map change; this might be for
>>> another patch, and it isn't directly related to continuous mapping?
>>
>> rrpc_lun->rev_lock and rev_trans_map change is related to
>> non-continuous mapping, it's not directly related to continuous
>> mapping.
>> Put this change in  another patch along with non-continuous mapping
>> support and this patch would be only add reserve/release thing, is
>> that your suggestion?
>
> Yes, that would be great. Then we keep it separate. I'll like to do some
> benchmarks with the patch on and off, to see the performance difference.
>
>>
>>>  2. Instead of dynamically assigning new luns when not available, what
>>> about taking a list of lun ids instead?
>>>
>>
>> Seems you prefer user make the choice ?
>
> Yes, I want it to be deterministic. For example, if we do it
> dynamically, the user might first allocate 2-4, and then allocate 1-3 ,
> which will actually allocate 0,1,5. Then later, a user tries to allocate
> on 0, and instead gets returned 6. It quickly makes it difficult to use.
>
>> But the target creation can still fail if one of the list lun ids is
>> not available although there may be enough free luns.
>
> Agree, the user would have to look up the free luns and then resubmit
> the target allocation.
--
To unsubscribe from this list: send the line "unsubscribe linux-block" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Patch
diff mbox

diff --git a/drivers/lightnvm/core.c b/drivers/lightnvm/core.c
index 27a59e8..59a4bf9 100644
--- a/drivers/lightnvm/core.c
+++ b/drivers/lightnvm/core.c
@@ -468,6 +468,11 @@  static int nvm_core_init(struct nvm_dev *dev)
 				dev->luns_per_chnl *
 				dev->nr_chnls;
 	dev->total_pages = dev->total_blocks * dev->pgs_per_blk;
+	dev->lun_map = kcalloc(BITS_TO_LONGS(dev->nr_luns),
+			sizeof(unsigned long), GFP_KERNEL);
+	if (!dev->lun_map)
+		return -ENOMEM;
+
 	INIT_LIST_HEAD(&dev->online_targets);
 	mutex_init(&dev->mlock);
 	spin_lock_init(&dev->lock);
@@ -610,6 +615,7 @@  void nvm_unregister(char *disk_name)
 	up_write(&nvm_lock);

 	nvm_exit(dev);
+	kfree(dev->lun_map);
 	kfree(dev);
 }
 EXPORT_SYMBOL(nvm_unregister);
diff --git a/drivers/lightnvm/gennvm.c b/drivers/lightnvm/gennvm.c
index 6e2685d..6419898 100644
--- a/drivers/lightnvm/gennvm.c
+++ b/drivers/lightnvm/gennvm.c
@@ -188,6 +188,9 @@  static int gennvm_block_map(u64 slba, u32 nlb,
__le64 *entries, void *private)
 		lun_id = div_u64(pba, dev->sec_per_lun);
 		lun = &gn->luns[lun_id];

+		if (!test_bit(lun_id, dev->lun_map))
+			__set_bit(lun_id, dev->lun_map);
+
 		/* Calculate block offset into lun */
 		pba = pba - (dev->sec_per_lun * lun_id);
 		blk = &lun->vlun.blocks[div_u64(pba, dev->sec_per_blk)];
@@ -478,10 +481,23 @@  static int gennvm_erase_blk(struct nvm_dev *dev,
struct nvm_block *blk,
 	return nvm_erase_ppa(dev, &addr, 1);
 }

+static int gennvm_reserve_lun(struct nvm_dev *dev, int lunid)
+{
+	return test_and_set_bit(lunid, dev->lun_map);
+}
+
+static void gennvm_release_lun(struct nvm_dev *dev, int lunid)
+{
+	WARN_ON(!test_and_clear_bit(lunid, dev->lun_map));
+}
+
 static struct nvm_lun *gennvm_get_lun(struct nvm_dev *dev, int lunid)
 {
 	struct gen_nvm *gn = dev->mp;

+	if (unlikely(lunid >= dev->nr_luns))
+		return NULL;
+
 	return &gn->luns[lunid].vlun;
 }

@@ -523,6 +539,8 @@  static struct nvmm_type gennvm = {
 	.erase_blk		= gennvm_erase_blk,

 	.get_lun		= gennvm_get_lun,
+	.reserve_lun		= gennvm_reserve_lun,
+	.release_lun		= gennvm_release_lun,
 	.lun_info_print		= gennvm_lun_info_print,

 	.get_area		= gennvm_get_area,
diff --git a/drivers/lightnvm/rrpc.c b/drivers/lightnvm/rrpc.c
index 20afe1c..0a99ebc 100644
--- a/drivers/lightnvm/rrpc.c
+++ b/drivers/lightnvm/rrpc.c
@@ -26,25 +26,32 @@  static int rrpc_submit_io(struct rrpc *rrpc, struct
bio *bio,
 		for ((i) = 0, rlun = &(rrpc)->luns[0]; \
 			(i) < (rrpc)->nr_luns; (i)++, rlun = &(rrpc)->luns[(i)])

+static inline u64 lun_poffset(struct nvm_dev *dev, struct nvm_lun *lun)
+{
+	return lun->id * dev->sec_per_lun;
+}
+
 static void rrpc_page_invalidate(struct rrpc *rrpc, struct rrpc_addr *a)
 {
 	struct rrpc_block *rblk = a->rblk;
-	unsigned int pg_offset;
+	struct rrpc_lun *rlun = rblk->rlun;
+	u64 pg_offset;

-	lockdep_assert_held(&rrpc->rev_lock);
+	lockdep_assert_held(&rlun->rev_lock);

 	if (a->addr == ADDR_EMPTY || !rblk)
 		return;

 	spin_lock(&rblk->lock);

-	div_u64_rem(a->addr, rrpc->dev->pgs_per_blk, &pg_offset);
+	div_u64_rem(a->addr, rrpc->dev->pgs_per_blk, (u32 *)&pg_offset);
 	WARN_ON(test_and_set_bit(pg_offset, rblk->invalid_pages));
 	rblk->nr_invalid_pages++;

 	spin_unlock(&rblk->lock);

-	rrpc->rev_trans_map[a->addr - rrpc->poffset].addr = ADDR_EMPTY;
+	pg_offset = lun_poffset(rrpc->dev, rlun->parent);
+	rlun->rev_trans_map[a->addr - pg_offset].addr = ADDR_EMPTY;
 }

 static void rrpc_invalidate_range(struct rrpc *rrpc, sector_t slba,
@@ -52,14 +59,15 @@  static void rrpc_invalidate_range(struct rrpc *rrpc,
sector_t slba,
 {
 	sector_t i;

-	spin_lock(&rrpc->rev_lock);
 	for (i = slba; i < slba + len; i++) {
 		struct rrpc_addr *gp = &rrpc->trans_map[i];
+		struct rrpc_lun *rlun = gp->rblk->rlun;

+		spin_lock(&rlun->rev_lock);
 		rrpc_page_invalidate(rrpc, gp);
+		spin_unlock(&rlun->rev_lock);
 		gp->rblk = NULL;
 	}
-	spin_unlock(&rrpc->rev_lock);
 }

 static struct nvm_rq *rrpc_inflight_laddr_acquire(struct rrpc *rrpc,
@@ -281,13 +289,14 @@  static void rrpc_end_sync_bio(struct bio *bio)
 static int rrpc_move_valid_pages(struct rrpc *rrpc, struct rrpc_block
*rblk)
 {
 	struct request_queue *q = rrpc->dev->q;
+	struct rrpc_lun *rlun = rblk->rlun;
 	struct rrpc_rev_addr *rev;
 	struct nvm_rq *rqd;
 	struct bio *bio;
 	struct page *page;
 	int slot;
 	int nr_pgs_per_blk = rrpc->dev->pgs_per_blk;
-	u64 phys_addr;
+	u64 phys_addr, poffset;
 	DECLARE_COMPLETION_ONSTACK(wait);

 	if (bitmap_full(rblk->invalid_pages, nr_pgs_per_blk))
@@ -303,6 +312,7 @@  static int rrpc_move_valid_pages(struct rrpc *rrpc,
struct rrpc_block *rblk)
 	if (!page)
 		return -ENOMEM;

+	poffset = lun_poffset(rrpc->dev, rlun->parent);
 	while ((slot = find_first_zero_bit(rblk->invalid_pages,
 					    nr_pgs_per_blk)) < nr_pgs_per_blk) {

@@ -310,23 +320,23 @@  static int rrpc_move_valid_pages(struct rrpc
*rrpc, struct rrpc_block *rblk)
 		phys_addr = (rblk->parent->id * nr_pgs_per_blk) + slot;

 try:
-		spin_lock(&rrpc->rev_lock);
+		spin_lock(&rlun->rev_lock);
 		/* Get logical address from physical to logical table */
-		rev = &rrpc->rev_trans_map[phys_addr - rrpc->poffset];
+		rev = &rlun->rev_trans_map[phys_addr - poffset];
 		/* already updated by previous regular write */
 		if (rev->addr == ADDR_EMPTY) {
-			spin_unlock(&rrpc->rev_lock);
+			spin_unlock(&rlun->rev_lock);
 			continue;
 		}

 		rqd = rrpc_inflight_laddr_acquire(rrpc, rev->addr, 1);
 		if (IS_ERR_OR_NULL(rqd)) {
-			spin_unlock(&rrpc->rev_lock);
+			spin_unlock(&rlun->rev_lock);
 			schedule();
 			goto try;
 		}

-		spin_unlock(&rrpc->rev_lock);
+		spin_unlock(&rlun->rev_lock);

 		/* Perform read to do GC */
 		bio->bi_iter.bi_sector = rrpc_get_sector(rev->addr);
@@ -395,7 +405,7 @@  static void rrpc_block_gc(struct work_struct *work)
 	struct rrpc_block *rblk = gcb->rblk;
 	struct nvm_dev *dev = rrpc->dev;
 	struct nvm_lun *lun = rblk->parent->lun;
-	struct rrpc_lun *rlun = &rrpc->luns[lun->id - rrpc->lun_offset];
+	struct rrpc_lun *rlun = lun->private;

 	mempool_free(gcb, rrpc->gcb_pool);
 	pr_debug("nvm: block '%lu' being reclaimed\n", rblk->parent->id);
@@ -496,9 +506,9 @@  static void rrpc_gc_queue(struct work_struct *work)
 									ws_gc);
 	struct rrpc *rrpc = gcb->rrpc;
 	struct rrpc_block *rblk = gcb->rblk;
-	struct nvm_lun *lun = rblk->parent->lun;
 	struct nvm_block *blk = rblk->parent;
-	struct rrpc_lun *rlun = &rrpc->luns[lun->id - rrpc->lun_offset];
+	struct nvm_lun *lun = blk->lun;
+	struct rrpc_lun *rlun = lun->private;

 	spin_lock(&rlun->lock);
 	list_add_tail(&rblk->prio, &rlun->prio_list);
@@ -549,22 +559,24 @@  static struct rrpc_lun *rrpc_get_lun_rr(struct
rrpc *rrpc, int is_gc)
 static struct rrpc_addr *rrpc_update_map(struct rrpc *rrpc, sector_t laddr,
 					struct rrpc_block *rblk, u64 paddr)
 {
+	struct rrpc_lun *rlun = rblk->rlun;
 	struct rrpc_addr *gp;
 	struct rrpc_rev_addr *rev;
+	u64 poffset = lun_poffset(rrpc->dev, rlun->parent);

 	BUG_ON(laddr >= rrpc->nr_pages);

 	gp = &rrpc->trans_map[laddr];
-	spin_lock(&rrpc->rev_lock);
+	spin_lock(&rlun->rev_lock);
 	if (gp->rblk)
 		rrpc_page_invalidate(rrpc, gp);

 	gp->addr = paddr;
 	gp->rblk = rblk;

-	rev = &rrpc->rev_trans_map[gp->addr - rrpc->poffset];
+	rev = &rlun->rev_trans_map[gp->addr - poffset];
 	rev->addr = laddr;
-	spin_unlock(&rrpc->rev_lock);
+	spin_unlock(&rlun->rev_lock);

 	return gp;
 }
@@ -953,8 +965,6 @@  static void rrpc_requeue(struct work_struct *work)

 static void rrpc_gc_free(struct rrpc *rrpc)
 {
-	struct rrpc_lun *rlun;
-	int i;

 	if (rrpc->krqd_wq)
 		destroy_workqueue(rrpc->krqd_wq);
@@ -962,16 +972,6 @@  static void rrpc_gc_free(struct rrpc *rrpc)
 	if (rrpc->kgc_wq)
 		destroy_workqueue(rrpc->kgc_wq);

-	if (!rrpc->luns)
-		return;
-
-	for (i = 0; i < rrpc->nr_luns; i++) {
-		rlun = &rrpc->luns[i];
-
-		if (!rlun->blocks)
-			break;
-		vfree(rlun->blocks);
-	}
 }

 static int rrpc_gc_init(struct rrpc *rrpc)
@@ -992,7 +992,6 @@  static int rrpc_gc_init(struct rrpc *rrpc)

 static void rrpc_map_free(struct rrpc *rrpc)
 {
-	vfree(rrpc->rev_trans_map);
 	vfree(rrpc->trans_map);
 }

@@ -1000,19 +999,28 @@  static int rrpc_l2p_update(u64 slba, u32 nlb,
__le64 *entries, void *private)
 {
 	struct rrpc *rrpc = (struct rrpc *)private;
 	struct nvm_dev *dev = rrpc->dev;
-	struct rrpc_addr *addr = rrpc->trans_map + slba;
-	struct rrpc_rev_addr *raddr = rrpc->rev_trans_map;
+	struct rrpc_addr *addr;
+	struct rrpc_rev_addr *raddr;
 	sector_t max_pages = dev->total_pages * (dev->sec_size >> 9);
-	u64 elba = slba + nlb;
-	u64 i;
+	int page_size = dev->sec_per_pg * dev->sec_size;
+	u64 elba, i;
+
+	elba = slba + nlb;

 	if (unlikely(elba > dev->total_pages)) {
 		pr_err("nvm: L2P data from device is out of bounds!\n");
 		return -EINVAL;
 	}

+	slba -= rrpc->soffset >> (ilog2(page_size) - 9);
+	addr = rrpc->trans_map + slba;
 	for (i = 0; i < nlb; i++) {
+		struct rrpc_lun *rlun;
+		struct nvm_lun *lun;
 		u64 pba = le64_to_cpu(entries[i]);
+		u64 poffset;
+		int lunid;
+
 		/* LNVM treats address-spaces as silos, LBA and PBA are
 		 * equally large and zero-indexed.
 		 */
@@ -1028,8 +1036,15 @@  static int rrpc_l2p_update(u64 slba, u32 nlb,
__le64 *entries, void *private)
 		if (!pba)
 			continue;

+		lunid = div_u64(pba, dev->sec_per_lun);
+		lun = dev->mt->get_lun(dev, lunid);
+		if (unlikely(!lun))
+			return -EINVAL;
+		rlun = lun->private;
+		raddr = rlun->rev_trans_map;
+		poffset = lun_poffset(dev, lun);
 		addr[i].addr = pba;
-		raddr[pba].addr = slba + i;
+		raddr[pba - poffset].addr = slba + i;
 	}

 	return 0;
@@ -1049,17 +1064,11 @@  static int rrpc_map_init(struct rrpc *rrpc)
 	if (!rrpc->trans_map)
 		return -ENOMEM;

-	rrpc->rev_trans_map = vmalloc(sizeof(struct rrpc_rev_addr)
-							* rrpc->nr_pages);
-	if (!rrpc->rev_trans_map)
-		return -ENOMEM;

 	for (i = 0; i < rrpc->nr_pages; i++) {
 		struct rrpc_addr *p = &rrpc->trans_map[i];
-		struct rrpc_rev_addr *r = &rrpc->rev_trans_map[i];

 		p->addr = ADDR_EMPTY;
-		r->addr = ADDR_EMPTY;
 	}

 	if (!dev->ops->get_l2p_tbl)
@@ -1130,22 +1139,86 @@  static void rrpc_core_free(struct rrpc *rrpc)

 static void rrpc_luns_free(struct rrpc *rrpc)
 {
+	struct nvm_dev *dev = rrpc->dev;
+	struct rrpc_lun *rlun;
+	struct nvm_lun *lun;
+	int i;
+
+	if (!rrpc->luns)
+		return;
+
+	for (i = 0; i < rrpc->nr_luns; i++) {
+		rlun = &rrpc->luns[i];
+		if (!rlun)
+			break;
+		lun = rlun->parent;
+		dev->mt->release_lun(dev, lun->id);
+		vfree(rlun->rev_trans_map);
+		vfree(rlun->blocks);
+	}
 	kfree(rrpc->luns);
+	rrpc->luns = NULL;
+
+}
+
+static int rrpc_lun_init(struct rrpc *rrpc, struct rrpc_lun *rlun,
+							struct nvm_lun *lun)
+{
+	struct nvm_dev *dev = rrpc->dev;
+	int i;
+
+	rlun->rrpc = rrpc;
+	rlun->parent = lun;
+
+	rlun->rev_trans_map = vmalloc(sizeof(struct rrpc_rev_addr) *
+							dev->sec_per_lun);
+	if (!rlun->rev_trans_map)
+		return -ENOMEM;
+
+	for (i = 0; i < dev->sec_per_lun; i++) {
+		struct rrpc_rev_addr *r = &rlun->rev_trans_map[i];
+
+		r->addr = ADDR_EMPTY;
+	}
+
+	rlun->blocks = vzalloc(sizeof(struct rrpc_block) * dev->blks_per_lun);
+	if (!rlun->blocks) {
+		vfree(rlun->rev_trans_map);
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < dev->blks_per_lun; i++) {
+		struct rrpc_block *rblk = &rlun->blocks[i];
+		struct nvm_block *blk = &lun->blocks[i];
+
+		rblk->parent = blk;
+		rblk->rlun = rlun;
+		INIT_LIST_HEAD(&rblk->prio);
+		spin_lock_init(&rblk->lock);
+	}
+
+	lun->private = rlun;
+	INIT_LIST_HEAD(&rlun->prio_list);
+	INIT_LIST_HEAD(&rlun->open_list);
+	INIT_LIST_HEAD(&rlun->closed_list);
+	INIT_WORK(&rlun->ws_gc, rrpc_lun_gc);
+	spin_lock_init(&rlun->lock);
+	spin_lock_init(&rlun->rev_lock);
+
+	return 0;
 }

 static int rrpc_luns_init(struct rrpc *rrpc, int lun_begin, int lun_end)
 {
 	struct nvm_dev *dev = rrpc->dev;
 	struct rrpc_lun *rlun;
-	int i, j;
+	int i, ret;

 	if (dev->pgs_per_blk > MAX_INVALID_PAGES_STORAGE * BITS_PER_LONG) {
 		pr_err("rrpc: number of pages per block too high.");
 		return -EINVAL;
 	}

-	spin_lock_init(&rrpc->rev_lock);
-
 	rrpc->luns = kcalloc(rrpc->nr_luns, sizeof(struct rrpc_lun),
 								GFP_KERNEL);
 	if (!rrpc->luns)
@@ -1153,40 +1226,35 @@  static int rrpc_luns_init(struct rrpc *rrpc, int
lun_begin, int lun_end)

 	/* 1:1 mapping */
 	for (i = 0; i < rrpc->nr_luns; i++) {
-		struct nvm_lun *lun = dev->mt->get_lun(dev, lun_begin + i);
+		int lunid = lun_begin + i;
+		struct nvm_lun *lun;
+
+		if (dev->mt->reserve_lun(dev, lunid)) {
+			pr_err("rrpc: lun %u is already allocated\n", lunid);
+			ret = -EINVAL;
+			goto err;
+		}
+
+		lun = dev->mt->get_lun(dev, lunid);
+		if (!lun) {
+			ret = -EINVAL;
+			goto err;
+		}

 		rlun = &rrpc->luns[i];
-		rlun->rrpc = rrpc;
-		rlun->parent = lun;
-		INIT_LIST_HEAD(&rlun->prio_list);
-		INIT_LIST_HEAD(&rlun->open_list);
-		INIT_LIST_HEAD(&rlun->closed_list);
-
-		INIT_WORK(&rlun->ws_gc, rrpc_lun_gc);
-		spin_lock_init(&rlun->lock);
+		ret = rrpc_lun_init(rrpc, rlun, lun);
+		if (ret)
+			goto err;

 		rrpc->total_blocks += dev->blks_per_lun;
 		rrpc->nr_pages += dev->sec_per_lun;
-
-		rlun->blocks = vzalloc(sizeof(struct rrpc_block) *
-						rrpc->dev->blks_per_lun);
-		if (!rlun->blocks)
-			goto err;
-
-		for (j = 0; j < rrpc->dev->blks_per_lun; j++) {
-			struct rrpc_block *rblk = &rlun->blocks[j];
-			struct nvm_block *blk = &lun->blocks[j];
-
-			rblk->parent = blk;
-			rblk->rlun = rlun;
-			INIT_LIST_HEAD(&rblk->prio);
-			spin_lock_init(&rblk->lock);
-		}
 	}

 	return 0;
 err:
-	return -ENOMEM;
+	rrpc_luns_free(rrpc);
+	return ret;
+
 }

 /* returns 0 on success and stores the beginning address in *begin */
@@ -1258,14 +1326,16 @@  static sector_t rrpc_capacity(void *private)
 static void rrpc_block_map_update(struct rrpc *rrpc, struct rrpc_block
*rblk)
 {
 	struct nvm_dev *dev = rrpc->dev;
+	struct rrpc_lun *rlun = rblk->rlun;
 	int offset;
 	struct rrpc_addr *laddr;
-	u64 paddr, pladdr;
+	u64 paddr, pladdr, poffset;

+	poffset = lun_poffset(dev, rlun->parent);
 	for (offset = 0; offset < dev->pgs_per_blk; offset++) {
 		paddr = block_to_addr(rrpc, rblk) + offset;

-		pladdr = rrpc->rev_trans_map[paddr].addr;
+		pladdr = rlun->rev_trans_map[paddr - poffset].addr;
 		if (pladdr == ADDR_EMPTY)
 			continue;

@@ -1374,9 +1444,6 @@  static void *rrpc_init(struct nvm_dev *dev, struct
gendisk *tdisk,
 		goto err;
 	}

-	rrpc->poffset = dev->sec_per_lun * lun_begin;
-	rrpc->lun_offset = lun_begin;
-
 	ret = rrpc_core_init(rrpc);
 	if (ret) {
 		pr_err("nvm: rrpc: could not initialize core\n");
diff --git a/drivers/lightnvm/rrpc.h b/drivers/lightnvm/rrpc.h
index 9380c68..4d756d8 100644
--- a/drivers/lightnvm/rrpc.h
+++ b/drivers/lightnvm/rrpc.h
@@ -86,6 +86,9 @@  struct rrpc_lun {
 					 */

 	struct work_struct ws_gc;
+	/* store a reverse map for garbage collection */
+	struct rrpc_rev_addr *rev_trans_map;
+	spinlock_t rev_lock;

 	spinlock_t lock;
 };
@@ -124,9 +127,6 @@  struct rrpc {
 	 * addresses are used when writing to the disk block device.
 	 */
 	struct rrpc_addr *trans_map;
-	/* also store a reverse map for garbage collection */
-	struct rrpc_rev_addr *rev_trans_map;
-	spinlock_t rev_lock;

 	struct rrpc_inflight inflights;

diff --git a/include/linux/lightnvm.h b/include/linux/lightnvm.h
index 18f1bb0..a33af4f 100644
--- a/include/linux/lightnvm.h
+++ b/include/linux/lightnvm.h
@@ -271,6 +271,7 @@  struct nvm_lun {
 	spinlock_t lock;

 	struct nvm_block *blocks;
+	void *private;
 };

 enum {
@@ -342,6 +343,8 @@  struct nvm_dev {
 	int nr_luns;
 	unsigned max_pages_per_blk;

+	unsigned long *lun_map;
+
 	void *ppalist_pool;

 	struct nvm_id identity;
@@ -462,6 +465,8 @@  typedef int (nvmm_submit_io_fn)(struct nvm_dev *,
struct nvm_rq *);
 typedef int (nvmm_erase_blk_fn)(struct nvm_dev *, struct nvm_block *,
 								unsigned long);
 typedef struct nvm_lun *(nvmm_get_lun_fn)(struct nvm_dev *, int);
+typedef int (nvmm_reserve_lun(struct nvm_dev *, int));
+typedef void (nvmm_release_lun(struct nvm_dev *, int));
 typedef void (nvmm_lun_info_print_fn)(struct nvm_dev *);