nvme-pci: implement the HMB entry number and size limitations
[linux-2.6-block.git] / drivers / nvme / host / pci.c
index 74a124a062640ae77abb8554881e1a93222abd2d..4a2121335f48a0b4af31b413e04af8f9a6a52a2e 100644 (file)
@@ -109,6 +109,7 @@ struct nvme_dev {
        /* host memory buffer support: */
        u64 host_mem_size;
        u32 nr_host_mem_descs;
+       dma_addr_t host_mem_descs_dma;
        struct nvme_host_mem_buf_desc *host_mem_descs;
        void **host_mem_desc_bufs;
 };
@@ -555,8 +556,10 @@ static blk_status_t nvme_setup_prps(struct nvme_dev *dev, struct request *req)
        int nprps, i;
 
        length -= (page_size - offset);
-       if (length <= 0)
+       if (length <= 0) {
+               iod->first_dma = 0;
                return BLK_STS_OK;
+       }
 
        dma_len -= (page_size - offset);
        if (dma_len) {
@@ -666,7 +669,7 @@ static blk_status_t nvme_map_data(struct nvme_dev *dev, struct request *req,
                if (blk_rq_map_integrity_sg(q, req->bio, &iod->meta_sg) != 1)
                        goto out_unmap;
 
-               if (rq_data_dir(req))
+               if (req_op(req) == REQ_OP_WRITE)
                        nvme_dif_remap(req, nvme_dif_prep);
 
                if (!dma_map_sg(dev->dev, &iod->meta_sg, 1, dma_dir))
@@ -694,7 +697,7 @@ static void nvme_unmap_data(struct nvme_dev *dev, struct request *req)
        if (iod->nents) {
                dma_unmap_sg(dev->dev, iod->sg, iod->nents, dma_dir);
                if (blk_integrity_rq(req)) {
-                       if (!rq_data_dir(req))
+                       if (req_op(req) == REQ_OP_READ)
                                nvme_dif_remap(req, nvme_dif_complete);
                        dma_unmap_sg(dev->dev, &iod->meta_sg, 1, dma_dir);
                }
@@ -801,6 +804,7 @@ static inline void nvme_handle_cqe(struct nvme_queue *nvmeq,
                return;
        }
 
+       nvmeq->cqe_seen = 1;
        req = blk_mq_tag_to_rq(*nvmeq->tags, cqe->command_id);
        nvme_end_request(req, cqe->status, cqe->result);
 }
@@ -830,10 +834,8 @@ static void nvme_process_cq(struct nvme_queue *nvmeq)
                consumed++;
        }
 
-       if (consumed) {
+       if (consumed)
                nvme_ring_cq_doorbell(nvmeq);
-               nvmeq->cqe_seen = 1;
-       }
 }
 
 static irqreturn_t nvme_irq(int irq, void *data)
@@ -1377,6 +1379,7 @@ static int nvme_alloc_admin_tags(struct nvme_dev *dev)
 
                if (blk_mq_alloc_tag_set(&dev->admin_tagset))
                        return -ENOMEM;
+               dev->ctrl.admin_tagset = &dev->admin_tagset;
 
                dev->ctrl.admin_q = blk_mq_init_queue(&dev->admin_tagset);
                if (IS_ERR(dev->ctrl.admin_q)) {
@@ -1566,16 +1569,10 @@ static inline void nvme_release_cmb(struct nvme_dev *dev)
 
 static int nvme_set_host_mem(struct nvme_dev *dev, u32 bits)
 {
-       size_t len = dev->nr_host_mem_descs * sizeof(*dev->host_mem_descs);
+       u64 dma_addr = dev->host_mem_descs_dma;
        struct nvme_command c;
-       u64 dma_addr;
        int ret;
 
-       dma_addr = dma_map_single(dev->dev, dev->host_mem_descs, len,
-                       DMA_TO_DEVICE);
-       if (dma_mapping_error(dev->dev, dma_addr))
-               return -ENOMEM;
-
        memset(&c, 0, sizeof(c));
        c.features.opcode       = nvme_admin_set_features;
        c.features.fid          = cpu_to_le32(NVME_FEAT_HOST_MEM_BUF);
@@ -1592,7 +1589,6 @@ static int nvme_set_host_mem(struct nvme_dev *dev, u32 bits)
                         "failed to set host mem (err %d, flags %#x).\n",
                         ret, bits);
        }
-       dma_unmap_single(dev->dev, dma_addr, len, DMA_TO_DEVICE);
        return ret;
 }
 
@@ -1610,25 +1606,31 @@ static void nvme_free_host_mem(struct nvme_dev *dev)
 
        kfree(dev->host_mem_desc_bufs);
        dev->host_mem_desc_bufs = NULL;
-       kfree(dev->host_mem_descs);
+       dma_free_coherent(dev->dev,
+                       dev->nr_host_mem_descs * sizeof(*dev->host_mem_descs),
+                       dev->host_mem_descs, dev->host_mem_descs_dma);
        dev->host_mem_descs = NULL;
 }
 
-static int nvme_alloc_host_mem(struct nvme_dev *dev, u64 min, u64 preferred)
+static int __nvme_alloc_host_mem(struct nvme_dev *dev, u64 preferred,
+               u32 chunk_size)
 {
        struct nvme_host_mem_buf_desc *descs;
-       u32 chunk_size, max_entries, len;
+       u32 max_entries, len;
+       dma_addr_t descs_dma;
        int i = 0;
        void **bufs;
        u64 size = 0, tmp;
 
-       /* start big and work our way down */
-       chunk_size = min(preferred, (u64)PAGE_SIZE << MAX_ORDER);
-retry:
        tmp = (preferred + chunk_size - 1);
        do_div(tmp, chunk_size);
        max_entries = tmp;
-       descs = kcalloc(max_entries, sizeof(*descs), GFP_KERNEL);
+
+       if (dev->ctrl.hmmaxd && dev->ctrl.hmmaxd < max_entries)
+               max_entries = dev->ctrl.hmmaxd;
+
+       descs = dma_zalloc_coherent(dev->dev, max_entries * sizeof(*descs),
+                       &descs_dma, GFP_KERNEL);
        if (!descs)
                goto out;
 
@@ -1650,18 +1652,13 @@ retry:
                i++;
        }
 
