]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blobdiff - drivers/lightnvm/gennvm.c
Merge tag 'armsoc-arm64' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
[mirror_ubuntu-zesty-kernel.git] / drivers / lightnvm / gennvm.c
index b74174c6d021b02a0bf311b2b4d83c1e6843bf40..ca7880082d80648647e6d08258d25afedbbb1e90 100644 (file)
@@ -35,6 +35,165 @@ static const struct block_device_operations gen_fops = {
        .owner          = THIS_MODULE,
 };
 
+static int gen_reserve_luns(struct nvm_dev *dev, struct nvm_target *t,
+                           int lun_begin, int lun_end)
+{
+       int i;
+
+       for (i = lun_begin; i <= lun_end; i++) {
+               if (test_and_set_bit(i, dev->lun_map)) {
+                       pr_err("nvm: lun %d already allocated\n", i);
+                       goto err;
+               }
+       }
+
+       return 0;
+
+err:
+       while (--i > lun_begin)
+               clear_bit(i, dev->lun_map);
+
+       return -EBUSY;
+}
+
+static void gen_release_luns_err(struct nvm_dev *dev, int lun_begin,
+                                int lun_end)
+{
+       int i;
+
+       for (i = lun_begin; i <= lun_end; i++)
+               WARN_ON(!test_and_clear_bit(i, dev->lun_map));
+}
+
+static void gen_remove_tgt_dev(struct nvm_tgt_dev *tgt_dev)
+{
+       struct nvm_dev *dev = tgt_dev->parent;
+       struct gen_dev_map *dev_map = tgt_dev->map;
+       int i, j;
+
+       for (i = 0; i < dev_map->nr_chnls; i++) {
+               struct gen_ch_map *ch_map = &dev_map->chnls[i];
+               int *lun_offs = ch_map->lun_offs;
+               int ch = i + ch_map->ch_off;
+
+               for (j = 0; j < ch_map->nr_luns; j++) {
+                       int lun = j + lun_offs[j];
+                       int lunid = (ch * dev->geo.luns_per_chnl) + lun;
+
+                       WARN_ON(!test_and_clear_bit(lunid, dev->lun_map));
+               }
+
+               kfree(ch_map->lun_offs);
+       }
+
+       kfree(dev_map->chnls);
+       kfree(dev_map);
+       kfree(tgt_dev->luns);
+       kfree(tgt_dev);
+}
+
+static struct nvm_tgt_dev *gen_create_tgt_dev(struct nvm_dev *dev,
+                                             int lun_begin, int lun_end)
+{
+       struct nvm_tgt_dev *tgt_dev = NULL;
+       struct gen_dev_map *dev_rmap = dev->rmap;
+       struct gen_dev_map *dev_map;
+       struct ppa_addr *luns;
+       int nr_luns = lun_end - lun_begin + 1;
+       int luns_left = nr_luns;
+       int nr_chnls = nr_luns / dev->geo.luns_per_chnl;
+       int nr_chnls_mod = nr_luns % dev->geo.luns_per_chnl;
+       int bch = lun_begin / dev->geo.luns_per_chnl;
+       int blun = lun_begin % dev->geo.luns_per_chnl;
+       int lunid = 0;
+       int lun_balanced = 1;
+       int prev_nr_luns;
+       int i, j;
+
+       nr_chnls = nr_luns / dev->geo.luns_per_chnl;
+       nr_chnls = (nr_chnls_mod == 0) ? nr_chnls : nr_chnls + 1;
+
+       dev_map = kmalloc(sizeof(struct gen_dev_map), GFP_KERNEL);
+       if (!dev_map)
+               goto err_dev;
+
+       dev_map->chnls = kcalloc(nr_chnls, sizeof(struct gen_ch_map),
+                                                               GFP_KERNEL);
+       if (!dev_map->chnls)
+               goto err_chnls;
+
+       luns = kcalloc(nr_luns, sizeof(struct ppa_addr), GFP_KERNEL);
+       if (!luns)
+               goto err_luns;
+
+       prev_nr_luns = (luns_left > dev->geo.luns_per_chnl) ?
+                                       dev->geo.luns_per_chnl : luns_left;
+       for (i = 0; i < nr_chnls; i++) {
+               struct gen_ch_map *ch_rmap = &dev_rmap->chnls[i + bch];
+               int *lun_roffs = ch_rmap->lun_offs;
+               struct gen_ch_map *ch_map = &dev_map->chnls[i];
+               int *lun_offs;
+               int luns_in_chnl = (luns_left > dev->geo.luns_per_chnl) ?
+                                       dev->geo.luns_per_chnl : luns_left;
+
+               if (lun_balanced && prev_nr_luns != luns_in_chnl)
+                       lun_balanced = 0;
+
+               ch_map->ch_off = ch_rmap->ch_off = bch;
+               ch_map->nr_luns = luns_in_chnl;
+
+               lun_offs = kcalloc(luns_in_chnl, sizeof(int), GFP_KERNEL);
+               if (!lun_offs)
+                       goto err_ch;
+
+               for (j = 0; j < luns_in_chnl; j++) {
+                       luns[lunid].ppa = 0;
+                       luns[lunid].g.ch = i;
+                       luns[lunid++].g.lun = j;
+
+                       lun_offs[j] = blun;
+                       lun_roffs[j + blun] = blun;
+               }
+
+               ch_map->lun_offs = lun_offs;
+
+               /* when starting a new channel, lun offset is reset */
+               blun = 0;
+               luns_left -= luns_in_chnl;
+       }
+
+       dev_map->nr_chnls = nr_chnls;
+
+       tgt_dev = kmalloc(sizeof(struct nvm_tgt_dev), GFP_KERNEL);
+       if (!tgt_dev)
+               goto err_ch;
+
+       memcpy(&tgt_dev->geo, &dev->geo, sizeof(struct nvm_geo));
+       /* Target device only owns a portion of the physical device */
+       tgt_dev->geo.nr_chnls = nr_chnls;
+       tgt_dev->geo.nr_luns = nr_luns;
+       tgt_dev->geo.luns_per_chnl = (lun_balanced) ? prev_nr_luns : -1;
+       tgt_dev->total_secs = nr_luns * tgt_dev->geo.sec_per_lun;
+       tgt_dev->q = dev->q;
+       tgt_dev->map = dev_map;
+       tgt_dev->luns = luns;
+       memcpy(&tgt_dev->identity, &dev->identity, sizeof(struct nvm_id));
+
+       tgt_dev->parent = dev;
+
+       return tgt_dev;
+err_ch:
+       while (--i > 0)
+               kfree(dev_map->chnls[i].lun_offs);
+       kfree(luns);
+err_luns:
+       kfree(dev_map->chnls);
+err_chnls:
+       kfree(dev_map);
+err_dev:
+       return tgt_dev;
+}
+
 static int gen_create_tgt(struct nvm_dev *dev, struct nvm_ioctl_create *create)
 {
        struct gen_dev *gn = dev->mp;
@@ -43,6 +202,7 @@ static int gen_create_tgt(struct nvm_dev *dev, struct nvm_ioctl_create *create)
        struct gendisk *tdisk;
        struct nvm_tgt_type *tt;
        struct nvm_target *t;
+       struct nvm_tgt_dev *tgt_dev;
        void *targetdata;
 
        tt = nvm_find_target_type(create->tgttype, 1);
@@ -64,9 +224,18 @@ static int gen_create_tgt(struct nvm_dev *dev, struct nvm_ioctl_create *create)
        if (!t)
                return -ENOMEM;
 
+       if (gen_reserve_luns(dev, t, s->lun_begin, s->lun_end))
+               goto err_t;
+
+       tgt_dev = gen_create_tgt_dev(dev, s->lun_begin, s->lun_end);
+       if (!tgt_dev) {
+               pr_err("nvm: could not create target device\n");
+               goto err_reserve;
+       }
+
        tqueue = blk_alloc_queue_node(GFP_KERNEL, dev->q->node);
        if (!tqueue)
-               goto err_t;
+               goto err_dev;
        blk_queue_make_request(tqueue, tt->make_rq);
 
        tdisk = alloc_disk(0);
@@ -80,7 +249,7 @@ static int gen_create_tgt(struct nvm_dev *dev, struct nvm_ioctl_create *create)
        tdisk->fops = &gen_fops;
        tdisk->queue = tqueue;
 
-       targetdata = tt->init(dev, tdisk, s->lun_begin, s->lun_end);
+       targetdata = tt->init(tgt_dev, tdisk);
        if (IS_ERR(targetdata))
                goto err_init;
 
@@ -94,7 +263,7 @@ static int gen_create_tgt(struct nvm_dev *dev, struct nvm_ioctl_create *create)
 
        t->type = tt;
        t->disk = tdisk;
-       t->dev = dev;
+       t->dev = tgt_dev;
 
        mutex_lock(&gn->lock);
        list_add_tail(&t->list, &gn->targets);
@@ -105,6 +274,10 @@ err_init:
        put_disk(tdisk);
 err_queue:
        blk_cleanup_queue(tqueue);
+err_dev:
+       kfree(tgt_dev);
+err_reserve:
+       gen_release_luns_err(dev, s->lun_begin, s->lun_end);
 err_t:
        kfree(t);
        return -ENOMEM;
@@ -122,6 +295,7 @@ static void __gen_remove_target(struct nvm_target *t)
        if (tt->exit)
                tt->exit(tdisk->private_data);
 
+       gen_remove_tgt_dev(t->dev);
        put_disk(tdisk);
 
        list_del(&t->list);
@@ -160,10 +334,11 @@ static int gen_remove_tgt(struct nvm_dev *dev, struct nvm_ioctl_remove *remove)
 
 static int gen_get_area(struct nvm_dev *dev, sector_t *lba, sector_t len)
 {
+       struct nvm_geo *geo = &dev->geo;
        struct gen_dev *gn = dev->mp;
        struct gen_area *area, *prev, *next;
        sector_t begin = 0;
-       sector_t max_sectors = (dev->sec_size * dev->total_secs) >> 9;
+       sector_t max_sectors = (geo->sec_size * dev->total_secs) >> 9;
 
        if (len > max_sectors)
                return -EINVAL;
@@ -220,240 +395,74 @@ static void gen_put_area(struct nvm_dev *dev, sector_t begin)
        spin_unlock(&dev->lock);
 }
 
-static void gen_blocks_free(struct nvm_dev *dev)
-{
-       struct gen_dev *gn = dev->mp;
-       struct gen_lun *lun;
-       int i;
-
-       gen_for_each_lun(gn, lun, i) {
-               if (!lun->vlun.blocks)
-                       break;
-               vfree(lun->vlun.blocks);
-       }
-}
-
-static void gen_luns_free(struct nvm_dev *dev)
-{
-       struct gen_dev *gn = dev->mp;
-
-       kfree(gn->luns);
-}
-
-static int gen_luns_init(struct nvm_dev *dev, struct gen_dev *gn)
-{
-       struct gen_lun *lun;
-       int i;
-
-       gn->luns = kcalloc(dev->nr_luns, sizeof(struct gen_lun), GFP_KERNEL);
-       if (!gn->luns)
-               return -ENOMEM;
-
-       gen_for_each_lun(gn, lun, i) {
-               spin_lock_init(&lun->vlun.lock);
-               INIT_LIST_HEAD(&lun->free_list);
-               INIT_LIST_HEAD(&lun->used_list);
-               INIT_LIST_HEAD(&lun->bb_list);
-
-               lun->reserved_blocks = 2; /* for GC only */
-               lun->vlun.id = i;
-               lun->vlun.lun_id = i % dev->luns_per_chnl;
-               lun->vlun.chnl_id = i / dev->luns_per_chnl;
-               lun->vlun.nr_free_blocks = dev->blks_per_lun;
-       }
-       return 0;
-}
-
-static int gen_block_bb(struct gen_dev *gn, struct ppa_addr ppa,
-                                                       u8 *blks, int nr_blks)
-{
-       struct nvm_dev *dev = gn->dev;
-       struct gen_lun *lun;
-       struct nvm_block *blk;
-       int i;
-
-       nr_blks = nvm_bb_tbl_fold(dev, blks, nr_blks);
-       if (nr_blks < 0)
-               return nr_blks;
-
-       lun = &gn->luns[(dev->luns_per_chnl * ppa.g.ch) + ppa.g.lun];
-
-       for (i = 0; i < nr_blks; i++) {
-               if (blks[i] == 0)
-                       continue;
-
-               blk = &lun->vlun.blocks[i];
-               list_move_tail(&blk->list, &lun->bb_list);
-               lun->vlun.nr_free_blocks--;
-       }
-
-       return 0;
-}
-
-static int gen_block_map(u64 slba, u32 nlb, __le64 *entries, void *private)
-{
-       struct nvm_dev *dev = private;
-       struct gen_dev *gn = dev->mp;
-       u64 elba = slba + nlb;
-       struct gen_lun *lun;
-       struct nvm_block *blk;
-       u64 i;
-       int lun_id;
-
-       if (unlikely(elba > dev->total_secs)) {
-               pr_err("gen: L2P data from device is out of bounds!\n");
-               return -EINVAL;
-       }
-
-       for (i = 0; i < nlb; i++) {
-               u64 pba = le64_to_cpu(entries[i]);
-
-               if (unlikely(pba >= dev->total_secs && pba != U64_MAX)) {
-                       pr_err("gen: L2P data entry is out of bounds!\n");
-                       return -EINVAL;
-               }
-
-               /* Address zero is a special one. The first page on a disk is
-                * protected. It often holds internal device boot
-                * information.
-                */
-               if (!pba)
-                       continue;
-
-               /* resolve block from physical address */
-               lun_id = div_u64(pba, dev->sec_per_lun);
-               lun = &gn->luns[lun_id];
-
-               /* Calculate block offset into lun */
-               pba = pba - (dev->sec_per_lun * lun_id);
-               blk = &lun->vlun.blocks[div_u64(pba, dev->sec_per_blk)];
-
-               if (!blk->state) {
-                       /* at this point, we don't know anything about the
-                        * block. It's up to the FTL on top to re-etablish the
-                        * block state. The block is assumed to be open.
-                        */
-                       list_move_tail(&blk->list, &lun->used_list);
-                       blk->state = NVM_BLK_ST_TGT;
-                       lun->vlun.nr_free_blocks--;
-               }
-       }
-
-       return 0;
-}
-
-static int gen_blocks_init(struct nvm_dev *dev, struct gen_dev *gn)
-{
-       struct gen_lun *lun;
-       struct nvm_block *block;
-       sector_t lun_iter, blk_iter, cur_block_id = 0;
-       int ret, nr_blks;
-       u8 *blks;
-
-       nr_blks = dev->blks_per_lun * dev->plane_mode;
-       blks = kmalloc(nr_blks, GFP_KERNEL);
-       if (!blks)
-               return -ENOMEM;
-
-       gen_for_each_lun(gn, lun, lun_iter) {
-               lun->vlun.blocks = vzalloc(sizeof(struct nvm_block) *
-                                                       dev->blks_per_lun);
-               if (!lun->vlun.blocks) {
-                       kfree(blks);
-                       return -ENOMEM;
-               }
-
-               for (blk_iter = 0; blk_iter < dev->blks_per_lun; blk_iter++) {
-                       block = &lun->vlun.blocks[blk_iter];
-
-                       INIT_LIST_HEAD(&block->list);
-
-                       block->lun = &lun->vlun;
-                       block->id = cur_block_id++;
-
-                       /* First block is reserved for device */
-                       if (unlikely(lun_iter == 0 && blk_iter == 0)) {
-                               lun->vlun.nr_free_blocks--;
-                               continue;
-                       }
-
-                       list_add_tail(&block->list, &lun->free_list);
-               }
-
-               if (dev->ops->get_bb_tbl) {
-                       struct ppa_addr ppa;
-
-                       ppa.ppa = 0;
-                       ppa.g.ch = lun->vlun.chnl_id;
-                       ppa.g.lun = lun->vlun.lun_id;
-
-                       ret = nvm_get_bb_tbl(dev, ppa, blks);
-                       if (ret)
-                               pr_err("gen: could not get BB table\n");
-
-                       ret = gen_block_bb(gn, ppa, blks, nr_blks);
-                       if (ret)
-                               pr_err("gen: BB table map failed\n");
-               }
-       }
-
-       if ((dev->identity.dom & NVM_RSP_L2P) && dev->ops->get_l2p_tbl) {
-               ret = dev->ops->get_l2p_tbl(dev, 0, dev->total_secs,
-                                                       gen_block_map, dev);
-               if (ret) {
-                       pr_err("gen: could not read L2P table.\n");
-                       pr_warn("gen: default block initialization");
-               }
-       }
-
-       kfree(blks);
-       return 0;
-}
-
 static void gen_free(struct nvm_dev *dev)
 {
-       gen_blocks_free(dev);
-       gen_luns_free(dev);
        kfree(dev->mp);
+       kfree(dev->rmap);
        dev->mp = NULL;
 }
 
 static int gen_register(struct nvm_dev *dev)
 {
        struct gen_dev *gn;
-       int ret;
+       struct gen_dev_map *dev_rmap;
+       int i, j;
 
        if (!try_module_get(THIS_MODULE))
                return -ENODEV;
 
        gn = kzalloc(sizeof(struct gen_dev), GFP_KERNEL);
        if (!gn)
-               return -ENOMEM;
+               goto err_gn;
+
+       dev_rmap = kmalloc(sizeof(struct gen_dev_map), GFP_KERNEL);
+       if (!dev_rmap)
+               goto err_rmap;
+
+       dev_rmap->chnls = kcalloc(dev->geo.nr_chnls, sizeof(struct gen_ch_map),
+                                                               GFP_KERNEL);
+       if (!dev_rmap->chnls)
+               goto err_chnls;
+
+       for (i = 0; i < dev->geo.nr_chnls; i++) {
+               struct gen_ch_map *ch_rmap;
+               int *lun_roffs;
+               int luns_in_chnl = dev->geo.luns_per_chnl;
+
+               ch_rmap = &dev_rmap->chnls[i];
+
+               ch_rmap->ch_off = -1;
+               ch_rmap->nr_luns = luns_in_chnl;
+
+               lun_roffs = kcalloc(luns_in_chnl, sizeof(int), GFP_KERNEL);
+               if (!lun_roffs)
+                       goto err_ch;
+
+               for (j = 0; j < luns_in_chnl; j++)
+                       lun_roffs[j] = -1;
+
+               ch_rmap->lun_offs = lun_roffs;
+       }
 
        gn->dev = dev;
-       gn->nr_luns = dev->nr_luns;
+       gn->nr_luns = dev->geo.nr_luns;
        INIT_LIST_HEAD(&gn->area_list);
        mutex_init(&gn->lock);
        INIT_LIST_HEAD(&gn->targets);
        dev->mp = gn;
-
-       ret = gen_luns_init(dev, gn);
-       if (ret) {
-               pr_err("gen: could not initialize luns\n");
-               goto err;
-       }
-
-       ret = gen_blocks_init(dev, gn);
-       if (ret) {
-               pr_err("gen: could not initialize blocks\n");
-               goto err;
-       }
+       dev->rmap = dev_rmap;
 
        return 1;
-err:
+err_ch:
+       while (--i >= 0)
+               kfree(dev_rmap->chnls[i].lun_offs);
+err_chnls:
+       kfree(dev_rmap);
+err_rmap:
        gen_free(dev);
+err_gn:
        module_put(THIS_MODULE);
-       return ret;
+       return -ENOMEM;
 }
 
 static void gen_unregister(struct nvm_dev *dev)
@@ -463,7 +472,7 @@ static void gen_unregister(struct nvm_dev *dev)
 
        mutex_lock(&gn->lock);
        list_for_each_entry_safe(t, tmp, &gn->targets, list) {
-               if (t->dev != dev)
+               if (t->dev->parent != dev)
                        continue;
                __gen_remove_target(t);
        }
@@ -473,168 +482,142 @@ static void gen_unregister(struct nvm_dev *dev)
        module_put(THIS_MODULE);
 }
 
