|
@@ -18,8 +18,6 @@
|
|
*
|
|
*
|
|
*/
|
|
*/
|
|
|
|
|
|
-#include <linux/blkdev.h>
|
|
|
|
-#include <linux/blk-mq.h>
|
|
|
|
#include <linux/list.h>
|
|
#include <linux/list.h>
|
|
#include <linux/types.h>
|
|
#include <linux/types.h>
|
|
#include <linux/sem.h>
|
|
#include <linux/sem.h>
|
|
@@ -28,42 +26,37 @@
|
|
#include <linux/miscdevice.h>
|
|
#include <linux/miscdevice.h>
|
|
#include <linux/lightnvm.h>
|
|
#include <linux/lightnvm.h>
|
|
#include <linux/sched/sysctl.h>
|
|
#include <linux/sched/sysctl.h>
|
|
-#include <uapi/linux/lightnvm.h>
|
|
|
|
|
|
|
|
static LIST_HEAD(nvm_tgt_types);
|
|
static LIST_HEAD(nvm_tgt_types);
|
|
static LIST_HEAD(nvm_mgrs);
|
|
static LIST_HEAD(nvm_mgrs);
|
|
static LIST_HEAD(nvm_devices);
|
|
static LIST_HEAD(nvm_devices);
|
|
-static LIST_HEAD(nvm_targets);
|
|
|
|
static DECLARE_RWSEM(nvm_lock);
|
|
static DECLARE_RWSEM(nvm_lock);
|
|
|
|
|
|
-static struct nvm_target *nvm_find_target(const char *name)
|
|
|
|
|
|
+struct nvm_tgt_type *nvm_find_target_type(const char *name, int lock)
|
|
{
|
|
{
|
|
- struct nvm_target *tgt;
|
|
|
|
|
|
+ struct nvm_tgt_type *tmp, *tt = NULL;
|
|
|
|
|
|
- list_for_each_entry(tgt, &nvm_targets, list)
|
|
|
|
- if (!strcmp(name, tgt->disk->disk_name))
|
|
|
|
- return tgt;
|
|
|
|
|
|
+ if (lock)
|
|
|
|
+ down_write(&nvm_lock);
|
|
|
|
|
|
- return NULL;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-static struct nvm_tgt_type *nvm_find_target_type(const char *name)
|
|
|
|
-{
|
|
|
|
- struct nvm_tgt_type *tt;
|
|
|
|
-
|
|
|
|
- list_for_each_entry(tt, &nvm_tgt_types, list)
|
|
|
|
- if (!strcmp(name, tt->name))
|
|
|
|
- return tt;
|
|
|
|
|
|
+ list_for_each_entry(tmp, &nvm_tgt_types, list)
|
|
|
|
+ if (!strcmp(name, tmp->name)) {
|
|
|
|
+ tt = tmp;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
|
|
- return NULL;
|
|
|
|
|
|
+ if (lock)
|
|
|
|
+ up_write(&nvm_lock);
|
|
|
|
+ return tt;
|
|
}
|
|
}
|
|
|
|
+EXPORT_SYMBOL(nvm_find_target_type);
|
|
|
|
|
|
int nvm_register_tgt_type(struct nvm_tgt_type *tt)
|
|
int nvm_register_tgt_type(struct nvm_tgt_type *tt)
|
|
{
|
|
{
|
|
int ret = 0;
|
|
int ret = 0;
|
|
|
|
|
|
down_write(&nvm_lock);
|
|
down_write(&nvm_lock);
|
|
- if (nvm_find_target_type(tt->name))
|
|
|
|
|
|
+ if (nvm_find_target_type(tt->name, 0))
|
|
ret = -EEXIST;
|
|
ret = -EEXIST;
|
|
else
|
|
else
|
|
list_add(&tt->list, &nvm_tgt_types);
|
|
list_add(&tt->list, &nvm_tgt_types);
|
|
@@ -605,42 +598,11 @@ err_fmtype:
|
|
return ret;
|
|
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;
|
|
|
|
-
|
|
|
|
- lockdep_assert_held(&nvm_lock);
|
|
|
|
-
|
|
|
|
- del_gendisk(tdisk);
|
|
|
|
- blk_cleanup_queue(q);
|
|
|
|
-
|
|
|
|
- if (tt->exit)
|
|
|
|
- tt->exit(tdisk->private_data);
|
|
|
|
-
|
|
|
|
- put_disk(tdisk);
|
|
|
|
-
|
|
|
|
- list_del(&t->list);
|
|
|
|
- kfree(t);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
static void nvm_free_mgr(struct nvm_dev *dev)
|
|
static void nvm_free_mgr(struct nvm_dev *dev)
|
|
{
|
|
{
|
|
- struct nvm_target *tgt, *tmp;
|
|
|
|
-
|
|
|
|
if (!dev->mt)
|
|
if (!dev->mt)
|
|
return;
|
|
return;
|
|
|
|
|
|
- down_write(&nvm_lock);
|
|
|
|
- list_for_each_entry_safe(tgt, tmp, &nvm_targets, list) {
|
|
|
|
- if (tgt->dev != dev)
|
|
|
|
- continue;
|
|
|
|
-
|
|
|
|
- nvm_remove_target(tgt);
|
|
|
|
- }
|
|
|
|
- up_write(&nvm_lock);
|
|
|
|
-
|
|
|
|
dev->mt->unregister_mgr(dev);
|
|
dev->mt->unregister_mgr(dev);
|
|
dev->mt = NULL;
|
|
dev->mt = NULL;
|
|
}
|
|
}
|
|
@@ -787,91 +749,6 @@ void nvm_unregister(char *disk_name)
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(nvm_unregister);
|
|
EXPORT_SYMBOL(nvm_unregister);
|
|
|
|
|
|
-static const struct block_device_operations nvm_fops = {
|
|
|
|
- .owner = THIS_MODULE,
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
-static int nvm_create_target(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;
|
|
|
|
- void *targetdata;
|
|
|
|
-
|
|
|
|
- if (!dev->mt) {
|
|
|
|
- pr_info("nvm: device has no media manager registered.\n");
|
|
|
|
- return -ENODEV;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- down_write(&nvm_lock);
|
|
|
|
- tt = nvm_find_target_type(create->tgttype);
|
|
|
|
- if (!tt) {
|
|
|
|
- pr_err("nvm: target type %s not found\n", create->tgttype);
|
|
|
|
- up_write(&nvm_lock);
|
|
|
|
- return -EINVAL;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- t = nvm_find_target(create->tgtname);
|
|
|
|
- if (t) {
|
|
|
|
- pr_err("nvm: target name already exists.\n");
|
|
|
|
- up_write(&nvm_lock);
|
|
|
|
- return -EINVAL;
|
|
|
|
- }
|
|
|
|
- up_write(&nvm_lock);
|
|
|
|
-
|
|
|
|
- t = kmalloc(sizeof(struct nvm_target), GFP_KERNEL);
|
|
|
|
- if (!t)
|
|
|
|
- return -ENOMEM;
|
|
|
|
-
|
|
|
|
- tqueue = blk_alloc_queue_node(GFP_KERNEL, dev->q->node);
|
|
|
|
- if (!tqueue)
|
|
|
|
- goto err_t;
|
|
|
|
- blk_queue_make_request(tqueue, tt->make_rq);
|
|
|
|
-
|
|
|
|
- tdisk = alloc_disk(0);
|
|
|
|
- if (!tdisk)
|
|
|
|
- goto err_queue;
|
|
|
|
-
|
|
|
|
- sprintf(tdisk->disk_name, "%s", create->tgtname);
|
|
|
|
- tdisk->flags = GENHD_FL_EXT_DEVT;
|
|
|
|
- tdisk->major = 0;
|
|
|
|
- tdisk->first_minor = 0;
|
|
|
|
- tdisk->fops = &nvm_fops;
|
|
|
|
- tdisk->queue = tqueue;
|
|
|
|
-
|
|
|
|
- targetdata = tt->init(dev, tdisk, s->lun_begin, s->lun_end);
|
|
|
|
- if (IS_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);
|
|
|
|
-
|
|
|
|
- t->type = tt;
|
|
|
|
- t->disk = tdisk;
|
|
|
|
- t->dev = dev;
|
|
|
|
-
|
|
|
|
- down_write(&nvm_lock);
|
|
|
|
- list_add_tail(&t->list, &nvm_targets);
|
|
|
|
- up_write(&nvm_lock);
|
|
|
|
-
|
|
|
|
- return 0;
|
|
|
|
-err_init:
|
|
|
|
- put_disk(tdisk);
|
|
|
|
-err_queue:
|
|
|
|
- blk_cleanup_queue(tqueue);
|
|
|
|
-err_t:
|
|
|
|
- kfree(t);
|
|
|
|
- return -ENOMEM;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
static int __nvm_configure_create(struct nvm_ioctl_create *create)
|
|
static int __nvm_configure_create(struct nvm_ioctl_create *create)
|
|
{
|
|
{
|
|
struct nvm_dev *dev;
|
|
struct nvm_dev *dev;
|
|
@@ -880,11 +757,17 @@ static int __nvm_configure_create(struct nvm_ioctl_create *create)
|
|
down_write(&nvm_lock);
|
|
down_write(&nvm_lock);
|
|
dev = nvm_find_nvm_dev(create->dev);
|
|
dev = nvm_find_nvm_dev(create->dev);
|
|
up_write(&nvm_lock);
|
|
up_write(&nvm_lock);
|
|
|
|
+
|
|
if (!dev) {
|
|
if (!dev) {
|
|
pr_err("nvm: device not found\n");
|
|
pr_err("nvm: device not found\n");
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (!dev->mt) {
|
|
|
|
+ pr_info("nvm: device has no media manager registered.\n");
|
|
|
|
+ return -ENODEV;
|
|
|
|
+ }
|
|
|
|
+
|
|
if (create->conf.type != NVM_CONFIG_TYPE_SIMPLE) {
|
|
if (create->conf.type != NVM_CONFIG_TYPE_SIMPLE) {
|
|
pr_err("nvm: config type not valid\n");
|
|
pr_err("nvm: config type not valid\n");
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
@@ -897,25 +780,7 @@ static int __nvm_configure_create(struct nvm_ioctl_create *create)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
|
|
- return nvm_create_target(dev, create);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-static int __nvm_configure_remove(struct nvm_ioctl_remove *remove)
|
|
|
|
-{
|
|
|
|
- struct nvm_target *t;
|
|
|
|
-
|
|
|
|
- down_write(&nvm_lock);
|
|
|
|
- t = nvm_find_target(remove->tgtname);
|
|
|
|
- if (!t) {
|
|
|
|
- pr_err("nvm: target \"%s\" doesn't exist.\n", remove->tgtname);
|
|
|
|
- up_write(&nvm_lock);
|
|
|
|
- return -EINVAL;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- nvm_remove_target(t);
|
|
|
|
- up_write(&nvm_lock);
|
|
|
|
-
|
|
|
|
- return 0;
|
|
|
|
|
|
+ return dev->mt->create_tgt(dev, create);
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_NVM_DEBUG
|
|
#ifdef CONFIG_NVM_DEBUG
|
|
@@ -950,8 +815,9 @@ static int nvm_configure_show(const char *val)
|
|
static int nvm_configure_remove(const char *val)
|
|
static int nvm_configure_remove(const char *val)
|
|
{
|
|
{
|
|
struct nvm_ioctl_remove remove;
|
|
struct nvm_ioctl_remove remove;
|
|
|
|
+ struct nvm_dev *dev;
|
|
char opcode;
|
|
char opcode;
|
|
- int ret;
|
|
|
|
|
|
+ int ret = 0;
|
|
|
|
|
|
ret = sscanf(val, "%c %256s", &opcode, remove.tgtname);
|
|
ret = sscanf(val, "%c %256s", &opcode, remove.tgtname);
|
|
if (ret != 2) {
|
|
if (ret != 2) {
|
|
@@ -961,7 +827,13 @@ static int nvm_configure_remove(const char *val)
|
|
|
|
|
|
remove.flags = 0;
|
|
remove.flags = 0;
|
|
|
|
|
|
- return __nvm_configure_remove(&remove);
|
|
|
|
|
|
+ list_for_each_entry(dev, &nvm_devices, devices) {
|
|
|
|
+ ret = dev->mt->remove_tgt(dev, &remove);
|
|
|
|
+ if (!ret)
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
}
|
|
}
|
|
|
|
|
|
static int nvm_configure_create(const char *val)
|
|
static int nvm_configure_create(const char *val)
|
|
@@ -1158,6 +1030,8 @@ static long nvm_ioctl_dev_create(struct file *file, void __user *arg)
|
|
static long nvm_ioctl_dev_remove(struct file *file, void __user *arg)
|
|
static long nvm_ioctl_dev_remove(struct file *file, void __user *arg)
|
|
{
|
|
{
|
|
struct nvm_ioctl_remove remove;
|
|
struct nvm_ioctl_remove remove;
|
|
|
|
+ struct nvm_dev *dev;
|
|
|
|
+ int ret = 0;
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
return -EPERM;
|
|
return -EPERM;
|
|
@@ -1172,7 +1046,13 @@ static long nvm_ioctl_dev_remove(struct file *file, void __user *arg)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
|
|
- return __nvm_configure_remove(&remove);
|
|
|
|
|
|
+ list_for_each_entry(dev, &nvm_devices, devices) {
|
|
|
|
+ ret = dev->mt->remove_tgt(dev, &remove);
|
|
|
|
+ if (!ret)
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
}
|
|
}
|
|
|
|
|
|
static void nvm_setup_nvm_sb_info(struct nvm_sb_info *info)
|
|
static void nvm_setup_nvm_sb_info(struct nvm_sb_info *info)
|