Commit e69397ea authored by Igor Konopko's avatar Igor Konopko Committed by Jens Axboe

lightnvm: track inflight target creations

When creation process is still in progress, target is not yet on
targets list. This causes a chance for removing whole lightnvm
subsystem by calling nvm_unregister() in the meantime and finally by
causing kernel panic inside target init function.

This patch changes the behaviour by adding kref variable which tracks
all the users of nvm_dev structure. When nvm_dev is allocated, kref
value is set to 1. Then before every target creation the value is
increased and decreased after target removal. The extra reference
is decreased when nvm subsystem is unregistered.
Signed-off-by: default avatarIgor Konopko <igor.j.konopko@intel.com>
Reviewed-by: default avatarJavier González <javier@javigon.com>
Signed-off-by: default avatarMatias Bjørling <mb@lightnvm.io>
Signed-off-by: default avatarJens Axboe <axboe@kernel.dk>
parent a24eab59
......@@ -45,6 +45,8 @@ struct nvm_dev_map {
int num_ch;
};
static void nvm_free(struct kref *ref);
static struct nvm_target *nvm_find_target(struct nvm_dev *dev, const char *name)
{
struct nvm_target *tgt;
......@@ -501,6 +503,7 @@ static int nvm_remove_tgt(struct nvm_dev *dev, struct nvm_ioctl_remove *remove)
}
__nvm_remove_target(t, true);
mutex_unlock(&dev->mlock);
kref_put(&dev->ref, nvm_free);
return 0;
}
......@@ -1094,15 +1097,16 @@ static int nvm_core_init(struct nvm_dev *dev)
return ret;
}
static void nvm_free(struct nvm_dev *dev)
static void nvm_free(struct kref *ref)
{
if (!dev)
return;
struct nvm_dev *dev = container_of(ref, struct nvm_dev, ref);
if (dev->dma_pool)
dev->ops->destroy_dma_pool(dev->dma_pool);
nvm_unregister_map(dev);
if (dev->rmap)
nvm_unregister_map(dev);
kfree(dev->lun_map);
kfree(dev);
}
......@@ -1139,7 +1143,13 @@ static int nvm_init(struct nvm_dev *dev)
struct nvm_dev *nvm_alloc_dev(int node)
{
return kzalloc_node(sizeof(struct nvm_dev), GFP_KERNEL, node);
struct nvm_dev *dev;
dev = kzalloc_node(sizeof(struct nvm_dev), GFP_KERNEL, node);
if (dev)
kref_init(&dev->ref);
return dev;
}
EXPORT_SYMBOL(nvm_alloc_dev);
......@@ -1147,12 +1157,16 @@ int nvm_register(struct nvm_dev *dev)
{
int ret, exp_pool_size;
if (!dev->q || !dev->ops)
if (!dev->q || !dev->ops) {
kref_put(&dev->ref, nvm_free);
return -EINVAL;
}
ret = nvm_init(dev);
if (ret)
if (ret) {
kref_put(&dev->ref, nvm_free);
return ret;
}
exp_pool_size = max_t(int, PAGE_SIZE,
(NVM_MAX_VLBA * (sizeof(u64) + dev->geo.sos)));
......@@ -1162,7 +1176,7 @@ int nvm_register(struct nvm_dev *dev)
exp_pool_size);
if (!dev->dma_pool) {
pr_err("nvm: could not create dma pool\n");
nvm_free(dev);
kref_put(&dev->ref, nvm_free);
return -ENOMEM;
}
......@@ -1184,6 +1198,7 @@ void nvm_unregister(struct nvm_dev *dev)
if (t->dev->parent != dev)
continue;
__nvm_remove_target(t, false);
kref_put(&dev->ref, nvm_free);
}
mutex_unlock(&dev->mlock);
......@@ -1191,13 +1206,14 @@ void nvm_unregister(struct nvm_dev *dev)
list_del(&dev->devices);
up_write(&nvm_lock);
nvm_free(dev);
kref_put(&dev->ref, nvm_free);
}
EXPORT_SYMBOL(nvm_unregister);
static int __nvm_configure_create(struct nvm_ioctl_create *create)
{
struct nvm_dev *dev;
int ret;
down_write(&nvm_lock);
dev = nvm_find_nvm_dev(create->dev);
......@@ -1208,7 +1224,12 @@ static int __nvm_configure_create(struct nvm_ioctl_create *create)
return -EINVAL;
}
return nvm_create_tgt(dev, create);
kref_get(&dev->ref);
ret = nvm_create_tgt(dev, create);
if (ret)
kref_put(&dev->ref, nvm_free);
return ret;
}
static long nvm_ioctl_info(struct file *file, void __user *arg)
......
......@@ -428,6 +428,7 @@ struct nvm_dev {
char name[DISK_NAME_LEN];
void *private_data;
struct kref ref;
void *rmap;
struct mutex mlock;
......
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