lightnvm: specify target's logical address area
authorWenwei Tao <ww.tao0320@gmail.com>
Thu, 3 Mar 2016 14:06:37 +0000 (15:06 +0100)
committerJens Axboe <axboe@fb.com>
Sat, 19 Mar 2016 01:10:37 +0000 (18:10 -0700)
We can create more than one target on a lightnvm
device by specifying its begin lun and end lun.

But only specify the physical address area is not
enough, we need to get the corresponding non-
intersection logical address area division from
the backend device's logcial address space.
Otherwise the targets on the device might use
the same logical addresses cause incorrect
information in the device's l2p table.

Signed-off-by: Wenwei Tao <ww.tao0320@gmail.com>
Signed-off-by: Matias Bjørling <m@bjorling.me>
Signed-off-by: Jens Axboe <axboe@fb.com>
drivers/lightnvm/core.c
drivers/lightnvm/gennvm.c
drivers/lightnvm/gennvm.h
drivers/lightnvm/rrpc.c
drivers/lightnvm/rrpc.h
include/linux/lightnvm.h

index 0d1fb6b40c468e9bb4f455c06336972e0551f668..2925fd0b82bb718d44dd7a63ac0f00dbf40d4d33 100644 (file)
@@ -466,6 +466,7 @@ static int nvm_core_init(struct nvm_dev *dev)
        dev->total_secs = dev->nr_luns * dev->sec_per_lun;
        INIT_LIST_HEAD(&dev->online_targets);
        mutex_init(&dev->mlock);
+       spin_lock_init(&dev->lock);
 
        return 0;
 }
index d65ec36a22313a21df24f64ec68d3bbb6e91b6cf..d460b37bb016eebf3e16e4c5266377e1a657835f 100644 (file)
 
 #include "gennvm.h"
 
