|
@@ -72,6 +72,10 @@ module_param(nvme_char_major, int, 0);
|
|
static int use_threaded_interrupts;
|
|
static int use_threaded_interrupts;
|
|
module_param(use_threaded_interrupts, int, 0);
|
|
module_param(use_threaded_interrupts, int, 0);
|
|
|
|
|
|
|
|
+static bool use_cmb_sqes = true;
|
|
|
|
+module_param(use_cmb_sqes, bool, 0644);
|
|
|
|
+MODULE_PARM_DESC(use_cmb_sqes, "use controller's memory buffer for I/O SQes");
|
|
|
|
+
|
|
static DEFINE_SPINLOCK(dev_list_lock);
|
|
static DEFINE_SPINLOCK(dev_list_lock);
|
|
static LIST_HEAD(dev_list);
|
|
static LIST_HEAD(dev_list);
|
|
static struct task_struct *nvme_thread;
|
|
static struct task_struct *nvme_thread;
|
|
@@ -103,6 +107,7 @@ struct nvme_queue {
|
|
char irqname[24]; /* nvme4294967295-65535\0 */
|
|
char irqname[24]; /* nvme4294967295-65535\0 */
|
|
spinlock_t q_lock;
|
|
spinlock_t q_lock;
|
|
struct nvme_command *sq_cmds;
|
|
struct nvme_command *sq_cmds;
|
|
|
|
+ struct nvme_command __iomem *sq_cmds_io;
|
|
volatile struct nvme_completion *cqes;
|
|
volatile struct nvme_completion *cqes;
|
|
struct blk_mq_tags **tags;
|
|
struct blk_mq_tags **tags;
|
|
dma_addr_t sq_dma_addr;
|
|
dma_addr_t sq_dma_addr;
|
|
@@ -379,27 +384,28 @@ static void *nvme_finish_cmd(struct nvme_queue *nvmeq, int tag,
|
|
*
|
|
*
|
|
* Safe to use from interrupt context
|
|
* Safe to use from interrupt context
|
|
*/
|
|
*/
|
|
-static int __nvme_submit_cmd(struct nvme_queue *nvmeq, struct nvme_command *cmd)
|
|
|
|
|
|
+static void __nvme_submit_cmd(struct nvme_queue *nvmeq,
|
|
|
|
+ struct nvme_command *cmd)
|
|
{
|
|
{
|
|
u16 tail = nvmeq->sq_tail;
|
|
u16 tail = nvmeq->sq_tail;
|
|
|
|
|
|
- memcpy(&nvmeq->sq_cmds[tail], cmd, sizeof(*cmd));
|
|
|
|
|
|
+ if (nvmeq->sq_cmds_io)
|
|
|
|
+ memcpy_toio(&nvmeq->sq_cmds_io[tail], cmd, sizeof(*cmd));
|
|
|
|
+ else
|
|
|
|
+ memcpy(&nvmeq->sq_cmds[tail], cmd, sizeof(*cmd));
|
|
|
|
+
|
|
if (++tail == nvmeq->q_depth)
|
|
if (++tail == nvmeq->q_depth)
|
|
tail = 0;
|
|
tail = 0;
|
|
writel(tail, nvmeq->q_db);
|
|
writel(tail, nvmeq->q_db);
|
|
nvmeq->sq_tail = tail;
|
|
nvmeq->sq_tail = tail;
|
|
-
|
|
|
|
- return 0;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
-static int nvme_submit_cmd(struct nvme_queue *nvmeq, struct nvme_command *cmd)
|
|
|
|
|
|
+static void nvme_submit_cmd(struct nvme_queue *nvmeq, struct nvme_command *cmd)
|
|
{
|
|
{
|
|
unsigned long flags;
|
|
unsigned long flags;
|
|
- int ret;
|
|
|
|
spin_lock_irqsave(&nvmeq->q_lock, flags);
|
|
spin_lock_irqsave(&nvmeq->q_lock, flags);
|
|
- ret = __nvme_submit_cmd(nvmeq, cmd);
|
|
|
|
|
|
+ __nvme_submit_cmd(nvmeq, cmd);
|
|
spin_unlock_irqrestore(&nvmeq->q_lock, flags);
|
|
spin_unlock_irqrestore(&nvmeq->q_lock, flags);
|
|
- return ret;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
static __le64 **iod_list(struct nvme_iod *iod)
|
|
static __le64 **iod_list(struct nvme_iod *iod)
|
|
@@ -730,18 +736,16 @@ static int nvme_setup_prps(struct nvme_dev *dev, struct nvme_iod *iod,
|
|
static void nvme_submit_priv(struct nvme_queue *nvmeq, struct request *req,
|
|
static void nvme_submit_priv(struct nvme_queue *nvmeq, struct request *req,
|
|
struct nvme_iod *iod)
|
|
struct nvme_iod *iod)
|
|
{
|
|
{
|
|
- struct nvme_command *cmnd = &nvmeq->sq_cmds[nvmeq->sq_tail];
|
|
|
|
|
|
+ struct nvme_command cmnd;
|
|
|
|
|
|
- memcpy(cmnd, req->cmd, sizeof(struct nvme_command));
|
|
|
|
- cmnd->rw.command_id = req->tag;
|
|
|
|
|
|
+ memcpy(&cmnd, req->cmd, sizeof(cmnd));
|
|
|
|
+ cmnd.rw.command_id = req->tag;
|
|
if (req->nr_phys_segments) {
|
|
if (req->nr_phys_segments) {
|
|
- cmnd->rw.prp1 = cpu_to_le64(sg_dma_address(iod->sg));
|
|
|
|
- cmnd->rw.prp2 = cpu_to_le64(iod->first_dma);
|
|
|
|
|
|
+ cmnd.rw.prp1 = cpu_to_le64(sg_dma_address(iod->sg));
|
|
|
|
+ cmnd.rw.prp2 = cpu_to_le64(iod->first_dma);
|
|
}
|
|
}
|
|
|
|
|
|
- if (++nvmeq->sq_tail == nvmeq->q_depth)
|
|
|
|
- nvmeq->sq_tail = 0;
|
|
|
|
- writel(nvmeq->sq_tail, nvmeq->q_db);
|
|
|
|
|
|
+ __nvme_submit_cmd(nvmeq, &cmnd);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -754,45 +758,41 @@ static void nvme_submit_discard(struct nvme_queue *nvmeq, struct nvme_ns *ns,
|
|
{
|
|
{
|
|
struct nvme_dsm_range *range =
|
|
struct nvme_dsm_range *range =
|
|
(struct nvme_dsm_range *)iod_list(iod)[0];
|
|
(struct nvme_dsm_range *)iod_list(iod)[0];
|
|
- struct nvme_command *cmnd = &nvmeq->sq_cmds[nvmeq->sq_tail];
|
|
|
|
|
|
+ struct nvme_command cmnd;
|
|
|
|
|
|
range->cattr = cpu_to_le32(0);
|
|
range->cattr = cpu_to_le32(0);
|
|
range->nlb = cpu_to_le32(blk_rq_bytes(req) >> ns->lba_shift);
|
|
range->nlb = cpu_to_le32(blk_rq_bytes(req) >> ns->lba_shift);
|
|
range->slba = cpu_to_le64(nvme_block_nr(ns, blk_rq_pos(req)));
|
|
range->slba = cpu_to_le64(nvme_block_nr(ns, blk_rq_pos(req)));
|
|
|
|
|
|
- memset(cmnd, 0, sizeof(*cmnd));
|
|
|
|
- cmnd->dsm.opcode = nvme_cmd_dsm;
|
|
|
|
- cmnd->dsm.command_id = req->tag;
|
|
|
|
- cmnd->dsm.nsid = cpu_to_le32(ns->ns_id);
|
|
|
|
- cmnd->dsm.prp1 = cpu_to_le64(iod->first_dma);
|
|
|
|
- cmnd->dsm.nr = 0;
|
|
|
|
- cmnd->dsm.attributes = cpu_to_le32(NVME_DSMGMT_AD);
|
|
|
|
|
|
+ memset(&cmnd, 0, sizeof(cmnd));
|
|
|
|
+ cmnd.dsm.opcode = nvme_cmd_dsm;
|
|
|
|
+ cmnd.dsm.command_id = req->tag;
|
|
|
|
+ cmnd.dsm.nsid = cpu_to_le32(ns->ns_id);
|
|
|
|
+ cmnd.dsm.prp1 = cpu_to_le64(iod->first_dma);
|
|
|
|
+ cmnd.dsm.nr = 0;
|
|
|
|
+ cmnd.dsm.attributes = cpu_to_le32(NVME_DSMGMT_AD);
|
|
|
|
|
|
- if (++nvmeq->sq_tail == nvmeq->q_depth)
|
|
|
|
- nvmeq->sq_tail = 0;
|
|
|
|
- writel(nvmeq->sq_tail, nvmeq->q_db);
|
|
|
|
|
|
+ __nvme_submit_cmd(nvmeq, &cmnd);
|
|
}
|
|
}
|
|
|
|
|
|
static void nvme_submit_flush(struct nvme_queue *nvmeq, struct nvme_ns *ns,
|
|
static void nvme_submit_flush(struct nvme_queue *nvmeq, struct nvme_ns *ns,
|
|
int cmdid)
|
|
int cmdid)
|
|
{
|
|
{
|
|
- struct nvme_command *cmnd = &nvmeq->sq_cmds[nvmeq->sq_tail];
|
|
|
|
|
|
+ struct nvme_command cmnd;
|
|
|
|
|
|
- memset(cmnd, 0, sizeof(*cmnd));
|
|
|
|
- cmnd->common.opcode = nvme_cmd_flush;
|
|
|
|
- cmnd->common.command_id = cmdid;
|
|
|
|
- cmnd->common.nsid = cpu_to_le32(ns->ns_id);
|
|
|
|
|
|
+ memset(&cmnd, 0, sizeof(cmnd));
|
|
|
|
+ cmnd.common.opcode = nvme_cmd_flush;
|
|
|
|
+ cmnd.common.command_id = cmdid;
|
|
|
|
+ cmnd.common.nsid = cpu_to_le32(ns->ns_id);
|
|
|
|
|
|
- if (++nvmeq->sq_tail == nvmeq->q_depth)
|
|
|
|
- nvmeq->sq_tail = 0;
|
|
|
|
- writel(nvmeq->sq_tail, nvmeq->q_db);
|
|
|
|
|
|
+ __nvme_submit_cmd(nvmeq, &cmnd);
|
|
}
|
|
}
|
|
|
|
|
|
static int nvme_submit_iod(struct nvme_queue *nvmeq, struct nvme_iod *iod,
|
|
static int nvme_submit_iod(struct nvme_queue *nvmeq, struct nvme_iod *iod,
|
|
struct nvme_ns *ns)
|
|
struct nvme_ns *ns)
|
|
{
|
|
{
|
|
struct request *req = iod_get_private(iod);
|
|
struct request *req = iod_get_private(iod);
|
|
- struct nvme_command *cmnd;
|
|
|
|
|
|
+ struct nvme_command cmnd;
|
|
u16 control = 0;
|
|
u16 control = 0;
|
|
u32 dsmgmt = 0;
|
|
u32 dsmgmt = 0;
|
|
|
|
|
|
@@ -804,19 +804,16 @@ static int nvme_submit_iod(struct nvme_queue *nvmeq, struct nvme_iod *iod,
|
|
if (req->cmd_flags & REQ_RAHEAD)
|
|
if (req->cmd_flags & REQ_RAHEAD)
|
|
dsmgmt |= NVME_RW_DSM_FREQ_PREFETCH;
|
|
dsmgmt |= NVME_RW_DSM_FREQ_PREFETCH;
|
|
|
|
|
|
- cmnd = &nvmeq->sq_cmds[nvmeq->sq_tail];
|
|
|
|
- memset(cmnd, 0, sizeof(*cmnd));
|
|
|
|
|
|
+ memset(&cmnd, 0, sizeof(cmnd));
|
|
|
|
+ cmnd.rw.opcode = (rq_data_dir(req) ? nvme_cmd_write : nvme_cmd_read);
|
|
|
|
+ cmnd.rw.command_id = req->tag;
|
|
|
|
+ cmnd.rw.nsid = cpu_to_le32(ns->ns_id);
|
|
|
|
+ cmnd.rw.prp1 = cpu_to_le64(sg_dma_address(iod->sg));
|
|
|
|
+ cmnd.rw.prp2 = cpu_to_le64(iod->first_dma);
|
|
|
|
+ cmnd.rw.slba = cpu_to_le64(nvme_block_nr(ns, blk_rq_pos(req)));
|
|
|
|
+ cmnd.rw.length = cpu_to_le16((blk_rq_bytes(req) >> ns->lba_shift) - 1);
|
|
|
|
|
|
- cmnd->rw.opcode = (rq_data_dir(req) ? nvme_cmd_write : nvme_cmd_read);
|
|
|
|
- cmnd->rw.command_id = req->tag;
|
|
|
|
- cmnd->rw.nsid = cpu_to_le32(ns->ns_id);
|
|
|
|
- cmnd->rw.prp1 = cpu_to_le64(sg_dma_address(iod->sg));
|
|
|
|
- cmnd->rw.prp2 = cpu_to_le64(iod->first_dma);
|
|
|
|
- cmnd->rw.slba = cpu_to_le64(nvme_block_nr(ns, blk_rq_pos(req)));
|
|
|
|
- cmnd->rw.length = cpu_to_le16((blk_rq_bytes(req) >> ns->lba_shift) - 1);
|
|
|
|
-
|
|
|
|
- if (blk_integrity_rq(req)) {
|
|
|
|
- cmnd->rw.metadata = cpu_to_le64(sg_dma_address(iod->meta_sg));
|
|
|
|
|
|
+ if (ns->ms) {
|
|
switch (ns->pi_type) {
|
|
switch (ns->pi_type) {
|
|
case NVME_NS_DPS_PI_TYPE3:
|
|
case NVME_NS_DPS_PI_TYPE3:
|
|
control |= NVME_RW_PRINFO_PRCHK_GUARD;
|
|
control |= NVME_RW_PRINFO_PRCHK_GUARD;
|
|
@@ -825,19 +822,21 @@ static int nvme_submit_iod(struct nvme_queue *nvmeq, struct nvme_iod *iod,
|
|
case NVME_NS_DPS_PI_TYPE2:
|
|
case NVME_NS_DPS_PI_TYPE2:
|
|
control |= NVME_RW_PRINFO_PRCHK_GUARD |
|
|
control |= NVME_RW_PRINFO_PRCHK_GUARD |
|
|
NVME_RW_PRINFO_PRCHK_REF;
|
|
NVME_RW_PRINFO_PRCHK_REF;
|
|
- cmnd->rw.reftag = cpu_to_le32(
|
|
|
|
|
|
+ cmnd.rw.reftag = cpu_to_le32(
|
|
nvme_block_nr(ns, blk_rq_pos(req)));
|
|
nvme_block_nr(ns, blk_rq_pos(req)));
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
- } else if (ns->ms)
|
|
|
|
- control |= NVME_RW_PRINFO_PRACT;
|
|
|
|
|
|
+ if (blk_integrity_rq(req))
|
|
|
|
+ cmnd.rw.metadata =
|
|
|
|
+ cpu_to_le64(sg_dma_address(iod->meta_sg));
|
|
|
|
+ else
|
|
|
|
+ control |= NVME_RW_PRINFO_PRACT;
|
|
|
|
+ }
|
|
|
|
|
|
- cmnd->rw.control = cpu_to_le16(control);
|
|
|
|
- cmnd->rw.dsmgmt = cpu_to_le32(dsmgmt);
|
|
|
|
|
|
+ cmnd.rw.control = cpu_to_le16(control);
|
|
|
|
+ cmnd.rw.dsmgmt = cpu_to_le32(dsmgmt);
|
|
|
|
|
|
- if (++nvmeq->sq_tail == nvmeq->q_depth)
|
|
|
|
- nvmeq->sq_tail = 0;
|
|
|
|
- writel(nvmeq->sq_tail, nvmeq->q_db);
|
|
|
|
|
|
+ __nvme_submit_cmd(nvmeq, &cmnd);
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -1080,7 +1079,8 @@ static int nvme_submit_async_admin_req(struct nvme_dev *dev)
|
|
c.common.command_id = req->tag;
|
|
c.common.command_id = req->tag;
|
|
|
|
|
|
blk_mq_free_request(req);
|
|
blk_mq_free_request(req);
|
|
- return __nvme_submit_cmd(nvmeq, &c);
|
|
|
|
|
|
+ __nvme_submit_cmd(nvmeq, &c);
|
|
|
|
+ return 0;
|
|
}
|
|
}
|
|
|
|
|
|
static int nvme_submit_admin_async_cmd(struct nvme_dev *dev,
|
|
static int nvme_submit_admin_async_cmd(struct nvme_dev *dev,
|
|
@@ -1103,7 +1103,8 @@ static int nvme_submit_admin_async_cmd(struct nvme_dev *dev,
|
|
|
|
|
|
cmd->common.command_id = req->tag;
|
|
cmd->common.command_id = req->tag;
|
|
|
|
|
|
- return nvme_submit_cmd(nvmeq, cmd);
|
|
|
|
|
|
+ nvme_submit_cmd(nvmeq, cmd);
|
|
|
|
+ return 0;
|
|
}
|
|
}
|
|
|
|
|
|
static int adapter_delete_queue(struct nvme_dev *dev, u8 opcode, u16 id)
|
|
static int adapter_delete_queue(struct nvme_dev *dev, u8 opcode, u16 id)
|
|
@@ -1315,12 +1316,7 @@ static void nvme_abort_req(struct request *req)
|
|
|
|
|
|
dev_warn(nvmeq->q_dmadev, "Aborting I/O %d QID %d\n", req->tag,
|
|
dev_warn(nvmeq->q_dmadev, "Aborting I/O %d QID %d\n", req->tag,
|
|
nvmeq->qid);
|
|
nvmeq->qid);
|
|
- if (nvme_submit_cmd(dev->queues[0], &cmd) < 0) {
|
|
|
|
- dev_warn(nvmeq->q_dmadev,
|
|
|
|
- "Could not abort I/O %d QID %d",
|
|
|
|
- req->tag, nvmeq->qid);
|
|
|
|
- blk_mq_free_request(abort_req);
|
|
|
|
- }
|
|
|
|
|
|
+ nvme_submit_cmd(dev->queues[0], &cmd);
|
|
}
|
|
}
|
|
|
|
|
|
static void nvme_cancel_queue_ios(struct request *req, void *data, bool reserved)
|
|
static void nvme_cancel_queue_ios(struct request *req, void *data, bool reserved)
|
|
@@ -1374,7 +1370,8 @@ static void nvme_free_queue(struct nvme_queue *nvmeq)
|
|
{
|
|
{
|
|
dma_free_coherent(nvmeq->q_dmadev, CQ_SIZE(nvmeq->q_depth),
|
|
dma_free_coherent(nvmeq->q_dmadev, CQ_SIZE(nvmeq->q_depth),
|
|
(void *)nvmeq->cqes, nvmeq->cq_dma_addr);
|
|
(void *)nvmeq->cqes, nvmeq->cq_dma_addr);
|
|
- dma_free_coherent(nvmeq->q_dmadev, SQ_SIZE(nvmeq->q_depth),
|
|
|
|
|
|
+ if (nvmeq->sq_cmds)
|
|
|
|
+ dma_free_coherent(nvmeq->q_dmadev, SQ_SIZE(nvmeq->q_depth),
|
|
nvmeq->sq_cmds, nvmeq->sq_dma_addr);
|
|
nvmeq->sq_cmds, nvmeq->sq_dma_addr);
|
|
kfree(nvmeq);
|
|
kfree(nvmeq);
|
|
}
|
|
}
|
|
@@ -1447,6 +1444,47 @@ static void nvme_disable_queue(struct nvme_dev *dev, int qid)
|
|
spin_unlock_irq(&nvmeq->q_lock);
|
|
spin_unlock_irq(&nvmeq->q_lock);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int nvme_cmb_qdepth(struct nvme_dev *dev, int nr_io_queues,
|
|
|
|
+ int entry_size)
|
|
|
|
+{
|
|
|
|
+ int q_depth = dev->q_depth;
|
|
|
|
+ unsigned q_size_aligned = roundup(q_depth * entry_size, dev->page_size);
|
|
|
|
+
|
|
|
|
+ if (q_size_aligned * nr_io_queues > dev->cmb_size) {
|
|
|
|
+ u64 mem_per_q = div_u64(dev->cmb_size, nr_io_queues);
|
|
|
|
+ mem_per_q = round_down(mem_per_q, dev->page_size);
|
|
|
|
+ q_depth = div_u64(mem_per_q, entry_size);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Ensure the reduced q_depth is above some threshold where it
|
|
|
|
+ * would be better to map queues in system memory with the
|
|
|
|
+ * original depth
|
|
|
|
+ */
|
|
|
|
+ if (q_depth < 64)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return q_depth;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int nvme_alloc_sq_cmds(struct nvme_dev *dev, struct nvme_queue *nvmeq,
|
|
|
|
+ int qid, int depth)
|
|
|
|
+{
|
|
|
|
+ if (qid && dev->cmb && use_cmb_sqes && NVME_CMB_SQS(dev->cmbsz)) {
|
|
|
|
+ unsigned offset = (qid - 1) *
|
|
|
|
+ roundup(SQ_SIZE(depth), dev->page_size);
|
|
|
|
+ nvmeq->sq_dma_addr = dev->cmb_dma_addr + offset;
|
|
|
|
+ nvmeq->sq_cmds_io = dev->cmb + offset;
|
|
|
|
+ } else {
|
|
|
|
+ nvmeq->sq_cmds = dma_alloc_coherent(dev->dev, SQ_SIZE(depth),
|
|
|
|
+ &nvmeq->sq_dma_addr, GFP_KERNEL);
|
|
|
|
+ if (!nvmeq->sq_cmds)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
static struct nvme_queue *nvme_alloc_queue(struct nvme_dev *dev, int qid,
|
|
static struct nvme_queue *nvme_alloc_queue(struct nvme_dev *dev, int qid,
|
|
int depth)
|
|
int depth)
|
|
{
|
|
{
|
|
@@ -1459,9 +1497,7 @@ static struct nvme_queue *nvme_alloc_queue(struct nvme_dev *dev, int qid,
|
|
if (!nvmeq->cqes)
|
|
if (!nvmeq->cqes)
|
|
goto free_nvmeq;
|
|
goto free_nvmeq;
|
|
|
|
|
|
- nvmeq->sq_cmds = dma_alloc_coherent(dev->dev, SQ_SIZE(depth),
|
|
|
|
- &nvmeq->sq_dma_addr, GFP_KERNEL);
|
|
|
|
- if (!nvmeq->sq_cmds)
|
|
|
|
|
|
+ if (nvme_alloc_sq_cmds(dev, nvmeq, qid, depth))
|
|
goto free_cqdma;
|
|
goto free_cqdma;
|
|
|
|
|
|
nvmeq->q_dmadev = dev->dev;
|
|
nvmeq->q_dmadev = dev->dev;
|
|
@@ -1696,6 +1732,12 @@ static int nvme_configure_admin_queue(struct nvme_dev *dev)
|
|
page_shift = dev_page_max;
|
|
page_shift = dev_page_max;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ dev->subsystem = readl(&dev->bar->vs) >= NVME_VS(1, 1) ?
|
|
|
|
+ NVME_CAP_NSSRC(cap) : 0;
|
|
|
|
+
|
|
|
|
+ if (dev->subsystem && (readl(&dev->bar->csts) & NVME_CSTS_NSSRO))
|
|
|
|
+ writel(NVME_CSTS_NSSRO, &dev->bar->csts);
|
|
|
|
+
|
|
result = nvme_disable_ctrl(dev, cap);
|
|
result = nvme_disable_ctrl(dev, cap);
|
|
if (result < 0)
|
|
if (result < 0)
|
|
return result;
|
|
return result;
|
|
@@ -1856,6 +1898,15 @@ static int nvme_user_cmd(struct nvme_dev *dev, struct nvme_ns *ns,
|
|
return status;
|
|
return status;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int nvme_subsys_reset(struct nvme_dev *dev)
|
|
|
|
+{
|
|
|
|
+ if (!dev->subsystem)
|
|
|
|
+ return -ENOTTY;
|
|
|
|
+
|
|
|
|
+ writel(0x4E564D65, &dev->bar->nssr); /* "NVMe" */
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
static int nvme_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd,
|
|
static int nvme_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd,
|
|
unsigned long arg)
|
|
unsigned long arg)
|
|
{
|
|
{
|
|
@@ -1989,7 +2040,7 @@ static int nvme_revalidate_disk(struct gendisk *disk)
|
|
!ns->ext)
|
|
!ns->ext)
|
|
nvme_init_integrity(ns);
|
|
nvme_init_integrity(ns);
|
|
|
|
|
|
- if (ns->ms && !blk_get_integrity(disk))
|
|
|
|
|
|
+ if (ns->ms && !(ns->ms == 8 && ns->pi_type) && !blk_get_integrity(disk))
|
|
set_capacity(disk, 0);
|
|
set_capacity(disk, 0);
|
|
else
|
|
else
|
|
set_capacity(disk, le64_to_cpup(&id->nsze) << (ns->lba_shift - 9));
|
|
set_capacity(disk, le64_to_cpup(&id->nsze) << (ns->lba_shift - 9));
|
|
@@ -2020,7 +2071,10 @@ static int nvme_kthread(void *data)
|
|
spin_lock(&dev_list_lock);
|
|
spin_lock(&dev_list_lock);
|
|
list_for_each_entry_safe(dev, next, &dev_list, node) {
|
|
list_for_each_entry_safe(dev, next, &dev_list, node) {
|
|
int i;
|
|
int i;
|
|
- if (readl(&dev->bar->csts) & NVME_CSTS_CFS) {
|
|
|
|
|
|
+ u32 csts = readl(&dev->bar->csts);
|
|
|
|
+
|
|
|
|
+ if ((dev->subsystem && (csts & NVME_CSTS_NSSRO)) ||
|
|
|
|
+ csts & NVME_CSTS_CFS) {
|
|
if (work_busy(&dev->reset_work))
|
|
if (work_busy(&dev->reset_work))
|
|
continue;
|
|
continue;
|
|
list_del_init(&dev->node);
|
|
list_del_init(&dev->node);
|
|
@@ -2080,8 +2134,11 @@ static void nvme_alloc_ns(struct nvme_dev *dev, unsigned nsid)
|
|
list_add_tail(&ns->list, &dev->namespaces);
|
|
list_add_tail(&ns->list, &dev->namespaces);
|
|
|
|
|
|
blk_queue_logical_block_size(ns->queue, 1 << ns->lba_shift);
|
|
blk_queue_logical_block_size(ns->queue, 1 << ns->lba_shift);
|
|
- if (dev->max_hw_sectors)
|
|
|
|
|
|
+ if (dev->max_hw_sectors) {
|
|
blk_queue_max_hw_sectors(ns->queue, dev->max_hw_sectors);
|
|
blk_queue_max_hw_sectors(ns->queue, dev->max_hw_sectors);
|
|
|
|
+ blk_queue_max_segments(ns->queue,
|
|
|
|
+ ((dev->max_hw_sectors << 9) / dev->page_size) + 1);
|
|
|
|
+ }
|
|
if (dev->stripe_size)
|
|
if (dev->stripe_size)
|
|
blk_queue_chunk_sectors(ns->queue, dev->stripe_size >> 9);
|
|
blk_queue_chunk_sectors(ns->queue, dev->stripe_size >> 9);
|
|
if (dev->vwc & NVME_CTRL_VWC_PRESENT)
|
|
if (dev->vwc & NVME_CTRL_VWC_PRESENT)
|
|
@@ -2159,6 +2216,58 @@ static int set_queue_count(struct nvme_dev *dev, int count)
|
|
return min(result & 0xffff, result >> 16) + 1;
|
|
return min(result & 0xffff, result >> 16) + 1;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void __iomem *nvme_map_cmb(struct nvme_dev *dev)
|
|
|
|
+{
|
|
|
|
+ u64 szu, size, offset;
|
|
|
|
+ u32 cmbloc;
|
|
|
|
+ resource_size_t bar_size;
|
|
|
|
+ struct pci_dev *pdev = to_pci_dev(dev->dev);
|
|
|
|
+ void __iomem *cmb;
|
|
|
|
+ dma_addr_t dma_addr;
|
|
|
|
+
|
|
|
|
+ if (!use_cmb_sqes)
|
|
|
|
+ return NULL;
|
|
|
|
+
|
|
|
|
+ dev->cmbsz = readl(&dev->bar->cmbsz);
|
|
|
|
+ if (!(NVME_CMB_SZ(dev->cmbsz)))
|
|
|
|
+ return NULL;
|
|
|
|
+
|
|
|
|
+ cmbloc = readl(&dev->bar->cmbloc);
|
|
|
|
+
|
|
|
|
+ szu = (u64)1 << (12 + 4 * NVME_CMB_SZU(dev->cmbsz));
|
|
|
|
+ size = szu * NVME_CMB_SZ(dev->cmbsz);
|
|
|
|
+ offset = szu * NVME_CMB_OFST(cmbloc);
|
|
|
|
+ bar_size = pci_resource_len(pdev, NVME_CMB_BIR(cmbloc));
|
|
|
|
+
|
|
|
|
+ if (offset > bar_size)
|
|
|
|
+ return NULL;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Controllers may support a CMB size larger than their BAR,
|
|
|
|
+ * for example, due to being behind a bridge. Reduce the CMB to
|
|
|
|
+ * the reported size of the BAR
|
|
|
|
+ */
|
|
|
|
+ if (size > bar_size - offset)
|
|
|
|
+ size = bar_size - offset;
|
|
|
|
+
|
|
|
|
+ dma_addr = pci_resource_start(pdev, NVME_CMB_BIR(cmbloc)) + offset;
|
|
|
|
+ cmb = ioremap_wc(dma_addr, size);
|
|
|
|
+ if (!cmb)
|
|
|
|
+ return NULL;
|
|
|
|
+
|
|
|
|
+ dev->cmb_dma_addr = dma_addr;
|
|
|
|
+ dev->cmb_size = size;
|
|
|
|
+ return cmb;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static inline void nvme_release_cmb(struct nvme_dev *dev)
|
|
|
|
+{
|
|
|
|
+ if (dev->cmb) {
|
|
|
|
+ iounmap(dev->cmb);
|
|
|
|
+ dev->cmb = NULL;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
static size_t db_bar_size(struct nvme_dev *dev, unsigned nr_io_queues)
|
|
static size_t db_bar_size(struct nvme_dev *dev, unsigned nr_io_queues)
|
|
{
|
|
{
|
|
return 4096 + ((nr_io_queues + 1) * 8 * dev->db_stride);
|
|
return 4096 + ((nr_io_queues + 1) * 8 * dev->db_stride);
|
|
@@ -2177,6 +2286,15 @@ static int nvme_setup_io_queues(struct nvme_dev *dev)
|
|
if (result < nr_io_queues)
|
|
if (result < nr_io_queues)
|
|
nr_io_queues = result;
|
|
nr_io_queues = result;
|
|
|
|
|
|
|
|
+ if (dev->cmb && NVME_CMB_SQS(dev->cmbsz)) {
|
|
|
|
+ result = nvme_cmb_qdepth(dev, nr_io_queues,
|
|
|
|
+ sizeof(struct nvme_command));
|
|
|
|
+ if (result > 0)
|
|
|
|
+ dev->q_depth = result;
|
|
|
|
+ else
|
|
|
|
+ nvme_release_cmb(dev);
|
|
|
|
+ }
|
|
|
|
+
|
|
size = db_bar_size(dev, nr_io_queues);
|
|
size = db_bar_size(dev, nr_io_queues);
|
|
if (size > 8192) {
|
|
if (size > 8192) {
|
|
iounmap(dev->bar);
|
|
iounmap(dev->bar);
|
|
@@ -2344,7 +2462,6 @@ static int nvme_dev_add(struct nvme_dev *dev)
|
|
{
|
|
{
|
|
struct pci_dev *pdev = to_pci_dev(dev->dev);
|
|
struct pci_dev *pdev = to_pci_dev(dev->dev);
|
|
int res;
|
|
int res;
|
|
- unsigned nn;
|
|
|
|
struct nvme_id_ctrl *ctrl;
|
|
struct nvme_id_ctrl *ctrl;
|
|
int shift = NVME_CAP_MPSMIN(readq(&dev->bar->cap)) + 12;
|
|
int shift = NVME_CAP_MPSMIN(readq(&dev->bar->cap)) + 12;
|
|
|
|
|
|
@@ -2354,7 +2471,6 @@ static int nvme_dev_add(struct nvme_dev *dev)
|
|
return -EIO;
|
|
return -EIO;
|
|
}
|
|
}
|
|
|
|
|
|
- nn = le32_to_cpup(&ctrl->nn);
|
|
|
|
dev->oncs = le16_to_cpup(&ctrl->oncs);
|
|
dev->oncs = le16_to_cpup(&ctrl->oncs);
|
|
dev->abort_limit = ctrl->acl + 1;
|
|
dev->abort_limit = ctrl->acl + 1;
|
|
dev->vwc = ctrl->vwc;
|
|
dev->vwc = ctrl->vwc;
|
|
@@ -2440,6 +2556,8 @@ static int nvme_dev_map(struct nvme_dev *dev)
|
|
dev->q_depth = min_t(int, NVME_CAP_MQES(cap) + 1, NVME_Q_DEPTH);
|
|
dev->q_depth = min_t(int, NVME_CAP_MQES(cap) + 1, NVME_Q_DEPTH);
|
|
dev->db_stride = 1 << NVME_CAP_STRIDE(cap);
|
|
dev->db_stride = 1 << NVME_CAP_STRIDE(cap);
|
|
dev->dbs = ((void __iomem *)dev->bar) + 4096;
|
|
dev->dbs = ((void __iomem *)dev->bar) + 4096;
|
|
|
|
+ if (readl(&dev->bar->vs) >= NVME_VS(1, 2))
|
|
|
|
+ dev->cmb = nvme_map_cmb(dev);
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
@@ -2820,6 +2938,8 @@ static long nvme_dev_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
|
|
case NVME_IOCTL_RESET:
|
|
case NVME_IOCTL_RESET:
|
|
dev_warn(dev->dev, "resetting controller\n");
|
|
dev_warn(dev->dev, "resetting controller\n");
|
|
return nvme_reset(dev);
|
|
return nvme_reset(dev);
|
|
|
|
+ case NVME_IOCTL_SUBSYS_RESET:
|
|
|
|
+ return nvme_subsys_reset(dev);
|
|
default:
|
|
default:
|
|
return -ENOTTY;
|
|
return -ENOTTY;
|
|
}
|
|
}
|
|
@@ -3145,6 +3265,7 @@ static void nvme_remove(struct pci_dev *pdev)
|
|
nvme_dev_remove_admin(dev);
|
|
nvme_dev_remove_admin(dev);
|
|
device_destroy(nvme_class, MKDEV(nvme_char_major, dev->instance));
|
|
device_destroy(nvme_class, MKDEV(nvme_char_major, dev->instance));
|
|
nvme_free_queues(dev, 0);
|
|
nvme_free_queues(dev, 0);
|
|
|
|
+ nvme_release_cmb(dev);
|
|
nvme_release_prp_pools(dev);
|
|
nvme_release_prp_pools(dev);
|
|
kref_put(&dev->kref, nvme_free_dev);
|
|
kref_put(&dev->kref, nvme_free_dev);
|
|
}
|
|
}
|