Merge branch 'for-4.4/integrity' of git://git.kernel.dk/linux-block
[firefly-linux-kernel-4.4.55.git] / drivers / nvme / host / pci.c
index 9bea542afc4f72f65d2d2d6e0f4d233b70db0446..381d2a0aa4615222a6b00c6f5eca6361a5fb0ed6 100644 (file)
@@ -591,6 +591,7 @@ static void req_completion(struct nvme_queue *nvmeq, void *ctx,
        struct request *req = iod_get_private(iod);
        struct nvme_cmd_info *cmd_rq = blk_mq_rq_to_pdu(req);
        u16 status = le16_to_cpup(&cqe->status) >> 1;
+       bool requeue = false;
        int error = 0;
 
        if (unlikely(status)) {
@@ -598,12 +599,13 @@ static void req_completion(struct nvme_queue *nvmeq, void *ctx,
                    && (jiffies - req->start_time) < req->timeout) {
                        unsigned long flags;
 
+                       requeue = true;
                        blk_mq_requeue_request(req);
                        spin_lock_irqsave(req->q->queue_lock, flags);
                        if (!blk_queue_stopped(req->q))
                                blk_mq_kick_requeue_list(req->q);
                        spin_unlock_irqrestore(req->q->queue_lock, flags);
-                       return;
+                       goto release_iod;
                }
 
                if (req->cmd_type == REQ_TYPE_DRV_PRIV) {
@@ -626,6 +628,7 @@ static void req_completion(struct nvme_queue *nvmeq, void *ctx,
                        "completing aborted command with status:%04x\n",
                        error);
 
+release_iod:
        if (iod->nents) {
                dma_unmap_sg(nvmeq->dev->dev, iod->sg, iod->nents,
                        rq_data_dir(req) ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
@@ -638,7 +641,8 @@ static void req_completion(struct nvme_queue *nvmeq, void *ctx,
        }
        nvme_free_iod(nvmeq->dev, iod);
 
-       blk_mq_complete_request(req, error);
+       if (likely(!requeue))
+               blk_mq_complete_request(req, error);
 }
 
 /* length is in bytes.  gfp flags indicates whether we may sleep. */
@@ -1932,6 +1936,9 @@ static void nvme_free_ns(struct kref *kref)
 {
        struct nvme_ns *ns = container_of(kref, struct nvme_ns, kref);
 
+       if (ns->type == NVME_NS_LIGHTNVM)
+               nvme_nvm_unregister(ns->queue, ns->disk->disk_name);
+
        spin_lock(&dev_list_lock);
        ns->disk->private_data = NULL;
        spin_unlock(&dev_list_lock);
@@ -2001,6 +2008,16 @@ static int nvme_revalidate_disk(struct gendisk *disk)
                return -ENODEV;
        }
 
+       if (nvme_nvm_ns_supported(ns, id) && ns->type != NVME_NS_LIGHTNVM) {
+               if (nvme_nvm_register(ns->queue, disk->disk_name)) {
+                       dev_warn(dev->dev,
+                               "%s: LightNVM init failure\n", __func__);
+                       kfree(id);
+                       return -ENODEV;
+               }
+               ns->type = NVME_NS_LIGHTNVM;
+       }
+
        old_ms = ns->ms;
        lbaf = id->flbas & NVME_NS_FLBAS_LBA_MASK;
        ns->lba_shift = id->lbaf[lbaf].ds;
@@ -2032,7 +2049,9 @@ static int nvme_revalidate_disk(struct gendisk *disk)
        if (ns->ms && !ns->ext)
                nvme_init_integrity(ns);
 
-       if (ns->ms && !(ns->ms == 8 && ns->pi_type) && !blk_get_integrity(disk))
+       if ((ns->ms && !(ns->ms == 8 && ns->pi_type) &&
+                                               !blk_get_integrity(disk)) ||
+                                               ns->type == NVME_NS_LIGHTNVM)
                set_capacity(disk, 0);
        else
                set_capacity(disk, le64_to_cpup(&id->nsze) << (ns->lba_shift - 9));
@@ -2156,17 +2175,19 @@ static void nvme_alloc_ns(struct nvme_dev *dev, unsigned nsid)
                goto out_free_disk;
 
        kref_get(&dev->kref);
-       add_disk(ns->disk);
-       if (ns->ms) {
-               struct block_device *bd = bdget_disk(ns->disk, 0);
-               if (!bd)
-                       return;
-               if (blkdev_get(bd, FMODE_READ, NULL)) {
-                       bdput(bd);
-                       return;
+       if (ns->type != NVME_NS_LIGHTNVM) {
+               add_disk(ns->disk);
+               if (ns->ms) {
+                       struct block_device *bd = bdget_disk(ns->disk, 0);
+                       if (!bd)
+                               return;
+                       if (blkdev_get(bd, FMODE_READ, NULL)) {
+                               bdput(bd);
+                               return;
+                       }
+                       blkdev_reread_part(bd);
+                       blkdev_put(bd, FMODE_READ);
                }
-               blkdev_reread_part(bd);
-               blkdev_put(bd, FMODE_READ);
        }
        return;
  out_free_disk: