Various block optimizations
Subsystems (10)
Jens Axboe
maintainerv2 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 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
Jens Axboe
maintainerPATCH v2 1/11 nvme: provide optimized poll function for separate poll queues
If we have separate poll queues, we know that they aren't using interrupts. Hence we don't need to disable interrupts around finding completions.
Provide a separate set of blk_mq_ops for such devices.
--- drivers/nvme/host/pci.c | 45 +++++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 8 deletions(-)
drivers/nvme/host/pci.c +37 -8
@@ -1082,6 +1082,23 @@ static int nvme_poll(struct blk_mq_hw_ctx *hctx, unsigned int tag)return __nvme_poll (nvmeq, tag); } +static int nvme_poll_noirq (struct blk_mq_hw_ctx *hctx,unsigned int tag) +{ +struct nvme_queue *nvmeq = hctx->driver_data; +u16 start, end; +bool found; + +if (!nvme_cqe_pending (nvmeq)) +return 0 ; + +spin_lock (&nvmeq->cq_lock); + found =nvme_process_cq (nvmeq, &start, &end, tag); +spin_unlock (&nvmeq->cq_lock); + +nvme_complete_cqes (nvmeq, start, end); +return found; +} +static void nvme_pci_submit_async_event (struct nvme_ctrl *ctrl) {struct nvme_dev *dev =to_nvme_dev (ctrl);
@@ -1584,17 +1601,25 @@ static const struct blk_mq_ops nvme_mq_admin_ops = { .timeout = nvme_timeout, }; +#define NVME_SHARED_MQ_OPS \ + .queue_rq = nvme_queue_rq, \ + .rq_flags_to_type = nvme_rq_flags_to_type, \ + .complete = nvme_pci_complete_rq, \ + .init_hctx = nvme_init_hctx, \ + .init_request = nvme_init_request, \ + .map_queues = nvme_pci_map_queues, \ + .timeout = nvme_timeout \ +static const struct blk_mq_ops nvme_mq_ops = { - .queue_rq = nvme_queue_rq, - .rq_flags_to_type = nvme_rq_flags_to_type, - .complete = nvme_pci_complete_rq, - .init_hctx = nvme_init_hctx, - .init_request = nvme_init_request, - .map_queues = nvme_pci_map_queues, - .timeout = nvme_timeout, +NVME_SHARED_MQ_OPS , .poll = nvme_poll, }; +static const struct blk_mq_ops nvme_mq_poll_noirq_ops = { +NVME_SHARED_MQ_OPS , + .poll = nvme_poll_noirq, +}; +static void nvme_dev_remove_admin (struct nvme_dev *dev) {if (dev->ctrl.admin_q && !blk_queue_dying (dev->ctrl.admin_q)) {
@@ -2274,7 +2299,11 @@ static int nvme_dev_add(struct nvme_dev *dev)int ret;if (!dev->ctrl.tagset) { - dev->tagset.ops = &nvme_mq_ops; +if (!dev->io_queues[NVMEQ_TYPE_POLL ]) + dev->tagset.ops = &nvme_mq_ops; +else + dev->tagset.ops = &nvme_mq_poll_noirq_ops; + dev->tagset.nr_hw_queues = dev->online_queues -1 ; dev->tagset.nr_maps =NVMEQ_TYPE_NR ; dev->tagset.timeout =NVME_IO_TIMEOUT ;
Signature · 1 line
2.17.1
Re: [PATCH 01/11] nvme: provide optimized poll function for separate poll queues
Quoted 5 lines · Jens Axboe
If we have separate poll queues, we know that they aren't using interrupts. Hence we don't need to disable interrupts around finding completions.
Provide a separate set of
blk_mq_opsfor such devices.
This looks ok, but I'd prefer if we could offer to just support polling with the separate queue. That way we get ourselves out of all kinds of potential races of the interrupt path vs poll path.
Jens Axboe
maintainerRe: [PATCH 01/11] nvme: provide optimized poll function for separate poll queues
Quoted 10 lines · Christoph Hellwig
Quoted 5 lines · Jens Axboe
If we have separate poll queues, we know that they aren't using interrupts. Hence we don't need to disable interrupts around finding completions.
Provide a separate set of
blk_mq_opsfor such devices.This looks ok, but I'd prefer if we could offer to just support polling with the separate queue. That way we get ourselves out of all kinds of potential races of the interrupt path vs poll path.
As Keith mentioned, we do use polling to find missing completions in case of timeouts. And that has actually been really useful.
I'd rather keep such a change separate. If we do go down that route, then there are more optimizations we can make.
Finally, let's not forget that polling is/was still a win even if we did trigger interrupts. That's how NVMe has been since polling was introduced. While the newer stuff is a lot more efficient, I don't think we should totally abandon an easy opt-in for polling for hardware unless we have strong reasons to do so.
Signature · 1 line
Jens Axboe
Jens Axboe
maintainerPATCH v2 2/11 block: add queue_is_mq() helper
In reply to Jens AxboeVarious spots check for q->mq_ops being non-NULL, but provide a helper to do this instead.
Where the ->mq_ops != NULL check is redundant, remove it.
Since mq == rq-based now that legacy is gone, get rid of the queue_is_rq_based() and just use queue_is_mq() everywhere.
--- block/blk-cgroup.c | 8 ++++---- block/blk-core.c | 12 ++++++------ block/blk-flush.c | 3 +-- block/blk-mq.c | 2 +- block/blk-sysfs.c | 14 +++++++------- block/blk-throttle.c | 2 +- block/blk-wbt.c | 2 +- block/blk-zoned.c | 2 +- block/bsg.c | 2 +- block/elevator.c | 11 +++++------ block/genhd.c | 8 ++++---- drivers/md/dm-rq.c | 2 +- drivers/md/dm-table.c | 4 ++-- include/linux/blkdev.h | 6 +----- 14 files changed, 36 insertions(+), 42 deletions(-)
block/blk-cgroup.c +4 -4
@@ -1324,7 +1324,7 @@ int blkcg_activate_policy(struct request_queue *q,if (blkcg_policy_enabled (q, pol))return 0 ; -if (q->mq_ops) +if (queue_is_mq (q))blk_mq_freeze_queue (q); pd_prealloc:if (!pd_prealloc) {
@@ -1363,7 +1363,7 @@ int blkcg_activate_policy(struct request_queue *q,spin_unlock_irq (&q->queue_lock); out_bypass_end: -if (q->mq_ops) +if (queue_is_mq (q))blk_mq_unfreeze_queue (q);if (pd_prealloc) pol->pd_free_fn (pd_prealloc);
@@ -1387,7 +1387,7 @@ void blkcg_deactivate_policy(struct request_queue *q,if (!blkcg_policy_enabled (q, pol))return ; -if (q->mq_ops) +if (queue_is_mq (q))blk_mq_freeze_queue (q);spin_lock_irq (&q->queue_lock);
@@ -1405,7 +1405,7 @@ void blkcg_deactivate_policy(struct request_queue *q,spin_unlock_irq (&q->queue_lock); -if (q->mq_ops) +if (queue_is_mq (q))blk_mq_unfreeze_queue (q); }EXPORT_SYMBOL_GPL (blkcg_deactivate_policy);
block/blk-core.c +6 -6
@@ -232,7 +232,7 @@ void blk_sync_queue(struct request_queue *q)del_timer_sync (&q->timeout);cancel_work_sync (&q->timeout_work); -if (q->mq_ops) { +if (queue_is_mq (q)) {struct blk_mq_hw_ctx *hctx;int i;
@@ -281,7 +281,7 @@ void blk_set_queue_dying(struct request_queue *q)*/ blk_freeze_queue_start (q); -if (q->mq_ops) +if (queue_is_mq (q))blk_mq_wake_waiters (q);/* Make blk_queue_enter() reexamine the DYING flag. */
@@ -356,7 +356,7 @@ void blk_cleanup_queue(struct request_queue *q)* blk_freeze_queue() should be enough for cases of passthrough * request. */ -if (q->mq_ops &&blk_queue_init_done (q)) +if (queue_is_mq (q) &&blk_queue_init_done (q))blk_mq_quiesce_queue (q);/* for synchronous bio-based driver finish in-flight integrity i/o */
@@ -374,7 +374,7 @@ void blk_cleanup_queue(struct request_queue *q)blk_exit_queue (q); -if (q->mq_ops) +if (queue_is_mq (q))blk_mq_free_queue (q);percpu_ref_exit (&q->q_usage_counter);
@@ -982,7 +982,7 @@ generic_make_request_checks(struct bio *bio)* For a REQ_NOWAIT based request, return -EOPNOTSUPP * if queue is not a request based queue. */ -if ((bio->bi_opf &REQ_NOWAIT ) && !queue_is_rq_based (q)) +if ((bio->bi_opf &REQ_NOWAIT ) && !queue_is_mq (q)) goto not_supported;if (should_fail_bio (bio))
@@ -1657,7 +1657,7 @@ EXPORT_SYMBOL_GPL(rq_flush_dcache_pages);*/ int blk_lld_busy (struct request_queue *q) { -if (q->mq_ops && q->mq_ops->busy) +if (queue_is_mq (q) && q->mq_ops->busy)return q->mq_ops->busy (q);return 0 ;
block/blk-flush.c +1 -2
@@ -273,8 +273,7 @@ static void blk_kick_flush(struct request_queue *q, struct blk_flush_queue *fq,* assigned to empty flushes, and we deadlock if we are expecting * other requests to make progress. Don't defer for that case. */ -if (!list_empty (&fq->flush_data_in_flight) && - !(q->mq_ops && q->elevator) && +if (!list_empty (&fq->flush_data_in_flight) && q->elevator &&time_before (jiffies, fq->flush_pending_since +FLUSH_PENDING_TIMEOUT ))return ;
block/blk-mq.c +1 -1
@@ -150,7 +150,7 @@ void blk_freeze_queue_start(struct request_queue *q) freeze_depth =atomic_inc_return (&q->mq_freeze_depth);if (freeze_depth ==1 ) {percpu_ref_kill (&q->q_usage_counter); -if (q->mq_ops) +if (queue_is_mq (q))blk_mq_run_hw_queues (q, false); } }
block/blk-sysfs.c +7 -7
@@ -68,7 +68,7 @@ queue_requests_store(struct request_queue *q, const char *page, size_t count)unsigned long nr;int ret, err; -if (!q->mq_ops) +if (!queue_is_mq (q))return -EINVAL ; ret =queue_var_store (&nr, page, count);
@@ -835,12 +835,12 @@ static void __blk_release_queue(struct work_struct *work)blk_queue_free_zone_bitmaps (q); -if (q->mq_ops) +if (queue_is_mq (q))blk_mq_release (q);blk_trace_shutdown (q); -if (q->mq_ops) +if (queue_is_mq (q))blk_mq_debugfs_unregister (q);bioset_exit (&q->bio_split);
@@ -914,7 +914,7 @@ int blk_register_queue(struct gendisk *disk) goto unlock; } -if (q->mq_ops) { +if (queue_is_mq (q)) {__blk_mq_register_dev (dev, q);blk_mq_debugfs_register (q); }
@@ -925,7 +925,7 @@ int blk_register_queue(struct gendisk *disk)blk_throtl_register_queue (q); -if ((q->mq_ops && q->elevator)) { +if (q->elevator) { ret =elv_register_queue (q);if (ret) {mutex_unlock (&q->sysfs_lock);
@@ -974,7 +974,7 @@ void blk_unregister_queue(struct gendisk *disk)* Remove the sysfs attributes before unregistering the queue data * structures that can be modified through sysfs. */ -if (q->mq_ops) +if (queue_is_mq (q))blk_mq_unregister_dev (disk_to_dev (disk), q);mutex_unlock (&q->sysfs_lock);
@@ -983,7 +983,7 @@ void blk_unregister_queue(struct gendisk *disk)blk_trace_remove_sysfs (disk_to_dev (disk));mutex_lock (&q->sysfs_lock); -if (q->mq_ops && q->elevator) +if (q->elevator)elv_unregister_queue (q);mutex_unlock (&q->sysfs_lock);
block/blk-throttle.c +1 -1
@@ -2456,7 +2456,7 @@ void blk_throtl_register_queue(struct request_queue *q) td->throtl_slice =DFL_THROTL_SLICE_HD ;#endif - td->track_bio_latency = !queue_is_rq_based (q); + td->track_bio_latency = !queue_is_mq (q);if (!td->track_bio_latency)blk_stat_enable_accounting (q); }
block/blk-wbt.c +1 -1
@@ -701,7 +701,7 @@ void wbt_enable_default(struct request_queue *q)if (!test_bit (QUEUE_FLAG_REGISTERED , &q->queue_flags))return ; -if (q->mq_ops &&IS_ENABLED (CONFIG_BLK_WBT_MQ )) +if (queue_is_mq (q) &&IS_ENABLED (CONFIG_BLK_WBT_MQ ))wbt_init (q); }EXPORT_SYMBOL_GPL (wbt_enable_default);
block/blk-zoned.c +1 -1
@@ -421,7 +421,7 @@ int blk_revalidate_disk_zones(struct gendisk *disk)* BIO based queues do not use a scheduler so only q->nr_zones * needs to be updated so that the sysfs exposed value is correct. */ -if (!queue_is_rq_based (q)) { +if (!queue_is_mq (q)) { q->nr_zones = nr_zones;return 0 ; }
block/bsg.c +1 -1
@@ -471,7 +471,7 @@ int bsg_register_queue(struct request_queue *q, struct device *parent,/* * we need a proper transport to send commands, not a stacked device */ -if (!queue_is_rq_based (q)) +if (!queue_is_mq (q))return 0 ; bcd = &q->bsg_dev;
block/elevator.c +5 -6
@@ -667,7 +667,7 @@ static int __elevator_change(struct request_queue *q, const char *name)/* * Special case for mq, turn off scheduling */ -if (q->mq_ops && !strncmp (name,"none" ,4 )) +if (!strncmp (name,"none" ,4 ))return elevator_switch (q,NULL );strlcpy (elevator_name, name,sizeof (elevator_name));
@@ -685,8 +685,7 @@ static int __elevator_change(struct request_queue *q, const char *name)static inline bool elv_support_iosched (struct request_queue *q) { -if (q->mq_ops && q->tag_set && (q->tag_set->flags & -BLK_MQ_F_NO_SCHED )) +if (q->tag_set && (q->tag_set->flags &BLK_MQ_F_NO_SCHED ))return false;return true; }
@@ -696,7 +695,7 @@ ssize_t elv_iosched_store(struct request_queue *q, const char *name, {int ret; -if (!q->mq_ops || !elv_support_iosched (q)) +if (!queue_is_mq (q) || !elv_support_iosched (q))return count; ret =__elevator_change (q, name);
@@ -713,7 +712,7 @@ ssize_t elv_iosched_show(struct request_queue *q, char *name)struct elevator_type *__e;int len =0 ; -if (!queue_is_rq_based (q)) +if (!queue_is_mq (q))return sprintf (name,"none\n" );if (!q->elevator)
@@ -732,7 +731,7 @@ ssize_t elv_iosched_show(struct request_queue *q, char *name) }spin_unlock (&elv_list_lock); -if (q->mq_ops && q->elevator) +if (q->elevator) len +=sprintf (name+len,"none" ); len +=sprintf (len+name,"\n" );
block/genhd.c +4 -4
@@ -47,7 +47,7 @@ static void disk_release_events(struct gendisk *disk);void part_inc_in_flight (struct request_queue *q,struct hd_struct *part,int rw) { -if (q->mq_ops) +if (queue_is_mq (q))return ;atomic_inc (&part->in_flight[rw]);
@@ -57,7 +57,7 @@ void part_inc_in_flight(struct request_queue *q, struct hd_struct *part, int rw)void part_dec_in_flight (struct request_queue *q,struct hd_struct *part,int rw) { -if (q->mq_ops) +if (queue_is_mq (q))return ;atomic_dec (&part->in_flight[rw]);
@@ -68,7 +68,7 @@ void part_dec_in_flight(struct request_queue *q, struct hd_struct *part, int rw)void part_in_flight (struct request_queue *q,struct hd_struct *part,unsigned int inflight[2 ]) { -if (q->mq_ops) { +if (queue_is_mq (q)) {blk_mq_in_flight (q, part, inflight);return ; }
@@ -85,7 +85,7 @@ void part_in_flight(struct request_queue *q, struct hd_struct *part,void part_in_flight_rw (struct request_queue *q,struct hd_struct *part,unsigned int inflight[2 ]) { -if (q->mq_ops) { +if (queue_is_mq (q)) {blk_mq_in_flight_rw (q, part, inflight);return ; }
drivers/md/dm-rq.c +1 -1
@@ -43,7 +43,7 @@ static unsigned dm_get_blk_mq_queue_depth(void)int dm_request_based (struct mapped_device *md) { -return queue_is_rq_based (md->queue); +return queue_is_mq (md->queue); }void dm_start_queue (struct request_queue *q)
drivers/md/dm-table.c +2 -2
@@ -919,12 +919,12 @@ static int device_is_rq_based(struct dm_target *ti, struct dm_dev *dev,struct request_queue *q =bdev_get_queue (dev->bdev);struct verify_rq_based_data *v = data; -if (q->mq_ops) +if (queue_is_mq (q)) v->mq_count++;else v->sq_count++; -return queue_is_rq_based (q); +return queue_is_mq (q); }static int dm_table_determine_type (struct dm_table *t)
include/linux/blkdev.h +1 -5
@@ -656,11 +656,7 @@ static inline bool blk_account_rq(struct request *rq)#define rq_data_dir (rq) (op_is_write(req_op(rq)) ? WRITE : READ) -/* -* Driver can handle struct request, if it either has an old style -* request_fn defined, or is blk-mq based. -*/ -static inline bool queue_is_rq_based (struct request_queue *q) +static inline bool queue_is_mq (struct request_queue *q) {return q->mq_ops; }
Signature · 1 line
2.17.1
Re: [PATCH 02/11] block: add queue_is_mq() helper
Quoted 9 lines · Jens Axboe
Various spots check for
q->mq_opsbeing non-NULL, but provide a helper to do this instead.Where the ->mq_ops != NULL check is redundant, remove it.
Since mq == rq-based now that legacy is gone, get rid of the
queue_is_rq_based()and just usequeue_is_mq()everywhere.Signed-off-by Jens Axboe <[email protected]>
Looks good,
Jens Axboe
maintainerPATCH v2 3/11 blk-rq-qos: inline check for q->rq_qos functions
In reply to Jens AxboePut the short code in the fast path, where we don't have any functions attached to the queue. This minimizes the impact on the hot path in the core code.
--- block/blk-rq-qos.c | 63 +++++++++++++++++++++------------------------- block/blk-rq-qos.h | 59 +++++++++++++++++++++++++++++++++++++------ 2 files changed, 80 insertions(+), 42 deletions(-)
block/blk-rq-qos.c +28 -35
@@ -27,74 +27,67 @@ bool rq_wait_inc_below(struct rq_wait *rq_wait, unsigned int limit)return atomic_inc_below (&rq_wait->inflight, limit); } -void rq_qos_cleanup (struct request_queue *q,struct bio *bio) +void __rq_qos_cleanup (struct rq_qos *rqos,struct bio *bio) { -struct rq_qos *rqos; - -for (rqos = q->rq_qos; rqos; rqos = rqos->next) { +do {if (rqos->ops->cleanup) rqos->ops->cleanup (rqos, bio); - } + rqos = rqos->next; + }while (rqos); } -void rq_qos_done (struct request_queue *q,struct request *rq) +void __rq_qos_done (struct rq_qos *rqos,struct request *rq) { -struct rq_qos *rqos; - -for (rqos = q->rq_qos; rqos; rqos = rqos->next) { +do {if (rqos->ops->done) rqos->ops->done (rqos, rq); - } + rqos = rqos->next; + }while (rqos); } -void rq_qos_issue (struct request_queue *q,struct request *rq) +void __rq_qos_issue (struct rq_qos *rqos,struct request *rq) { -struct rq_qos *rqos; - -for (rqos = q->rq_qos; rqos; rqos = rqos->next) { +do {if (rqos->ops->issue) rqos->ops->issue (rqos, rq); - } + rqos = rqos->next; + }while (rqos); } -void rq_qos_requeue (struct request_queue *q,struct request *rq) +void __rq_qos_requeue (struct rq_qos *rqos,struct request *rq) { -struct rq_qos *rqos; - -for (rqos = q->rq_qos; rqos; rqos = rqos->next) { +do {if (rqos->ops->requeue) rqos->ops->requeue (rqos, rq); - } + rqos = rqos->next; + }while (rqos); } -void rq_qos_throttle (struct request_queue *q,struct bio *bio) +void __rq_qos_throttle (struct rq_qos *rqos,struct bio *bio) { -struct rq_qos *rqos; - -for (rqos = q->rq_qos; rqos; rqos = rqos->next) { +do {if (rqos->ops->throttle) rqos->ops->throttle (rqos, bio); - } + rqos = rqos->next; + }while (rqos); } -void rq_qos_track (struct request_queue *q,struct request *rq,struct bio *bio) +void __rq_qos_track (struct rq_qos *rqos,struct request *rq,struct bio *bio) { -struct rq_qos *rqos; - -for (rqos = q->rq_qos; rqos; rqos = rqos->next) { +do {if (rqos->ops->track) rqos->ops->track (rqos, rq, bio); - } + rqos = rqos->next; + }while (rqos); } -void rq_qos_done_bio (struct request_queue *q,struct bio *bio) +void __rq_qos_done_bio (struct rq_qos *rqos,struct bio *bio) { -struct rq_qos *rqos; - -for (rqos = q->rq_qos; rqos; rqos = rqos->next) { +do {if (rqos->ops->done_bio) rqos->ops->done_bio (rqos, bio); - } + rqos = rqos->next; + }while (rqos); }/*
block/blk-rq-qos.h +52 -7
64 lines not on this page · raw message
Signature · 1 line
2.17.1
Re: [PATCH 03/11] blk-rq-qos: inline check for q->rq_qos functions
Quoted 3 lines · Jens Axboe
Put the short code in the fast path, where we don't have any functions attached to the queue. This minimizes the impact on the hot path in the core code.
This looks mechanically fine:
But since I seem to have missed the introduction of it - why do we need multiple struct rq_qos per request to start with? This sort of stacking seems rather odd and counter-productive, and the commit introducing this code doesn't explain the rationale at all.
Jens Axboe
maintainerRe: [PATCH 03/11] blk-rq-qos: inline check for q->rq_qos functions
Quoted 5 lines · Christoph Hellwig
Quoted 3 lines · Jens Axboe
Put the short code in the fast path, where we don't have any functions attached to the queue. This minimizes the impact on the hot path in the core code.
Quoted 8 lines
This looks mechanically fine:
Reviewed-by Christoph Hellwig <[email protected]>But since I seem to have missed the introduction of it - why do we need multiple struct
rq_qosper request to start with? This sort of stacking seems rather odd and counter-productive, and the commit introducing this code doesn't explain the rationale at all.
Per request-queue, not per request. One would be iolatency, one would be wbt, etc.
Signature · 1 line
Jens Axboe
Jens Axboe
maintainerPATCH v2 4/11 block: avoid ordered task state change for polled IO
In reply to Jens AxboeEnsure that writes to the dio/bio waiter field are ordered correctly. With the smp_rmb() before the READ_ONCE() check, we should be able to use a more relaxed ordering for the task state setting. We don't need a heavier barrier on the wakeup side after writing the waiter field, since we either going to be in the task we care about, or go through wake_up_process() which implies a strong enough barrier.
For the core poll helper, the task state setting don't need to imply any atomics, as it's the current task itself that is being modified and we're not going to sleep.
--- block/blk-mq.c | 4 ++-- fs/block_dev.c | 9 +++++++-- fs/iomap.c | 4 +++- mm/page_io.c | 4 +++- 4 files changed, 15 insertions(+), 6 deletions(-)
block/blk-mq.c +2 -2
@@ -3331,12 +3331,12 @@ static bool __blk_mq_poll(struct blk_mq_hw_ctx *hctx, struct request *rq) ret = q->mq_ops->poll (hctx, rq->tag);if (ret >0 ) { hctx->poll_success++; -set_current_state (TASK_RUNNING ); +__set_current_state (TASK_RUNNING );return true; }if (signal_pending_state (state, current)) -set_current_state (TASK_RUNNING ); +__set_current_state (TASK_RUNNING );if (current->state ==TASK_RUNNING )return true;
fs/block_dev.c +7 -2
@@ -237,9 +237,12 @@ __blkdev_direct_IO_simple(struct kiocb *iocb, struct iov_iter *iter, qc =submit_bio (&bio);for (;;) { -set_current_state (TASK_UNINTERRUPTIBLE ); +__set_current_state (TASK_UNINTERRUPTIBLE ); + +smp_rmb ();if (!READ_ONCE (bio.bi_private))break ; +if (!(iocb->ki_flags &IOCB_HIPRI ) || !blk_poll (bdev_get_queue (bdev), qc))io_schedule ();
@@ -403,7 +406,9 @@ __blkdev_direct_IO(struct kiocb *iocb, struct iov_iter *iter, int nr_pages)return -EIOCBQUEUED ;for (;;) { -set_current_state (TASK_UNINTERRUPTIBLE ); +__set_current_state (TASK_UNINTERRUPTIBLE ); + +smp_rmb ();if (!READ_ONCE (dio->waiter))break ;
fs/iomap.c +3 -1
@@ -1888,7 +1888,9 @@ iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter,return -EIOCBQUEUED ;for (;;) { -set_current_state (TASK_UNINTERRUPTIBLE ); +__set_current_state (TASK_UNINTERRUPTIBLE ); + +smp_rmb ();if (!READ_ONCE (dio->submit.waiter))break ;
mm/page_io.c +3 -1
@@ -405,7 +405,9 @@ int swap_readpage(struct page *page, bool synchronous)bio_get (bio); qc =submit_bio (bio);while (synchronous) { -set_current_state (TASK_UNINTERRUPTIBLE ); +__set_current_state (TASK_UNINTERRUPTIBLE ); + +smp_rmb ();if (!READ_ONCE (bio->bi_private))break ;
Signature · 1 line
2.17.1
Re: [PATCH 04/11] block: avoid ordered task state change for polled IO
Quoted 88 lines · Jens Axboe
Ensure that writes to the dio/bio waiter field are ordered correctly. With the
smp_rmb()before theREAD_ONCE()check, we should be able to use a more relaxed ordering for the task state setting. We don't need a heavier barrier on the wakeup side after writing the waiter field, since we either going to be in the task we care about, or go throughwake_up_process()which implies a strong enough barrier.For the core poll helper, the task state setting don't need to imply any atomics, as it's the current task itself that is being modified and we're not going to sleep.
Signed-off-by Jens Axboe <[email protected]>--- block/blk-mq.c | 4 ++-- fs/block_dev.c | 9 +++++++-- fs/iomap.c | 4 +++- mm/page_io.c | 4 +++- 4 files changed, 15 insertions(+), 6 deletions(-)block/blk-mq.c +2
-2@@ -3331,12 +3331,12 @@ static bool __blk_mq_poll(struct blk_mq_hw_ctx *hctx, struct request *rq) ret = q->mq_ops->poll (hctx, rq->tag);if (ret >0 ) { hctx->poll_success++; -set_current_state (TASK_RUNNING ); +__set_current_state (TASK_RUNNING );return true; }if (signal_pending_state (state, current)) -set_current_state (TASK_RUNNING ); +__set_current_state (TASK_RUNNING );if (current->state ==TASK_RUNNING )return true;fs/block_dev.c +7
-2@@ -237,9 +237,12 @@ __blkdev_direct_IO_simple(struct kiocb *iocb, struct iov_iter *iter, qc =submit_bio (&bio);for (;;) { -set_current_state (TASK_UNINTERRUPTIBLE ); +__set_current_state (TASK_UNINTERRUPTIBLE ); + +smp_rmb ();if (!READ_ONCE (bio.bi_private))break ; +if (!(iocb->ki_flags &IOCB_HIPRI ) || !blk_poll (bdev_get_queue (bdev), qc))io_schedule ();@@ -403,7 +406,9 @@ __blkdev_direct_IO(struct kiocb *iocb, struct iov_iter *iter, int nr_pages)return -EIOCBQUEUED ;for (;;) { -set_current_state (TASK_UNINTERRUPTIBLE ); +__set_current_state (TASK_UNINTERRUPTIBLE ); + +smp_rmb ();if (!READ_ONCE (dio->waiter))break ;fs/iomap.c +3
-1@@ -1888,7 +1888,9 @@ iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter,return -EIOCBQUEUED ;for (;;) { -set_current_state (TASK_UNINTERRUPTIBLE ); +__set_current_state (TASK_UNINTERRUPTIBLE ); + +smp_rmb ();if (!READ_ONCE (dio->submit.waiter))break ;mm/page_io.c +0
-0
mm/page_io.c int swap_readpage(struct page *page, bool synchronous) · line 411
Quoted 8 lines
mm/page_io.c +3
-1@@ -405,7 +405,9 @@ int swap_readpage(struct page *page, bool synchronous)bio_get (bio); qc =submit_bio (bio);while (synchronous) { -set_current_state (TASK_UNINTERRUPTIBLE ); +__set_current_state (TASK_UNINTERRUPTIBLE ); + +smp_rmb ();if (!READ_ONCE (bio->bi_private))
I think any smp_rmb() should have a big fact comment explaining it.
Also to help stupid people like me that dont understand why we even need it here given the READ_ONCE below.
Jens Axboe
maintainerRe: [PATCH 04/11] block: avoid ordered task state change for polled IO
Quoted 100 lines · Christoph Hellwig
Quoted 88 lines · Jens Axboe
Ensure that writes to the dio/bio waiter field are ordered correctly. With the
smp_rmb()before theREAD_ONCE()check, we should be able to use a more relaxed ordering for the task state setting. We don't need a heavier barrier on the wakeup side after writing the waiter field, since we either going to be in the task we care about, or go throughwake_up_process()which implies a strong enough barrier.For the core poll helper, the task state setting don't need to imply any atomics, as it's the current task itself that is being modified and we're not going to sleep.
Signed-off-by Jens Axboe <[email protected]>--- block/blk-mq.c | 4 ++-- fs/block_dev.c | 9 +++++++-- fs/iomap.c | 4 +++- mm/page_io.c | 4 +++- 4 files changed, 15 insertions(+), 6 deletions(-)block/blk-mq.c +2
-2@@ -3331,12 +3331,12 @@ static bool __blk_mq_poll(struct blk_mq_hw_ctx *hctx, struct request *rq) ret = q->mq_ops->poll (hctx, rq->tag);if (ret >0 ) { hctx->poll_success++; -set_current_state (TASK_RUNNING ); +__set_current_state (TASK_RUNNING );return true; }if (signal_pending_state (state, current)) -set_current_state (TASK_RUNNING ); +__set_current_state (TASK_RUNNING );if (current->state ==TASK_RUNNING )return true;fs/block_dev.c +7
-2@@ -237,9 +237,12 @@ __blkdev_direct_IO_simple(struct kiocb *iocb, struct iov_iter *iter, qc =submit_bio (&bio);for (;;) { -set_current_state (TASK_UNINTERRUPTIBLE ); +__set_current_state (TASK_UNINTERRUPTIBLE ); + +smp_rmb ();if (!READ_ONCE (bio.bi_private))break ; +if (!(iocb->ki_flags &IOCB_HIPRI ) || !blk_poll (bdev_get_queue (bdev), qc))io_schedule ();@@ -403,7 +406,9 @@ __blkdev_direct_IO(struct kiocb *iocb, struct iov_iter *iter, int nr_pages)return -EIOCBQUEUED ;for (;;) { -set_current_state (TASK_UNINTERRUPTIBLE ); +__set_current_state (TASK_UNINTERRUPTIBLE ); + +smp_rmb ();if (!READ_ONCE (dio->waiter))break ;fs/iomap.c +3
-1@@ -1888,7 +1888,9 @@ iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter,return -EIOCBQUEUED ;for (;;) { -set_current_state (TASK_UNINTERRUPTIBLE ); +__set_current_state (TASK_UNINTERRUPTIBLE ); + +smp_rmb ();if (!READ_ONCE (dio->submit.waiter))break ;mm/page_io.c +0
-0mm/page_io.c int swap_readpage(struct page *page, bool synchronous) · line 411
Quoted 8 lines
mm/page_io.c +3
-1@@ -405,7 +405,9 @@ int swap_readpage(struct page *page, bool synchronous)bio_get (bio); qc =submit_bio (bio);while (synchronous) { -set_current_state (TASK_UNINTERRUPTIBLE ); +__set_current_state (TASK_UNINTERRUPTIBLE ); + +smp_rmb ();if (!READ_ONCE (bio->bi_private))I think any
smp_rmb()should have a big fact comment explaining it.
Quoted 2 lines
Also to help stupid people like me that dont understand why we even need it here given the
READ_ONCEbelow.
Thinking about it, I don't think we need it at all. The barrier for the task check is done on the wakeup side, the READ_ONCE() should be enough for the read below. I'll update it.
Signature · 1 line
Jens Axboe
Jens Axboe
maintainerPATCH v2 5/11 block: add polled wakeup task helper
In reply to Jens AxboeIf we're polling for IO on a device that doesn't use interrupts, then IO completion loop (and wake of task) is done by submitting task itself. If that is the case, then we don't need to enter the wake_up_process() function, we can simply mark ourselves as TASK_RUNNING.
--- fs/block_dev.c | 4 ++-- fs/iomap.c | 2 +- include/linux/blkdev.h | 13 +++++++++++++ mm/page_io.c | 2 +- 4 files changed, 17 insertions(+), 4 deletions(-)
fs/block_dev.c +2 -2
@@ -181,7 +181,7 @@ static void blkdev_bio_end_io_simple(struct bio *bio)struct task_struct *waiter = bio->bi_private;WRITE_ONCE (bio->bi_private,NULL ); -wake_up_process (waiter); +blk_wake_io_task (waiter); } static ssize_t
@@ -308,7 +308,7 @@ static void blkdev_bio_end_io(struct bio *bio)struct task_struct *waiter = dio->waiter;WRITE_ONCE (dio->waiter,NULL ); -wake_up_process (waiter); +blk_wake_io_task (waiter); } }
fs/iomap.c +1 -1
@@ -1525,7 +1525,7 @@ static void iomap_dio_bio_end_io(struct bio *bio)if (dio->wait_for_completion) {struct task_struct *waiter = dio->submit.waiter;WRITE_ONCE (dio->submit.waiter,NULL ); -wake_up_process (waiter); +blk_wake_io_task (waiter); }else if (dio->flags &IOMAP_DIO_WRITE ) {struct inode *inode =file_inode (dio->iocb->ki_filp);
include/linux/blkdev.h +13 -0
@@ -1772,4 +1772,17 @@ static inline int blkdev_issue_flush(struct block_device *bdev, gfp_t gfp_mask,#endif /* CONFIG_BLOCK */ +static inline void blk_wake_io_task (struct task_struct *waiter) +{ +/* +* If we're polling, the task itself is doing the completions. For +* that case, we don't need to signal a wakeup, it's enough to just +* mark us as RUNNING. +*/ +if (waiter == current) +__set_current_state (TASK_RUNNING ); +else +wake_up_process (waiter); +} +#endif
mm/page_io.c +1 -1
@@ -140,7 +140,7 @@ static void end_swap_bio_read(struct bio *bio)unlock_page (page);WRITE_ONCE (bio->bi_private,NULL );bio_put (bio); -wake_up_process (waiter); +blk_wake_io_task (waiter);put_task_struct (waiter); }
Signature · 1 line
2.17.1
Re: [PATCH 05/11] block: add polled wakeup task helper
Quoted 6 lines · Jens Axboe
If we're polling for IO on a device that doesn't use interrupts, then IO completion loop (and wake of task) is done by submitting task itself. If that is the case, then we don't need to enter the
wake_up_process()function, we can simply mark ourselves asTASK_RUNNING.Signed-off-by Jens Axboe <[email protected]>
Looks fine,
Jens Axboe
maintainerPATCH v2 6/11 block: have ->poll_fn() return number of entries polled
In reply to Jens AxboeWe currently only really support sync poll, ie poll with 1 IO in flight. This prepares us for supporting async poll.
Note that the returned value isn't necessarily 100% accurate. If poll races with IRQ completion, we assume that the fact that the task is now runnable means we found at least one entry. In reality it could be more than 1, or not even 1. This is fine, the caller will just need to take this into account.
--- block/blk-mq.c | 18 +++++++++--------- drivers/nvme/host/multipath.c | 4 ++-- include/linux/blkdev.h | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-)
block/blk-mq.c +9 -9
@@ -38,7 +38,7 @@#include "blk-mq-sched.h" #include "blk-rq-qos.h" -static bool blk_mq_poll (struct request_queue *q,blk_qc_t cookie); +static int blk_mq_poll (struct request_queue *q,blk_qc_t cookie);static void blk_mq_poll_stats_start (struct request_queue *q);static void blk_mq_poll_stats_fn (struct blk_stat_callback *cb);
@@ -3305,7 +3305,7 @@ static bool blk_mq_poll_hybrid_sleep(struct request_queue *q,return true; } -static bool __blk_mq_poll (struct blk_mq_hw_ctx *hctx,struct request *rq) +static int __blk_mq_poll (struct blk_mq_hw_ctx *hctx,struct request *rq) {struct request_queue *q = hctx->queue;long state;
@@ -3318,7 +3318,7 @@ static bool __blk_mq_poll(struct blk_mq_hw_ctx *hctx, struct request *rq)* straight to the busy poll loop. */ if (blk_mq_poll_hybrid_sleep (q, hctx, rq)) -return true; +return 1 ; hctx->poll_considered++;
@@ -3332,30 +3332,30 @@ static bool __blk_mq_poll(struct blk_mq_hw_ctx *hctx, struct request *rq)if (ret >0 ) { hctx->poll_success++;__set_current_state (TASK_RUNNING ); -return true; +return ret; }if (signal_pending_state (state, current))__set_current_state (TASK_RUNNING );if (current->state ==TASK_RUNNING ) -return true; +return 1 ;if (ret <0 )break ;cpu_relax (); }__set_current_state (TASK_RUNNING ); -return false; +return 0 ; } -static bool blk_mq_poll (struct request_queue *q,blk_qc_t cookie) +static int blk_mq_poll (struct request_queue *q,blk_qc_t cookie) {struct blk_mq_hw_ctx *hctx;struct request *rq;if (!test_bit (QUEUE_FLAG_POLL , &q->queue_flags)) -return false; +return 0 ; hctx = q->queue_hw_ctx[blk_qc_t_to_queue_num (cookie)];if (!blk_qc_t_is_internal (cookie))
@@ -3369,7 +3369,7 @@ static bool blk_mq_poll(struct request_queue *q, blk_qc_t cookie)* so we should be safe with just the NULL check. */ if (!rq) -return false; +return 0 ; }return __blk_mq_poll (hctx, rq);
drivers/nvme/host/multipath.c +2 -2
@@ -220,11 +220,11 @@ static blk_qc_t nvme_ns_head_make_request(struct request_queue *q,return ret; } -static bool 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) {struct nvme_ns_head *head = q->queuedata;struct nvme_ns *ns; -bool found = false; +int found =0 ;int srcu_idx; srcu_idx =srcu_read_lock (&head->srcu);
include/linux/blkdev.h +1 -1
@@ -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 bool (poll_q_fn ) (struct request_queue *q,blk_qc_t ); +typedef int (poll_q_fn ) (struct request_queue *q,blk_qc_t );struct bio_vec ;typedef int (dma_drain_needed_fn )(struct request *);
Signature · 1 line
2.17.1
Jens Axboe
maintainerPATCH v2 7/11 blk-mq: when polling for IO, look for any completion
In reply to Jens AxboeIf 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-mq.c | 69 +++++++++++++++++++++++------------------ drivers/nvme/host/pci.c | 14 ++++----- 2 files changed, 46 insertions(+), 37 deletions(-)
block/blk-mq.c +39 -30
@@ -3266,9 +3266,7 @@ static bool blk_mq_poll_hybrid_sleep(struct request_queue *q,* 0: use half of prev avg * >0: use this specific value */ -if (q->poll_nsec ==-1 ) -return false; -else if (q->poll_nsec >0 ) +if (q->poll_nsec >0 ) nsecs = q->poll_nsec;else nsecs =blk_mq_poll_nsecs (q, hctx, rq);
@@ -3305,21 +3303,36 @@ static bool blk_mq_poll_hybrid_sleep(struct request_queue *q,return true; } -static int __blk_mq_poll (struct blk_mq_hw_ctx *hctx,struct request *rq) +static bool blk_mq_poll_hybrid (struct request_queue *q, +struct blk_mq_hw_ctx *hctx,blk_qc_t cookie) +{ +struct request *rq; + +if (q->poll_nsec ==-1 ) +return false; + +if (!blk_qc_t_is_internal (cookie)) + rq =blk_mq_tag_to_rq (hctx->tags,blk_qc_t_to_tag (cookie)); +else { + rq =blk_mq_tag_to_rq (hctx->sched_tags,blk_qc_t_to_tag (cookie)); +/* +* With scheduling, if the request has completed, we'll +* get a NULL return here, as we clear the sched tag when +* that happens. The request still remains valid, like always, +* so we should be safe with just the NULL check. +*/ +if (!rq) +return false; + } + +return blk_mq_poll_hybrid_sleep (q, hctx, rq); +} + +static int __blk_mq_poll (struct blk_mq_hw_ctx *hctx) {struct request_queue *q = hctx->queue;long state; -/* -* If we sleep, have the caller restart the poll loop to reset -* the state. Like for the other success return cases, the -* caller is responsible for checking if the IO completed. If -* the IO isn't complete, we'll get called again and will go -* straight to the busy poll loop. -*/ -if (blk_mq_poll_hybrid_sleep (q, hctx, rq)) -return 1 ; - hctx->poll_considered++; state = current->state;
@@ -3328,7 +3341,7 @@ static int __blk_mq_poll(struct blk_mq_hw_ctx *hctx, struct request *rq) hctx->poll_invoked++; - ret = q->mq_ops->poll (hctx, rq->tag); + ret = q->mq_ops->poll (hctx,-1U );if (ret >0 ) { hctx->poll_success++;__set_current_state (TASK_RUNNING );
@@ -3352,27 +3365,23 @@ static int __blk_mq_poll(struct blk_mq_hw_ctx *hctx, struct request *rq)static int blk_mq_poll (struct request_queue *q,blk_qc_t cookie) {struct blk_mq_hw_ctx *hctx; -struct request *rq;if (!test_bit (QUEUE_FLAG_POLL , &q->queue_flags))return 0 ; hctx = q->queue_hw_ctx[blk_qc_t_to_queue_num (cookie)]; -if (!blk_qc_t_is_internal (cookie)) - rq =blk_mq_tag_to_rq (hctx->tags,blk_qc_t_to_tag (cookie)); -else { - rq =blk_mq_tag_to_rq (hctx->sched_tags,blk_qc_t_to_tag (cookie)); -/* -* With scheduling, if the request has completed, we'll -* get a NULL return here, as we clear the sched tag when -* that happens. The request still remains valid, like always, -* so we should be safe with just the NULL check. -*/ -if (!rq) -return 0 ; - } -return __blk_mq_poll (hctx, rq); +/* +* If we sleep, have the caller restart the poll loop to reset +* the state. Like for the other success return cases, the +* caller is responsible for checking if the IO completed. If +* the IO isn't complete, we'll get called again and will go +* straight to the busy poll loop. +*/ +if (blk_mq_poll_hybrid (q, hctx, cookie)) +return 1 ; + +return __blk_mq_poll (hctx); }unsigned int blk_mq_rq_cpu (struct request *rq)
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 ;
Signature · 1 line
2.17.1
Re: [PATCH 07/11] blk-mq: when polling for IO, look for any completion
Quoted 5 lines · Jens Axboe
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.
Well, if we always put -1 in we can as well remove the argument, especially given that you change the prototype in the patch before this one.
Still digesting the rest of this.
Jens Axboe
maintainerRe: [PATCH 07/11] blk-mq: when polling for IO, look for any completion
Quoted 7 lines · Christoph Hellwig
Quoted 5 lines · Jens Axboe
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.
Quoted 5 lines
Well, if we always put -1 in we can as well remove the argument, especially given that you change the prototype in the patch before this one.
Still digesting the rest of this.
We can remove the argument for the mq_ops->poll, as long as we retain it for the q->poll. For the latter, we need it for destination lookup.
Signature · 1 line
Jens Axboe
Jens Axboe
maintainerRe: [PATCH 07/11] blk-mq: when polling for IO, look for any completion
Quoted 12 lines · Jens Axboe
Quoted 11 lines · Christoph Hellwig
Quoted 5 lines · Jens Axboe
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.Well, if we always put -1 in we can as well remove the argument, especially given that you change the prototype in the patch before this one.
Quoted 5 lines
Quoted 1 line
Still digesting the rest of this.
We can remove the argument for the
mq_ops->poll, as long as we retain it for theq->poll. For the latter, we need it for destination lookup.
Alright, went over this. I killed the nvme-fc poll implementation, it isn't very useful at all. I fixed up the RDMA polling to work fine with this, and then I killed the parameter. It's all in my mq-perf branch, I believe now it should be palatable and good to go.
Meat of it:
http://git.kernel.dk/cgit/linux-block/commit/?h=mq-perf&id=fee7a35981777e5df508baaacebff50147aad966
and removal:
http://git.kernel.dk/cgit/linux-block/commit/?h=mq-perf&id=bec293e546c6c7b01a276998a287427ba7e8b775
Signature · 1 line
Jens Axboe
Jens Axboe
maintainerPATCH v2 8/11 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 | 10 +++++----- drivers/nvme/host/multipath.c | 4 ++-- drivers/nvme/target/io-cmd-bdev.c | 2 +- fs/block_dev.c | 4 ++-- fs/direct-io.c | 2 +- fs/iomap.c | 2 +- include/linux/blkdev.h | 4 ++-- mm/page_io.c | 2 +- 9 files changed, 17 insertions(+), 17 deletions(-)
block/blk-core.c +2 -2
@@ -1284,14 +1284,14 @@ blk_qc_t submit_bio(struct bio *bio) }EXPORT_SYMBOL (submit_bio); -bool blk_poll (struct request_queue *q,blk_qc_t cookie) +bool 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 +5 -5
@@ -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);
@@ -3328,7 +3328,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 blk_mq_hw_ctx *hctx) +static int __blk_mq_poll (struct blk_mq_hw_ctx *hctx,bool spin) {struct request_queue *q = hctx->queue;long state;
@@ -3353,7 +3353,7 @@ static int __blk_mq_poll(struct blk_mq_hw_ctx *hctx)if (current->state ==TASK_RUNNING )return 1 ; -if (ret <0 ) +if (ret <0 || !spin)break ;cpu_relax (); }
@@ -3362,7 +3362,7 @@ static int __blk_mq_poll(struct blk_mq_hw_ctx *hctx)return 0 ; } -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;
@@ -3381,7 +3381,7 @@ static int blk_mq_poll(struct request_queue *q, blk_qc_t cookie)if (blk_mq_poll_hybrid (q, hctx, cookie))return 1 ; -return __blk_mq_poll (hctx); +return __blk_mq_poll (hctx, spin); }unsigned int blk_mq_rq_cpu (struct request *rq)
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; }
drivers/nvme/target/io-cmd-bdev.c +1 -1
@@ -116,7 +116,7 @@ static void nvmet_bdev_execute_rw(struct nvmet_req *req) cookie =submit_bio (bio); -blk_poll (bdev_get_queue (req->ns->bdev), cookie); +blk_poll (bdev_get_queue (req->ns->bdev), cookie, true); }static void nvmet_bdev_execute_flush (struct nvmet_req *req)
fs/block_dev.c +2 -2
@@ -244,7 +244,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 );
@@ -413,7 +413,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
@@ -1897,7 +1897,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 *);
@@ -868,7 +868,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); +bool 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
@@ -411,7 +411,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
Jens Axboe
maintainerPATCH v2 9/11 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
@@ -3336,7 +3336,7 @@ static int __blk_mq_poll(struct blk_mq_hw_ctx *hctx, bool spin) hctx->poll_considered++; state = current->state; -while (!need_resched ()) { + do { int ret; hctx->poll_invoked++;
@@ -3356,7 +3356,7 @@ static int __blk_mq_poll(struct blk_mq_hw_ctx *hctx, 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 v2 10/11 block: for async O_DIRECT, mark us as polling if asked to
In reply to Jens AxboeInherit the iocb IOCB_HIPRI flag, and pass on REQ_HIPRI for those kinds of requests.
--- fs/block_dev.c | 3 +++ 1 file changed, 3 insertions(+)
fs/block_dev.c +3 -0
@@ -386,6 +386,9 @@ __blkdev_direct_IO(struct kiocb *iocb, struct iov_iter *iter, int nr_pages) nr_pages =iov_iter_npages (iter,BIO_MAX_PAGES );if (!nr_pages) { +if (iocb->ki_flags &IOCB_HIPRI ) + bio->bi_opf |=REQ_HIPRI ; + qc =submit_bio (bio);break ; }
Signature · 1 line
2.17.1
Re: [PATCH 10/11] block: for async O_DIRECT, mark us as polling if asked to
Quoted 2 lines · Jens Axboe
Inherit the iocb
IOCB_HIPRIflag, and pass onREQ_HIPRIfor those kinds of requests.
Looks fine,
Re: [PATCH 10/11] block: for async O_DIRECT, mark us as polling if asked to
Quoted 5 lines · Christoph Hellwig
Quoted 2 lines · Jens Axboe
Inherit the iocb
IOCB_HIPRIflag, and pass onREQ_HIPRIfor those kinds of requests.Looks fine,
Actually. Who is going to poll for them? With the separate poll queue this means they are now on the poll queue where there are no interrupts, but aio won't end up calling blk_poll..
Jens Axboe
maintainerRe: [PATCH 10/11] block: for async O_DIRECT, mark us as polling if asked to
Quoted 10 lines · Christoph Hellwig
Quoted 5 lines · Christoph Hellwig
Quoted 2 lines · Jens Axboe
Inherit the iocb
IOCB_HIPRIflag, and pass onREQ_HIPRIfor those kinds of requests.Looks fine,
Actually. Who is going to poll for them? With the separate poll queue this means they are now on the poll queue where there are no interrupts, but aio won't end up calling
blk_poll..
It's a prep patch, I have patches that enable polling for libaio. So aio will call blk_poll.
Signature · 1 line
Jens Axboe
Jens Axboe
maintainerPATCH v2 11/11 block: don't plug for aio/O_DIRECT HIPRI IO
In reply to Jens AxboeThose will go straight to issue inside blk-mq, so don't bother setting up a block plug for them.
--- fs/block_dev.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-)
fs/block_dev.c +10 -2
@@ -356,7 +356,13 @@ __blkdev_direct_IO(struct kiocb *iocb, struct iov_iter *iter, int nr_pages) dio->multi_bio = false; dio->should_dirty = is_read &&iter_is_iovec (iter); -blk_start_plug (&plug); +/* +* Don't plug for HIPRI/polled IO, as those should go straight +* to issue +*/ +if (!(iocb->ki_flags &IOCB_HIPRI )) +blk_start_plug (&plug); +for (;;) {bio_set_dev (bio, bdev); bio->bi_iter.bi_sector = pos >>9 ;
@@ -403,7 +409,9 @@ __blkdev_direct_IO(struct kiocb *iocb, struct iov_iter *iter, int nr_pages)submit_bio (bio); bio =bio_alloc (GFP_KERNEL , nr_pages); } -blk_finish_plug (&plug); + +if (!(iocb->ki_flags &IOCB_HIPRI )) +blk_finish_plug (&plug);if (!is_sync)return -EIOCBQUEUED ;
Signature · 1 line
2.17.1
Re: [PATCH 11/11] block: don't plug for aio/O_DIRECT HIPRI IO
Quoted 2 lines · Jens Axboe
Those will go straight to issue inside blk-mq, so don't bother setting up a block plug for them.
Looks fine,