+static int gennvm_get_area(struct nvm_dev *dev, sector_t *lba, sector_t len)
+{
+       struct gen_nvm *gn = dev->mp;
+       struct gennvm_area *area, *prev, *next;
+       sector_t begin = 0;
+       sector_t max_sectors = (dev->sec_size * dev->total_secs) >> 9;
+
+       if (len > max_sectors)
+               return -EINVAL;
+
+       area = kmalloc(sizeof(struct gennvm_area), GFP_KERNEL);
+       if (!area)
+               return -ENOMEM;
+
+       prev = NULL;
+
+       spin_lock(&dev->lock);
+       list_for_each_entry(next, &gn->area_list, list) {
+               if (begin + len > next->begin) {
+                       begin = next->end;
+                       prev = next;
+                       continue;
+               }
+               break;
+       }
+
+       if ((begin + len) > max_sectors) {
+               spin_unlock(&dev->lock);
+               kfree(area);
+               return -EINVAL;
+       }
+
+       area->begin = *lba = begin;
+       area->end = begin + len;
+
+       if (prev) /* insert into sorted order */
+               list_add(&area->list, &prev->list);
+       else
+               list_add(&area->list, &gn->area_list);
+       spin_unlock(&dev->lock);
+
+       return 0;
+}
+
+static void gennvm_put_area(struct nvm_dev *dev, sector_t begin)
+{
+       struct gen_nvm *gn = dev->mp;
+       struct gennvm_area *area;
+
+       spin_lock(&dev->lock);
+       list_for_each_entry(area, &gn->area_list, list) {
+               if (area->begin != begin)
+                       continue;
+
+               list_del(&area->list);
+               spin_unlock(&dev->lock);
+               kfree(area);
+               return;
+       }
+       spin_unlock(&dev->lock);
+}
+
 static void gennvm_blocks_free(struct nvm_dev *dev)
 {
        struct gen_nvm *gn = dev->mp;
@@ -229,6 +291,7 @@ static int gennvm_register(struct nvm_dev *dev)
 
        gn->dev = dev;
        gn->nr_luns = dev->nr_luns;
+       INIT_LIST_HEAD(&gn->area_list);
        dev->mp = gn;
 
        ret = gennvm_luns_init(dev, gn);
@@ -465,6 +528,10 @@ static struct nvmm_type gennvm = {
 
        .get_lun                = gennvm_get_lun,
        .lun_info_print         = gennvm_lun_info_print,
+
+       .get_area               = gennvm_get_area,
+       .put_area               = gennvm_put_area,
+
 };
 
 static int __init gennvm_module_init(void)
index 9c24b5b32dac2cb37acc39a5d7c54449296fe231..04d7c23cfc61756ea3bdbd149be630c4b7cf8369 100644 (file)
@@ -39,8 +39,14 @@ struct gen_nvm {
 
        int nr_luns;
        struct gen_lun *luns;
+       struct list_head area_list;
 };
 
+struct gennvm_area {
+       struct list_head list;
+       sector_t begin;
+       sector_t end;   /* end is excluded */
+};
 #define gennvm_for_each_lun(bm, lun, i) \
                for ((i) = 0, lun = &(bm)->luns[0]; \
                        (i) < (bm)->nr_luns; (i)++, lun = &(bm)->luns[(i)])
index 82343783aa47239b427fee1bbc7e066e5160b630..c1e3c83f06b3f7b4a45944fc99e353bab7825d25 100644 (file)
@@ -1053,8 +1053,11 @@ static int rrpc_map_init(struct rrpc *rrpc)
 {
        struct nvm_dev *dev = rrpc->dev;
        sector_t i;
+       u64 slba;
        int ret;
 
+       slba = rrpc->soffset >> (ilog2(dev->sec_size) - 9);
+
        rrpc->trans_map = vzalloc(sizeof(struct rrpc_addr) * rrpc->nr_sects);
        if (!rrpc->trans_map)
                return -ENOMEM;
@@ -1076,7 +1079,7 @@ static int rrpc_map_init(struct rrpc *rrpc)
                return 0;
 
        /* Bring up the mapping table from device */
-       ret = dev->ops->get_l2p_tbl(dev, 0, dev->total_secs, rrpc_l2p_update,
+       ret = dev->ops->get_l2p_tbl(dev, slba, rrpc->nr_sects, rrpc_l2p_update,
                                                                        rrpc);
        if (ret) {
                pr_err("nvm: rrpc: could not read L2P table.\n");
@@ -1086,7 +1089,6 @@ static int rrpc_map_init(struct rrpc *rrpc)
        return 0;
 }
 
-
 /* Minimum pages needed within a lun */
 #define PAGE_POOL_SIZE 16
 #define ADDR_POOL_SIZE 64
@@ -1200,12 +1202,33 @@ err:
        return -ENOMEM;
 }
 
+/* returns 0 on success and stores the beginning address in *begin */
+static int rrpc_area_init(struct rrpc *rrpc, sector_t *begin)
+{
+       struct nvm_dev *dev = rrpc->dev;
+       struct nvmm_type *mt = dev->mt;
+       sector_t size = rrpc->nr_sects * dev->sec_size;
+
+       size >>= 9;
+
+       return mt->get_area(dev, begin, size);
+}
+
+static void rrpc_area_free(struct rrpc *rrpc)
+{
+       struct nvm_dev *dev = rrpc->dev;
+       struct nvmm_type *mt = dev->mt;
+
+       mt->put_area(dev, rrpc->soffset);
+}
+
 static void rrpc_free(struct rrpc *rrpc)
 {
        rrpc_gc_free(rrpc);
        rrpc_map_free(rrpc);
        rrpc_core_free(rrpc);
        rrpc_luns_free(rrpc);
+       rrpc_area_free(rrpc);
 
        kfree(rrpc);
 }
@@ -1327,6 +1350,7 @@ static void *rrpc_init(struct nvm_dev *dev, struct gendisk *tdisk,
        struct request_queue *bqueue = dev->q;
        struct request_queue *tqueue = tdisk->queue;
        struct rrpc *rrpc;
+       sector_t soffset;
        int ret;
 
        if (!(dev->identity.dom & NVM_RSP_L2P)) {
@@ -1352,6 +1376,13 @@ static void *rrpc_init(struct nvm_dev *dev, struct gendisk *tdisk,
        /* simple round-robin strategy */
        atomic_set(&rrpc->next_lun, -1);
 
+       ret = rrpc_area_init(rrpc, &soffset);
+       if (ret < 0) {
+               pr_err("nvm: rrpc: could not initialize area\n");
+               return ERR_PTR(ret);
+       }
+       rrpc->soffset = soffset;
+
        ret = rrpc_luns_init(rrpc, lun_begin, lun_end);
        if (ret) {
                pr_err("nvm: rrpc: could not initialize luns\n");
index 855f4a5ca7ddeb75fbc618b1038312865b28cb97..2653484a3b407a2101311b281ce6b4ccc57d027b 100644 (file)
@@ -97,6 +97,7 @@ struct rrpc {
        struct nvm_dev *dev;
        struct gendisk *disk;
 
+       sector_t soffset; /* logical sector offset */
        u64 poffset; /* physical page offset */
        int lun_offset;
 
index c3c43184a787027abd104bc0e46fbbbeb071b212..b466bd9f2cf8a23aed9c85c11ff8fd3f10967fe4 100644 (file)
@@ -355,6 +355,7 @@ struct nvm_dev {
        char name[DISK_NAME_LEN];
 
        struct mutex mlock;
+       spinlock_t lock;
 };
 
 static inline struct ppa_addr generic_to_dev_addr(struct nvm_dev *dev,
@@ -467,6 +468,9 @@ typedef int (nvmm_erase_blk_fn)(struct nvm_dev *, struct nvm_block *,
 typedef struct nvm_lun *(nvmm_get_lun_fn)(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);
+typedef void (nvmm_put_area_fn)(struct nvm_dev *, sector_t);
+
 struct nvmm_type {
        const char *name;
        unsigned int version[3];
@@ -491,6 +495,10 @@ struct nvmm_type {
 
        /* Statistics */
        nvmm_lun_info_print_fn *lun_info_print;
+
+       nvmm_get_area_fn *get_area;
+       nvmm_put_area_fn *put_area;
+
        struct list_head list;
 };