nvme ->queue_rq updates
Subsystems (2)
Christoph Hellwig
maintainerThis series has a few changes for the ->queue_rq path. The first is a simple fix, the second a cleanup and the third a major cleanup which also micro optimizes the time spent with irqs disabled.
Christoph Hellwig
maintainerPATCH 1/3 nvme: add missing unmaps in nvme_queue_rq
When we fail various metadata related operations in nvme_queue_rq we need to unmap the data SGL.
--- drivers/nvme/host/pci.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-)
drivers/nvme/host/pci.c +12 -3
@@ -904,19 +904,28 @@ static int nvme_queue_rq(struct blk_mq_hw_ctx *hctx, goto retry_cmd; }if (blk_integrity_rq (req)) { -if (blk_rq_count_integrity_sg (req->q, req->bio) !=1 ) +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 ) + 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)) +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; + } } }
Signature · 1 line
1.9.1
PATCH 1/3 nvme: add missing unmaps in nvme_queue_rq
Quoted 36 lines · Christoph Hellwig
When we fail various metadata related operations in
nvme_queue_rqwe need to unmap the data SGL.Cc stable at vger.kernel.orgSigned-off-by Christoph Hellwig <hch at lst.de>--- drivers/nvme/host/pci.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-)drivers/nvme/host/pci.c +8
-2@@ -904,19 +904,28 @@ static int nvme_queue_rq(struct blk_mq_hw_ctx *hctx, goto retry_cmd; }if (blk_integrity_rq (req)) { -if (blk_rq_count_integrity_sg (req->q, req->bio) !=1 ) +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 ) + 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);
drivers/nvme/host/pci.c static int nvme_queue_rq(struct blk_mq_hw_ctx *hctx, · line 931
Quoted 10 lines
drivers/nvme/host/pci.c +4
-1-if (!dma_map_sg (nvmeq->q_dmadev, iod->meta_sg,1 , dma_dir)) +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; + } } }
Hi Christoph,
Would it be better to unmap the data sg once at error_cmd tag?
Christoph Hellwig
maintainerPATCH 1/3 nvme: add missing unmaps in nvme_queue_rq
Quoted 1 line · Sagi Grimberg
Would it be better to unmap the data sg once at
error_cmdtag?
We'd need unmap versions of the error_cmd and retry_cmd goto labels, which was too much churn for this quick fix. Patch 3 now has a single exit path with a ret variable for the helper this has been factored out to, so this has been taken care off there.
Christoph Hellwig
maintainerPATCH 2/3 nvme: simplify nvme_setup_prps calling convention
In reply to Christoph HellwigPass back a true/false value instead of the length which needs a compare with the bytes in the request and drop the pointless gfp_t argument.
--- drivers/nvme/host/pci.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-)
drivers/nvme/host/pci.c +10 -12
@@ -655,9 +655,8 @@ static void req_completion(struct nvme_queue *nvmeq, void *ctx,blk_mq_complete_request (req, status); } -/* length is in bytes. gfp flags indicates whether we may sleep. */ -static int nvme_setup_prps (struct nvme_dev *dev,struct nvme_iod *iod, -int total_len,gfp_t gfp) +static bool nvme_setup_prps (struct nvme_dev *dev,struct nvme_iod *iod, +int total_len) {struct dma_pool *pool;int length = total_len;
@@ -673,7 +672,7 @@ static int nvme_setup_prps(struct nvme_dev *dev, struct nvme_iod *iod, length -= (page_size - offset);if (length <=0 ) -return total_len; +return true; dma_len -= (page_size - offset);if (dma_len) {
@@ -686,7 +685,7 @@ static int nvme_setup_prps(struct nvme_dev *dev, struct nvme_iod *iod,if (length <= page_size) { iod->first_dma = dma_addr; -return total_len; +return true; } nprps =DIV_ROUND_UP (length, page_size);
@@ -698,11 +697,11 @@ static int nvme_setup_prps(struct nvme_dev *dev, struct nvme_iod *iod, iod->npages =1 ; } - prp_list =dma_pool_alloc (pool, gfp, &prp_dma); + prp_list =dma_pool_alloc (pool,GFP_ATOMIC , &prp_dma);if (!prp_list) { iod->first_dma = dma_addr; iod->npages =-1 ; -return (total_len - length) + page_size; +return false; } list[0 ] = prp_list; iod->first_dma = prp_dma;
@@ -710,9 +709,9 @@ static int nvme_setup_prps(struct nvme_dev *dev, struct nvme_iod *iod,for (;;) {if (i == page_size >>3 ) {__le64 *old_prp_list = prp_list; - prp_list =dma_pool_alloc (pool, gfp, &prp_dma); + prp_list =dma_pool_alloc (pool,GFP_ATOMIC , &prp_dma);if (!prp_list) -return total_len - length; +return false; list[iod->npages++] = prp_list; prp_list[0 ] = old_prp_list[i -1 ]; old_prp_list[i -1 ] =cpu_to_le64 (prp_dma);
@@ -732,7 +731,7 @@ static int nvme_setup_prps(struct nvme_dev *dev, struct nvme_iod *iod, dma_len =sg_dma_len (sg); } -return total_len; +return true; } static void nvme_submit_priv(struct nvme_queue *nvmeq, struct request *req,
@@ -898,8 +897,7 @@ static int nvme_queue_rq(struct blk_mq_hw_ctx *hctx,if (!dma_map_sg (nvmeq->q_dmadev, iod->sg, iod->nents, dma_dir)) goto retry_cmd; -if (blk_rq_bytes (req) != -nvme_setup_prps (dev, iod,blk_rq_bytes (req),GFP_ATOMIC )) { +if (!nvme_setup_prps (dev, iod,blk_rq_bytes (req))) {dma_unmap_sg (dev->dev, iod->sg, iod->nents, dma_dir); goto retry_cmd; }
Signature · 1 line
1.9.1
Christoph Hellwig
maintainerPATCH 3/3 nvme: refactor nvme_queue_rq
In reply to Christoph HellwigThis "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.
--- drivers/nvme/host/pci.c | 219 +++++++++++++++++++++--------------------------- 1 file changed, 97 insertions(+), 122 deletions(-)
drivers/nvme/host/pci.c +97 -122
@@ -734,19 +734,53 @@ static bool nvme_setup_prps(struct nvme_dev *dev, struct nvme_iod *iod,return true; } -static void nvme_submit_priv (struct nvme_queue *nvmeq,struct request *req, -struct nvme_iod *iod) +static int nvme_map_data (struct nvme_dev *dev,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 ; + +sg_init_table (iod->sg, req->nr_phys_segments); + iod->nents =blk_rq_map_sg (q, req, iod->sg); +if (!iod->nents) + goto out; + + 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; -memcpy (&cmnd, req->cmd,sizeof (cmnd)); - cmnd.rw.command_id = req->tag; -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); +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; }/*
@@ -754,46 +788,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 * the iod. */ -static void nvme_submit_discard (struct nvme_queue *nvmeq,struct nvme_ns *ns, -struct request *req,struct nvme_iod *iod) +static int nvme_setup_discard (struct nvme_queue *nvmeq,struct nvme_ns *ns, +struct nvme_iod *iod,struct nvme_command *cmnd) { -struct nvme_dsm_range *range = - (struct nvme_dsm_range *)iod_list (iod)[0 ]; -struct nvme_command cmnd; +struct request *req =iod_get_private (iod); +struct nvme_dsm_range *range; + + 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->nlb =cpu_to_le32 (blk_rq_bytes (req) >> ns->lba_shift); 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 ); - -__nvme_submit_cmd (nvmeq, &cmnd); +memset (cmnd,0 ,sizeof (*cmnd)); + cmnd->dsm.opcode = nvme_cmd_dsm; + 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 ); +return BLK_MQ_RQ_QUEUE_OK ; } -static void nvme_submit_flush (struct nvme_queue *nvmeq,struct nvme_ns *ns, -int cmdid) +static void nvme_setup_flush (struct nvme_ns *ns,struct nvme_command *cmnd) { -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); - -__nvme_submit_cmd (nvmeq, &cmnd); +memset (cmnd,0 ,sizeof (*cmnd)); + cmnd->common.opcode = nvme_cmd_flush; + cmnd->common.nsid =cpu_to_le32 (ns->ns_id); } -static int nvme_submit_iod (struct nvme_queue *nvmeq,struct nvme_iod *iod, -struct nvme_ns *ns) +static void nvme_setup_rw (struct nvme_ns *ns,struct request *req, +struct nvme_command *cmnd) { -struct request *req =iod_get_private (iod); -struct nvme_command cmnd;u16 control =0 ;u32 dsmgmt =0 ;
@@ -805,14 +835,12 @@ static int nvme_submit_iod(struct nvme_queue *nvmeq, struct nvme_iod *iod,if (req->cmd_flags &REQ_RAHEAD ) dsmgmt |=NVME_RW_DSM_FREQ_PREFETCH ; -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 ); +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.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) {switch (ns->pi_type) {
@@ -823,23 +851,16 @@ static int nvme_submit_iod(struct nvme_queue *nvmeq, struct nvme_iod *iod,case NVME_NS_DPS_PI_TYPE2 : control |=NVME_RW_PRINFO_PRCHK_GUARD |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)));break ; } -if (blk_integrity_rq (req)) - cmnd.rw.metadata = -cpu_to_le64 (sg_dma_address (iod->meta_sg)); -else +if (!blk_integrity_rq (req)) control |=NVME_RW_PRINFO_PRACT ; } - cmnd.rw.control =cpu_to_le16 (control); - cmnd.rw.dsmgmt =cpu_to_le32 (dsmgmt); - -__nvme_submit_cmd (nvmeq, &cmnd); - -return 0 ; + cmnd->rw.control =cpu_to_le16 (control); + cmnd->rw.dsmgmt =cpu_to_le32 (dsmgmt); }/*
@@ -854,7 +875,8 @@ static int nvme_queue_rq(struct blk_mq_hw_ctx *hctx,struct request *req = bd->rq;struct nvme_cmd_info *cmd =blk_mq_rq_to_pdu (req);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
@@ -874,80 +896,33 @@ static int nvme_queue_rq(struct blk_mq_hw_ctx *hctx,return BLK_MQ_RQ_QUEUE_BUSY ;if (req->cmd_flags &REQ_DISCARD ) { -void *range; -/* -* We reuse the small pool to allocate the 16-byte range here -* as it is not worth having a special pool for these or -* additional cases to handle freeing the iod. -*/ - range =dma_pool_alloc (dev->prp_small_pool,GFP_ATOMIC , - &iod->first_dma); -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); - iod->nents =blk_rq_map_sg (req->q, req, iod->sg); -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); + ret =nvme_setup_discard (nvmeq, ns, iod, &cmnd); + }else { +if (req->cmd_type ==REQ_TYPE_DRV_PRIV ) +memcpy (&cmnd, req->cmd,sizeof (cmnd)); +else if (req->cmd_flags &REQ_FLUSH ) +nvme_setup_flush (ns, &cmnd); +else +nvme_setup_rw (ns, req, &cmnd); -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; - } - } +if (req->nr_phys_segments) + ret =nvme_map_data (dev, iod, &cmnd); } +if (ret) + goto out; + + cmnd.common.command_id = req->tag;nvme_set_info (cmd, iod, req_completion); -spin_lock_irq (&nvmeq->q_lock); -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); +spin_lock_irq (&nvmeq->q_lock); +__nvme_submit_cmd (nvmeq, &cmnd);nvme_process_cq (nvmeq);spin_unlock_irq (&nvmeq->q_lock);return BLK_MQ_RQ_QUEUE_OK ; - - error_cmd: -nvme_free_iod (dev, iod); -return BLK_MQ_RQ_QUEUE_ERROR ; - retry_cmd: +out:nvme_free_iod (dev, iod); -return BLK_MQ_RQ_QUEUE_BUSY ; +return ret; }static int nvme_process_cq (struct nvme_queue *nvmeq)
Signature · 1 line
1.9.1
PATCH 3/3 nvme: refactor nvme_queue_rq
Hi Christoph,
Review
Quoted 2 lines
(quoted diff) +2
-0+ if (!nvme_setup_prps(dev, iod, blk_rq_bytes(req))) + goto out_unmap;
Git am complains about whitespacing here.
The set looks fine to me. I'm tempted to do some performance testing with the microoptimizations present here. Have you seen any results yourself?
Christoph Hellwig
maintainerPATCH 3/3 nvme: refactor nvme_queue_rq
Quoted 5 lines · Jon Derrick
Hi Christoph,
Review
Quoted 2 lines
(quoted diff) +2
-0+ if (!nvme_setup_prps(dev, iod, blk_rq_bytes(req))) + goto out_unmap;Git am complains about whitespacing here.
Hmm, checkpatch was fine with it. I know there was odd whitespacing on the lines before I touched i but I though I fixed all that.
Quoted 1 line
The set looks fine to me. I'm tempted to do some performance testing with the microoptimizations present here. Have you seen any results yourself?
On the PCIe side I've only tested it with normal Flash devices where I didn't see a difference, so numbers would be welcome.
Christoph Hellwig
maintainerPATCH 3/3 nvme: refactor nvme_queue_rq
In reply to Jon DerrickQuoted 5 lines · Jon Derrick
Hi Christoph,
Review
Quoted 2 lines
(quoted diff) +2
-0+ if (!nvme_setup_prps(dev, iod, blk_rq_bytes(req))) + goto out_unmap;Git am complains about whitespacing here.
Ok, I fixed the whitespaces in patch 2 and reintroducem them in 3, I'll fix it in the next resend.