-static struct nvm_block *gen_get_blk(struct nvm_dev *dev,
-                               struct nvm_lun *vlun, unsigned long flags)
+static int gen_map_to_dev(struct nvm_tgt_dev *tgt_dev, struct ppa_addr *p)
 {
-       struct gen_lun *lun = container_of(vlun, struct gen_lun, vlun);
-       struct nvm_block *blk = NULL;
-       int is_gc = flags & NVM_IOTYPE_GC;
-
-       spin_lock(&vlun->lock);
-       if (list_empty(&lun->free_list)) {
-               pr_err_ratelimited("gen: lun %u have no free pages available",
-                                                               lun->vlun.id);
-               goto out;
+       struct gen_dev_map *dev_map = tgt_dev->map;
+       struct gen_ch_map *ch_map = &dev_map->chnls[p->g.ch];
+       int lun_off = ch_map->lun_offs[p->g.lun];
+       struct nvm_dev *dev = tgt_dev->parent;
+       struct gen_dev_map *dev_rmap = dev->rmap;
+       struct gen_ch_map *ch_rmap;
+       int lun_roff;
+
+       p->g.ch += ch_map->ch_off;
+       p->g.lun += lun_off;
+
+       ch_rmap = &dev_rmap->chnls[p->g.ch];
+       lun_roff = ch_rmap->lun_offs[p->g.lun];
+
+       if (unlikely(ch_rmap->ch_off < 0 || lun_roff < 0)) {
+               pr_err("nvm: corrupted device partition table\n");
+               return -EINVAL;
        }
 
-       if (!is_gc && lun->vlun.nr_free_blocks < lun->reserved_blocks)
-               goto out;
-
-       blk = list_first_entry(&lun->free_list, struct nvm_block, list);
-
-       list_move_tail(&blk->list, &lun->used_list);
-       blk->state = NVM_BLK_ST_TGT;
-       lun->vlun.nr_free_blocks--;
-out:
-       spin_unlock(&vlun->lock);
-       return blk;
-}
-
-static void gen_put_blk(struct nvm_dev *dev, struct nvm_block *blk)
-{
-       struct nvm_lun *vlun = blk->lun;
-       struct gen_lun *lun = container_of(vlun, struct gen_lun, vlun);
-
-       spin_lock(&vlun->lock);
-       if (blk->state & NVM_BLK_ST_TGT) {
-               list_move_tail(&blk->list, &lun->free_list);
-               lun->vlun.nr_free_blocks++;
-               blk->state = NVM_BLK_ST_FREE;
-       } else if (blk->state & NVM_BLK_ST_BAD) {
-               list_move_tail(&blk->list, &lun->bb_list);
-               blk->state = NVM_BLK_ST_BAD;
-       } else {
-               WARN_ON_ONCE(1);
-               pr_err("gen: erroneous block type (%lu -> %u)\n",
-                                                       blk->id, blk->state);
-               list_move_tail(&blk->list, &lun->bb_list);
-       }
-       spin_unlock(&vlun->lock);
+       return 0;
 }
 
-static void gen_mark_blk(struct nvm_dev *dev, struct ppa_addr ppa, int type)
+static int gen_map_to_tgt(struct nvm_tgt_dev *tgt_dev, struct ppa_addr *p)
 {
-       struct gen_dev *gn = dev->mp;
-       struct gen_lun *lun;
-       struct nvm_block *blk;
-
-       pr_debug("gen: ppa  (ch: %u lun: %u blk: %u pg: %u) -> %u\n",
-                       ppa.g.ch, ppa.g.lun, ppa.g.blk, ppa.g.pg, type);
-
-       if (unlikely(ppa.g.ch > dev->nr_chnls ||
-                                       ppa.g.lun > dev->luns_per_chnl ||
-                                       ppa.g.blk > dev->blks_per_lun)) {
-               WARN_ON_ONCE(1);
-               pr_err("gen: ppa broken (ch: %u > %u lun: %u > %u blk: %u > %u",
-                               ppa.g.ch, dev->nr_chnls,
-                               ppa.g.lun, dev->luns_per_chnl,
-                               ppa.g.blk, dev->blks_per_lun);
-               return;
-       }
+       struct nvm_dev *dev = tgt_dev->parent;
+       struct gen_dev_map *dev_rmap = dev->rmap;
+       struct gen_ch_map *ch_rmap = &dev_rmap->chnls[p->g.ch];
+       int lun_roff = ch_rmap->lun_offs[p->g.lun];
 
-       lun = &gn->luns[(dev->luns_per_chnl * ppa.g.ch) + ppa.g.lun];
-       blk = &lun->vlun.blocks[ppa.g.blk];
+       p->g.ch -= ch_rmap->ch_off;
+       p->g.lun -= lun_roff;
 
-       /* will be moved to bb list on put_blk from target */
-       blk->state = type;
+       return 0;
 }
 
-/*
- * mark block bad in gen. It is expected that the target recovers separately
- */
-static void gen_mark_blk_bad(struct nvm_dev *dev, struct nvm_rq *rqd)
+static int gen_trans_rq(struct nvm_tgt_dev *tgt_dev, struct nvm_rq *rqd,
+                       int flag)
 {
-       int bit = -1;
-       int max_secs = dev->ops->max_phys_sect;
-       void *comp_bits = &rqd->ppa_status;
+       gen_trans_fn *f;
+       int i;
+       int ret = 0;
 
-       nvm_addr_to_generic_mode(dev, rqd);
+       f = (flag == TRANS_TGT_TO_DEV) ? gen_map_to_dev : gen_map_to_tgt;
 
-       /* look up blocks and mark them as bad */
-       if (rqd->nr_ppas == 1) {
-               gen_mark_blk(dev, rqd->ppa_addr, NVM_BLK_ST_BAD);
-               return;
+       if (rqd->nr_ppas == 1)
+               return f(tgt_dev, &rqd->ppa_addr);
+
+       for (i = 0; i < rqd->nr_ppas; i++) {
+               ret = f(tgt_dev, &rqd->ppa_list[i]);
+               if (ret)
+                       goto out;
        }
 
-       while ((bit = find_next_bit(comp_bits, max_secs, bit + 1)) < max_secs)
-               gen_mark_blk(dev, rqd->ppa_list[bit], NVM_BLK_ST_BAD);
+out:
+       return ret;
 }
 
 static void gen_end_io(struct nvm_rq *rqd)
 {
+       struct nvm_tgt_dev *tgt_dev = rqd->dev;
        struct nvm_tgt_instance *ins = rqd->ins;
 
-       if (rqd->error == NVM_RSP_ERR_FAILWRITE)
-               gen_mark_blk_bad(rqd->dev, rqd);
+       /* Convert address space */
+       if (tgt_dev)
+               gen_trans_rq(tgt_dev, rqd, TRANS_DEV_TO_TGT);
 
        ins->tt->end_io(rqd);
 }
 
-static int gen_submit_io(struct nvm_dev *dev, struct nvm_rq *rqd)
+static int gen_submit_io(struct nvm_tgt_dev *tgt_dev, struct nvm_rq *rqd)
 {
+       struct nvm_dev *dev = tgt_dev->parent;
+
        if (!dev->ops->submit_io)
                return -ENODEV;
 
        /* Convert address space */
+       gen_trans_rq(tgt_dev, rqd, TRANS_TGT_TO_DEV);
        nvm_generic_to_addr_mode(dev, rqd);
 
-       rqd->dev = dev;
+       rqd->dev = tgt_dev;
        rqd->end_io = gen_end_io;
        return dev->ops->submit_io(dev, rqd);
 }
 
-static int gen_erase_blk(struct nvm_dev *dev, struct nvm_block *blk,
-                                                       unsigned long flags)
+static int gen_erase_blk(struct nvm_tgt_dev *tgt_dev, struct ppa_addr *p,
+                        int flags)
 {
-       struct ppa_addr addr = block_to_ppa(dev, blk);
+       /* Convert address space */
+       gen_map_to_dev(tgt_dev, p);
 
-       return nvm_erase_ppa(dev, &addr, 1);
+       return nvm_erase_ppa(tgt_dev->parent, p, 1, flags);
 }
 
-static int gen_reserve_lun(struct nvm_dev *dev, int lunid)
+static struct ppa_addr gen_trans_ppa(struct nvm_tgt_dev *tgt_dev,
+                                    struct ppa_addr p, int direction)
 {
-       return test_and_set_bit(lunid, dev->lun_map);
-}
+       gen_trans_fn *f;
+       struct ppa_addr ppa = p;
 
-static void gen_release_lun(struct nvm_dev *dev, int lunid)
-{
-       WARN_ON(!test_and_clear_bit(lunid, dev->lun_map));
+       f = (direction == TRANS_TGT_TO_DEV) ? gen_map_to_dev : gen_map_to_tgt;
+       f(tgt_dev, &ppa);
+
+       return ppa;
 }
 
-static struct nvm_lun *gen_get_lun(struct nvm_dev *dev, int lunid)
+static void gen_part_to_tgt(struct nvm_dev *dev, sector_t *entries,
+                              int len)
 {
-       struct gen_dev *gn = dev->mp;
-
-       if (unlikely(lunid >= dev->nr_luns))
-               return NULL;
+       struct nvm_geo *geo = &dev->geo;
+       struct gen_dev_map *dev_rmap = dev->rmap;
+       u64 i;
 
-       return &gn->luns[lunid].vlun;
-}
+       for (i = 0; i < len; i++) {
+               struct gen_ch_map *ch_rmap;
+               int *lun_roffs;
+               struct ppa_addr gaddr;
+               u64 pba = le64_to_cpu(entries[i]);
+               int off;
+               u64 diff;
 
-static void gen_lun_info_print(struct nvm_dev *dev)
-{
-       struct gen_dev *gn = dev->mp;
-       struct gen_lun *lun;
-       unsigned int i;
+               if (!pba)
+                       continue;
 
+               gaddr = linear_to_generic_addr(geo, pba);
+               ch_rmap = &dev_rmap->chnls[gaddr.g.ch];
+               lun_roffs = ch_rmap->lun_offs;
 
-       gen_for_each_lun(gn, lun, i) {
-               spin_lock(&lun->vlun.lock);
+               off = gaddr.g.ch * geo->luns_per_chnl + gaddr.g.lun;
 
-               pr_info("%s: lun%8u\t%u\n", dev->name, i,
-                                               lun->vlun.nr_free_blocks);
+               diff = ((ch_rmap->ch_off * geo->luns_per_chnl) +
+                               (lun_roffs[gaddr.g.lun])) * geo->sec_per_lun;
 
-               spin_unlock(&lun->vlun.lock);
+               entries[i] -= cpu_to_le64(diff);
        }
 }
 
@@ -648,22 +631,14 @@ static struct nvmm_type gen = {
        .create_tgt             = gen_create_tgt,
        .remove_tgt             = gen_remove_tgt,
 
-       .get_blk                = gen_get_blk,
-       .put_blk                = gen_put_blk,
-
        .submit_io              = gen_submit_io,
        .erase_blk              = gen_erase_blk,
 
-       .mark_blk               = gen_mark_blk,
-
-       .get_lun                = gen_get_lun,
-       .reserve_lun            = gen_reserve_lun,
-       .release_lun            = gen_release_lun,
-       .lun_info_print         = gen_lun_info_print,
-
        .get_area               = gen_get_area,
        .put_area               = gen_put_area,
 
+       .trans_ppa              = gen_trans_ppa,
+       .part_to_tgt            = gen_part_to_tgt,
 };
 
 static int __init gen_module_init(void)