* [PATCH] block: plug attempts to batch allocate tags multiple times [not found] <CGME20250901082648epcas5p18f81021213f2b8a050efa25f76e0fb54@epcas5p1.samsung.com> @ 2025-09-01 8:22 ` Xue He 2025-09-02 8:47 ` Yu Kuai 0 siblings, 1 reply; 10+ messages in thread From: Xue He @ 2025-09-01 8:22 UTC (permalink / raw) To: axboe; +Cc: linux-block, linux-kernel, hexue From: hexue <xue01.he@samsung.com> In the existing plug mechanism, tags are allocated in batches based on the number of requests. However, testing has shown that the plug only attempts batch allocation of tags once at the beginning of a batch of I/O operations. Since the tag_mask does not always have enough available tags to satisfy the requested number, a full batch allocation is not guaranteed to succeed each time. The remaining tags are then allocated individually (occurs frequently), leading to multiple single-tag allocation overheads. This patch aims to allow the remaining I/O operations to retry batch allocation of tags, reducing the overhead caused by multiple individual tag allocations. ------------------------------------------------------------------------ test result During testing of the PCIe Gen4 SSD Samsung PM9A3, the perf tool observed CPU improvements. The CPU usage of the original function _blk_mq_alloc_requests function was 1.39%, which decreased to 0.82% after modification. Additionally, performance variations were observed on different devices. workload:randread blocksize:4k thread:1 ------------------------------------------------------------------------ PCIe Gen3 SSD PCIe Gen4 SSD PCIe Gen5 SSD native kernel 553k iops 633k iops 793k iops modified 553k iops 635k iops 801k iops with Optane SSDs, the performance like two device one thread cmd :sudo taskset -c 0 ./t/io_uring -b512 -d128 -c32 -s32 -p1 -F1 -B1 -n1 -r4 /dev/nvme0n1 /dev/nvme1n1 base: 6.4 Million IOPS patch: 6.49 Million IOPS two device two thread cmd: sudo taskset -c 0 ./t/io_uring -b512 -d128 -c32 -s32 -p1 -F1 -B1 -n1 -r4 /dev/nvme0n1 /dev/nvme1n1 base: 7.34 Million IOPS patch: 7.48 Million IOPS ------------------------------------------------------------------------- Signed-off-by: hexue <xue01.he@samsung.com> --- block/blk-mq.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/block/blk-mq.c b/block/blk-mq.c index b67d6c02eceb..1fb280764b76 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -587,9 +587,9 @@ static struct request *blk_mq_rq_cache_fill(struct request_queue *q, if (blk_queue_enter(q, flags)) return NULL; - plug->nr_ios = 1; - rq = __blk_mq_alloc_requests(&data); + plug->nr_ios = data.nr_tags; + if (unlikely(!rq)) blk_queue_exit(q); return rq; @@ -3034,11 +3034,13 @@ static struct request *blk_mq_get_new_requests(struct request_queue *q, if (plug) { data.nr_tags = plug->nr_ios; - plug->nr_ios = 1; data.cached_rqs = &plug->cached_rqs; } rq = __blk_mq_alloc_requests(&data); + if (plug) + plug->nr_ios = data.nr_tags; + if (unlikely(!rq)) rq_qos_cleanup(q, bio); return rq; -- 2.34.1 ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] block: plug attempts to batch allocate tags multiple times 2025-09-01 8:22 ` [PATCH] block: plug attempts to batch allocate tags multiple times Xue He @ 2025-09-02 8:47 ` Yu Kuai [not found] ` <CGME20250903084608epcas5p19a0ad4f0d1bad27889426e525d0c4598@epcas5p1.samsung.com> 0 siblings, 1 reply; 10+ messages in thread From: Yu Kuai @ 2025-09-02 8:47 UTC (permalink / raw) To: Xue He, axboe; +Cc: linux-block, linux-kernel, yukuai (C) Hi, 在 2025/09/01 16:22, Xue He 写道: > From: hexue <xue01.he@samsung.com> > > In the existing plug mechanism, tags are allocated in batches based on > the number of requests. However, testing has shown that the plug only > attempts batch allocation of tags once at the beginning of a batch of > I/O operations. Since the tag_mask does not always have enough available > tags to satisfy the requested number, a full batch allocation is not > guaranteed to succeed each time. The remaining tags are then allocated > individually (occurs frequently), leading to multiple single-tag > allocation overheads. > > This patch aims to allow the remaining I/O operations to retry batch > allocation of tags, reducing the overhead caused by multiple > individual tag allocations. > > ------------------------------------------------------------------------ > test result > During testing of the PCIe Gen4 SSD Samsung PM9A3, the perf tool > observed CPU improvements. The CPU usage of the original function > _blk_mq_alloc_requests function was 1.39%, which decreased to 0.82% > after modification. > > Additionally, performance variations were observed on different devices. > workload:randread > blocksize:4k > thread:1 > ------------------------------------------------------------------------ > PCIe Gen3 SSD PCIe Gen4 SSD PCIe Gen5 SSD > native kernel 553k iops 633k iops 793k iops > modified 553k iops 635k iops 801k iops > > with Optane SSDs, the performance like > two device one thread > cmd :sudo taskset -c 0 ./t/io_uring -b512 -d128 -c32 -s32 -p1 -F1 -B1 > -n1 -r4 /dev/nvme0n1 /dev/nvme1n1 > How many hw_queues and how many tags in each hw_queues in your nvme? I feel it's unlikely that tags can be exhausted, usually cpu will become bottleneck first. > base: 6.4 Million IOPS > patch: 6.49 Million IOPS > > two device two thread > cmd: sudo taskset -c 0 ./t/io_uring -b512 -d128 -c32 -s32 -p1 -F1 -B1 > -n1 -r4 /dev/nvme0n1 /dev/nvme1n1 > > base: 7.34 Million IOPS > patch: 7.48 Million IOPS > ------------------------------------------------------------------------- > > Signed-off-by: hexue <xue01.he@samsung.com> > --- > block/blk-mq.c | 8 +++++--- > 1 file changed, 5 insertions(+), 3 deletions(-) > > diff --git a/block/blk-mq.c b/block/blk-mq.c > index b67d6c02eceb..1fb280764b76 100644 > --- a/block/blk-mq.c > +++ b/block/blk-mq.c > @@ -587,9 +587,9 @@ static struct request *blk_mq_rq_cache_fill(struct request_queue *q, > if (blk_queue_enter(q, flags)) > return NULL; > > - plug->nr_ios = 1; > - > rq = __blk_mq_alloc_requests(&data); > + plug->nr_ios = data.nr_tags; > + > if (unlikely(!rq)) > blk_queue_exit(q); > return rq; > @@ -3034,11 +3034,13 @@ static struct request *blk_mq_get_new_requests(struct request_queue *q, > > if (plug) { > data.nr_tags = plug->nr_ios; > - plug->nr_ios = 1; > data.cached_rqs = &plug->cached_rqs; > } > > rq = __blk_mq_alloc_requests(&data); > + if (plug) > + plug->nr_ios = data.nr_tags; > + > if (unlikely(!rq)) > rq_qos_cleanup(q, bio); > return rq; > In __blk_mq_alloc_requests(), if __blk_mq_alloc_requests_batch() failed, data->nr_tags is set to 1, so plug->nr_ios = data.nr_tags will still set plug->nr_ios to 1 in this case. What am I missing? Thanks, Kuai ^ permalink raw reply [flat|nested] 10+ messages in thread
[parent not found: <CGME20250903084608epcas5p19a0ad4f0d1bad27889426e525d0c4598@epcas5p1.samsung.com>]
* Re: [PATCH] block: plug attempts to batch allocate tags multiple times [not found] ` <CGME20250903084608epcas5p19a0ad4f0d1bad27889426e525d0c4598@epcas5p1.samsung.com> @ 2025-09-03 8:41 ` Xue He 2025-09-03 9:35 ` Yu Kuai 0 siblings, 1 reply; 10+ messages in thread From: Xue He @ 2025-09-03 8:41 UTC (permalink / raw) To: yukuai1, axboe; +Cc: linux-block, linux-kernel, yukuai3 On 2025/09/02 08:47 AM, Yu Kuai wrote: >On 2025/09/01 16:22, Xue He wrote: ...... >> This patch aims to allow the remaining I/O operations to retry batch >> allocation of tags, reducing the overhead caused by multiple >> individual tag allocations. >> >> ------------------------------------------------------------------------ >> test result >> During testing of the PCIe Gen4 SSD Samsung PM9A3, the perf tool >> observed CPU improvements. The CPU usage of the original function >> _blk_mq_alloc_requests function was 1.39%, which decreased to 0.82% >> after modification. >> >> Additionally, performance variations were observed on different devices. >> workload:randread >> blocksize:4k >> thread:1 >> ------------------------------------------------------------------------ >> PCIe Gen3 SSD PCIe Gen4 SSD PCIe Gen5 SSD >> native kernel 553k iops 633k iops 793k iops >> modified 553k iops 635k iops 801k iops >> >> with Optane SSDs, the performance like >> two device one thread >> cmd :sudo taskset -c 0 ./t/io_uring -b512 -d128 -c32 -s32 -p1 -F1 -B1 >> -n1 -r4 /dev/nvme0n1 /dev/nvme1n1 >> > >How many hw_queues and how many tags in each hw_queues in your nvme? >I feel it's unlikely that tags can be exhausted, usually cpu will become >bottleneck first. the information of my nvme like this: number of CPU: 16 memory: 16G nvme nvme0: 16/0/16 default/read/poll queue cat /sys/class/nvme/nvme0/nvme0n1/queue/nr_requests 1023 In more precise terms, I think it is not that the tags are fully exhausted, but rather that after scanning the bitmap for free bits, the remaining contiguous bits are nsufficient to meet the requirement (have but not enough). The specific function involved is __sbitmap_queue_get_batch in lib/sbitmap.c. get_mask = ((1UL << nr_tags) - 1) << nr; if (nr_tags > 1) { printk("before %ld\n", get_mask); } while (!atomic_long_try_cmpxchg(ptr, &val, get_mask | val)) ; get_mask = (get_mask & ~val) >> nr; where during the batch acquisition of contiguous free bits, an atomic operation is performed, resulting in the actual tag_mask obtained differing from the originally requested one. Am I missing something? >> base: 6.4 Million IOPS >> patch: 6.49 Million IOPS >> >> two device two thread >> cmd: sudo taskset -c 0 ./t/io_uring -b512 -d128 -c32 -s32 -p1 -F1 -B1 >> -n1 -r4 /dev/nvme0n1 /dev/nvme1n1 >> >> base: 7.34 Million IOPS >> patch: 7.48 Million IOPS >> ------------------------------------------------------------------------- >> >> Signed-off-by: hexue <xue01.he@samsung.com> >> --- >> block/blk-mq.c | 8 +++++--- >> 1 file changed, 5 insertions(+), 3 deletions(-) >> >> diff --git a/block/blk-mq.c b/block/blk-mq.c >> index b67d6c02eceb..1fb280764b76 100644 >> --- a/block/blk-mq.c >> +++ b/block/blk-mq.c >> @@ -587,9 +587,9 @@ static struct request *blk_mq_rq_cache_fill(struct request_queue *q, >> if (blk_queue_enter(q, flags)) >> return NULL; >> >> - plug->nr_ios = 1; >> - >> rq = __blk_mq_alloc_requests(&data); >> + plug->nr_ios = data.nr_tags; >> + >> if (unlikely(!rq)) >> blk_queue_exit(q); >> return rq; >> @@ -3034,11 +3034,13 @@ static struct request *blk_mq_get_new_requests(struct request_queue *q, >> >> if (plug) { >> data.nr_tags = plug->nr_ios; >> - plug->nr_ios = 1; >> data.cached_rqs = &plug->cached_rqs; >> } >> >> rq = __blk_mq_alloc_requests(&data); >> + if (plug) >> + plug->nr_ios = data.nr_tags; >> + >> if (unlikely(!rq)) >> rq_qos_cleanup(q, bio); >> return rq; >> > >In __blk_mq_alloc_requests(), if __blk_mq_alloc_requests_batch() failed, >data->nr_tags is set to 1, so plug->nr_ios = data.nr_tags will still set >plug->nr_ios to 1 in this case. > >What am I missing? yes, you are right, if __blk_mq_alloc_requests_batch() failed, it will set to 1. However, in this case, it did not fail to execute; instead, the allocated number of tags was insufficient, as only a partial number were allocated. Therefore, the function is considered successfully executed. >Thanks, >Kuai > Thanks, Xue ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] block: plug attempts to batch allocate tags multiple times 2025-09-03 8:41 ` Xue He @ 2025-09-03 9:35 ` Yu Kuai [not found] ` <CGME20250904063643epcas5p3a831ee47fb91bae02b07dfe398b77dd8@epcas5p3.samsung.com> [not found] ` <CGME20250912031032epcas5p3f38da43ad6cf93b849bb44f14e49c8f9@epcas5p3.samsung.com> 0 siblings, 2 replies; 10+ messages in thread From: Yu Kuai @ 2025-09-03 9:35 UTC (permalink / raw) To: Xue He, yukuai1, axboe; +Cc: linux-block, linux-kernel, yukuai (C) Hi, 在 2025/09/03 16:41, Xue He 写道: > On 2025/09/02 08:47 AM, Yu Kuai wrote: >> On 2025/09/01 16:22, Xue He wrote: > ...... >>> This patch aims to allow the remaining I/O operations to retry batch >>> allocation of tags, reducing the overhead caused by multiple >>> individual tag allocations. >>> >>> ------------------------------------------------------------------------ >>> test result >>> During testing of the PCIe Gen4 SSD Samsung PM9A3, the perf tool >>> observed CPU improvements. The CPU usage of the original function >>> _blk_mq_alloc_requests function was 1.39%, which decreased to 0.82% >>> after modification. >>> >>> Additionally, performance variations were observed on different devices. >>> workload:randread >>> blocksize:4k >>> thread:1 >>> ------------------------------------------------------------------------ >>> PCIe Gen3 SSD PCIe Gen4 SSD PCIe Gen5 SSD >>> native kernel 553k iops 633k iops 793k iops >>> modified 553k iops 635k iops 801k iops >>> >>> with Optane SSDs, the performance like >>> two device one thread >>> cmd :sudo taskset -c 0 ./t/io_uring -b512 -d128 -c32 -s32 -p1 -F1 -B1 >>> -n1 -r4 /dev/nvme0n1 /dev/nvme1n1 >>> >> >> How many hw_queues and how many tags in each hw_queues in your nvme? >> I feel it's unlikely that tags can be exhausted, usually cpu will become >> bottleneck first. > > the information of my nvme like this: > number of CPU: 16 > memory: 16G > nvme nvme0: 16/0/16 default/read/poll queue > cat /sys/class/nvme/nvme0/nvme0n1/queue/nr_requests > 1023 > > In more precise terms, I think it is not that the tags are fully exhausted, > but rather that after scanning the bitmap for free bits, the remaining > contiguous bits are nsufficient to meet the requirement (have but not enough). > The specific function involved is __sbitmap_queue_get_batch in lib/sbitmap.c. > get_mask = ((1UL << nr_tags) - 1) << nr; > if (nr_tags > 1) { > printk("before %ld\n", get_mask); > } > while (!atomic_long_try_cmpxchg(ptr, &val, > get_mask | val)) > ; > get_mask = (get_mask & ~val) >> nr; > > where during the batch acquisition of contiguous free bits, an atomic operation > is performed, resulting in the actual tag_mask obtained differing from the > originally requested one. Yes, so this function will likely to obtain less tags than nr_tags,the mask is always start from first zero bit with nr_tags bit, and sbitmap_deferred_clear() is called uncondionally, it's likely there are non-zero bits within this range. Just wonder, do you consider fixing this directly in __blk_mq_alloc_requests_batch()? - call sbitmap_deferred_clear() and retry on allocation failure, so that the whole word can be used even if previous allocated request are done, especially for nvme with huge tag depths; - retry blk_mq_get_tags() until data->nr_tags is zero; > > Am I missing something? > >>> base: 6.4 Million IOPS >>> patch: 6.49 Million IOPS >>> >>> two device two thread >>> cmd: sudo taskset -c 0 ./t/io_uring -b512 -d128 -c32 -s32 -p1 -F1 -B1 >>> -n1 -r4 /dev/nvme0n1 /dev/nvme1n1 >>> >>> base: 7.34 Million IOPS >>> patch: 7.48 Million IOPS >>> ------------------------------------------------------------------------- >>> >>> Signed-off-by: hexue <xue01.he@samsung.com> >>> --- >>> block/blk-mq.c | 8 +++++--- >>> 1 file changed, 5 insertions(+), 3 deletions(-) >>> >>> diff --git a/block/blk-mq.c b/block/blk-mq.c >>> index b67d6c02eceb..1fb280764b76 100644 >>> --- a/block/blk-mq.c >>> +++ b/block/blk-mq.c >>> @@ -587,9 +587,9 @@ static struct request *blk_mq_rq_cache_fill(struct request_queue *q, >>> if (blk_queue_enter(q, flags)) >>> return NULL; >>> >>> - plug->nr_ios = 1; >>> - >>> rq = __blk_mq_alloc_requests(&data); >>> + plug->nr_ios = data.nr_tags; >>> + >>> if (unlikely(!rq)) >>> blk_queue_exit(q); >>> return rq; >>> @@ -3034,11 +3034,13 @@ static struct request *blk_mq_get_new_requests(struct request_queue *q, >>> >>> if (plug) { >>> data.nr_tags = plug->nr_ios; >>> - plug->nr_ios = 1; >>> data.cached_rqs = &plug->cached_rqs; >>> } >>> >>> rq = __blk_mq_alloc_requests(&data); >>> + if (plug) >>> + plug->nr_ios = data.nr_tags; >>> + >>> if (unlikely(!rq)) >>> rq_qos_cleanup(q, bio); >>> return rq; >>> >> >> In __blk_mq_alloc_requests(), if __blk_mq_alloc_requests_batch() failed, >> data->nr_tags is set to 1, so plug->nr_ios = data.nr_tags will still set >> plug->nr_ios to 1 in this case. >> >> What am I missing? > > yes, you are right, if __blk_mq_alloc_requests_batch() failed, it will set > to 1. However, in this case, it did not fail to execute; instead, the > allocated number of tags was insufficient, as only a partial number were > allocated. Therefore, the function is considered successfully executed. > Thanks for the explanation, I understand this now. Thanks, Kuai >> Thanks, >> Kuai >> > > Thanks, > Xue > > . > ^ permalink raw reply [flat|nested] 10+ messages in thread
[parent not found: <CGME20250904063643epcas5p3a831ee47fb91bae02b07dfe398b77dd8@epcas5p3.samsung.com>]
* Re: [PATCH] block: plug attempts to batch allocate tags multiple times [not found] ` <CGME20250904063643epcas5p3a831ee47fb91bae02b07dfe398b77dd8@epcas5p3.samsung.com> @ 2025-09-04 6:32 ` Xue He 0 siblings, 0 replies; 10+ messages in thread From: Xue He @ 2025-09-04 6:32 UTC (permalink / raw) To: yukuai1, axboe; +Cc: linux-block, linux-kernel, yukuai3 On 2025/09/03/18:35PM, Yu Kuai wrote: >On 2025/09/03 16:41 PM, Xue He wrote: >> On 2025/09/02 08:47 AM, Yu Kuai wrote: >>> On 2025/09/01 16:22 PM, Xue He wrote: >> ...... >> >> the information of my nvme like this: >> number of CPU: 16 >> memory: 16G >> nvme nvme0: 16/0/16 default/read/poll queue >> cat /sys/class/nvme/nvme0/nvme0n1/queue/nr_requests >> 1023 >> >> In more precise terms, I think it is not that the tags are fully exhausted, >> but rather that after scanning the bitmap for free bits, the remaining >> contiguous bits are nsufficient to meet the requirement (have but not enough). >> The specific function involved is __sbitmap_queue_get_batch in lib/sbitmap.c. >> get_mask = ((1UL << nr_tags) - 1) << nr; >> if (nr_tags > 1) { >> printk("before %ld\n", get_mask); >> } >> while (!atomic_long_try_cmpxchg(ptr, &val, >> get_mask | val)) >> ; >> get_mask = (get_mask & ~val) >> nr; >> >> where during the batch acquisition of contiguous free bits, an atomic operation >> is performed, resulting in the actual tag_mask obtained differing from the >> originally requested one. > >Yes, so this function will likely to obtain less tags than nr_tags,the >mask is always start from first zero bit with nr_tags bit, and >sbitmap_deferred_clear() is called uncondionally, it's likely there are >non-zero bits within this range. > >Just wonder, do you consider fixing this directly in >__blk_mq_alloc_requests_batch()? > > - call sbitmap_deferred_clear() and retry on allocation failure, so >that the whole word can be used even if previous allocated request are >done, especially for nvme with huge tag depths; > - retry blk_mq_get_tags() until data->nr_tags is zero; > I haven't tried this yet, as I'm concerned that if it spin here, it might introduce more latency. Anyway, I may try to implement this idea and do some tests to observe the results. Thanks. ^ permalink raw reply [flat|nested] 10+ messages in thread
[parent not found: <CGME20250912031032epcas5p3f38da43ad6cf93b849bb44f14e49c8f9@epcas5p3.samsung.com>]
* Re: [PATCH] block: plug attempts to batch allocate tags multiple times [not found] ` <CGME20250912031032epcas5p3f38da43ad6cf93b849bb44f14e49c8f9@epcas5p3.samsung.com> @ 2025-09-12 3:06 ` Xue He 2025-09-15 1:22 ` Yu Kuai 0 siblings, 1 reply; 10+ messages in thread From: Xue He @ 2025-09-12 3:06 UTC (permalink / raw) To: yukuai1; +Cc: axboe, linux-block, linux-kernel, xue01.he, yukuai3 On 2025/09/03 18:35 PM, Yu Kuai wrote: >On 2025/09/03 16:41 PM, Xue He wrote: >> On 2025/09/02 08:47 AM, Yu Kuai wrote: >>> On 2025/09/01 16:22, Xue He wrote: >> ...... > > >Yes, so this function will likely to obtain less tags than nr_tags,the >mask is always start from first zero bit with nr_tags bit, and >sbitmap_deferred_clear() is called uncondionally, it's likely there are >non-zero bits within this range. > >Just wonder, do you consider fixing this directly in >__blk_mq_alloc_requests_batch()? > > - call sbitmap_deferred_clear() and retry on allocation failure, so >that the whole word can be used even if previous allocated request are >done, especially for nvme with huge tag depths; > - retry blk_mq_get_tags() until data->nr_tags is zero; Hi, Yu Kuai, I'm not entirely sure if I understand correctly, but during each tag allocation, sbitmap_deferred_clear() is typically called first, as seen in the __sbitmap_queue_get_batch() function. for (i = 0; i < sb->map_nr; i++) { struct sbitmap_word *map = &sb->map[index]; unsigned long get_mask; unsigned int map_depth = __map_depth(sb, index); unsigned long val; sbitmap_deferred_clear(map, 0, 0, 0); ------------------------------------------------------------------------ so I try to recall blk_mq_get_tags() until data->nr_tags is zero, like: - int i, nr = 0; - tag_mask = blk_mq_get_tags(data, data->nr_tags, &tag_offset); - if (unlikely(!tag_mask)) - return NULL; - - tags = blk_mq_tags_from_data(data); - for (i = 0; tag_mask; i++) { - if (!(tag_mask & (1UL << i))) - continue; - tag = tag_offset + i; - prefetch(tags->static_rqs[tag]); - tag_mask &= ~(1UL << i); - rq = blk_mq_rq_ctx_init(data, tags, tag); - rq_list_add_head(data->cached_rqs, rq); - nr++; - } - if (!(data->rq_flags & RQF_SCHED_TAGS)) - blk_mq_add_active_requests(data->hctx, nr); - /* caller already holds a reference, add for remainder */ - percpu_ref_get_many(&data->q->q_usage_counter, nr - 1); - data->nr_tags -= nr; + do { + int i, nr = 0; + tag_mask = blk_mq_get_tags(data, data->nr_tags, &tag_offset); + if (unlikely(!tag_mask)) + return NULL; + tags = blk_mq_tags_from_data(data); + for (i = 0; tag_mask; i++) { + if (!(tag_mask & (1UL << i))) + continue; + tag = tag_offset + i; + prefetch(tags->static_rqs[tag]); + tag_mask &= ~(1UL << i); + rq = blk_mq_rq_ctx_init(data, tags, tag); + rq_list_add_head(data->cached_rqs, rq); + nr++; + } + if (!(data->rq_flags & RQF_SCHED_TAGS)) + blk_mq_add_active_requests(data->hctx, nr); + /* caller already holds a reference, add for remainder */ + percpu_ref_get_many(&data->q->q_usage_counter, nr - 1); + data->nr_tags -= nr; + } while (data->nr_tags); I added a loop structure, it also achieve a good results like before, but I have a question: although the loop will retry tag allocation when the required number of tags is not met, there is a risk of an infinite loop, right? However, I couldn't think of a safer condition to terminate the loop. Do you have any suggestions? Thanks, Xue ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] block: plug attempts to batch allocate tags multiple times 2025-09-12 3:06 ` Xue He @ 2025-09-15 1:22 ` Yu Kuai [not found] ` <CGME20250917091039epcas5p1855bfdd913923953d69be9d736685f42@epcas5p1.samsung.com> 0 siblings, 1 reply; 10+ messages in thread From: Yu Kuai @ 2025-09-15 1:22 UTC (permalink / raw) To: Xue He, yukuai1; +Cc: axboe, linux-block, linux-kernel, yukuai (C) Hi, 在 2025/09/12 11:06, Xue He 写道: > On 2025/09/03 18:35 PM, Yu Kuai wrote: >> On 2025/09/03 16:41 PM, Xue He wrote: >>> On 2025/09/02 08:47 AM, Yu Kuai wrote: >>>> On 2025/09/01 16:22, Xue He wrote: >>> ...... >> >> >> Yes, so this function will likely to obtain less tags than nr_tags,the >> mask is always start from first zero bit with nr_tags bit, and >> sbitmap_deferred_clear() is called uncondionally, it's likely there are >> non-zero bits within this range. >> >> Just wonder, do you consider fixing this directly in >> __blk_mq_alloc_requests_batch()? >> >> - call sbitmap_deferred_clear() and retry on allocation failure, so >> that the whole word can be used even if previous allocated request are >> done, especially for nvme with huge tag depths; >> - retry blk_mq_get_tags() until data->nr_tags is zero; > > Hi, Yu Kuai, I'm not entirely sure if I understand correctly, but during > each tag allocation, sbitmap_deferred_clear() is typically called first, > as seen in the __sbitmap_queue_get_batch() function. > > for (i = 0; i < sb->map_nr; i++) { > struct sbitmap_word *map = &sb->map[index]; > unsigned long get_mask; > unsigned int map_depth = __map_depth(sb, index); > unsigned long val; > > sbitmap_deferred_clear(map, 0, 0, 0); Yes, this is called first each time, and I don't feel this is good for performance anyway. I think it can be dealyed after a try first, like sbitmap_find_bit_in_word(). > ------------------------------------------------------------------------ > so I try to recall blk_mq_get_tags() until data->nr_tags is zero, like: > > - int i, nr = 0; > > - tag_mask = blk_mq_get_tags(data, data->nr_tags, &tag_offset); > - if (unlikely(!tag_mask)) > - return NULL; > - > - tags = blk_mq_tags_from_data(data); > - for (i = 0; tag_mask; i++) { > - if (!(tag_mask & (1UL << i))) > - continue; > - tag = tag_offset + i; > - prefetch(tags->static_rqs[tag]); > - tag_mask &= ~(1UL << i); > - rq = blk_mq_rq_ctx_init(data, tags, tag); > - rq_list_add_head(data->cached_rqs, rq); > - nr++; > - } > - if (!(data->rq_flags & RQF_SCHED_TAGS)) > - blk_mq_add_active_requests(data->hctx, nr); > - /* caller already holds a reference, add for remainder */ > - percpu_ref_get_many(&data->q->q_usage_counter, nr - 1); > - data->nr_tags -= nr; > + do { > + int i, nr = 0; > + tag_mask = blk_mq_get_tags(data, data->nr_tags, &tag_offset); > + if (unlikely(!tag_mask)) > + return NULL; > + tags = blk_mq_tags_from_data(data); > + for (i = 0; tag_mask; i++) { > + if (!(tag_mask & (1UL << i))) > + continue; > + tag = tag_offset + i; > + prefetch(tags->static_rqs[tag]); > + tag_mask &= ~(1UL << i); > + rq = blk_mq_rq_ctx_init(data, tags, tag); > + rq_list_add_head(data->cached_rqs, rq); > + nr++; > + } > + if (!(data->rq_flags & RQF_SCHED_TAGS)) > + blk_mq_add_active_requests(data->hctx, nr); > + /* caller already holds a reference, add for remainder */ > + percpu_ref_get_many(&data->q->q_usage_counter, nr - 1); > + data->nr_tags -= nr; > + } while (data->nr_tags); > > I added a loop structure, it also achieve a good results like before, > but I have a question: although the loop will retry tag allocation > when the required number of tags is not met, there is a risk of an > infinite loop, right? However, I couldn't think of a safer condition > to terminate the loop. Do you have any suggestions? Yes, this is what I have in mind. Why do you think there can be infinite loop? We should allcocate at least one tag by blk_mq_get_tags() in each loop, or return directly. Thanks, Kuai > > Thanks, > Xue > > . > ^ permalink raw reply [flat|nested] 10+ messages in thread
[parent not found: <CGME20250917091039epcas5p1855bfdd913923953d69be9d736685f42@epcas5p1.samsung.com>]
* Re: [PATCH] block: plug attempts to batch allocate tags multiple times [not found] ` <CGME20250917091039epcas5p1855bfdd913923953d69be9d736685f42@epcas5p1.samsung.com> @ 2025-09-17 9:06 ` Xue He 0 siblings, 0 replies; 10+ messages in thread From: Xue He @ 2025-09-17 9:06 UTC (permalink / raw) To: yukuai1; +Cc: axboe, linux-block, linux-kernel, xue01.he, yukuai3 On 2025/09/15 10:22 AM, Yu Kuai wrote: >On 2025/09/12 11:06, Xue He wrote: >> On 2025/09/03 18:35 PM, Yu Kuai wrote: >>> On 2025/09/03 16:41 PM, Xue He wrote: >>>> On 2025/09/02 08:47 AM, Yu Kuai wrote: >>>>> On 2025/09/01 16:22, Xue He wrote: >>>> ...... >> >> I added a loop structure, it also achieve a good results like before, >> but I have a question: although the loop will retry tag allocation >> when the required number of tags is not met, there is a risk of an >> infinite loop, right? However, I couldn't think of a safer condition >> to terminate the loop. Do you have any suggestions? > >Yes, this is what I have in mind. Why do you think there can be infinite >loop? We should allcocate at least one tag by blk_mq_get_tags() in each >loop, or return directly. Understand your point now. I will send v2 patch. Thanks, Xue ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v2] block: plug attempts to batch allocate tags multiple times
@ 2025-09-18 7:55 Xue He
[not found] ` <CGME20250924053907epcas5p10c550389234cb8feff8e5625d1d3d2a1@epcas5p1.samsung.com>
0 siblings, 1 reply; 10+ messages in thread
From: Xue He @ 2025-09-18 7:55 UTC (permalink / raw)
To: axboe, akpm; +Cc: linux-block, linux-kernel, hexue
In the existing plug mechanism, tags are allocated in batches based on
the number of requests. However, testing has shown that the plug only
attempts batch allocation of tags once at the beginning of a batch of
I/O operations. Since the tag_mask does not always have enough available
tags to satisfy the requested number, a full batch allocation is not
guaranteed to succeed each time. The remaining tags are then allocated
individually (occurs frequently), leading to multiple single-tag
allocation overheads.
This patch aims to retry batch allocation of tags when the initial batch
allocation fails to reach the requested number, thereby reducing the
overhead of individual allocation attempts.
--------------------------------------------------------------------
perf:
base code: __blk_mq_alloc_requests() 1.33%
patch:__blk_mq_alloc_requests() 0.72%
-------------------------------------------------------------------
Signed-off-by: hexue <xue01.he@samsung.com>
---
block/blk-mq.c | 43 +++++++++++++++++++++++--------------------
lib/sbitmap.c | 7 ++++---
2 files changed, 27 insertions(+), 23 deletions(-)
diff --git a/block/blk-mq.c b/block/blk-mq.c
index ba3a4b77f578..3ed8da176831 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -456,28 +456,31 @@ __blk_mq_alloc_requests_batch(struct blk_mq_alloc_data *data)
struct blk_mq_tags *tags;
struct request *rq;
unsigned long tag_mask;
- int i, nr = 0;
- tag_mask = blk_mq_get_tags(data, data->nr_tags, &tag_offset);
- if (unlikely(!tag_mask))
- return NULL;
+ do {
+ int i, nr = 0;
- tags = blk_mq_tags_from_data(data);
- for (i = 0; tag_mask; i++) {
- if (!(tag_mask & (1UL << i)))
- continue;
- tag = tag_offset + i;
- prefetch(tags->static_rqs[tag]);
- tag_mask &= ~(1UL << i);
- rq = blk_mq_rq_ctx_init(data, tags, tag);
- rq_list_add_head(data->cached_rqs, rq);
- nr++;
- }
- if (!(data->rq_flags & RQF_SCHED_TAGS))
- blk_mq_add_active_requests(data->hctx, nr);
- /* caller already holds a reference, add for remainder */
- percpu_ref_get_many(&data->q->q_usage_counter, nr - 1);
- data->nr_tags -= nr;
+ tag_mask = blk_mq_get_tags(data, data->nr_tags, &tag_offset);
+ if (unlikely(!tag_mask))
+ return NULL;
+
+ tags = blk_mq_tags_from_data(data);
+ for (i = 0; tag_mask; i++) {
+ if (!(tag_mask & (1UL << i)))
+ continue;
+ tag = tag_offset + i;
+ prefetch(tags->static_rqs[tag]);
+ tag_mask &= ~(1UL << i);
+ rq = blk_mq_rq_ctx_init(data, tags, tag);
+ rq_list_add_head(data->cached_rqs, rq);
+ nr++;
+ }
+ if (!(data->rq_flags & RQF_SCHED_TAGS))
+ blk_mq_add_active_requests(data->hctx, nr);
+ /* caller already holds a reference, add for remainder */
+ percpu_ref_get_many(&data->q->q_usage_counter, nr - 1);
+ data->nr_tags -= nr;
+ } while (data->nr_tags);
return rq_list_pop(data->cached_rqs);
}
diff --git a/lib/sbitmap.c b/lib/sbitmap.c
index 4d188d05db15..4ac303842aec 100644
--- a/lib/sbitmap.c
+++ b/lib/sbitmap.c
@@ -534,10 +534,11 @@ unsigned long __sbitmap_queue_get_batch(struct sbitmap_queue *sbq, int nr_tags,
unsigned int map_depth = __map_depth(sb, index);
unsigned long val;
- sbitmap_deferred_clear(map, 0, 0, 0);
val = READ_ONCE(map->word);
- if (val == (1UL << (map_depth - 1)) - 1)
- goto next;
+ if (val == (1UL << (map_depth - 1)) - 1) {
+ if (!sbitmap_deferred_clear(map, map_depth, 0, 0))
+ goto next;
+ }
nr = find_first_zero_bit(&val, map_depth);
if (nr + nr_tags <= map_depth) {
--
2.34.1
^ permalink raw reply [flat|nested] 10+ messages in thread[parent not found: <CGME20250924053907epcas5p10c550389234cb8feff8e5625d1d3d2a1@epcas5p1.samsung.com>]
* Re: [PATCH] block: plug attempts to batch allocate tags multiple times [not found] ` <CGME20250924053907epcas5p10c550389234cb8feff8e5625d1d3d2a1@epcas5p1.samsung.com> @ 2025-09-24 5:34 ` Xue He 0 siblings, 0 replies; 10+ messages in thread From: Xue He @ 2025-09-24 5:34 UTC (permalink / raw) To: akpm, axboe; +Cc: linux-block, linux-kernel >+ if (val == (1UL << (map_depth - 1)) - 1) { >+ if (!sbitmap_deferred_clear(map, map_depth, 0, 0)) >+ goto next; >+ } > > nr = find_first_zero_bit(&val, map_depth); > if (nr + nr_tags <= map_depth) { Hi, just a kindly ping... ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2] block: plug attempts to batch allocate tags multiple times
2025-09-18 7:55 [PATCH v2] " Xue He
@ 2025-09-24 9:23 Yu Kuai
[not found] ` <CGME20250924114923epcas5p24d397694d8ce433d126136b79f3fd21d@epcas5p2.samsung.com>
0 siblings, 1 reply; 10+ messages in thread
From: Yu Kuai @ 2025-09-24 9:23 UTC (permalink / raw)
To: Xue He, axboe, akpm; +Cc: linux-block, linux-kernel, yukuai (C)
Hi,
I'm not in the cc list, so it can take sometime before I notice this
patch.
在 2025/09/18 15:55, Xue He 写道:
> In the existing plug mechanism, tags are allocated in batches based on
> the number of requests. However, testing has shown that the plug only
> attempts batch allocation of tags once at the beginning of a batch of
> I/O operations. Since the tag_mask does not always have enough available
> tags to satisfy the requested number, a full batch allocation is not
> guaranteed to succeed each time. The remaining tags are then allocated
> individually (occurs frequently), leading to multiple single-tag
> allocation overheads.
>
> This patch aims to retry batch allocation of tags when the initial batch
> allocation fails to reach the requested number, thereby reducing the
> overhead of individual allocation attempts.
>
> --------------------------------------------------------------------
> perf:
> base code: __blk_mq_alloc_requests() 1.33%
> patch:__blk_mq_alloc_requests() 0.72%
> -------------------------------------------------------------------
>
> Signed-off-by: hexue <xue01.he@samsung.com>
> ---
Please add change log.
> block/blk-mq.c | 43 +++++++++++++++++++++++--------------------
> lib/sbitmap.c | 7 ++++---
> 2 files changed, 27 insertions(+), 23 deletions(-)
>
> diff --git a/block/blk-mq.c b/block/blk-mq.c
> index ba3a4b77f578..3ed8da176831 100644
> --- a/block/blk-mq.c
> +++ b/block/blk-mq.c
> @@ -456,28 +456,31 @@ __blk_mq_alloc_requests_batch(struct blk_mq_alloc_data *data)
> struct blk_mq_tags *tags;
> struct request *rq;
> unsigned long tag_mask;
> - int i, nr = 0;
>
> - tag_mask = blk_mq_get_tags(data, data->nr_tags, &tag_offset);
> - if (unlikely(!tag_mask))
> - return NULL;
> + do {
> + int i, nr = 0;
>
> - tags = blk_mq_tags_from_data(data);
> - for (i = 0; tag_mask; i++) {
> - if (!(tag_mask & (1UL << i)))
> - continue;
> - tag = tag_offset + i;
> - prefetch(tags->static_rqs[tag]);
> - tag_mask &= ~(1UL << i);
> - rq = blk_mq_rq_ctx_init(data, tags, tag);
> - rq_list_add_head(data->cached_rqs, rq);
> - nr++;
> - }
> - if (!(data->rq_flags & RQF_SCHED_TAGS))
> - blk_mq_add_active_requests(data->hctx, nr);
> - /* caller already holds a reference, add for remainder */
> - percpu_ref_get_many(&data->q->q_usage_counter, nr - 1);
> - data->nr_tags -= nr;
> + tag_mask = blk_mq_get_tags(data, data->nr_tags, &tag_offset);
> + if (unlikely(!tag_mask))
> + return NULL;
> +
> + tags = blk_mq_tags_from_data(data);
> + for (i = 0; tag_mask; i++) {
> + if (!(tag_mask & (1UL << i)))
> + continue;
> + tag = tag_offset + i;
> + prefetch(tags->static_rqs[tag]);
> + tag_mask &= ~(1UL << i);
> + rq = blk_mq_rq_ctx_init(data, tags, tag);
> + rq_list_add_head(data->cached_rqs, rq);
> + nr++;
> + }
> + if (!(data->rq_flags & RQF_SCHED_TAGS))
> + blk_mq_add_active_requests(data->hctx, nr);
> + /* caller already holds a reference, add for remainder */
> + percpu_ref_get_many(&data->q->q_usage_counter, nr - 1);
This should move outside of the loop, the remainder handling is one time
thing.
> + data->nr_tags -= nr;
> + } while (data->nr_tags);
>
> return rq_list_pop(data->cached_rqs);
> }
> diff --git a/lib/sbitmap.c b/lib/sbitmap.c
> index 4d188d05db15..4ac303842aec 100644
> --- a/lib/sbitmap.c
> +++ b/lib/sbitmap.c
> @@ -534,10 +534,11 @@ unsigned long __sbitmap_queue_get_batch(struct sbitmap_queue *sbq, int nr_tags,
> unsigned int map_depth = __map_depth(sb, index);
> unsigned long val;
>
> - sbitmap_deferred_clear(map, 0, 0, 0);
> val = READ_ONCE(map->word);
> - if (val == (1UL << (map_depth - 1)) - 1)
> - goto next;
> + if (val == (1UL << (map_depth - 1)) - 1) {
> + if (!sbitmap_deferred_clear(map, map_depth, 0, 0))
> + goto next;
This looks wrong, you're still using the old val after
sbitmap_deferred_clear().
> + }
>
> nr = find_first_zero_bit(&val, map_depth);
> if (nr + nr_tags <= map_depth) {
>
And I think if above checking failed, sbitmap_deferred_clear() should be
called and retry as well.
Thanks,
Kuai
^ permalink raw reply [flat|nested] 10+ messages in thread[parent not found: <CGME20250924114923epcas5p24d397694d8ce433d126136b79f3fd21d@epcas5p2.samsung.com>]
* Re: [PATCH] block: plug attempts to batch allocate tags multiple times [not found] ` <CGME20250924114923epcas5p24d397694d8ce433d126136b79f3fd21d@epcas5p2.samsung.com> @ 2025-09-24 11:44 ` Xue He 0 siblings, 0 replies; 10+ messages in thread From: Xue He @ 2025-09-24 11:44 UTC (permalink / raw) To: yukuai1; +Cc: akpm, axboe, linux-block, linux-kernel, xue01.he, yukuai3 On 2025/09/24 18:23, Yu Kuai wrote: >On 2025/09/18 15:55, Xue He write: >Hi, > >I'm not in the cc list, so it can take sometime before I notice this >patch. Oh sorry I missed this, I'll add you in cc list, and resend the v3 patch, thank you. ^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2025-09-24 13:54 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
[not found] <CGME20250901082648epcas5p18f81021213f2b8a050efa25f76e0fb54@epcas5p1.samsung.com>
2025-09-01 8:22 ` [PATCH] block: plug attempts to batch allocate tags multiple times Xue He
2025-09-02 8:47 ` Yu Kuai
[not found] ` <CGME20250903084608epcas5p19a0ad4f0d1bad27889426e525d0c4598@epcas5p1.samsung.com>
2025-09-03 8:41 ` Xue He
2025-09-03 9:35 ` Yu Kuai
[not found] ` <CGME20250904063643epcas5p3a831ee47fb91bae02b07dfe398b77dd8@epcas5p3.samsung.com>
2025-09-04 6:32 ` Xue He
[not found] ` <CGME20250912031032epcas5p3f38da43ad6cf93b849bb44f14e49c8f9@epcas5p3.samsung.com>
2025-09-12 3:06 ` Xue He
2025-09-15 1:22 ` Yu Kuai
[not found] ` <CGME20250917091039epcas5p1855bfdd913923953d69be9d736685f42@epcas5p1.samsung.com>
2025-09-17 9:06 ` Xue He
2025-09-18 7:55 [PATCH v2] " Xue He
[not found] ` <CGME20250924053907epcas5p10c550389234cb8feff8e5625d1d3d2a1@epcas5p1.samsung.com>
2025-09-24 5:34 ` [PATCH] " Xue He
2025-09-24 9:23 [PATCH v2] " Yu Kuai
[not found] ` <CGME20250924114923epcas5p24d397694d8ce433d126136b79f3fd21d@epcas5p2.samsung.com>
2025-09-24 11:44 ` [PATCH] " Xue He
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®