Commit 8e79b5cb authored by Javier González's avatar Javier González Committed by Jens Axboe

lightnvm: move block provisioning to targets

In order to naturally support multi-target instances on an Open-Channel
SSD, targets should own the LUNs they get blocks from and manage
provisioning internally. This is done in several steps.

This patch moves the block provisioning inside of the target and removes
the get/put block interface from the media manager.
Signed-off-by: default avatarJavier González <javier@cnexlabs.com>
Signed-off-by: default avatarMatias Bjørling <m@bjorling.me>
Signed-off-by: default avatarJens Axboe <axboe@fb.com>
parent 8176117b
......@@ -176,20 +176,6 @@ static struct nvm_dev *nvm_find_nvm_dev(const char *name)
return NULL;
}
struct nvm_block *nvm_get_blk(struct nvm_dev *dev, struct nvm_lun *lun,
unsigned long flags)
{
return dev->mt->get_blk(dev, lun, flags);
}
EXPORT_SYMBOL(nvm_get_blk);
/* Assumes that all valid pages have already been moved on release to bm */
void nvm_put_blk(struct nvm_dev *dev, struct nvm_block *blk)
{
return dev->mt->put_blk(dev, blk);
}
EXPORT_SYMBOL(nvm_put_blk);
void nvm_mark_blk(struct nvm_dev *dev, struct ppa_addr ppa, int type)
{
return dev->mt->mark_blk(dev, ppa, type);
......@@ -266,10 +252,11 @@ EXPORT_SYMBOL(nvm_generic_to_addr_mode);
int nvm_set_rqd_ppalist(struct nvm_dev *dev, struct nvm_rq *rqd,
const struct ppa_addr *ppas, int nr_ppas, int vblk)
{
struct nvm_geo *geo = &dev->geo;
int i, plane_cnt, pl_idx;
struct ppa_addr ppa;
if ((!vblk || dev->plane_mode == NVM_PLANE_SINGLE) && nr_ppas == 1) {
if ((!vblk || geo->plane_mode == NVM_PLANE_SINGLE) && nr_ppas == 1) {
rqd->nr_ppas = nr_ppas;
rqd->ppa_addr = ppas[0];
......@@ -287,7 +274,7 @@ int nvm_set_rqd_ppalist(struct nvm_dev *dev, struct nvm_rq *rqd,
for (i = 0; i < nr_ppas; i++)
rqd->ppa_list[i] = ppas[i];
} else {
plane_cnt = dev->plane_mode;
plane_cnt = geo->plane_mode;
rqd->nr_ppas *= plane_cnt;
for (i = 0; i < nr_ppas; i++) {
......@@ -465,17 +452,18 @@ EXPORT_SYMBOL(nvm_submit_ppa);
*/
int nvm_bb_tbl_fold(struct nvm_dev *dev, u8 *blks, int nr_blks)
{
struct nvm_geo *geo = &dev->geo;
int blk, offset, pl, blktype;
if (nr_blks != dev->blks_per_lun * dev->plane_mode)
if (nr_blks != geo->blks_per_lun * geo->plane_mode)
return -EINVAL;
for (blk = 0; blk < dev->blks_per_lun; blk++) {
offset = blk * dev->plane_mode;
for (blk = 0; blk < geo->blks_per_lun; blk++) {
offset = blk * geo->plane_mode;
blktype = blks[offset];
/* Bad blocks on any planes take precedence over other types */
for (pl = 0; pl < dev->plane_mode; pl++) {
for (pl = 0; pl < geo->plane_mode; pl++) {
if (blks[offset + pl] &
(NVM_BLK_T_BAD|NVM_BLK_T_GRWN_BAD)) {
blktype = blks[offset + pl];
......@@ -486,7 +474,7 @@ int nvm_bb_tbl_fold(struct nvm_dev *dev, u8 *blks, int nr_blks)
blks[blk] = blktype;
}
return dev->blks_per_lun;
return geo->blks_per_lun;
}
EXPORT_SYMBOL(nvm_bb_tbl_fold);
......@@ -500,9 +488,10 @@ EXPORT_SYMBOL(nvm_get_bb_tbl);
static int nvm_init_slc_tbl(struct nvm_dev *dev, struct nvm_id_group *grp)
{
struct nvm_geo *geo = &dev->geo;
int i;
dev->lps_per_blk = dev->pgs_per_blk;
dev->lps_per_blk = geo->pgs_per_blk;
dev->lptbl = kcalloc(dev->lps_per_blk, sizeof(int), GFP_KERNEL);
if (!dev->lptbl)
return -ENOMEM;
......@@ -548,29 +537,32 @@ static int nvm_core_init(struct nvm_dev *dev)
{
struct nvm_id *id = &dev->identity;
struct nvm_id_group *grp = &id->groups[0];
struct nvm_geo *geo = &dev->geo;
int ret;
/* device values */
dev->nr_chnls = grp->num_ch;
dev->luns_per_chnl = grp->num_lun;
dev->pgs_per_blk = grp->num_pg;
dev->blks_per_lun = grp->num_blk;
dev->nr_planes = grp->num_pln;
dev->fpg_size = grp->fpg_sz;
dev->pfpg_size = grp->fpg_sz * grp->num_pln;
dev->sec_size = grp->csecs;
dev->oob_size = grp->sos;
dev->sec_per_pg = grp->fpg_sz / grp->csecs;
dev->mccap = grp->mccap;
memcpy(&dev->ppaf, &id->ppaf, sizeof(struct nvm_addr_format));
dev->plane_mode = NVM_PLANE_SINGLE;
dev->max_rq_size = dev->ops->max_phys_sect * dev->sec_size;
/* Whole device values */
geo->nr_chnls = grp->num_ch;
geo->luns_per_chnl = grp->num_lun;
/* Generic device values */
geo->pgs_per_blk = grp->num_pg;
geo->blks_per_lun = grp->num_blk;
geo->nr_planes = grp->num_pln;
geo->fpg_size = grp->fpg_sz;
geo->pfpg_size = grp->fpg_sz * grp->num_pln;
geo->sec_size = grp->csecs;
geo->oob_size = grp->sos;
geo->sec_per_pg = grp->fpg_sz / grp->csecs;
geo->mccap = grp->mccap;
memcpy(&geo->ppaf, &id->ppaf, sizeof(struct nvm_addr_format));
geo->plane_mode = NVM_PLANE_SINGLE;
geo->max_rq_size = dev->ops->max_phys_sect * geo->sec_size;
if (grp->mpos & 0x020202)
dev->plane_mode = NVM_PLANE_DOUBLE;
geo->plane_mode = NVM_PLANE_DOUBLE;
if (grp->mpos & 0x040404)
dev->plane_mode = NVM_PLANE_QUAD;
geo->plane_mode = NVM_PLANE_QUAD;
if (grp->mtype != 0) {
pr_err("nvm: memory type not supported\n");
......@@ -578,13 +570,13 @@ static int nvm_core_init(struct nvm_dev *dev)
}
/* calculated values */
dev->sec_per_pl = dev->sec_per_pg * dev->nr_planes;
dev->sec_per_blk = dev->sec_per_pl * dev->pgs_per_blk;
dev->sec_per_lun = dev->sec_per_blk * dev->blks_per_lun;
dev->nr_luns = dev->luns_per_chnl * dev->nr_chnls;
geo->sec_per_pl = geo->sec_per_pg * geo->nr_planes;
geo->sec_per_blk = geo->sec_per_pl * geo->pgs_per_blk;
geo->sec_per_lun = geo->sec_per_blk * geo->blks_per_lun;
geo->nr_luns = geo->luns_per_chnl * geo->nr_chnls;
dev->total_secs = dev->nr_luns * dev->sec_per_lun;
dev->lun_map = kcalloc(BITS_TO_LONGS(dev->nr_luns),
dev->total_secs = geo->nr_luns * geo->sec_per_lun;
dev->lun_map = kcalloc(BITS_TO_LONGS(geo->nr_luns),
sizeof(unsigned long), GFP_KERNEL);
if (!dev->lun_map)
return -ENOMEM;
......@@ -611,7 +603,7 @@ static int nvm_core_init(struct nvm_dev *dev)
mutex_init(&dev->mlock);
spin_lock_init(&dev->lock);
blk_queue_logical_block_size(dev->q, dev->sec_size);
blk_queue_logical_block_size(dev->q, geo->sec_size);
return 0;
err_fmtype:
......@@ -645,6 +637,7 @@ void nvm_free(struct nvm_dev *dev)
static int nvm_init(struct nvm_dev *dev)
{
struct nvm_geo *geo = &dev->geo;
int ret = -EINVAL;
if (!dev->q || !dev->ops)
......@@ -676,9 +669,9 @@ static int nvm_init(struct nvm_dev *dev)
}
pr_info("nvm: registered %s [%u/%u/%u/%u/%u/%u]\n",
dev->name, dev->sec_per_pg, dev->nr_planes,
dev->pgs_per_blk, dev->blks_per_lun, dev->nr_luns,
dev->nr_chnls);
dev->name, geo->sec_per_pg, geo->nr_planes,
geo->pgs_per_blk, geo->blks_per_lun,
geo->nr_luns, geo->nr_chnls);
return 0;
err:
pr_err("nvm: failed to initialize nvm\n");
......@@ -771,9 +764,9 @@ static int __nvm_configure_create(struct nvm_ioctl_create *create)
}
s = &create->conf.s;
if (s->lun_begin > s->lun_end || s->lun_end > dev->nr_luns) {
if (s->lun_begin > s->lun_end || s->lun_end > dev->geo.nr_luns) {
pr_err("nvm: lun out of bound (%u:%u > %u)\n",
s->lun_begin, s->lun_end, dev->nr_luns);
s->lun_begin, s->lun_end, dev->geo.nr_luns);
return -EINVAL;
}
......
......@@ -74,6 +74,36 @@ static void gen_release_luns(struct nvm_dev *dev, struct nvm_target *t)
}
}
static void gen_remove_tgt_dev(struct nvm_tgt_dev *tgt_dev)
{
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;
int nr_luns = lun_end - lun_begin + 1;
tgt_dev = kmalloc(sizeof(struct nvm_tgt_dev), GFP_KERNEL);
if (!tgt_dev)
goto out;
memcpy(&tgt_dev->geo, &dev->geo, sizeof(struct nvm_geo));
tgt_dev->geo.nr_chnls = (nr_luns / (dev->geo.luns_per_chnl + 1)) + 1;
tgt_dev->geo.nr_luns = nr_luns;
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;
memcpy(&tgt_dev->identity, &dev->identity, sizeof(struct nvm_id));
tgt_dev->parent = dev;
out:
return tgt_dev;
}
static int gen_create_tgt(struct nvm_dev *dev, struct nvm_ioctl_create *create)
{
struct gen_dev *gn = dev->mp;
......@@ -82,6 +112,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);
......@@ -108,9 +139,13 @@ static int gen_create_tgt(struct nvm_dev *dev, struct nvm_ioctl_create *create)
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)
goto err_reserve;
tqueue = blk_alloc_queue_node(GFP_KERNEL, dev->q->node);
if (!tqueue)
goto err_reserve;
goto err_dev;
blk_queue_make_request(tqueue, tt->make_rq);
tdisk = alloc_disk(0);
......@@ -124,7 +159,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, s->lun_begin, s->lun_end);
if (IS_ERR(targetdata))
goto err_init;
......@@ -138,7 +173,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);
......@@ -149,6 +184,8 @@ static int gen_create_tgt(struct nvm_dev *dev, struct nvm_ioctl_create *create)
put_disk(tdisk);
err_queue:
blk_cleanup_queue(tqueue);
err_dev:
kfree(tgt_dev);
err_reserve:
gen_release_luns(dev, t);
err_t:
......@@ -168,7 +205,8 @@ static void __gen_remove_target(struct nvm_target *t)
if (tt->exit)
tt->exit(tdisk->private_data);
gen_release_luns(t->dev, t);
gen_release_luns(t->dev->parent, t);
gen_remove_tgt_dev(t->dev);
put_disk(tdisk);
list_del(&t->list);
......@@ -207,10 +245,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;
......@@ -289,10 +328,11 @@ static void gen_luns_free(struct nvm_dev *dev)
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(dev->nr_luns, sizeof(struct nvm_lun), GFP_KERNEL);
gn->luns = kcalloc(geo->nr_luns, sizeof(struct nvm_lun), GFP_KERNEL);
if (!gn->luns)
return -ENOMEM;
......@@ -305,9 +345,9 @@ static int gen_luns_init(struct nvm_dev *dev, struct gen_dev *gn)
spin_lock_init(&lun->lock);
lun->id = i;
lun->lun_id = i % dev->luns_per_chnl;
lun->chnl_id = i / dev->luns_per_chnl;
lun->nr_free_blocks = dev->blks_per_lun;
lun->lun_id = i % geo->luns_per_chnl;
lun->chnl_id = i / geo->luns_per_chnl;
lun->nr_free_blocks = geo->blks_per_lun;
}
return 0;
}
......@@ -324,7 +364,7 @@ static int gen_block_bb(struct gen_dev *gn, struct ppa_addr ppa,
if (nr_blks < 0)
return nr_blks;
lun = &gn->luns[(dev->luns_per_chnl * ppa.g.ch) + ppa.g.lun];
lun = &gn->luns[(dev->geo.luns_per_chnl * ppa.g.ch) + ppa.g.lun];
for (i = 0; i < nr_blks; i++) {
if (blks[i] == NVM_BLK_T_FREE)
......@@ -342,6 +382,7 @@ static int gen_block_bb(struct gen_dev *gn, struct ppa_addr ppa,
static int gen_block_map(u64 slba, u32 nlb, __le64 *entries, void *private)
{
struct nvm_dev *dev = private;
struct nvm_geo *geo = &dev->geo;
struct gen_dev *gn = dev->mp;
u64 elba = slba + nlb;
struct nvm_lun *lun;
......@@ -370,12 +411,12 @@ static int gen_block_map(u64 slba, u32 nlb, __le64 *entries, void *private)
continue;
/* resolve block from physical address */
lun_id = div_u64(pba, dev->sec_per_lun);
lun_id = div_u64(pba, geo->sec_per_lun);
lun = &gn->luns[lun_id];
/* Calculate block offset into lun */
pba = pba - (dev->sec_per_lun * lun_id);
blk = &lun->blocks[div_u64(pba, dev->sec_per_blk)];
pba = pba - (geo->sec_per_lun * lun_id);
blk = &lun->blocks[div_u64(pba, geo->sec_per_blk)];
if (!blk->state) {
/* at this point, we don't know anything about the
......@@ -393,26 +434,27 @@ static int gen_block_map(u64 slba, u32 nlb, __le64 *entries, void *private)
static int gen_blocks_init(struct nvm_dev *dev, struct gen_dev *gn)
{
struct nvm_geo *geo = &dev->geo;
struct nvm_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;
nr_blks = geo->blks_per_lun * geo->plane_mode;
blks = kmalloc(nr_blks, GFP_KERNEL);
if (!blks)
return -ENOMEM;
gen_for_each_lun(gn, lun, lun_iter) {
lun->blocks = vzalloc(sizeof(struct nvm_block) *
dev->blks_per_lun);
geo->blks_per_lun);
if (!lun->blocks) {
kfree(blks);
return -ENOMEM;
}
for (blk_iter = 0; blk_iter < dev->blks_per_lun; blk_iter++) {
for (blk_iter = 0; blk_iter < geo->blks_per_lun; blk_iter++) {
block = &lun->blocks[blk_iter];
INIT_LIST_HEAD(&block->list);
......@@ -474,7 +516,7 @@ static int gen_register(struct nvm_dev *dev)
return -ENOMEM;
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);
......@@ -506,7 +548,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);
}
......@@ -516,55 +558,9 @@ 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 *lun, unsigned long flags)
{
struct nvm_block *blk = NULL;
int is_gc = flags & NVM_IOTYPE_GC;
spin_lock(&lun->lock);
if (list_empty(&lun->free_list)) {
pr_err_ratelimited("gen: lun %u have no free pages available",
lun->id);
goto out;
}
if (!is_gc && lun->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->nr_free_blocks--;
out:
spin_unlock(&lun->lock);
return blk;
}
static void gen_put_blk(struct nvm_dev *dev, struct nvm_block *blk)
{
struct nvm_lun *lun = blk->lun;
spin_lock(&lun->lock);
if (blk->state & NVM_BLK_ST_TGT) {
list_move_tail(&blk->list, &lun->free_list);
lun->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(&lun->lock);
}
static void gen_mark_blk(struct nvm_dev *dev, struct ppa_addr ppa, int type)
{
struct nvm_geo *geo = &dev->geo;
struct gen_dev *gn = dev->mp;
struct nvm_lun *lun;
struct nvm_block *blk;
......@@ -572,18 +568,18 @@ static void gen_mark_blk(struct nvm_dev *dev, struct ppa_addr ppa, int type)
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)) {
if (unlikely(ppa.g.ch > geo->nr_chnls ||
ppa.g.lun > geo->luns_per_chnl ||
ppa.g.blk > geo->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);
ppa.g.ch, geo->nr_chnls,
ppa.g.lun, geo->luns_per_chnl,
ppa.g.blk, geo->blks_per_lun);
return;
}
lun = &gn->luns[(dev->luns_per_chnl * ppa.g.ch) + ppa.g.lun];
lun = &gn->luns[(geo->luns_per_chnl * ppa.g.ch) + ppa.g.lun];
blk = &lun->blocks[ppa.g.blk];
/* will be moved to bb list on put_blk from target */
......@@ -621,7 +617,7 @@ static struct nvm_lun *gen_get_lun(struct nvm_dev *dev, int lunid)
{
struct gen_dev *gn = dev->mp;
if (unlikely(lunid >= dev->nr_luns))
if (unlikely(lunid >= dev->geo.nr_luns))
return NULL;
return &gn->luns[lunid];
......@@ -654,9 +650,6 @@ 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,
......
This diff is collapsed.
......@@ -52,9 +52,12 @@ struct rrpc_rq {
};
struct rrpc_block {
unsigned long id;
struct nvm_block *parent;
struct rrpc_lun *rlun;
struct list_head prio;
struct list_head prio; /* LUN CG list */
struct list_head list; /* LUN free, used, bb list */
#define MAX_INVALID_PAGES_STORAGE 8
/* Bitmap for invalid page intries */
......@@ -64,6 +67,8 @@ struct rrpc_block {
/* number of pages that are invalid, wrt host page size */
unsigned int nr_invalid_pages;
int state;
spinlock_t lock;
atomic_t data_cmnt_size; /* data pages committed to stable storage */
};
......@@ -71,6 +76,7 @@ struct rrpc_block {
struct rrpc_lun {
struct rrpc *rrpc;
struct nvm_lun *parent;
struct rrpc_block *cur, *gc_cur;
struct rrpc_block *blocks; /* Reference to block allocation */
......@@ -79,6 +85,8 @@ struct rrpc_lun {
struct work_struct ws_gc;
int reserved_blocks;
spinlock_t lock;
};
......@@ -86,7 +94,7 @@ struct rrpc {
/* instance must be kept in top to resolve rrpc in unprep */
struct nvm_tgt_instance instance;
struct nvm_dev *dev;
struct nvm_tgt_dev *dev;
struct gendisk *disk;
sector_t soffset; /* logical sector offset */
......@@ -151,7 +159,8 @@ static inline struct rrpc_block *rrpc_get_rblk(struct rrpc_lun *rlun,
int blk_id)
{
struct rrpc *rrpc = rlun->rrpc;
int lun_blk = blk_id % rrpc->dev->blks_per_lun;
struct nvm_tgt_dev *dev = rrpc->dev;
int lun_blk = blk_id % dev->geo.blks_per_lun;
return &rlun->blocks[lun_blk];
}
......
......@@ -62,7 +62,8 @@ static void nvm_cpu_to_sysblk(struct nvm_system_block *sb,
static int nvm_setup_sysblks(struct nvm_dev *dev, struct ppa_addr *sysblk_ppas)
{
int nr_rows = min_t(int, MAX_SYSBLKS, dev->nr_chnls);
struct nvm_geo *geo = &dev->geo;
int nr_rows = min_t(int, MAX_SYSBLKS, geo->nr_chnls);
int i;
for (i = 0; i < nr_rows; i++)
......@@ -71,7 +72,7 @@ static int nvm_setup_sysblks(struct nvm_dev *dev, struct ppa_addr *sysblk_ppas)
/* if possible, place sysblk at first channel, middle channel and last
* channel of the device. If not, create only one or two sys blocks
*/
switch (dev->nr_chnls) {
switch (geo->nr_chnls) {
case 2:
sysblk_ppas[1].g.ch = 1;
/* fall-through */
......@@ -80,8 +81,8 @@ static int nvm_setup_sysblks(struct nvm_dev *dev, struct ppa_addr *sysblk_ppas)
break;
default:
sysblk_ppas[0].g.ch = 0;
sysblk_ppas[1].g.ch = dev->nr_chnls / 2;
sysblk_ppas[2].g.ch = dev->nr_chnls - 1;
sysblk_ppas[1].g.ch = geo->nr_chnls / 2;
sysblk_ppas[2].g.ch = geo->nr_chnls - 1;
break;
}
......@@ -162,11 +163,12 @@ static int sysblk_get_host_blks(struct nvm_dev *dev, struct ppa_addr ppa,
static int nvm_get_all_sysblks(struct nvm_dev *dev, struct sysblk_scan *s,
struct ppa_addr *ppas, int get_free)
{
struct nvm_geo *geo = &dev->geo;
int i, nr_blks, ret = 0;
u8 *blks;
s->nr_ppas = 0;
nr_blks = dev->blks_per_lun * dev->plane_mode;
nr_blks = geo->blks_per_lun * geo->plane_mode;
blks = kmalloc(nr_blks, GFP_KERNEL);
if (!blks)
......@@ -210,13 +212,14 @@ static int nvm_get_all_sysblks(struct nvm_dev *dev, struct sysblk_scan *s,
static int nvm_scan_block(struct nvm_dev *dev, struct ppa_addr *ppa,
struct nvm_system_block *sblk)
{
struct nvm_geo *geo = &dev->geo;
struct nvm_system_block *cur;
int pg, ret, found = 0;
/* the full buffer for a flash page is allocated. Only the first of it
* contains the system block information
*/
cur = kmalloc(dev->pfpg_size, GFP_KERNEL);
cur = kmalloc(geo->pfpg_size, GFP_KERNEL);
if (!cur)
return -ENOMEM;
......@@ -225,7 +228,7 @@ static int nvm_scan_block(struct nvm_dev *dev, struct ppa_addr *ppa,
ppa->g.pg = ppa_to_slc(dev, pg);
ret = nvm_submit_ppa(dev, ppa, 1, NVM_OP_PREAD, NVM_IO_SLC_MODE,
cur, dev->pfpg_size);
cur, geo->pfpg_size);
if (ret) {
if (ret == NVM_RSP_ERR_EMPTYPAGE) {
pr_debug("nvm: sysblk scan empty ppa (%u %u %u %u)\n",
......@@ -276,6 +279,7 @@ static int nvm_sysblk_set_bb_tbl(struct nvm_dev *dev, struct sysblk_scan *s,
static int nvm_write_and_verify(struct nvm_dev *dev, struct nvm_sb_info *info,
struct sysblk_scan *s)
{
struct nvm_geo *geo = &dev->geo;
struct nvm_system_block nvmsb;
void *buf;
int i, sect, ret = 0;
......@@ -283,12 +287,12 @@ static int nvm_write_and_verify(struct nvm_dev *dev, struct nvm_sb_info *info,
nvm_cpu_to_sysblk(&nvmsb, info);
buf = kzalloc(dev->pfpg_size, GFP_KERNEL);
buf = kzalloc(geo->pfpg_size, GFP_KERNEL);
if (!buf)
return -ENOMEM;
memcpy(buf, &nvmsb, sizeof(struct nvm_system_block));
ppas = kcalloc(dev->sec_per_pg, sizeof(struct ppa_addr), GFP_KERNEL);
ppas = kcalloc(geo->sec_per_pg, sizeof(struct ppa_addr), GFP_KERNEL);
if (!ppas) {
ret = -ENOMEM;
goto err;
......@@ -305,15 +309,15 @@ static int nvm_write_and_verify(struct nvm_dev *dev, struct nvm_sb_info *info,
ppas[0].g.pg);
/* Expand to all sectors within a flash page */
if (dev->sec_per_pg > 1) {
for (sect = 1; sect < dev->sec_per_pg; sect++) {
if (geo->sec_per_pg > 1) {
for (sect = 1; sect < geo->sec_per_pg; sect++) {
ppas[sect].ppa = ppas[0].ppa;
ppas[sect].g.sec = sect;
}
}
ret = nvm_submit_ppa(dev, ppas, dev->sec_per_pg, NVM_OP_PWRITE,
NVM_IO_SLC_MODE, buf, dev->pfpg_size);
ret = nvm_submit_ppa(dev, ppas, geo->sec_per_pg, NVM_OP_PWRITE,
NVM_IO_SLC_MODE, buf, geo->pfpg_size);
if (ret) {
pr_err("nvm: sysblk failed program (%u %u %u)\n",
ppas[0].g.ch,
......@@ -322,8 +326,8 @@ static int nvm_write_and_verify(struct nvm_dev *dev, struct nvm_sb_info *info,
break;
}
ret = nvm_submit_ppa(dev, ppas, dev->sec_per_pg, NVM_OP_PREAD,
NVM_IO_SLC_MODE, buf, dev->pfpg_size);
ret = nvm_submit_ppa(dev, ppas, geo->sec_per_pg, NVM_OP_PREAD,
NVM_IO_SLC_MODE, buf, geo->pfpg_size);
if (ret) {
pr_err("nvm: sysblk failed read (%u %u %u)\n",
ppas[0].g.ch,
......@@ -527,6 +531,7 @@ int nvm_update_sysblock(struct nvm_dev *dev, struct nvm_sb_info *new)
int nvm_init_sysblock(struct nvm_dev *dev, struct nvm_sb_info *info)
{
struct nvm_geo *geo = &dev->geo;
struct ppa_addr sysblk_ppas[MAX_SYSBLKS];
struct sysblk_scan s;
int ret;
......@@ -541,7 +546,7 @@ int nvm_init_sysblock(struct nvm_dev *dev, struct nvm_sb_info *info)
if (!dev->ops->get_bb_tbl || !dev->ops->set_bb_tbl)
return -EINVAL;
if (!(dev->mccap & NVM_ID_CAP_SLC) || !dev->lps_per_blk) {
if (!(geo->mccap & NVM_ID_CAP_SLC) || !dev->lps_per_blk) {
pr_err("nvm: memory does not support SLC access\n");
return -EINVAL;
}
......@@ -571,11 +576,11 @@ static int factory_nblks(int nblks)
return (nblks + (BITS_PER_LONG - 1)) & ~(BITS_PER_LONG - 1);
}
static unsigned int factory_blk_offset(struct nvm_dev *dev, struct ppa_addr ppa)
static unsigned int factory_blk_offset(struct nvm_geo *geo, struct ppa_addr ppa)
{
int nblks = factory_nblks(dev->blks_per_lun);
int nblks = factory_nblks(geo->blks_per_lun);
return ((ppa.g.ch * dev->luns_per_chnl * nblks) + (ppa.g.lun * nblks)) /
return ((ppa.g.ch * geo->luns_per_chnl * nblks) + (ppa.g.lun * nblks)) /
BITS_PER_LONG;
}
......@@ -589,7 +594,7 @@ static int nvm_factory_blks(struct nvm_dev *dev, struct ppa_addr ppa,
if (nr_blks < 0)
return nr_blks;
lunoff = factory_blk_offset(dev, ppa);
lunoff = factory_blk_offset(&dev->geo, ppa);
/* non-set bits correspond to the block must be erased */
for (i = 0; i < nr_blks; i++) {
......@@ -618,19 +623,19 @@ static int nvm_factory_blks(struct nvm_dev *dev, struct ppa_addr ppa,
static int nvm_fact_get_blks(struct nvm_dev *dev, struct ppa_addr *erase_list,
int max_ppas, unsigned long *blk_bitmap)
{
struct nvm_geo *geo = &dev->geo;
struct ppa_addr ppa;
int ch, lun, blkid, idx, done = 0, ppa_cnt = 0;
unsigned long *offset;
while (!done) {
done = 1;
nvm_for_each_lun_ppa(dev, ppa, ch, lun) {
idx = factory_blk_offset(dev, ppa);
nvm_for_each_lun_ppa(geo, ppa, ch, lun) {
idx = factory_blk_offset(geo, ppa);
offset = &blk_bitmap[idx];
blkid = find_first_zero_bit(offset,
dev->blks_per_lun);
if (blkid >= dev->blks_per_lun)
blkid = find_first_zero_bit(offset, geo->blks_per_lun);
if (blkid >= geo->blks_per_lun)
continue;
set_bit(blkid, offset);
......@@ -655,16 +660,17 @@ static int nvm_fact_get_blks(struct nvm_dev *dev, struct ppa_addr *erase_list,
static int nvm_fact_select_blks(struct nvm_dev *dev, unsigned long *blk_bitmap,
int flags)
{
struct nvm_geo *geo = &dev->geo;
struct ppa_addr ppa;
int ch, lun, nr_blks, ret = 0;
u8 *blks;
nr_blks = dev->blks_per_lun * dev->plane_mode;
nr_blks = geo->blks_per_lun * geo->plane_mode;
blks = kmalloc(nr_blks, GFP_KERNEL);
if (!blks)
return -ENOMEM;
nvm_for_each_lun_ppa(dev, ppa, ch, lun) {
nvm_for_each_lun_ppa(geo, ppa, ch, lun) {
ret = nvm_get_bb_tbl(dev, ppa, blks);
if (ret)
pr_err("nvm: failed bb tbl for ch%u lun%u\n",
......@@ -682,14 +688,15 @@ static int nvm_fact_select_blks(struct nvm_dev *dev, unsigned long *blk_bitmap,
int nvm_dev_factory(struct nvm_dev *dev, int flags)
{
struct nvm_geo *geo = &dev->geo;
struct ppa_addr *ppas;
int ppa_cnt, ret = -ENOMEM;
int max_ppas = dev->ops->max_phys_sect / dev->nr_planes;
int max_ppas = dev->ops->max_phys_sect / geo->nr_planes;
struct ppa_addr sysblk_ppas[MAX_SYSBLKS];
struct sysblk_scan s;
unsigned long *blk_bitmap;
blk_bitmap = kzalloc(factory_nblks(dev->blks_per_lun) * dev->nr_luns,
blk_bitmap = kzalloc(factory_nblks(geo->blks_per_lun) * geo->nr_luns,
GFP_KERNEL);
if (!blk_bitmap)
return ret;
......
......@@ -352,6 +352,7 @@ static int nvme_nvm_get_l2p_tbl(struct nvm_dev *nvmdev, u64 slba, u32 nlb,
while (nlb) {
u32 cmd_nlb = min(nlb_pr_rq, nlb);
u64 elba = slba + cmd_nlb;
c.l2p.slba = cpu_to_le64(cmd_slba);
c.l2p.nlb = cpu_to_le32(cmd_nlb);
......@@ -365,6 +366,11 @@ static int nvme_nvm_get_l2p_tbl(struct nvm_dev *nvmdev, u64 slba, u32 nlb,
goto out;
}
if (unlikely(elba > nvmdev->total_secs)) {
pr_err("nvm: L2P data from device is out of bounds!\n");
return -EINVAL;
}
if (update_l2p(cmd_slba, cmd_nlb, entries, priv)) {
ret = -EINTR;
goto out;
......@@ -383,11 +389,12 @@ static int nvme_nvm_get_bb_tbl(struct nvm_dev *nvmdev, struct ppa_addr ppa,
u8 *blks)
{
struct request_queue *q = nvmdev->q;
struct nvm_geo *geo = &nvmdev->geo;
struct nvme_ns *ns = q->queuedata;
struct nvme_ctrl *ctrl = ns->ctrl;
struct nvme_nvm_command c = {};
struct nvme_nvm_bb_tbl *bb_tbl;
int nr_blks = nvmdev->blks_per_lun * nvmdev->plane_mode;
int nr_blks = geo->blks_per_lun * geo->plane_mode;
int tblsz = sizeof(struct nvme_nvm_bb_tbl) + nr_blks;
int ret = 0;
......@@ -428,7 +435,7 @@ static int nvme_nvm_get_bb_tbl(struct nvm_dev *nvmdev, struct ppa_addr ppa,
goto out;
}
memcpy(blks, bb_tbl->blk, nvmdev->blks_per_lun * nvmdev->plane_mode);
memcpy(blks, bb_tbl->blk, geo->blks_per_lun * geo->plane_mode);
out:
kfree(bb_tbl);
return ret;
......
......@@ -211,7 +211,7 @@ struct nvm_id {
struct nvm_target {
struct list_head list;
struct list_head lun_list;
struct nvm_dev *dev;
struct nvm_tgt_dev *dev;
struct nvm_tgt_type *type;
struct gendisk *disk;
};
......@@ -286,7 +286,6 @@ struct nvm_lun {
* free_list and used_list
*/
unsigned int nr_free_blocks; /* Number of unused blocks */
int reserved_blocks;
struct nvm_block *blocks;
};
......@@ -315,22 +314,12 @@ struct nvm_sb_info {
struct ppa_addr fs_ppa;
};
struct nvm_dev {
struct nvm_dev_ops *ops;
struct list_head devices;
/* Media manager */
struct nvmm_type *mt;
void *mp;
/* System blocks */
struct nvm_sb_info sb;
/* Device information */
/* Device generic information */
struct nvm_geo {
int nr_chnls;
int nr_luns;
int luns_per_chnl; /* -1 if channels are not symmetric */
int nr_planes;
int luns_per_chnl;
int sec_per_pg; /* only sectors for a single page */
int pgs_per_blk;
int blks_per_lun;
......@@ -350,14 +339,43 @@ struct nvm_dev {
int sec_per_pl; /* all sectors across planes */
int sec_per_blk;
int sec_per_lun;
};
struct nvm_tgt_dev {
/* Device information */
struct nvm_geo geo;
sector_t total_secs;
struct nvm_id identity;
struct request_queue *q;
struct nvmm_type *mt;
struct nvm_dev_ops *ops;
void *parent;
};
struct nvm_dev {
struct nvm_dev_ops *ops;
struct list_head devices;
/* Media manager */
struct nvmm_type *mt;
void *mp;
/* System blocks */
struct nvm_sb_info sb;
/* Device information */
struct nvm_geo geo;
/* lower page table */
int lps_per_blk;
int *lptbl;
unsigned long total_blocks;
unsigned long total_secs;
int nr_luns;
unsigned long *lun_map;
void *dma_pool;
......@@ -373,7 +391,7 @@ struct nvm_dev {
spinlock_t lock;
};
static inline struct ppa_addr linear_to_generic_addr(struct nvm_dev *dev,
static inline struct ppa_addr linear_to_generic_addr(struct nvm_geo *geo,
struct ppa_addr r)
{
struct ppa_addr l;
......@@ -382,22 +400,22 @@ static inline struct ppa_addr linear_to_generic_addr(struct nvm_dev *dev,
l.ppa = 0;
div_u64_rem(ppa, dev->sec_per_pg, &secs);
div_u64_rem(ppa, geo->sec_per_pg, &secs);
l.g.sec = secs;
sector_div(ppa, dev->sec_per_pg);
div_u64_rem(ppa, dev->pgs_per_blk, &pgs);
sector_div(ppa, geo->sec_per_pg);
div_u64_rem(ppa, geo->pgs_per_blk, &pgs);
l.g.pg = pgs;
sector_div(ppa, dev->pgs_per_blk);
div_u64_rem(ppa, dev->blks_per_lun, &blks);
sector_div(ppa, geo->pgs_per_blk);
div_u64_rem(ppa, geo->blks_per_lun, &blks);
l.g.blk = blks;
sector_div(ppa, dev->blks_per_lun);
div_u64_rem(ppa, dev->luns_per_chnl, &luns);
sector_div(ppa, geo->blks_per_lun);
div_u64_rem(ppa, geo->luns_per_chnl, &luns);
l.g.lun = luns;
sector_div(ppa, dev->luns_per_chnl);
sector_div(ppa, geo->luns_per_chnl);
l.g.ch = ppa;
return l;
......@@ -406,14 +424,15 @@ static inline struct ppa_addr linear_to_generic_addr(struct nvm_dev *dev,
static inline struct ppa_addr generic_to_dev_addr(struct nvm_dev *dev,
struct ppa_addr r)
{
struct nvm_geo *geo = &dev->geo;
struct ppa_addr l;
l.ppa = ((u64)r.g.blk) << dev->ppaf.blk_offset;
l.ppa |= ((u64)r.g.pg) << dev->ppaf.pg_offset;
l.ppa |= ((u64)r.g.sec) << dev->ppaf.sect_offset;
l.ppa |= ((u64)r.g.pl) << dev->ppaf.pln_offset;
l.ppa |= ((u64)r.g.lun) << dev->ppaf.lun_offset;
l.ppa |= ((u64)r.g.ch) << dev->ppaf.ch_offset;
l.ppa = ((u64)r.g.blk) << geo->ppaf.blk_offset;
l.ppa |= ((u64)r.g.pg) << geo->ppaf.pg_offset;
l.ppa |= ((u64)r.g.sec) << geo->ppaf.sect_offset;
l.ppa |= ((u64)r.g.pl) << geo->ppaf.pln_offset;
l.ppa |= ((u64)r.g.lun) << geo->ppaf.lun_offset;
l.ppa |= ((u64)r.g.ch) << geo->ppaf.ch_offset;
return l;
}
......@@ -421,24 +440,25 @@ static inline struct ppa_addr generic_to_dev_addr(struct nvm_dev *dev,
static inline struct ppa_addr dev_to_generic_addr(struct nvm_dev *dev,
struct ppa_addr r)
{
struct nvm_geo *geo = &dev->geo;
struct ppa_addr l;
l.ppa = 0;
/*
* (r.ppa << X offset) & X len bitmask. X eq. blk, pg, etc.
*/
l.g.blk = (r.ppa >> dev->ppaf.blk_offset) &
(((1 << dev->ppaf.blk_len) - 1));
l.g.pg |= (r.ppa >> dev->ppaf.pg_offset) &
(((1 << dev->ppaf.pg_len) - 1));
l.g.sec |= (r.ppa >> dev->ppaf.sect_offset) &
(((1 << dev->ppaf.sect_len) - 1));
l.g.pl |= (r.ppa >> dev->ppaf.pln_offset) &
(((1 << dev->ppaf.pln_len) - 1));
l.g.lun |= (r.ppa >> dev->ppaf.lun_offset) &
(((1 << dev->ppaf.lun_len) - 1));
l.g.ch |= (r.ppa >> dev->ppaf.ch_offset) &
(((1 << dev->ppaf.ch_len) - 1));
l.g.blk = (r.ppa >> geo->ppaf.blk_offset) &
(((1 << geo->ppaf.blk_len) - 1));
l.g.pg |= (r.ppa >> geo->ppaf.pg_offset) &
(((1 << geo->ppaf.pg_len) - 1));
l.g.sec |= (r.ppa >> geo->ppaf.sect_offset) &
(((1 << geo->ppaf.sect_len) - 1));
l.g.pl |= (r.ppa >> geo->ppaf.pln_offset) &
(((1 << geo->ppaf.pln_len) - 1));
l.g.lun |= (r.ppa >> geo->ppaf.lun_offset) &
(((1 << geo->ppaf.lun_len) - 1));
l.g.ch |= (r.ppa >> geo->ppaf.ch_offset) &
(((1 << geo->ppaf.ch_len) - 1));
return l;
}
......@@ -456,11 +476,12 @@ static inline void ppa_set_empty(struct ppa_addr *ppa_addr)
static inline struct ppa_addr block_to_ppa(struct nvm_dev *dev,
struct nvm_block *blk)
{
struct nvm_geo *geo = &dev->geo;
struct ppa_addr ppa;
struct nvm_lun *lun = blk->lun;
ppa.ppa = 0;
ppa.g.blk = blk->id % dev->blks_per_lun;
ppa.g.blk = blk->id % geo->blks_per_lun;
ppa.g.lun = lun->lun_id;
ppa.g.ch = lun->chnl_id;
......@@ -483,7 +504,8 @@ static inline int ppa_to_slc(struct nvm_dev *dev, int slc_pg)
typedef blk_qc_t (nvm_tgt_make_rq_fn)(struct request_queue *, struct bio *);
typedef sector_t (nvm_tgt_capacity_fn)(void *);
typedef void *(nvm_tgt_init_fn)(struct nvm_dev *, struct gendisk *, int, int);
typedef void *(nvm_tgt_init_fn)(struct nvm_tgt_dev *, struct gendisk *, int,
int);
typedef void (nvm_tgt_exit_fn)(void *);
struct nvm_tgt_type {
......@@ -516,9 +538,6 @@ typedef void (nvmm_unregister_fn)(struct nvm_dev *);
typedef int (nvmm_create_tgt_fn)(struct nvm_dev *, struct nvm_ioctl_create *);
typedef int (nvmm_remove_tgt_fn)(struct nvm_dev *, struct nvm_ioctl_remove *);
typedef struct nvm_block *(nvmm_get_blk_fn)(struct nvm_dev *,
struct nvm_lun *, unsigned long);
typedef void (nvmm_put_blk_fn)(struct nvm_dev *, struct nvm_block *);
typedef int (nvmm_submit_io_fn)(struct nvm_dev *, struct nvm_rq *);
typedef int (nvmm_erase_blk_fn)(struct nvm_dev *, struct nvm_block *, int);
typedef void (nvmm_mark_blk_fn)(struct nvm_dev *, struct ppa_addr, int);
......@@ -538,10 +557,6 @@ struct nvmm_type {
nvmm_create_tgt_fn *create_tgt;
nvmm_remove_tgt_fn *remove_tgt;
/* Block administration callbacks */
nvmm_get_blk_fn *get_blk;
nvmm_put_blk_fn *put_blk;
nvmm_submit_io_fn *submit_io;
nvmm_erase_blk_fn *erase_blk;
......@@ -563,10 +578,6 @@ struct nvmm_type {
extern int nvm_register_mgr(struct nvmm_type *);
extern void nvm_unregister_mgr(struct nvmm_type *);
extern struct nvm_block *nvm_get_blk(struct nvm_dev *, struct nvm_lun *,
unsigned long);
extern void nvm_put_blk(struct nvm_dev *, struct nvm_block *);
extern struct nvm_dev *nvm_alloc_dev(int);
extern int nvm_register(struct nvm_dev *);
extern void nvm_unregister(struct nvm_dev *);
......@@ -611,10 +622,10 @@ extern int nvm_init_sysblock(struct nvm_dev *, struct nvm_sb_info *);
extern int nvm_dev_factory(struct nvm_dev *, int flags);
#define nvm_for_each_lun_ppa(dev, ppa, chid, lunid) \
for ((chid) = 0, (ppa).ppa = 0; (chid) < (dev)->nr_chnls; \
#define nvm_for_each_lun_ppa(geo, ppa, chid, lunid) \
for ((chid) = 0, (ppa).ppa = 0; (chid) < (geo)->nr_chnls; \
(chid)++, (ppa).g.ch = (chid)) \
for ((lunid) = 0; (lunid) < (dev)->luns_per_chnl; \
for ((lunid) = 0; (lunid) < (geo)->luns_per_chnl; \
(lunid)++, (ppa).g.lun = (lunid))
#else /* CONFIG_NVM */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment