1379 lines · 30.1 KB · C
RawDownload
/* * Copyright (C) 2015 IT University of Copenhagen. All rights reserved. * Initial release: Matias Bjorling <m@bjorling.me> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING.  If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, * USA. * */ #include <linux/list.h>#include <linux/types.h>#include <linux/sem.h>#include <linux/bitmap.h>#include <linux/moduleparam.h>#include <linux/miscdevice.h>#include <linux/lightnvm.h>#include <linux/sched/sysctl.h> static LIST_HEAD(nvm_tgt_types);static DECLARE_RWSEM(nvm_tgtt_lock);static LIST_HEAD(nvm_devices);static DECLARE_RWSEM(nvm_lock); /* Map between virtual and physical channel and lun */struct nvm_ch_map {	int ch_off;	int nr_luns;	int *lun_offs;}; struct nvm_dev_map {	struct nvm_ch_map *chnls;	int nr_chnls;}; struct nvm_area {	struct list_head list;	sector_t begin;	sector_t end;	/* end is excluded */}; static struct nvm_target *nvm_find_target(struct nvm_dev *dev, const char *name){	struct nvm_target *tgt; 	list_for_each_entry(tgt, &dev->targets, list)		if (!strcmp(name, tgt->disk->disk_name))			return tgt; 	return NULL;} static int nvm_reserve_luns(struct nvm_dev *dev, 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 nvm_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 nvm_remove_tgt_dev(struct nvm_tgt_dev *tgt_dev, int clear){	struct nvm_dev *dev = tgt_dev->parent;	struct nvm_dev_map *dev_map = tgt_dev->map;	int i, j; 	for (i = 0; i < dev_map->nr_chnls; i++) {		struct nvm_ch_map *ch_map = &dev_map->chnls[i];		int *lun_offs = ch_map->lun_offs;		int ch = i + ch_map->ch_off; 		if (clear) {			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 *nvm_create_tgt_dev(struct nvm_dev *dev,					      int lun_begin, int lun_end){	struct nvm_tgt_dev *tgt_dev = NULL;	struct nvm_dev_map *dev_rmap = dev->rmap;	struct nvm_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 nvm_dev_map), GFP_KERNEL);	if (!dev_map)		goto err_dev; 	dev_map->chnls = kcalloc(nr_chnls, sizeof(struct nvm_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 nvm_ch_map *ch_rmap = &dev_rmap->chnls[i + bch];		int *lun_roffs = ch_rmap->lun_offs;		struct nvm_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 const struct block_device_operations nvm_fops = {	.owner		= THIS_MODULE,}; static int nvm_create_tgt(struct nvm_dev *dev, struct nvm_ioctl_create *create){	struct nvm_ioctl_create_simple *s = &create->conf.s;	struct request_queue *tqueue;	struct gendisk *tdisk;	struct nvm_tgt_type *tt;	struct nvm_target *t;	struct nvm_tgt_dev *tgt_dev;	void *targetdata;	int ret; 	tt = nvm_find_target_type(create->tgttype, 1);	if (!tt) {		pr_err("nvm: target type %s not found\n", create->tgttype);		return -EINVAL;	} 	mutex_lock(&dev->mlock);	t = nvm_find_target(dev, create->tgtname);	if (t) {		pr_err("nvm: target name already exists.\n");		mutex_unlock(&dev->mlock);		return -EINVAL;	}	mutex_unlock(&dev->mlock); 	ret = nvm_reserve_luns(dev, s->lun_begin, s->lun_end);	if (ret)		return ret; 	t = kmalloc(sizeof(struct nvm_target), GFP_KERNEL);	if (!t) {		ret = -ENOMEM;		goto err_reserve;	} 	tgt_dev = nvm_create_tgt_dev(dev, s->lun_begin, s->lun_end);	if (!tgt_dev) {		pr_err("nvm: could not create target device\n");		ret = -ENOMEM;		goto err_t;	} 	tdisk = alloc_disk(0);	if (!tdisk) {		ret = -ENOMEM;		goto err_dev;	} 	tqueue = blk_alloc_queue_node(GFP_KERNEL, dev->q->node);	if (!tqueue) {		ret = -ENOMEM;		goto err_disk;	}	blk_queue_make_request(tqueue, tt->make_rq); 	strlcpy(tdisk->disk_name, create->tgtname, sizeof(tdisk->disk_name));	tdisk->flags = GENHD_FL_EXT_DEVT;	tdisk->major = 0;	tdisk->first_minor = 0;	tdisk->fops = &nvm_fops;	tdisk->queue = tqueue; 	targetdata = tt->init(tgt_dev, tdisk, create->flags);	if (IS_ERR(targetdata)) {		ret = PTR_ERR(targetdata);		goto err_init;	} 	tdisk->private_data = targetdata;	tqueue->queuedata = targetdata; 	blk_queue_max_hw_sectors(tqueue, 8 * dev->ops->max_phys_sect); 	set_capacity(tdisk, tt->capacity(targetdata));	add_disk(tdisk); 	if (tt->sysfs_init && tt->sysfs_init(tdisk)) {		ret = -ENOMEM;		goto err_sysfs;	} 	t->type = tt;	t->disk = tdisk;	t->dev = tgt_dev; 	mutex_lock(&dev->mlock);	list_add_tail(&t->list, &dev->targets);	mutex_unlock(&dev->mlock); 	return 0;err_sysfs:	if (tt->exit)		tt->exit(targetdata);err_init:	blk_cleanup_queue(tqueue);	tdisk->queue = NULL;err_disk:	put_disk(tdisk);err_dev:	nvm_remove_tgt_dev(tgt_dev, 0);err_t:	kfree(t);err_reserve:	nvm_release_luns_err(dev, s->lun_begin, s->lun_end);	return ret;} static void __nvm_remove_target(struct nvm_target *t){	struct nvm_tgt_type *tt = t->type;	struct gendisk *tdisk = t->disk;	struct request_queue *q = tdisk->queue; 	del_gendisk(tdisk);	blk_cleanup_queue(q); 	if (tt->sysfs_exit)		tt->sysfs_exit(tdisk); 	if (tt->exit)		tt->exit(tdisk->private_data); 	nvm_remove_tgt_dev(t->dev, 1);	put_disk(tdisk); 	list_del(&t->list);	kfree(t);} /** * nvm_remove_tgt - Removes a target from the media manager * @dev:	device * @remove:	ioctl structure with target name to remove. * * Returns: * 0: on success * 1: on not found * <0: on error */static int nvm_remove_tgt(struct nvm_dev *dev, struct nvm_ioctl_remove *remove){	struct nvm_target *t; 	mutex_lock(&dev->mlock);	t = nvm_find_target(dev, remove->tgtname);	if (!t) {		mutex_unlock(&dev->mlock);		return 1;	}	__nvm_remove_target(t);	mutex_unlock(&dev->mlock); 	return 0;} static int nvm_register_map(struct nvm_dev *dev){	struct nvm_dev_map *rmap;	int i, j; 	rmap = kmalloc(sizeof(struct nvm_dev_map), GFP_KERNEL);	if (!rmap)		goto err_rmap; 	rmap->chnls = kcalloc(dev->geo.nr_chnls, sizeof(struct nvm_ch_map),								GFP_KERNEL);	if (!rmap->chnls)		goto err_chnls; 	for (i = 0; i < dev->geo.nr_chnls; i++) {		struct nvm_ch_map *ch_rmap;		int *lun_roffs;		int luns_in_chnl = dev->geo.luns_per_chnl; 		ch_rmap = &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;	} 	dev->rmap = rmap; 	return 0;err_ch:	while (--i >= 0)		kfree(rmap->chnls[i].lun_offs);err_chnls:	kfree(rmap);err_rmap:	return -ENOMEM;} static void nvm_unregister_map(struct nvm_dev *dev){	struct nvm_dev_map *rmap = dev->rmap;	int i; 	for (i = 0; i < dev->geo.nr_chnls; i++)		kfree(rmap->chnls[i].lun_offs); 	kfree(rmap->chnls);	kfree(rmap);} static void nvm_map_to_dev(struct nvm_tgt_dev *tgt_dev, struct ppa_addr *p){	struct nvm_dev_map *dev_map = tgt_dev->map;	struct nvm_ch_map *ch_map = &dev_map->chnls[p->g.ch];	int lun_off = ch_map->lun_offs[p->g.lun]; 	p->g.ch += ch_map->ch_off;	p->g.lun += lun_off;} static void nvm_map_to_tgt(struct nvm_tgt_dev *tgt_dev, struct ppa_addr *p){	struct nvm_dev *dev = tgt_dev->parent;	struct nvm_dev_map *dev_rmap = dev->rmap;	struct nvm_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;} static void nvm_ppa_tgt_to_dev(struct nvm_tgt_dev *tgt_dev,				struct ppa_addr *ppa_list, int nr_ppas){	int i; 	for (i = 0; i < nr_ppas; i++) {		nvm_map_to_dev(tgt_dev, &ppa_list[i]);		ppa_list[i] = generic_to_dev_addr(tgt_dev, ppa_list[i]);	}} static void nvm_ppa_dev_to_tgt(struct nvm_tgt_dev *tgt_dev,				struct ppa_addr *ppa_list, int nr_ppas){	int i; 	for (i = 0; i < nr_ppas; i++) {		ppa_list[i] = dev_to_generic_addr(tgt_dev, ppa_list[i]);		nvm_map_to_tgt(tgt_dev, &ppa_list[i]);	}} static void nvm_rq_tgt_to_dev(struct nvm_tgt_dev *tgt_dev, struct nvm_rq *rqd){	if (rqd->nr_ppas == 1) {		nvm_ppa_tgt_to_dev(tgt_dev, &rqd->ppa_addr, 1);		return;	} 	nvm_ppa_tgt_to_dev(tgt_dev, rqd->ppa_list, rqd->nr_ppas);} static void nvm_rq_dev_to_tgt(struct nvm_tgt_dev *tgt_dev, struct nvm_rq *rqd){	if (rqd->nr_ppas == 1) {		nvm_ppa_dev_to_tgt(tgt_dev, &rqd->ppa_addr, 1);		return;

Lines 1–500 of 1379.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t