Commit d0a712ce authored by Wenwei Tao's avatar Wenwei Tao Committed by Jens Axboe

lightnvm: missing nvm_lock acquire

To avoid race conditions, traverse dev, media manager,
and target lists and also register, unregister entries
to/from them, should be always under the nvm_lock control.
Signed-off-by: default avatarWenwei Tao <ww.tao0320@gmail.com>
Signed-off-by: default avatarMatias Bjørling <m@bjorling.me>
Signed-off-by: default avatarJens Axboe <axboe@fb.com>
parent 08236c6b
...@@ -123,6 +123,26 @@ void nvm_unregister_mgr(struct nvmm_type *mt) ...@@ -123,6 +123,26 @@ void nvm_unregister_mgr(struct nvmm_type *mt)
} }
EXPORT_SYMBOL(nvm_unregister_mgr); EXPORT_SYMBOL(nvm_unregister_mgr);
/* register with device with a supported manager */
static int register_mgr(struct nvm_dev *dev)
{
struct nvmm_type *mt;
int ret = 0;
list_for_each_entry(mt, &nvm_mgrs, list) {
ret = mt->register_mgr(dev);
if (ret > 0) {
dev->mt = mt;
break; /* successfully initialized */
}
}
if (!ret)
pr_info("nvm: no compatible nvm manager found.\n");
return ret;
}
static struct nvm_dev *nvm_find_nvm_dev(const char *name) static struct nvm_dev *nvm_find_nvm_dev(const char *name)
{ {
struct nvm_dev *dev; struct nvm_dev *dev;
...@@ -221,7 +241,6 @@ static void nvm_free(struct nvm_dev *dev) ...@@ -221,7 +241,6 @@ static void nvm_free(struct nvm_dev *dev)
static int nvm_init(struct nvm_dev *dev) static int nvm_init(struct nvm_dev *dev)
{ {
struct nvmm_type *mt;
int ret = -EINVAL; int ret = -EINVAL;
if (!dev->q || !dev->ops) if (!dev->q || !dev->ops)
...@@ -252,21 +271,13 @@ static int nvm_init(struct nvm_dev *dev) ...@@ -252,21 +271,13 @@ static int nvm_init(struct nvm_dev *dev)
goto err; goto err;
} }
/* register with device with a supported manager */ down_write(&nvm_lock);
list_for_each_entry(mt, &nvm_mgrs, list) { ret = register_mgr(dev);
ret = mt->register_mgr(dev); up_write(&nvm_lock);
if (ret < 0) if (ret < 0)
goto err; /* initialization failed */ goto err;
if (ret > 0) { if (!ret)
dev->mt = mt;
break; /* successfully initialized */
}
}
if (!ret) {
pr_info("nvm: no compatible manager found.\n");
return 0; return 0;
}
pr_info("nvm: registered %s [%u/%u/%u/%u/%u/%u]\n", pr_info("nvm: registered %s [%u/%u/%u/%u/%u/%u]\n",
dev->name, dev->sec_per_pg, dev->nr_planes, dev->name, dev->sec_per_pg, dev->nr_planes,
...@@ -337,15 +348,17 @@ EXPORT_SYMBOL(nvm_register); ...@@ -337,15 +348,17 @@ EXPORT_SYMBOL(nvm_register);
void nvm_unregister(char *disk_name) void nvm_unregister(char *disk_name)
{ {
struct nvm_dev *dev = nvm_find_nvm_dev(disk_name); struct nvm_dev *dev;
down_write(&nvm_lock);
dev = nvm_find_nvm_dev(disk_name);
if (!dev) { if (!dev) {
pr_err("nvm: could not find device %s to unregister\n", pr_err("nvm: could not find device %s to unregister\n",
disk_name); disk_name);
up_write(&nvm_lock);
return; return;
} }
down_write(&nvm_lock);
list_del(&dev->devices); list_del(&dev->devices);
up_write(&nvm_lock); up_write(&nvm_lock);
...@@ -363,38 +376,30 @@ static int nvm_create_target(struct nvm_dev *dev, ...@@ -363,38 +376,30 @@ static int nvm_create_target(struct nvm_dev *dev,
{ {
struct nvm_ioctl_create_simple *s = &create->conf.s; struct nvm_ioctl_create_simple *s = &create->conf.s;
struct request_queue *tqueue; struct request_queue *tqueue;
struct nvmm_type *mt;
struct gendisk *tdisk; struct gendisk *tdisk;
struct nvm_tgt_type *tt; struct nvm_tgt_type *tt;
struct nvm_target *t; struct nvm_target *t;
void *targetdata; void *targetdata;
int ret = 0; int ret = 0;
down_write(&nvm_lock);
if (!dev->mt) { if (!dev->mt) {
/* register with device with a supported NVM manager */ ret = register_mgr(dev);
list_for_each_entry(mt, &nvm_mgrs, list) { if (!ret)
ret = mt->register_mgr(dev); ret = -ENODEV;
if (ret < 0) if (ret < 0) {
return ret; /* initialization failed */ up_write(&nvm_lock);
if (ret > 0) { return ret;
dev->mt = mt;
break; /* successfully initialized */
}
}
if (!ret) {
pr_info("nvm: no compatible nvm manager found.\n");
return -ENODEV;
} }
} }
tt = nvm_find_target_type(create->tgttype); tt = nvm_find_target_type(create->tgttype);
if (!tt) { if (!tt) {
pr_err("nvm: target type %s not found\n", create->tgttype); pr_err("nvm: target type %s not found\n", create->tgttype);
up_write(&nvm_lock);
return -EINVAL; return -EINVAL;
} }
down_write(&nvm_lock);
list_for_each_entry(t, &dev->online_targets, list) { list_for_each_entry(t, &dev->online_targets, list) {
if (!strcmp(create->tgtname, t->disk->disk_name)) { if (!strcmp(create->tgtname, t->disk->disk_name)) {
pr_err("nvm: target name already exists.\n"); pr_err("nvm: target name already exists.\n");
...@@ -478,7 +483,9 @@ static int __nvm_configure_create(struct nvm_ioctl_create *create) ...@@ -478,7 +483,9 @@ static int __nvm_configure_create(struct nvm_ioctl_create *create)
struct nvm_dev *dev; struct nvm_dev *dev;
struct nvm_ioctl_create_simple *s; struct nvm_ioctl_create_simple *s;
down_write(&nvm_lock);
dev = nvm_find_nvm_dev(create->dev); dev = nvm_find_nvm_dev(create->dev);
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;
...@@ -537,7 +544,9 @@ static int nvm_configure_show(const char *val) ...@@ -537,7 +544,9 @@ static int nvm_configure_show(const char *val)
return -EINVAL; return -EINVAL;
} }
down_write(&nvm_lock);
dev = nvm_find_nvm_dev(devname); dev = nvm_find_nvm_dev(devname);
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;
......
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