-       if (!size || (min && size < min)) {
-               dev_warn(dev->ctrl.device,
-                       "failed to allocate host memory buffer.\n");
+       if (!size)
                goto out_free_bufs;
-       }
 
-       dev_info(dev->ctrl.device,
-               "allocated %lld MiB host memory buffer.\n",
-               size >> ilog2(SZ_1M));
        dev->nr_host_mem_descs = i;
        dev->host_mem_size = size;
        dev->host_mem_descs = descs;
+       dev->host_mem_descs_dma = descs_dma;
        dev->host_mem_desc_bufs = bufs;
        return 0;
 
@@ -1675,23 +1672,38 @@ out_free_bufs:
 
        kfree(bufs);
 out_free_descs:
-       kfree(descs);
+       dma_free_coherent(dev->dev, max_entries * sizeof(*descs), descs,
+                       descs_dma);
 out:
-       /* try a smaller chunk size if we failed early */
-       if (chunk_size >= PAGE_SIZE * 2 && (i == 0 || size < min)) {
-               chunk_size /= 2;
-               goto retry;
-       }
        dev->host_mem_descs = NULL;
        return -ENOMEM;
 }
 
-static void nvme_setup_host_mem(struct nvme_dev *dev)
+static int nvme_alloc_host_mem(struct nvme_dev *dev, u64 min, u64 preferred)
+{
+       u32 chunk_size;
+
+       /* start big and work our way down */
+       for (chunk_size = min_t(u64, preferred, PAGE_SIZE * MAX_ORDER_NR_PAGES);
+            chunk_size >= max_t(u32, dev->ctrl.hmminds * 4096, PAGE_SIZE * 2);
+            chunk_size /= 2) {
+               if (!__nvme_alloc_host_mem(dev, preferred, chunk_size)) {
+                       if (!min || dev->host_mem_size >= min)
+                               return 0;
+                       nvme_free_host_mem(dev);
+               }
+       }
+
+       return -ENOMEM;
+}
+
+static int nvme_setup_host_mem(struct nvme_dev *dev)
 {
        u64 max = (u64)max_host_mem_size_mb * SZ_1M;
        u64 preferred = (u64)dev->ctrl.hmpre * 4096;
        u64 min = (u64)dev->ctrl.hmmin * 4096;
        u32 enable_bits = NVME_HOST_MEM_ENABLE;
+       int ret = 0;
 
        preferred = min(preferred, max);
        if (min > max) {
@@ -1699,7 +1711,7 @@ static void nvme_setup_host_mem(struct nvme_dev *dev)
                        "min host memory (%lld MiB) above limit (%d MiB).\n",
                        min >> ilog2(SZ_1M), max_host_mem_size_mb);
                nvme_free_host_mem(dev);
-               return;
+               return 0;
        }
 
        /*
@@ -1713,12 +1725,21 @@ static void nvme_setup_host_mem(struct nvme_dev *dev)
        }
 
        if (!dev->host_mem_descs) {
-               if (nvme_alloc_host_mem(dev, min, preferred))
-                       return;
+               if (nvme_alloc_host_mem(dev, min, preferred)) {
+                       dev_warn(dev->ctrl.device,
+                               "failed to allocate host memory buffer.\n");
+                       return 0; /* controller must work without HMB */
+               }
+
+               dev_info(dev->ctrl.device,
+                       "allocated %lld MiB host memory buffer.\n",
+                       dev->host_mem_size >> ilog2(SZ_1M));
        }
 
-       if (nvme_set_host_mem(dev, enable_bits))
+       ret = nvme_set_host_mem(dev, enable_bits);
+       if (ret)
                nvme_free_host_mem(dev);
+       return ret;
 }
 
 static int nvme_setup_io_queues(struct nvme_dev *dev)
@@ -2162,8 +2183,11 @@ static void nvme_reset_work(struct work_struct *work)
                                 "unable to allocate dma for dbbuf\n");
        }
 
-       if (dev->ctrl.hmpre)
-               nvme_setup_host_mem(dev);
+       if (dev->ctrl.hmpre) {
+               result = nvme_setup_host_mem(dev);
+               if (result < 0)
+                       goto out;
+       }
 
        result = nvme_setup_io_queues(dev);
        if (result)
@@ -2495,6 +2519,10 @@ static const struct pci_device_id nvme_id_table[] = {
                .driver_data = NVME_QUIRK_DELAY_BEFORE_CHK_RDY, },
        { PCI_DEVICE(0x144d, 0xa822),   /* Samsung PM1725a */
                .driver_data = NVME_QUIRK_DELAY_BEFORE_CHK_RDY, },
+       { PCI_DEVICE(0x1d1d, 0x1f1f),   /* LighNVM qemu device */
+               .driver_data = NVME_QUIRK_LIGHTNVM, },
+       { PCI_DEVICE(0x1d1d, 0x2807),   /* CNEX WL */
+               .driver_data = NVME_QUIRK_LIGHTNVM, },
        { PCI_DEVICE_CLASS(PCI_CLASS_STORAGE_EXPRESS, 0xffffff) },
        { PCI_DEVICE(PCI_VENDOR_ID_APPLE, 0x2001) },
        { PCI_DEVICE(PCI_VENDOR_ID_APPLE, 0x2003) },