Various block optimizations
Subsystems (6)
Jens Axboe
maintainerv4 cover Various block optimizations
Some of these are optimizations, the latter part is prep work for supporting polling with aio.
Patches against my for-4.21/block branch. These patches can also be found in my mq-perf branch, though there are other patches sitting on top of this series (notably aio polling, as mentioned).
Changes since v3:
- Remove ->poll_queue() from nvme-fc
- Fold __nvme_rdma_recv_done() and nvme_rdma_recv_done()
- Drop merged patches
- Merge __blk_mq_poll() and blk_mq_poll()
- Add patch to never redirect polled completions
Changes since v2:
- Include polled swap IO in the poll optimizations
- Get rid of unnecessary write barrier for DIO wakeup
- Fix a potential stall if need_resched() was set and preempt wasn't enabled
- Provide separate mq_ops for NVMe with poll queues
- Drop q->mq_ops patch
- Rebase on top of for-4.21/block
Changes since v1:
- Improve nvme irq disabling for polled IO
- Fix barriers in the ordered wakeup for polled O_DIRECT
- Add patch to allow polling to find any command that is done
- Add patch to control whether polling spins or not
- Have async O_DIRECT mark a bio as pollable
- Don't plug for polling
Signature · 1 line
Jens Axboe
Jens Axboe
maintainerPATCH v4 1/6 blk-mq: when polling for IO, look for any completion
If we want to support async IO polling, then we have to allow finding completions that aren't just for the one we are looking for. Always pass in -1 to the mq_ops->poll() helper, and have that return how many events were found in this poll loop.
--- block/blk-core.c | 2 +- block/blk-mq.c | 71 ++++++++++++++++++++-------------------- drivers/nvme/host/pci.c | 14 ++++---- drivers/nvme/host/rdma.c | 39 +++++++++------------- include/linux/blkdev.h | 2 +- 5 files changed, 60 insertions(+), 68 deletions(-)
block/blk-core.c +1 -1
@@ -1273,7 +1273,7 @@ blk_qc_t submit_bio(struct bio *bio) }EXPORT_SYMBOL (submit_bio); -bool blk_poll (struct request_queue *q,blk_qc_t cookie) +int blk_poll (struct request_queue *q,blk_qc_t cookie) {if (!q->poll_fn || !blk_qc_t_valid (cookie))return false;
block/blk-mq.c +36 -35
108 lines not on this page · raw message
drivers/nvme/host/pci.c +7 -7
@@ -1012,15 +1012,15 @@ static inline void nvme_update_cq_head(struct nvme_queue *nvmeq) } } -static inline bool nvme_process_cq (struct nvme_queue *nvmeq,u16 *start, -u16 *end,int tag) +static inline int nvme_process_cq (struct nvme_queue *nvmeq,u16 *start, +u16 *end,unsigned int tag) { -bool found = false; +int found =0 ; *start = nvmeq->cq_head; -while (!found &&nvme_cqe_pending (nvmeq)) { -if (nvmeq->cqes[nvmeq->cq_head].command_id == tag) - found = true; +while (nvme_cqe_pending (nvmeq)) { +if (tag ==-1U || nvmeq->cqes[nvmeq->cq_head].command_id == tag) + found++;nvme_update_cq_head (nvmeq); } *end = nvmeq->cq_head;
@@ -1062,7 +1062,7 @@ static irqreturn_t nvme_irq_check(int irq, void *data)static int __nvme_poll (struct nvme_queue *nvmeq,unsigned int tag) {u16 start, end; -bool found; +int found;if (!nvme_cqe_pending (nvmeq))return 0 ;
drivers/nvme/host/rdma.c +15 -24
@@ -1409,12 +1409,11 @@ static void nvme_rdma_submit_async_event(struct nvme_ctrl *arg)WARN_ON_ONCE (ret); } -static int nvme_rdma_process_nvme_rsp (struct nvme_rdma_queue *queue, -struct nvme_completion *cqe,struct ib_wc *wc,int tag) +static void nvme_rdma_process_nvme_rsp (struct nvme_rdma_queue *queue, +struct nvme_completion *cqe,struct ib_wc *wc) {struct request *rq;struct nvme_rdma_request *req; -int ret =0 ; rq =blk_mq_tag_to_rq (nvme_rdma_tagset (queue), cqe->command_id);if (!rq) {
@@ -1422,7 +1421,7 @@ static int nvme_rdma_process_nvme_rsp(struct nvme_rdma_queue *queue,"tag 0x%x on QP %#x not found\n" , cqe->command_id, queue->qp->qp_num);nvme_rdma_error_recovery (queue->ctrl); -return ret; +return ; } req =blk_mq_rq_to_pdu (rq);
@@ -1437,6 +1436,8 @@ static int nvme_rdma_process_nvme_rsp(struct nvme_rdma_queue *queue,nvme_rdma_error_recovery (queue->ctrl); } }else if (req->mr) { +int ret; + ret =nvme_rdma_inv_rkey (queue, req);if (unlikely (ret <0 )) { dev_err(queue->ctrl->ctrl.device,
@@ -1445,19 +1446,14 @@ static int nvme_rdma_process_nvme_rsp(struct nvme_rdma_queue *queue,nvme_rdma_error_recovery (queue->ctrl); }/* the local invalidation completion will end the request */ -return 0 ; +return ; } -if (refcount_dec_and_test (&req->ref)) { -if (rq->tag == tag) - ret =1 ; +if (refcount_dec_and_test (&req->ref))nvme_end_request (rq, req->status, req->result); - } - -return ret; } -static int __nvme_rdma_recv_done (struct ib_cq *cq,struct ib_wc *wc,int tag) +static void nvme_rdma_recv_done (struct ib_cq *cq,struct ib_wc *wc) {struct nvme_rdma_qe *qe = container_of(wc->wr_cqe, struct nvme_rdma_qe, cqe);
@@ -1465,11 +1461,10 @@ static int __nvme_rdma_recv_done(struct ib_cq *cq, struct ib_wc *wc, int tag)struct ib_device *ibdev = queue->device->dev;struct nvme_completion *cqe = qe->data;const size_t len =sizeof (struct nvme_completion ); -int ret =0 ;if (unlikely (wc->status !=IB_WC_SUCCESS )) {nvme_rdma_wr_error (cq, wc,"RECV" ); -return 0 ; +return ; }ib_dma_sync_single_for_cpu (ibdev, qe->dma, len,DMA_FROM_DEVICE );
@@ -1484,16 +1479,10 @@ static int __nvme_rdma_recv_done(struct ib_cq *cq, struct ib_wc *wc, int tag)nvme_complete_async_event (&queue->ctrl->ctrl, cqe->status, &cqe->result); else - ret =nvme_rdma_process_nvme_rsp (queue, cqe, wc, tag); +nvme_rdma_process_nvme_rsp (queue ,cqe ,wc );ib_dma_sync_single_for_device (ibdev, qe->dma, len,DMA_FROM_DEVICE );nvme_rdma_post_recv (queue, qe); -return ret; -} - -static void nvme_rdma_recv_done (struct ib_cq *cq,struct ib_wc *wc) -{ -__nvme_rdma_recv_done (cq, wc,-1 ); }static int nvme_rdma_conn_established (struct nvme_rdma_queue *queue)
@@ -1758,10 +1747,12 @@ static int nvme_rdma_poll(struct blk_mq_hw_ctx *hctx, unsigned int tag)struct ib_cqe *cqe = wc.wr_cqe;if (cqe) { -if (cqe->done == nvme_rdma_recv_done) - found |=__nvme_rdma_recv_done (cq, &wc, tag); -else +if (cqe->done == nvme_rdma_recv_done) { +nvme_rdma_recv_done (cq, &wc); + found++; + }else { cqe->done (cq, &wc); + } } }
include/linux/blkdev.h +1 -1
@@ -867,7 +867,7 @@ extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,int blk_status_to_errno (blk_status_t status);blk_status_t errno_to_blk_status (int errno); -bool blk_poll (struct request_queue *q,blk_qc_t cookie); +int blk_poll (struct request_queue *q,blk_qc_t cookie);static inline struct request_queue *bdev_get_queue (struct block_device *bdev) {
Signature · 1 line
2.17.1
Re: [PATCH 1/6] blk-mq: when polling for IO, look for any completion
block/blk-core.c blk_qc_t submit_bio(struct bio *bio) · line 1276
Quoted 2 lines
block/blk-core.c +1
-1-bool blk_poll (struct request_queue *q,blk_qc_t cookie) +int blk_poll (struct request_queue *q,blk_qc_t cookie)
Can you add a comment explaining the return value?
block/blk-core.c blk_qc_t submit_bio(struct bio *bio) · line 1279
Quoted 3 lines
{
block/blk-core.c +0
-0if (!q->poll_fn || !blk_qc_t_valid (cookie))return false;
And false certainly isn't an integer constant..
Jens Axboe
maintainerRe: [PATCH 1/6] blk-mq: when polling for IO, look for any completion
Quoted 10 lines · Christoph Hellwig
block/blk-core.c blk_qc_t submit_bio(struct bio *bio) · line 1276
Quoted 2 lines
block/blk-core.c +1
-1-bool blk_poll (struct request_queue *q,blk_qc_t cookie) +int blk_poll (struct request_queue *q,blk_qc_t cookie)Can you add a comment explaining the return value?
block/blk-core.c blk_qc_t submit_bio(struct bio *bio) · line 1279
Quoted 3 lines
{
block/blk-core.c +0
-0if (!q->poll_fn || !blk_qc_t_valid (cookie))return false;And false certainly isn't an integer constant..
Done
Signature · 1 line
Jens Axboe
Jens Axboe
maintainerPATCH v4 2/6 blk-mq: remove 'tag' parameter from mq_ops->poll()
In reply to Jens AxboeWe always pass in -1 now and none of the callers use the tag value, remove the parameter.
--- block/blk-mq.c | 2 +- drivers/nvme/host/pci.c | 8 ++++---- drivers/nvme/host/rdma.c | 2 +- include/linux/blk-mq.h | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-)
block/blk-mq.c +1 -1
@@ -3380,7 +3380,7 @@ static int blk_mq_poll(struct request_queue *q, blk_qc_t cookie) hctx->poll_invoked++; - ret = q->mq_ops->poll (hctx,-1U ); + ret = q->mq_ops->poll (hctx);if (ret >0 ) { hctx->poll_success++;__set_current_state (TASK_RUNNING );
drivers/nvme/host/pci.c +4 -4
@@ -1075,14 +1075,14 @@ static int __nvme_poll(struct nvme_queue *nvmeq, unsigned int tag)return found; } -static int nvme_poll (struct blk_mq_hw_ctx *hctx,unsigned int tag) +static int nvme_poll (struct blk_mq_hw_ctx *hctx) {struct nvme_queue *nvmeq = hctx->driver_data; -return __nvme_poll (nvmeq, tag); +return __nvme_poll (nvmeq,-1 ); } -static int nvme_poll_noirq (struct blk_mq_hw_ctx *hctx,unsigned int tag) +static int nvme_poll_noirq (struct blk_mq_hw_ctx *hctx) {struct nvme_queue *nvmeq = hctx->driver_data;u16 start, end;
@@ -1092,7 +1092,7 @@ static int nvme_poll_noirq(struct blk_mq_hw_ctx *hctx, unsigned int tag)return 0 ;spin_lock (&nvmeq->cq_lock); - found =nvme_process_cq (nvmeq, &start, &end, tag); + found =nvme_process_cq (nvmeq, &start, &end,-1 );spin_unlock (&nvmeq->cq_lock);nvme_complete_cqes (nvmeq, start, end);
drivers/nvme/host/rdma.c +1 -1
@@ -1736,7 +1736,7 @@ static blk_status_t nvme_rdma_queue_rq(struct blk_mq_hw_ctx *hctx,return BLK_STS_IOERR ; } -static int nvme_rdma_poll (struct blk_mq_hw_ctx *hctx,unsigned int tag) +static int nvme_rdma_poll (struct blk_mq_hw_ctx *hctx) {struct nvme_rdma_queue *queue = hctx->driver_data;struct ib_cq *cq = queue->ib_cq;
include/linux/blk-mq.h +1 -1
@@ -132,7 +132,7 @@ typedef void (exit_request_fn)(struct blk_mq_tag_set *set, struct request *,typedef bool (busy_iter_fn )(struct blk_mq_hw_ctx *,struct request *,void *,bool );typedef bool (busy_tag_iter_fn )(struct request *,void *,bool ); -typedef int (poll_fn )(struct blk_mq_hw_ctx *,unsigned int ); +typedef int (poll_fn )(struct blk_mq_hw_ctx *);typedef int (map_queues_fn )(struct blk_mq_tag_set *set);typedef bool (busy_fn )(struct request_queue *);typedef void (complete_fn )(struct request *);
Signature · 1 line
2.17.1
Jens Axboe
maintainerPATCH v4 3/6 nvme: remove opportunistic polling from bdev target
In reply to Jens AxboeIt doesn't set HIPRI on the bio, so polling for it is pretty silly.
--- drivers/nvme/target/io-cmd-bdev.c | 2 -- 1 file changed, 2 deletions(-)
drivers/nvme/target/io-cmd-bdev.c +0 -2
@@ -115,8 +115,6 @@ static void nvmet_bdev_execute_rw(struct nvmet_req *req) } cookie =submit_bio (bio); - -blk_poll (bdev_get_queue (req->ns->bdev), cookie); }static void nvmet_bdev_execute_flush (struct nvmet_req *req)
Signature · 1 line
2.17.1
Re: [PATCH 3/6] nvme: remove opportunistic polling from bdev target
Quoted 3 lines · Jens Axboe
It doesn't set HIPRI on the bio, so polling for it is pretty silly.
Signed-off-by Jens Axboe <[email protected]>
Well, I know who forgot to mark it hipri :)
But for now this looks good, we need to revisit fabrics polling entirely once everything has settled.
Jens Axboe
maintainerPATCH v4 4/6 block: make blk_poll() take a parameter on whether to spin or not
In reply to Jens Axboeblk_poll() has always kept spinning until it found an IO. This is fine for SYNC polling, since we need to find one request we have pending, but in preparation for ASYNC polling it can be beneficial to just check if we have any entries available or not.
Existing callers are converted to pass in 'spin == true', to retain the old behavior.
--- block/blk-core.c | 4 ++-- block/blk-mq.c | 6 +++--- drivers/nvme/host/multipath.c | 4 ++-- fs/block_dev.c | 4 ++-- fs/direct-io.c | 2 +- fs/iomap.c | 2 +- include/linux/blkdev.h | 4 ++-- mm/page_io.c | 2 +- 8 files changed, 14 insertions(+), 14 deletions(-)
block/blk-core.c +2 -2
@@ -1273,14 +1273,14 @@ blk_qc_t submit_bio(struct bio *bio) }EXPORT_SYMBOL (submit_bio); -int blk_poll (struct request_queue *q,blk_qc_t cookie) +int blk_poll (struct request_queue *q,blk_qc_t cookie,bool spin) {if (!q->poll_fn || !blk_qc_t_valid (cookie))return false;if (current->plug)blk_flush_plug_list (current->plug, false); -return q->poll_fn (q, cookie); +return q->poll_fn (q, cookie, spin); }EXPORT_SYMBOL_GPL (blk_poll);
block/blk-mq.c +3 -3
@@ -38,7 +38,7 @@#include "blk-mq-sched.h" #include "blk-rq-qos.h" -static int blk_mq_poll (struct request_queue *q,blk_qc_t cookie); +static int blk_mq_poll (struct request_queue *q,blk_qc_t cookie,bool spin);static void blk_mq_poll_stats_start (struct request_queue *q);static void blk_mq_poll_stats_fn (struct blk_stat_callback *cb);
@@ -3352,7 +3352,7 @@ static bool blk_mq_poll_hybrid(struct request_queue *q,return blk_mq_poll_hybrid_sleep (q, hctx, rq); } -static int blk_mq_poll (struct request_queue *q,blk_qc_t cookie) +static int blk_mq_poll (struct request_queue *q,blk_qc_t cookie,bool spin) {struct blk_mq_hw_ctx *hctx;long state;
@@ -3392,7 +3392,7 @@ static int blk_mq_poll(struct request_queue *q, blk_qc_t cookie)if (current->state ==TASK_RUNNING )return 1 ; -if (ret <0 ) +if (ret <0 || !spin)break ;cpu_relax (); }
drivers/nvme/host/multipath.c +2 -2
@@ -220,7 +220,7 @@ static blk_qc_t nvme_ns_head_make_request(struct request_queue *q,return ret; } -static int nvme_ns_head_poll (struct request_queue *q,blk_qc_t qc) +static int nvme_ns_head_poll (struct request_queue *q,blk_qc_t qc,bool spin) {struct nvme_ns_head *head = q->queuedata;struct nvme_ns *ns;
@@ -230,7 +230,7 @@ static int nvme_ns_head_poll(struct request_queue *q, blk_qc_t qc) srcu_idx =srcu_read_lock (&head->srcu); ns =srcu_dereference (head->current_path[numa_node_id ()], &head->srcu);if (likely (ns &&nvme_path_is_optimized (ns))) - found = ns->queue->poll_fn (q, qc); + found = ns->queue->poll_fn (q, qc, spin);srcu_read_unlock (&head->srcu, srcu_idx);return found; }
fs/block_dev.c +2 -2
@@ -243,7 +243,7 @@ __blkdev_direct_IO_simple(struct kiocb *iocb, struct iov_iter *iter,break ;if (!(iocb->ki_flags &IOCB_HIPRI ) || - !blk_poll (bdev_get_queue (bdev), qc)) + !blk_poll (bdev_get_queue (bdev), qc, true))io_schedule (); }__set_current_state (TASK_RUNNING );
@@ -423,7 +423,7 @@ __blkdev_direct_IO(struct kiocb *iocb, struct iov_iter *iter, int nr_pages)break ;if (!(iocb->ki_flags &IOCB_HIPRI ) || - !blk_poll (bdev_get_queue (bdev), qc)) + !blk_poll (bdev_get_queue (bdev), qc, true))io_schedule (); }__set_current_state (TASK_RUNNING );
fs/direct-io.c +1 -1
@@ -518,7 +518,7 @@ static struct bio *dio_await_one(struct dio *dio) dio->waiter = current;spin_unlock_irqrestore (&dio->bio_lock, flags);if (!(dio->iocb->ki_flags &IOCB_HIPRI ) || - !blk_poll (dio->bio_disk->queue, dio->bio_cookie)) + !blk_poll (dio->bio_disk->queue, dio->bio_cookie, true))io_schedule ();/* wake up sets us TASK_RUNNING */ spin_lock_irqsave (&dio->bio_lock, flags);
fs/iomap.c +1 -1
@@ -1896,7 +1896,7 @@ iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter,if (!(iocb->ki_flags &IOCB_HIPRI ) || !dio->submit.last_queue || !blk_poll (dio->submit.last_queue, - dio->submit.cookie)) + dio->submit.cookie, true))io_schedule (); }__set_current_state (TASK_RUNNING );
include/linux/blkdev.h +2 -2
@@ -283,7 +283,7 @@ static inline unsigned short req_get_ioprio(struct request *req)struct blk_queue_ctx ;typedef blk_qc_t (make_request_fn ) (struct request_queue *q,struct bio *bio); -typedef int (poll_q_fn ) (struct request_queue *q,blk_qc_t ); +typedef int (poll_q_fn ) (struct request_queue *q,blk_qc_t ,bool spin);struct bio_vec ;typedef int (dma_drain_needed_fn )(struct request *);
@@ -867,7 +867,7 @@ extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *,int blk_status_to_errno (blk_status_t status);blk_status_t errno_to_blk_status (int errno); -int blk_poll (struct request_queue *q,blk_qc_t cookie); +int blk_poll (struct request_queue *q,blk_qc_t cookie,bool spin);static inline struct request_queue *bdev_get_queue (struct block_device *bdev) {
mm/page_io.c +1 -1
@@ -410,7 +410,7 @@ int swap_readpage(struct page *page, bool synchronous)if (!READ_ONCE (bio->bi_private))break ; -if (!blk_poll (disk->queue, qc)) +if (!blk_poll (disk->queue, qc, true))break ; }__set_current_state (TASK_RUNNING );
Signature · 1 line
2.17.1
Re: [PATCH 4/6] block: make blk_poll() take a parameter on whether to spin or not
block/blk-core.c blk_qc_t submit_bio(struct bio *bio) · line 1276
Quoted 2 lines
block/blk-core.c +1
-1-int blk_poll (struct request_queue *q,blk_qc_t cookie) +int blk_poll (struct request_queue *q,blk_qc_t cookie,bool spin)
The parameter will need some documentation.
Jens Axboe
maintainerRe: [PATCH 4/6] block: make blk_poll() take a parameter on whether to spin or not
Quoted 4 lines · Christoph Hellwig
block/blk-core.c blk_qc_t submit_bio(struct bio *bio) · line 1276
Quoted 2 lines
block/blk-core.c +1
-1-int blk_poll (struct request_queue *q,blk_qc_t cookie) +int blk_poll (struct request_queue *q,blk_qc_t cookie,bool spin)The parameter will need some documentation.
Done
Signature · 1 line
Jens Axboe
Jens Axboe
maintainerPATCH v4 5/6 blk-mq: ensure mq_ops ->poll() is entered at least once
In reply to Jens AxboeRight now we immediately bail if need_resched() is true, but we need to do at least one loop in case we have entries waiting. So just invert the need_resched() check, putting it at the bottom of the loop.
--- block/blk-mq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
block/blk-mq.c +2 -2
@@ -3375,7 +3375,7 @@ static int blk_mq_poll(struct request_queue *q, blk_qc_t cookie, bool spin) hctx->poll_considered++; state = current->state; -while (!need_resched ()) { + do { int ret; hctx->poll_invoked++;
@@ -3395,7 +3395,7 @@ static int blk_mq_poll(struct request_queue *q, blk_qc_t cookie, bool spin)if (ret <0 || !spin)break ;cpu_relax (); - } + }while (!need_resched ());__set_current_state (TASK_RUNNING );return 0 ;
Signature · 1 line
2.17.1
Jens Axboe
maintainerPATCH v4 6/6 blk-mq: never redirect polled IO completions
In reply to Jens AxboeIt's pointless to do so, we are by definition on the CPU we want/need to be, as that's the one waiting for a completion event.
--- block/blk-mq.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
block/blk-mq.c +6 -1
@@ -585,7 +585,12 @@ static void __blk_mq_complete_request(struct request *rq)return ; } -if (!test_bit (QUEUE_FLAG_SAME_COMP , &q->queue_flags)) { +/* +* For a polled request, always complete locallly, it's pointless +* to redirect the completion. +*/ +if ((rq->cmd_flags &REQ_HIPRI ) || + !test_bit (QUEUE_FLAG_SAME_COMP , &q->queue_flags)) { q->mq_ops->complete (rq);return ; }
Signature · 1 line
2.17.1
Re: [PATCH 6/6] blk-mq: never redirect polled IO completions
Quoted 4 lines · Jens Axboe
It's pointless to do so, we are by definition on the CPU we want/need to be, as that's the one waiting for a completion event.
Signed-off-by Jens Axboe <[email protected]>
Looks good,