*
  */
 
-#include <linux/blkdev.h>
-#include <linux/blk-mq.h>
 #include <linux/list.h>
 #include <linux/types.h>
 #include <linux/sem.h>
 #include <linux/miscdevice.h>
 #include <linux/lightnvm.h>
 #include <linux/sched/sysctl.h>
-#include <uapi/linux/lightnvm.h>
 
 static LIST_HEAD(nvm_tgt_types);
 static LIST_HEAD(nvm_mgrs);
 static LIST_HEAD(nvm_devices);
-static LIST_HEAD(nvm_targets);
 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 ret = 0;
 
        down_write(&nvm_lock);
-       if (nvm_find_target_type(tt->name))
+       if (nvm_find_target_type(tt->name, 0))
                ret = -EEXIST;
        else
                list_add(&tt->list, &nvm_tgt_types);
        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)
 {
-       struct nvm_target *tgt, *tmp;
-
        if (!dev->mt)
                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 = NULL;
 }
 }
 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)
 {
        struct nvm_dev *dev;
        down_write(&nvm_lock);
        dev = nvm_find_nvm_dev(create->dev);
        up_write(&nvm_lock);
+
        if (!dev) {
                pr_err("nvm: device not found\n");
                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) {
                pr_err("nvm: config type not valid\n");
                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
 static int nvm_configure_remove(const char *val)
 {
        struct nvm_ioctl_remove remove;
+       struct nvm_dev *dev;
        char opcode;
-       int ret;
+       int ret = 0;
 
        ret = sscanf(val, "%c %256s", &opcode, remove.tgtname);
        if (ret != 2) {
 
        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 long nvm_ioctl_dev_remove(struct file *file, void __user *arg)
 {
        struct nvm_ioctl_remove remove;
+       struct nvm_dev *dev;
+       int ret = 0;
 
        if (!capable(CAP_SYS_ADMIN))
                return -EPERM;
                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)
 
 
 #include "gennvm.h"
 
+static struct nvm_target *gen_find_target(struct gen_dev *gn, const char *name)
+{
+       struct nvm_target *tgt;
+
+       list_for_each_entry(tgt, &gn->targets, list)
+               if (!strcmp(name, tgt->disk->disk_name))
+                       return tgt;
+
+       return NULL;
+}
+
+static const struct block_device_operations gen_fops = {
+       .owner          = THIS_MODULE,
+};
+
+static int gen_create_tgt(struct nvm_dev *dev, struct nvm_ioctl_create *create)
+{
+       struct gen_dev *gn = dev->mp;
+       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;
+
+       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(&gn->lock);
+       t = gen_find_target(gn, create->tgtname);
+       if (t) {
+               pr_err("nvm: target name already exists.\n");
+               mutex_unlock(&gn->lock);
+               return -EINVAL;
+       }
+       mutex_unlock(&gn->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 = &gen_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;
+
+       mutex_lock(&gn->lock);
+       list_add_tail(&t->list, &gn->targets);
+       mutex_unlock(&gn->lock);
+
+       return 0;
+err_init:
+       put_disk(tdisk);
+err_queue:
+       blk_cleanup_queue(tqueue);
+err_t:
+       kfree(t);
+       return -ENOMEM;
+}
+
+static void __gen_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->exit)
+               tt->exit(tdisk->private_data);
+
+       put_disk(tdisk);
+
+       list_del(&t->list);
+       kfree(t);
+}
+
+/**
+ * gen_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 gen_remove_tgt(struct nvm_dev *dev, struct nvm_ioctl_remove *remove)
+{
+       struct gen_dev *gn = dev->mp;
+       struct nvm_target *t;
+
+       if (!gn)
+               return 1;
+
+       mutex_lock(&gn->lock);
+       t = gen_find_target(gn, remove->tgtname);
+       if (!t) {
+               mutex_unlock(&gn->lock);
+               return 1;
+       }
+       __gen_remove_target(t);
+       mutex_unlock(&gn->lock);
+
+       return 0;
+}
+
 static int gen_get_area(struct nvm_dev *dev, sector_t *lba, sector_t len)
 {
        struct gen_dev *gn = dev->mp;
        gn->dev = dev;
        gn->nr_luns = dev->nr_luns;
        INIT_LIST_HEAD(&gn->area_list);
+       mutex_init(&gn->lock);
+       INIT_LIST_HEAD(&gn->targets);
        dev->mp = gn;
 
        ret = gen_luns_init(dev, gn);
 
 static void gen_unregister(struct nvm_dev *dev)
 {
+       struct gen_dev *gn = dev->mp;
+       struct nvm_target *t, *tmp;
+
+       mutex_lock(&gn->lock);
+       list_for_each_entry_safe(t, tmp, &gn->targets, list) {
+               if (t->dev != dev)
+                       continue;
+               __gen_remove_target(t);
+       }
+       mutex_unlock(&gn->lock);
+
        gen_free(dev);
        module_put(THIS_MODULE);
 }
        .register_mgr           = gen_register,
        .unregister_mgr         = gen_unregister,
 
+       .create_tgt             = gen_create_tgt,
+       .remove_tgt             = gen_remove_tgt,
+
        .get_blk_unlocked       = gen_get_blk_unlocked,
        .put_blk_unlocked       = gen_put_blk_unlocked,
 
 
 #ifndef NVM_H
 #define NVM_H
 
+#include <linux/blkdev.h>
 #include <linux/types.h>
+#include <uapi/linux/lightnvm.h>
 
 enum {
        NVM_IO_OK = 0,
        struct list_head list;
 };
 
+extern struct nvm_tgt_type *nvm_find_target_type(const char *, int);
+
 extern int nvm_register_tgt_type(struct nvm_tgt_type *);
 extern void nvm_unregister_tgt_type(struct nvm_tgt_type *);
 
 
 typedef int (nvmm_register_fn)(struct nvm_dev *);
 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 *);
        nvmm_register_fn *register_mgr;
        nvmm_unregister_fn *unregister_mgr;
 
+       nvmm_create_tgt_fn *create_tgt;
+       nvmm_remove_tgt_fn *remove_tgt;
+
        /* Block administration callbacks */
        nvmm_get_blk_fn *get_blk_unlocked;
        nvmm_put_blk_fn *put_blk_unlocked;