Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs
[cascardo/linux.git] / drivers / lightnvm / gennvm.c
index 3d2762f..b74174c 100644 (file)
 
 #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;
@@ -295,6 +433,8 @@ static int gen_register(struct nvm_dev *dev)
        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);
@@ -318,19 +458,29 @@ err:
 
 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);
 }
 
-static struct nvm_block *gen_get_blk_unlocked(struct nvm_dev *dev,
+static struct nvm_block *gen_get_blk(struct nvm_dev *dev,
                                struct nvm_lun *vlun, unsigned long flags)
 {
        struct gen_lun *lun = container_of(vlun, struct gen_lun, vlun);
        struct nvm_block *blk = NULL;
        int is_gc = flags & NVM_IOTYPE_GC;
 
-       assert_spin_locked(&vlun->lock);
-
+       spin_lock(&vlun->lock);
        if (list_empty(&lun->free_list)) {
                pr_err_ratelimited("gen: lun %u have no free pages available",
                                                                lun->vlun.id);
@@ -345,29 +495,17 @@ static struct nvm_block *gen_get_blk_unlocked(struct nvm_dev *dev,
        list_move_tail(&blk->list, &lun->used_list);
        blk->state = NVM_BLK_ST_TGT;
        lun->vlun.nr_free_blocks--;
-
 out:
-       return blk;
-}
-
-static struct nvm_block *gen_get_blk(struct nvm_dev *dev,
-                               struct nvm_lun *vlun, unsigned long flags)
-{
-       struct nvm_block *blk;
-
-       spin_lock(&vlun->lock);
-       blk = gen_get_blk_unlocked(dev, vlun, flags);
        spin_unlock(&vlun->lock);
        return blk;
 }
 
-static void gen_put_blk_unlocked(struct nvm_dev *dev, struct nvm_block *blk)
+static void gen_put_blk(struct nvm_dev *dev, struct nvm_block *blk)
 {
        struct nvm_lun *vlun = blk->lun;
        struct gen_lun *lun = container_of(vlun, struct gen_lun, vlun);
 
-       assert_spin_locked(&vlun->lock);
-
+       spin_lock(&vlun->lock);
        if (blk->state & NVM_BLK_ST_TGT) {
                list_move_tail(&blk->list, &lun->free_list);
                lun->vlun.nr_free_blocks++;
@@ -381,14 +519,6 @@ static void gen_put_blk_unlocked(struct nvm_dev *dev, struct nvm_block *blk)
                                                        blk->id, blk->state);
                list_move_tail(&blk->list, &lun->bb_list);
        }
-}
-
-static void gen_put_blk(struct nvm_dev *dev, struct nvm_block *blk)
-{
-       struct nvm_lun *vlun = blk->lun;
-
-       spin_lock(&vlun->lock);
-       gen_put_blk_unlocked(dev, blk);
        spin_unlock(&vlun->lock);
 }
 
@@ -412,7 +542,7 @@ static void gen_mark_blk(struct nvm_dev *dev, struct ppa_addr ppa, int type)
                return;
        }
 
-       lun = &gn->luns[ppa.g.lun * ppa.g.ch];
+       lun = &gn->luns[(dev->luns_per_chnl * ppa.g.ch) + ppa.g.lun];
        blk = &lun->vlun.blocks[ppa.g.blk];
 
        /* will be moved to bb list on put_blk from target */
@@ -515,8 +645,8 @@ static struct nvmm_type gen = {
        .register_mgr           = gen_register,
        .unregister_mgr         = gen_unregister,
 
-       .get_blk_unlocked       = gen_get_blk_unlocked,
-       .put_blk_unlocked       = gen_put_blk_unlocked,
+       .create_tgt             = gen_create_tgt,
+       .remove_tgt             = gen_remove_tgt,
 
        .get_blk                = gen_get_blk,
        .put_blk                = gen_put_blk,