The memset() used is measurably slower in targeted benchmarks, wasting
about 1% of the total runtime, or 50% of the (later) hot path cached
bio alloc. Get rid of it and fill in the bio manually.
Signed-off-by: Jens Axboe <axboe@kernel.dk>
---
block/bio.c | 29 ++++++++++++++++++++++++++++-
1 file changed, 28 insertions(+), 1 deletion(-)
If this kiocb can safely use the polled bio allocation cache, then this
flag must be set. Generally this can be set for polled IO, where we will
not see IRQ completions of the request.
Signed-off-by: Jens Axboe <axboe@kernel.dk>
---
include/linux/fs.h | 2 ++
1 file changed, 2 insertions(+)
The bio alloc cache relies on the fact that a polled bio will complete
in process context, clear the cacheable flag if we disable polling
for a given bio.
Signed-off-by: Jens Axboe <axboe@kernel.dk>
---
block/blk-core.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
@@ -833,8 +833,11 @@ static noinline_for_stack bool submit_bio_checks(struct bio *bio)}}-if(!test_bit(QUEUE_FLAG_POLL,&q->queue_flags))+if(!test_bit(QUEUE_FLAG_POLL,&q->queue_flags)){+/* can't support alloc cache if we turn off polling */+bio_clear_flag(bio,BIO_PERCPU_CACHE);bio->bi_opf&=~REQ_HIPRI;+}switch(bio_op(bio)){caseREQ_OP_DISCARD:
Add a per-cpu bio_set cache for bio allocations, enabling us to quickly
recycle them instead of going through the slab allocator. This cache
isn't IRQ safe, and hence is only really suitable for polled IO.
Very simple - keeps a count of bio's in the cache, and maintains a max
of 512 with a slack of 64. If we get above max + slack, we drop slack
number of bio's.
Signed-off-by: Jens Axboe <axboe@kernel.dk>
---
block/bio.c | 135 +++++++++++++++++++++++++++++++++----
include/linux/bio.h | 13 ++++
include/linux/blk_types.h | 1 +
include/linux/cpuhotplug.h | 1 +
4 files changed, 136 insertions(+), 14 deletions(-)
@@ -628,16 +682,23 @@ void guard_bio_eod(struct bio *bio)**/voidbio_put(structbio*bio){-if(!bio_flagged(bio,BIO_REFFED))-bio_free(bio);-else{+if(unlikely(bio_flagged(bio,BIO_REFFED))){BIO_BUG_ON(!atomic_read(&bio->__bi_cnt));--/*-*lastputfreesit-*/if(atomic_dec_and_test(&bio->__bi_cnt))-bio_free(bio);+return;+}++if(bio_flagged(bio,BIO_PERCPU_CACHE)){+structbio_alloc_cache*cache;++bio_uninit(bio);+cache=per_cpu_ptr(bio->bi_pool->cache,get_cpu());+bio_list_add_head(&cache->free_list,bio);+if(++cache->nr>ALLOC_CACHE_MAX+ALLOC_CACHE_SLACK)+bio_alloc_cache_prune(cache,ALLOC_CACHE_SLACK);+put_cpu();+}else{+bio_free(bio);}}EXPORT_SYMBOL(bio_put);
@@ -1529,6 +1590,7 @@ int biovec_init_pool(mempool_t *pool, int pool_entries)*/voidbioset_exit(structbio_set*bs){+bio_alloc_cache_destroy(bs);if(bs->rescue_workqueue)destroy_workqueue(bs->rescue_workqueue);bs->rescue_workqueue=NULL;
@@ -1590,12 +1652,18 @@ int bioset_init(struct bio_set *bs,biovec_init_pool(&bs->bvec_pool,pool_size))gotobad;-if(!(flags&BIOSET_NEED_RESCUER))-return0;--bs->rescue_workqueue=alloc_workqueue("bioset",WQ_MEM_RECLAIM,0);-if(!bs->rescue_workqueue)-gotobad;+if(flags&BIOSET_NEED_RESCUER){+bs->rescue_workqueue=alloc_workqueue("bioset",+WQ_MEM_RECLAIM,0);+if(!bs->rescue_workqueue)+gotobad;+}+if(flags&BIOSET_PERCPU_CACHE){+bs->cache=alloc_percpu(structbio_alloc_cache);+if(!bs->cache)+gotobad;+cpuhp_state_add_instance_nocalls(CPUHP_BIO_DEAD,&bs->cpuhp_dead);+}return0;bad:
@@ -401,6 +401,7 @@ static inline struct bio *bio_next_split(struct bio *bio, int sectors,enum{BIOSET_NEED_BVECS=BIT(0),BIOSET_NEED_RESCUER=BIT(1),+BIOSET_PERCPU_CACHE=BIT(2),};externintbioset_init(structbio_set*,unsignedint,unsignedint,intflags);externvoidbioset_exit(structbio_set*);
@@ -301,6 +301,7 @@ enum {BIO_TRACKED,/* set if bio goes through the rq_qos path */BIO_REMAPPED,BIO_ZONE_WRITE_LOCKED,/* Owns a zoned device zone write lock */+BIO_PERCPU_CACHE,/* can participate in per-cpu alloc cache */BIO_FLAG_LAST};
Mark polled IO as being safe for dipping into the bio allocation
cache, in case the targeted bio_set has it enabled.
This brings an IOPOLL gen2 Optane QD=128 workload from ~3.0M IOPS to
~3.3M IOPS.
Signed-off-by: Jens Axboe <axboe@kernel.dk>
---
fs/io_uring.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
From: Christoph Hellwig <hch@lst.de>
Use bio_alloc_kiocb to dip into the percpu cache of bios when the
caller asks for it.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
---
fs/block_dev.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
From: Christoph Hellwig <hch@infradead.org> Date: 2021-08-12 16:21:49
On Thu, Aug 12, 2021 at 09:41:45AM -0600, Jens Axboe wrote:
If this kiocb can safely use the polled bio allocation cache, then this
flag must be set. Generally this can be set for polled IO, where we will
not see IRQ completions of the request.
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Looks good,
Reviewed-by: Christoph Hellwig <hch@lst.de>
If I understand the cpu hotplug state machine we should not get any new
cpu down callbacks after cpuhp_state_remove_instance_nocalls returned,
so what do we need the preempt disable here for?
+ /*
+ * Hot un-plug notifier for the per-cpu cache, if used
+ */
+ struct hlist_node cpuhp_dead;
Nit, even if we don't need the cpu up notifaction the node actually
provides both. So I'd reword the comment drop the _dead from the
member name.
Otherwise looks good:
Reviewed-by: Christoph Hellwig <hch@lst.de>
From: Christoph Hellwig <hch@infradead.org> Date: 2021-08-12 16:35:25
On Thu, Aug 12, 2021 at 09:41:47AM -0600, Jens Axboe wrote:
The bio alloc cache relies on the fact that a polled bio will complete
in process context, clear the cacheable flag if we disable polling
for a given bio.
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Looks good,
Reviewed-by: Christoph Hellwig <hch@lst.de>
If I understand the cpu hotplug state machine we should not get any new
cpu down callbacks after cpuhp_state_remove_instance_nocalls returned,
so what do we need the preempt disable here for?
I don't think we strictly need it. I can kill it.
quoted
+ /*
+ * Hot un-plug notifier for the per-cpu cache, if used
+ */
+ struct hlist_node cpuhp_dead;
Nit, even if we don't need the cpu up notifaction the node actually
provides both. So I'd reword the comment drop the _dead from the
member name.
Right, but we only sign up for the down call.
--
Jens Axboe
From: Christoph Hellwig <hch@infradead.org> Date: 2021-08-12 16:40:24
On Thu, Aug 12, 2021 at 09:41:48AM -0600, Jens Axboe wrote:
Mark polled IO as being safe for dipping into the bio allocation
cache, in case the targeted bio_set has it enabled.
This brings an IOPOLL gen2 Optane QD=128 workload from ~3.0M IOPS to
~3.3M IOPS.
Looks good,
Reviewed-by: Christoph Hellwig <hch@lst.de>
Didn't the cover letter say 3.5M+ IOPS, though?
On Thu, Aug 12, 2021 at 09:41:48AM -0600, Jens Axboe wrote:
quoted
Mark polled IO as being safe for dipping into the bio allocation
cache, in case the targeted bio_set has it enabled.
This brings an IOPOLL gen2 Optane QD=128 workload from ~3.0M IOPS to
~3.3M IOPS.
Looks good,
Reviewed-by: Christoph Hellwig <hch@lst.de>
Didn't the cover letter say 3.5M+ IOPS, though?
It does indeed, we've had some recent improvements so the range is now
more in the 3.2M -> 3.5M IOPS with the cache. Didn't update it, as it's
still roughly the same 10% bump.
--
Jens Axboe
From: Keith Busch <kbusch@kernel.org> Date: 2021-08-12 17:31:49
On Thu, Aug 12, 2021 at 09:41:47AM -0600, Jens Axboe wrote:
- if (!test_bit(QUEUE_FLAG_POLL, &q->queue_flags))
+ if (!test_bit(QUEUE_FLAG_POLL, &q->queue_flags)) {
+ /* can't support alloc cache if we turn off polling */
+ bio_clear_flag(bio, BIO_PERCPU_CACHE);
bio->bi_opf &= ~REQ_HIPRI;
+ }
It looks like you should also clear BIO_PERCPU_CACHE if this bio gets
split in blk_bio_segment_split().
On Thu, Aug 12, 2021 at 09:41:47AM -0600, Jens Axboe wrote:
quoted
- if (!test_bit(QUEUE_FLAG_POLL, &q->queue_flags))
+ if (!test_bit(QUEUE_FLAG_POLL, &q->queue_flags)) {
+ /* can't support alloc cache if we turn off polling */
+ bio_clear_flag(bio, BIO_PERCPU_CACHE);
bio->bi_opf &= ~REQ_HIPRI;
+ }
It looks like you should also clear BIO_PERCPU_CACHE if this bio gets
split in blk_bio_segment_split().
Indeed. Wonder if we should make that a small helper, as any clear of
REQ_HIPRI should clear BIO_PERCPU_CACHE as well.
@@ -821,11 +821,8 @@ static noinline_for_stack bool submit_bio_checks(struct bio *bio)}}-if(!test_bit(QUEUE_FLAG_POLL,&q->queue_flags)){-/* can't support alloc cache if we turn off polling */-bio_clear_flag(bio,BIO_PERCPU_CACHE);-bio->bi_opf&=~REQ_HIPRI;-}+if(!test_bit(QUEUE_FLAG_POLL,&q->queue_flags))+bio_clear_hipri(bio);switch(bio_op(bio)){caseREQ_OP_DISCARD:
@@ -821,11 +821,8 @@ static noinline_for_stack bool submit_bio_checks(struct bio *bio)}}-if(!test_bit(QUEUE_FLAG_POLL,&q->queue_flags)){-/* can't support alloc cache if we turn off polling */-bio_clear_flag(bio,BIO_PERCPU_CACHE);-bio->bi_opf&=~REQ_HIPRI;-}+if(!test_bit(QUEUE_FLAG_POLL,&q->queue_flags))+bio_clear_hipri(bio);
Since BIO_PERCPU_CACHE doesn't work without REQ_HIRPI, should this check
look more like this?
if (!test_bit(QUEUE_FLAG_POLL, &q->queue_flags))
bio->bi_opf &= ~REQ_HIPRI;
if (!(bio->bi_opf & REQ_HIPRI))
bio_clear_flag(bio, BIO_PERCPU_CACHE);
I realise the only BIO_PERCPU_CACHE user in this series never sets it
without REQ_HIPRI, but it looks like a problem waiting to happen if
nothing enforces this pairing: someone could set the CACHE flag on a
QUEUE_FLAG_POLL enabled queue without setting HIPRI and get the wrong
bio_put() action.
@@ -821,11 +821,8 @@ static noinline_for_stack bool submit_bio_checks(struct bio *bio)}}-if(!test_bit(QUEUE_FLAG_POLL,&q->queue_flags)){-/* can't support alloc cache if we turn off polling */-bio_clear_flag(bio,BIO_PERCPU_CACHE);-bio->bi_opf&=~REQ_HIPRI;-}+if(!test_bit(QUEUE_FLAG_POLL,&q->queue_flags))+bio_clear_hipri(bio);
Since BIO_PERCPU_CACHE doesn't work without REQ_HIRPI, should this check
look more like this?
if (!test_bit(QUEUE_FLAG_POLL, &q->queue_flags))
bio->bi_opf &= ~REQ_HIPRI;
if (!(bio->bi_opf & REQ_HIPRI))
bio_clear_flag(bio, BIO_PERCPU_CACHE);
I realise the only BIO_PERCPU_CACHE user in this series never sets it
without REQ_HIPRI, but it looks like a problem waiting to happen if
nothing enforces this pairing: someone could set the CACHE flag on a
QUEUE_FLAG_POLL enabled queue without setting HIPRI and get the wrong
bio_put() action.
I'd rather turn that into a WARN_ON or similar. But probably better to
do that on the freeing side, honestly. That'll be the most reliable way,
but a shame to add cycles to the hot path...
--
Jens Axboe
@@ -821,11 +821,8 @@ static noinline_for_stack bool submit_bio_checks(struct bio *bio)}}-if(!test_bit(QUEUE_FLAG_POLL,&q->queue_flags)){-/* can't support alloc cache if we turn off polling */-bio_clear_flag(bio,BIO_PERCPU_CACHE);-bio->bi_opf&=~REQ_HIPRI;-}+if(!test_bit(QUEUE_FLAG_POLL,&q->queue_flags))+bio_clear_hipri(bio);
Since BIO_PERCPU_CACHE doesn't work without REQ_HIRPI, should this check
look more like this?
if (!test_bit(QUEUE_FLAG_POLL, &q->queue_flags))
bio->bi_opf &= ~REQ_HIPRI;
if (!(bio->bi_opf & REQ_HIPRI))
bio_clear_flag(bio, BIO_PERCPU_CACHE);
I realise the only BIO_PERCPU_CACHE user in this series never sets it
without REQ_HIPRI, but it looks like a problem waiting to happen if
nothing enforces this pairing: someone could set the CACHE flag on a
QUEUE_FLAG_POLL enabled queue without setting HIPRI and get the wrong
bio_put() action.
I'd rather turn that into a WARN_ON or similar. But probably better to
do that on the freeing side, honestly. That'll be the most reliable way,
but a shame to add cycles to the hot path...
Yeah, it is a coding error if that happened, so a WARN sounds okay. I
also don't like adding these kinds of checks, so please feel free to not
include it if you think the usage is clear enough.
@@ -821,11 +821,8 @@ static noinline_for_stack bool submit_bio_checks(struct bio *bio)}}-if(!test_bit(QUEUE_FLAG_POLL,&q->queue_flags)){-/* can't support alloc cache if we turn off polling */-bio_clear_flag(bio,BIO_PERCPU_CACHE);-bio->bi_opf&=~REQ_HIPRI;-}+if(!test_bit(QUEUE_FLAG_POLL,&q->queue_flags))+bio_clear_hipri(bio);
Since BIO_PERCPU_CACHE doesn't work without REQ_HIRPI, should this check
look more like this?
if (!test_bit(QUEUE_FLAG_POLL, &q->queue_flags))
bio->bi_opf &= ~REQ_HIPRI;
if (!(bio->bi_opf & REQ_HIPRI))
bio_clear_flag(bio, BIO_PERCPU_CACHE);
I realise the only BIO_PERCPU_CACHE user in this series never sets it
without REQ_HIPRI, but it looks like a problem waiting to happen if
nothing enforces this pairing: someone could set the CACHE flag on a
QUEUE_FLAG_POLL enabled queue without setting HIPRI and get the wrong
bio_put() action.
I'd rather turn that into a WARN_ON or similar. But probably better to
do that on the freeing side, honestly. That'll be the most reliable way,
but a shame to add cycles to the hot path...
Yeah, it is a coding error if that happened, so a WARN sounds okay. I
also don't like adding these kinds of checks, so please feel free to not
include it if you think the usage is clear enough.
Just have to watch for new additions of IOCB_ALLOC_CACHE, which
thankfully shouldn't be too bad.
--
Jens Axboe