nvme: refactor nvme_queue_rq
This "backports" the structure I've used for the fabrics driver. It mostly started out as a cleanup so that I could actually understand the code, but I think it also qualifies as a micro-optimization due to the reduced time we hold q_lock and disable interrupts. Signed-off-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Keith Busch <keith.busch@intel.com> Signed-off-by: Jens Axboe <axboe@fb.com>
This commit is contained in:
Родитель
69d2b57174
Коммит
ba1ca37ea4
|
@ -788,19 +788,53 @@ static bool nvme_setup_prps(struct nvme_dev *dev, struct nvme_iod *iod,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nvme_submit_priv(struct nvme_queue *nvmeq, struct request *req,
|
static int nvme_map_data(struct nvme_dev *dev, struct nvme_iod *iod,
|
||||||
struct nvme_iod *iod)
|
struct nvme_command *cmnd)
|
||||||
{
|
{
|
||||||
struct nvme_command cmnd;
|
struct request *req = iod_get_private(iod);
|
||||||
|
struct request_queue *q = req->q;
|
||||||
|
enum dma_data_direction dma_dir = rq_data_dir(req) ?
|
||||||
|
DMA_TO_DEVICE : DMA_FROM_DEVICE;
|
||||||
|
int ret = BLK_MQ_RQ_QUEUE_ERROR;
|
||||||
|
|
||||||
memcpy(&cmnd, req->cmd, sizeof(cmnd));
|
sg_init_table(iod->sg, req->nr_phys_segments);
|
||||||
cmnd.rw.command_id = req->tag;
|
iod->nents = blk_rq_map_sg(q, req, iod->sg);
|
||||||
if (req->nr_phys_segments) {
|
if (!iod->nents)
|
||||||
cmnd.rw.prp1 = cpu_to_le64(sg_dma_address(iod->sg));
|
goto out;
|
||||||
cmnd.rw.prp2 = cpu_to_le64(iod->first_dma);
|
|
||||||
|
ret = BLK_MQ_RQ_QUEUE_BUSY;
|
||||||
|
if (!dma_map_sg(dev->dev, iod->sg, iod->nents, dma_dir))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (!nvme_setup_prps(dev, iod, blk_rq_bytes(req)))
|
||||||
|
goto out_unmap;
|
||||||
|
|
||||||
|
ret = BLK_MQ_RQ_QUEUE_ERROR;
|
||||||
|
if (blk_integrity_rq(req)) {
|
||||||
|
if (blk_rq_count_integrity_sg(q, req->bio) != 1)
|
||||||
|
goto out_unmap;
|
||||||
|
|
||||||
|
sg_init_table(iod->meta_sg, 1);
|
||||||
|
if (blk_rq_map_integrity_sg(q, req->bio, iod->meta_sg) != 1)
|
||||||
|
goto out_unmap;
|
||||||
|
|
||||||
|
if (rq_data_dir(req))
|
||||||
|
nvme_dif_remap(req, nvme_dif_prep);
|
||||||
|
|
||||||
|
if (!dma_map_sg(dev->dev, iod->meta_sg, 1, dma_dir))
|
||||||
|
goto out_unmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
__nvme_submit_cmd(nvmeq, &cmnd);
|
cmnd->rw.prp1 = cpu_to_le64(sg_dma_address(iod->sg));
|
||||||
|
cmnd->rw.prp2 = cpu_to_le64(iod->first_dma);
|
||||||
|
if (blk_integrity_rq(req))
|
||||||
|
cmnd->rw.metadata = cpu_to_le64(sg_dma_address(iod->meta_sg));
|
||||||
|
return BLK_MQ_RQ_QUEUE_OK;
|
||||||
|
|
||||||
|
out_unmap:
|
||||||
|
dma_unmap_sg(dev->dev, iod->sg, iod->nents, dma_dir);
|
||||||
|
out:
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -808,46 +842,42 @@ static void nvme_submit_priv(struct nvme_queue *nvmeq, struct request *req,
|
||||||
* worth having a special pool for these or additional cases to handle freeing
|
* worth having a special pool for these or additional cases to handle freeing
|
||||||
* the iod.
|
* the iod.
|
||||||
*/
|
*/
|
||||||
static void nvme_submit_discard(struct nvme_queue *nvmeq, struct nvme_ns *ns,
|
static int nvme_setup_discard(struct nvme_queue *nvmeq, struct nvme_ns *ns,
|
||||||
struct request *req, struct nvme_iod *iod)
|
struct nvme_iod *iod, struct nvme_command *cmnd)
|
||||||
{
|
{
|
||||||
struct nvme_dsm_range *range =
|
struct request *req = iod_get_private(iod);
|
||||||
(struct nvme_dsm_range *)iod_list(iod)[0];
|
struct nvme_dsm_range *range;
|
||||||
struct nvme_command cmnd;
|
|
||||||
|
range = dma_pool_alloc(nvmeq->dev->prp_small_pool, GFP_ATOMIC,
|
||||||
|
&iod->first_dma);
|
||||||
|
if (!range)
|
||||||
|
return BLK_MQ_RQ_QUEUE_BUSY;
|
||||||
|
iod_list(iod)[0] = (__le64 *)range;
|
||||||
|
iod->npages = 0;
|
||||||
|
|
||||||
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));
|
memset(cmnd, 0, sizeof(*cmnd));
|
||||||
cmnd.dsm.opcode = nvme_cmd_dsm;
|
cmnd->dsm.opcode = nvme_cmd_dsm;
|
||||||
cmnd.dsm.command_id = req->tag;
|
cmnd->dsm.nsid = cpu_to_le32(ns->ns_id);
|
||||||
cmnd.dsm.nsid = cpu_to_le32(ns->ns_id);
|
cmnd->dsm.prp1 = cpu_to_le64(iod->first_dma);
|
||||||
cmnd.dsm.prp1 = cpu_to_le64(iod->first_dma);
|
cmnd->dsm.nr = 0;
|
||||||
cmnd.dsm.nr = 0;
|
cmnd->dsm.attributes = cpu_to_le32(NVME_DSMGMT_AD);
|
||||||
cmnd.dsm.attributes = cpu_to_le32(NVME_DSMGMT_AD);
|
return BLK_MQ_RQ_QUEUE_OK;
|
||||||
|
|
||||||
__nvme_submit_cmd(nvmeq, &cmnd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nvme_submit_flush(struct nvme_queue *nvmeq, struct nvme_ns *ns,
|
static void nvme_setup_flush(struct nvme_ns *ns, struct nvme_command *cmnd)
|
||||||
int cmdid)
|
|
||||||
{
|
{
|
||||||
struct nvme_command cmnd;
|
memset(cmnd, 0, sizeof(*cmnd));
|
||||||
|
cmnd->common.opcode = nvme_cmd_flush;
|
||||||
memset(&cmnd, 0, sizeof(cmnd));
|
cmnd->common.nsid = cpu_to_le32(ns->ns_id);
|
||||||
cmnd.common.opcode = nvme_cmd_flush;
|
|
||||||
cmnd.common.command_id = cmdid;
|
|
||||||
cmnd.common.nsid = cpu_to_le32(ns->ns_id);
|
|
||||||
|
|
||||||
__nvme_submit_cmd(nvmeq, &cmnd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nvme_submit_iod(struct nvme_queue *nvmeq, struct nvme_iod *iod,
|
static void nvme_setup_rw(struct nvme_ns *ns, struct request *req,
|
||||||
struct nvme_ns *ns)
|
struct nvme_command *cmnd)
|
||||||
{
|
{
|
||||||
struct request *req = iod_get_private(iod);
|
|
||||||
struct nvme_command cmnd;
|
|
||||||
u16 control = 0;
|
u16 control = 0;
|
||||||
u32 dsmgmt = 0;
|
u32 dsmgmt = 0;
|
||||||
|
|
||||||
|
@ -859,14 +889,12 @@ 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;
|
||||||
|
|
||||||
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.opcode = (rq_data_dir(req) ? nvme_cmd_write : nvme_cmd_read);
|
||||||
cmnd.rw.command_id = req->tag;
|
cmnd->rw.command_id = req->tag;
|
||||||
cmnd.rw.nsid = cpu_to_le32(ns->ns_id);
|
cmnd->rw.nsid = cpu_to_le32(ns->ns_id);
|
||||||
cmnd.rw.prp1 = cpu_to_le64(sg_dma_address(iod->sg));
|
cmnd->rw.slba = cpu_to_le64(nvme_block_nr(ns, blk_rq_pos(req)));
|
||||||
cmnd.rw.prp2 = cpu_to_le64(iod->first_dma);
|
cmnd->rw.length = cpu_to_le16((blk_rq_bytes(req) >> ns->lba_shift) - 1);
|
||||||
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 (ns->ms) {
|
if (ns->ms) {
|
||||||
switch (ns->pi_type) {
|
switch (ns->pi_type) {
|
||||||
|
@ -877,23 +905,16 @@ 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;
|
||||||
}
|
}
|
||||||
if (blk_integrity_rq(req))
|
if (!blk_integrity_rq(req))
|
||||||
cmnd.rw.metadata =
|
|
||||||
cpu_to_le64(sg_dma_address(iod->meta_sg));
|
|
||||||
else
|
|
||||||
control |= NVME_RW_PRINFO_PRACT;
|
control |= NVME_RW_PRINFO_PRACT;
|
||||||
}
|
}
|
||||||
|
|
||||||
cmnd.rw.control = cpu_to_le16(control);
|
cmnd->rw.control = cpu_to_le16(control);
|
||||||
cmnd.rw.dsmgmt = cpu_to_le32(dsmgmt);
|
cmnd->rw.dsmgmt = cpu_to_le32(dsmgmt);
|
||||||
|
|
||||||
__nvme_submit_cmd(nvmeq, &cmnd);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -908,7 +929,8 @@ static int nvme_queue_rq(struct blk_mq_hw_ctx *hctx,
|
||||||
struct request *req = bd->rq;
|
struct request *req = bd->rq;
|
||||||
struct nvme_cmd_info *cmd = blk_mq_rq_to_pdu(req);
|
struct nvme_cmd_info *cmd = blk_mq_rq_to_pdu(req);
|
||||||
struct nvme_iod *iod;
|
struct nvme_iod *iod;
|
||||||
enum dma_data_direction dma_dir;
|
struct nvme_command cmnd;
|
||||||
|
int ret = BLK_MQ_RQ_QUEUE_OK;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If formated with metadata, require the block layer provide a buffer
|
* If formated with metadata, require the block layer provide a buffer
|
||||||
|
@ -928,80 +950,33 @@ static int nvme_queue_rq(struct blk_mq_hw_ctx *hctx,
|
||||||
return BLK_MQ_RQ_QUEUE_BUSY;
|
return BLK_MQ_RQ_QUEUE_BUSY;
|
||||||
|
|
||||||
if (req->cmd_flags & REQ_DISCARD) {
|
if (req->cmd_flags & REQ_DISCARD) {
|
||||||
void *range;
|
ret = nvme_setup_discard(nvmeq, ns, iod, &cmnd);
|
||||||
/*
|
} else {
|
||||||
* We reuse the small pool to allocate the 16-byte range here
|
if (req->cmd_type == REQ_TYPE_DRV_PRIV)
|
||||||
* as it is not worth having a special pool for these or
|
memcpy(&cmnd, req->cmd, sizeof(cmnd));
|
||||||
* additional cases to handle freeing the iod.
|
else if (req->cmd_flags & REQ_FLUSH)
|
||||||
*/
|
nvme_setup_flush(ns, &cmnd);
|
||||||
range = dma_pool_alloc(dev->prp_small_pool, GFP_ATOMIC,
|
else
|
||||||
&iod->first_dma);
|
nvme_setup_rw(ns, req, &cmnd);
|
||||||
if (!range)
|
|
||||||
goto retry_cmd;
|
|
||||||
iod_list(iod)[0] = (__le64 *)range;
|
|
||||||
iod->npages = 0;
|
|
||||||
} else if (req->nr_phys_segments) {
|
|
||||||
dma_dir = rq_data_dir(req) ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
|
|
||||||
|
|
||||||
sg_init_table(iod->sg, req->nr_phys_segments);
|
if (req->nr_phys_segments)
|
||||||
iod->nents = blk_rq_map_sg(req->q, req, iod->sg);
|
ret = nvme_map_data(dev, iod, &cmnd);
|
||||||
if (!iod->nents)
|
|
||||||
goto error_cmd;
|
|
||||||
|
|
||||||
if (!dma_map_sg(nvmeq->q_dmadev, iod->sg, iod->nents, dma_dir))
|
|
||||||
goto retry_cmd;
|
|
||||||
|
|
||||||
if (!nvme_setup_prps(dev, iod, blk_rq_bytes(req))) {
|
|
||||||
dma_unmap_sg(dev->dev, iod->sg, iod->nents, dma_dir);
|
|
||||||
goto retry_cmd;
|
|
||||||
}
|
|
||||||
if (blk_integrity_rq(req)) {
|
|
||||||
if (blk_rq_count_integrity_sg(req->q, req->bio) != 1) {
|
|
||||||
dma_unmap_sg(dev->dev, iod->sg, iod->nents,
|
|
||||||
dma_dir);
|
|
||||||
goto error_cmd;
|
|
||||||
}
|
|
||||||
|
|
||||||
sg_init_table(iod->meta_sg, 1);
|
|
||||||
if (blk_rq_map_integrity_sg(
|
|
||||||
req->q, req->bio, iod->meta_sg) != 1) {
|
|
||||||
dma_unmap_sg(dev->dev, iod->sg, iod->nents,
|
|
||||||
dma_dir);
|
|
||||||
goto error_cmd;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rq_data_dir(req))
|
|
||||||
nvme_dif_remap(req, nvme_dif_prep);
|
|
||||||
|
|
||||||
if (!dma_map_sg(nvmeq->q_dmadev, iod->meta_sg, 1, dma_dir)) {
|
|
||||||
dma_unmap_sg(dev->dev, iod->sg, iod->nents,
|
|
||||||
dma_dir);
|
|
||||||
goto error_cmd;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nvme_set_info(cmd, iod, req_completion);
|
if (ret)
|
||||||
spin_lock_irq(&nvmeq->q_lock);
|
goto out;
|
||||||
if (req->cmd_type == REQ_TYPE_DRV_PRIV)
|
|
||||||
nvme_submit_priv(nvmeq, req, iod);
|
|
||||||
else if (req->cmd_flags & REQ_DISCARD)
|
|
||||||
nvme_submit_discard(nvmeq, ns, req, iod);
|
|
||||||
else if (req->cmd_flags & REQ_FLUSH)
|
|
||||||
nvme_submit_flush(nvmeq, ns, req->tag);
|
|
||||||
else
|
|
||||||
nvme_submit_iod(nvmeq, iod, ns);
|
|
||||||
|
|
||||||
|
cmnd.common.command_id = req->tag;
|
||||||
|
nvme_set_info(cmd, iod, req_completion);
|
||||||
|
|
||||||
|
spin_lock_irq(&nvmeq->q_lock);
|
||||||
|
__nvme_submit_cmd(nvmeq, &cmnd);
|
||||||
nvme_process_cq(nvmeq);
|
nvme_process_cq(nvmeq);
|
||||||
spin_unlock_irq(&nvmeq->q_lock);
|
spin_unlock_irq(&nvmeq->q_lock);
|
||||||
return BLK_MQ_RQ_QUEUE_OK;
|
return BLK_MQ_RQ_QUEUE_OK;
|
||||||
|
out:
|
||||||
error_cmd:
|
|
||||||
nvme_free_iod(dev, iod);
|
nvme_free_iod(dev, iod);
|
||||||
return BLK_MQ_RQ_QUEUE_ERROR;
|
return ret;
|
||||||
retry_cmd:
|
|
||||||
nvme_free_iod(dev, iod);
|
|
||||||
return BLK_MQ_RQ_QUEUE_BUSY;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __nvme_process_cq(struct nvme_queue *nvmeq, unsigned int *tag)
|
static void __nvme_process_cq(struct nvme_queue *nvmeq, unsigned int *tag)
|
||||||
|
|
Загрузка…
Ссылка в новой задаче