]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/lightnvm/gennvm.c
lightnvm: transform target get/set bad block
[linux.git] / drivers / lightnvm / gennvm.c
index 19c3924ffa109538cb184c381daf4360911442a1..ca7880082d80648647e6d08258d25afedbbb1e90 100644 (file)
@@ -38,8 +38,6 @@ static const struct block_device_operations gen_fops = {
 static int gen_reserve_luns(struct nvm_dev *dev, struct nvm_target *t,
                            int lun_begin, int lun_end)
 {
-       struct gen_dev *gn = dev->mp;
-       struct nvm_lun *lun;
        int i;
 
        for (i = lun_begin; i <= lun_end; i++) {
@@ -47,35 +45,50 @@ static int gen_reserve_luns(struct nvm_dev *dev, struct nvm_target *t,
                        pr_err("nvm: lun %d already allocated\n", i);
                        goto err;
                }
-
-               lun = &gn->luns[i];
-               list_add_tail(&lun->list, &t->lun_list);
        }
 
        return 0;
 
 err:
-       while (--i > lun_begin) {
-               lun = &gn->luns[i];
+       while (--i > lun_begin)
                clear_bit(i, dev->lun_map);
-               list_del(&lun->list);
-       }
 
        return -EBUSY;
 }
 
-static void gen_release_luns(struct nvm_dev *dev, struct nvm_target *t)
+static void gen_release_luns_err(struct nvm_dev *dev, int lun_begin,
+                                int lun_end)
 {
-       struct nvm_lun *lun, *tmp;
+       int i;
 
-       list_for_each_entry_safe(lun, tmp, &t->lun_list, list) {
-               WARN_ON(!test_and_clear_bit(lun->id, dev->lun_map));
-               list_del(&lun->list);
-       }
+       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);
 }
 
@@ -83,24 +96,101 @@ 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 out;
+               goto err_ch;
 
        memcpy(&tgt_dev->geo, &dev->geo, sizeof(struct nvm_geo));
-       tgt_dev->geo.nr_chnls = (nr_luns / (dev->geo.luns_per_chnl + 1)) + 1;
+       /* 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->ops = dev->ops;
-       tgt_dev->mt = dev->mt;
+       tgt_dev->map = dev_map;
+       tgt_dev->luns = luns;
        memcpy(&tgt_dev->identity, &dev->identity, sizeof(struct nvm_id));
 
        tgt_dev->parent = dev;
 
-out:
+       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;
 }
 
@@ -134,14 +224,14 @@ static int gen_create_tgt(struct nvm_dev *dev, struct nvm_ioctl_create *create)
        if (!t)
                return -ENOMEM;
 
-       INIT_LIST_HEAD(&t->lun_list);
-
        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)
+       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)
@@ -159,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(tgt_dev, tdisk, &t->lun_list);
+       targetdata = tt->init(tgt_dev, tdisk);
        if (IS_ERR(targetdata))
                goto err_init;
 
@@ -187,7 +277,7 @@ static int gen_create_tgt(struct nvm_dev *dev, struct nvm_ioctl_create *create)
 err_dev:
        kfree(tgt_dev);
 err_reserve:
-       gen_release_luns(dev, t);
+       gen_release_luns_err(dev, s->lun_begin, s->lun_end);
 err_t:
        kfree(t);
        return -ENOMEM;
@@ -205,7 +295,6 @@ static void __gen_remove_target(struct nvm_target *t)
        if (tt->exit)
                tt->exit(tdisk->private_data);
 
-       gen_release_luns(t->dev->parent, t);
        gen_remove_tgt_dev(t->dev);
        put_disk(tdisk);
 
@@ -306,51 +395,54 @@ static void gen_put_area(struct nvm_dev *dev, sector_t begin)
        spin_unlock(&dev->lock);
 }
 
-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 nvm_geo *geo = &dev->geo;
-       struct nvm_lun *lun;
-       int i;
-
-       gn->luns = kcalloc(geo->nr_luns, sizeof(struct nvm_lun), GFP_KERNEL);
-       if (!gn->luns)
-               return -ENOMEM;
-
-       gen_for_each_lun(gn, lun, i) {
-               INIT_LIST_HEAD(&lun->list);
-
-               lun->id = i;
-               lun->lun_id = i % geo->luns_per_chnl;
-               lun->chnl_id = i / geo->luns_per_chnl;
-       }
-       return 0;
-}
-
 static void gen_free(struct nvm_dev *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->geo.nr_luns;
@@ -358,18 +450,19 @@ static int gen_register(struct nvm_dev *dev)
        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;
-       }
+       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)
@@ -389,29 +482,143 @@ static void gen_unregister(struct nvm_dev *dev)
        module_put(THIS_MODULE);
 }
 
+static int gen_map_to_dev(struct nvm_tgt_dev *tgt_dev, struct ppa_addr *p)
+{
+       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;
+       }
+
+       return 0;
+}
+
+static int gen_map_to_tgt(struct nvm_tgt_dev *tgt_dev, struct ppa_addr *p)
+{
+       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];
+
+       p->g.ch -= ch_rmap->ch_off;
+       p->g.lun -= lun_roff;
+
+       return 0;
+}
+
+static int gen_trans_rq(struct nvm_tgt_dev *tgt_dev, struct nvm_rq *rqd,
+                       int flag)
+{
+       gen_trans_fn *f;
+       int i;
+       int ret = 0;
+
+       f = (flag == TRANS_TGT_TO_DEV) ? gen_map_to_dev : gen_map_to_tgt;
+
+       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;
+       }
+
+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;
 
+       /* 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 ppa_addr *p, int flags)
+static int gen_erase_blk(struct nvm_tgt_dev *tgt_dev, struct ppa_addr *p,
+                        int flags)
+{
+       /* Convert address space */
+       gen_map_to_dev(tgt_dev, p);
+
+       return nvm_erase_ppa(tgt_dev->parent, p, 1, flags);
+}
+
+static struct ppa_addr gen_trans_ppa(struct nvm_tgt_dev *tgt_dev,
+                                    struct ppa_addr p, int direction)
 {
-       return nvm_erase_ppa(dev, p, 1, flags);
+       gen_trans_fn *f;
+       struct ppa_addr ppa = p;
+
+       f = (direction == TRANS_TGT_TO_DEV) ? gen_map_to_dev : gen_map_to_tgt;
+       f(tgt_dev, &ppa);
+
+       return ppa;
+}
+
+static void gen_part_to_tgt(struct nvm_dev *dev, sector_t *entries,
+                              int len)
+{
+       struct nvm_geo *geo = &dev->geo;
+       struct gen_dev_map *dev_rmap = dev->rmap;
+       u64 i;
+
+       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;
+
+               if (!pba)
+                       continue;
+
+               gaddr = linear_to_generic_addr(geo, pba);
+               ch_rmap = &dev_rmap->chnls[gaddr.g.ch];
+               lun_roffs = ch_rmap->lun_offs;
+
+               off = gaddr.g.ch * geo->luns_per_chnl + gaddr.g.lun;
+
+               diff = ((ch_rmap->ch_off * geo->luns_per_chnl) +
+                               (lun_roffs[gaddr.g.lun])) * geo->sec_per_lun;
+
+               entries[i] -= cpu_to_le64(diff);
+       }
 }
 
 static struct nvmm_type gen = {
@@ -430,6 +637,8 @@ static struct nvmm_type gen = {
        .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)