From: Jens Axboe <axboe@kernel.dk>
To: Chengfeng Lin <lin2530632123@gmail.com>
Cc: Pavel Begunkov <asml.silence@gmail.com>,
Robert Morris <rtm@csail.mit.edu>,
io-uring@vger.kernel.org, linux-kernel@vger.kernel.org,
regressions@lists.linux.dev
Subject: Re: [REGRESSION] io_uring/futex: scalar wait/wake slowdown after 079afb081c42
Date: Thu, 30 Jul 2026 09:49:54 -0600 [thread overview]
Message-ID: <aa84c27c-d9c5-469c-9e95-9f8dd3fe47cb@kernel.dk> (raw)
In-Reply-To: <e679211f-f036-4cd4-887a-430aa9376fc1@kernel.dk>
[-- Attachment #1: Type: text/plain, Size: 3120 bytes --]
On 7/30/26 8:34 AM, Jens Axboe wrote:
> On 7/30/26 4:42 AM, Chengfeng Lin wrote:
>> Hi Jens,
>>
>> I tested 079afb081c42 against its direct parent on bare metal. In a narrow
>> scalar io_uring futex wait/wake workload, the child was 9.27% slower. A
>> separate 338-line standalone reproduced the result at 10.02%. All compared
>> kernels actually ran with preempt=full.
>>
>> #regzbot introduced: 079afb081c4288e94d5e4223d3eb6306d853c68b
>> #regzbot title: io_uring scalar futex wait/wake slowdown
>>
>> This is a focused synthetic microbenchmark, not an application benchmark. It
>> uses one raw-UAPI ring and 32 cacheline-separated private futex words on one
>> pinned P-core. Each timed cycle submits 32 scalar IORING_OP_FUTEX_WAIT
>> requests, then 32 scalar IORING_OP_FUTEX_WAKE requests, and drains exactly 64
>> CQEs. Every wait must return 0 and every wake must return 1.
>>
>> I used a fresh boot for each point:
>>
>> 6a8118a77eec parent A -> 079afb081c42 child -> 6a8118a77eec parent B
>>
>> Each point had 3 warm-up rounds and 15 measured rounds. Every measured round
>> ran 512 cycles, or 16,384 wait/wake pairs. The results in ns/pair were:
>>
>> implementation parent A child parent B child vs midpoint
>> formal 180.079 196.647 179.856 +9.268%
>> standalone 180.171 197.856 179.502 +10.020%
>>
>> For the formal source, dropping the first measured round gave +9.269%. Parent
>> drift was -0.124%, and the maximum CV was 0.169%. The standalone drop-first
>> result was +9.996%, with -0.371% parent drift. All 90 scalar timing rows
>> passed the CQE, result, timeout, overflow, outstanding-request, and CPU
>> checks.
>>
>> An untimed child trace also hit io_futex_prep(), io_futex_wait(),
>> io_futex_wake(), and io_futex_complete() with the expected request counts.
>>
>> A matched WAITV -> WAKE profile changed by only +1.385%, below my preregistered
>> 5% signal gate, so my claim is limited to scalar wait/wake.
>>
>> I understand that 079afb fixes the exit-time use-after-free by keeping pending
>> private futex waits visible to cancellation before their mm state disappears.
>> Scalar WAIT and WAKE both use io_futex_prep(), so in the child both sides of
>> each measured pair execute the added tracking call. I am not suggesting a
>> revert.
>>
>> Is this per-request cost an expected trade-off for the lifetime fix, or could
>> the same exit/mm-lifetime guarantee be retained with cheaper tracking?
>>
>> Evidence bundle:
>>
>> https://github.com/lcf0399/linux-regression-evidence/tree/65c8cbf86f40cbe759e3f7db1d29d152ba03a8f2/io-uring-futex-inflight-wait-wake
>>
>> Standalone reproducer:
>>
>> https://github.com/lcf0399/linux-regression-evidence/tree/65c8cbf86f40cbe759e3f7db1d29d152ba03a8f2/io-uring-futex-inflight-wait-wake/reproducer
>
> Great report, thanks for that! I'll take a look at this. The inflight
> tracking is a bit of a big hammer for sure for this, and it isn't even
> needed on the wake side. Can you tell me what parameters you're using
> for the reproducers?
Try with these two patches.
--
Jens Axboe
[-- Attachment #2: 0002-io_uring-futex-only-mark-private-futex-waits-as-infl.patch --]
[-- Type: text/x-patch, Size: 2623 bytes --]
From b47f14f3025f908dc71932a2ae4b82ffcad2cd37 Mon Sep 17 00:00:00 2001
From: Jens Axboe <axboe@kernel.dk>
Date: Thu, 30 Jul 2026 06:50:47 -0600
Subject: [PATCH 2/2] io_uring/futex: only mark private futex waits as inflight
Inflight tracking of futex wait requests exists to ensure that do_exit()
-> io_uring_files_cancel() cancels them before the mm goes away, as a
private futex wait depends on the mm private futex hash staying alive
for the duration of the request.
Shared futexes have no such dependency. A FLAGS_SHARED request always
resolves to either an inode based key or an mm-shared key, both of which
fail futex_key_is_private() and hence always hash into the global futex
hash, whose lifetime isn't tied to the mm.
Only mark vectored futex waits as inflight if the futex is private.
Cc: stable@vger.kernel.org
Fixes: 079afb081c42 ("io_uring/futex: mark wait requests as inflight")
Link: https://lore.kernel.org/io-uring/CANGjgdn=R_qyUdE=j9za+vkmqcxacbP-84OHXF4nZ4ho9qRyVg@mail.gmail.com/
Signed-off-by: Jens Axboe <axboe@kernel.dk>
---
io_uring/futex.c | 17 +++++++++++++----
1 file changed, 13 insertions(+), 4 deletions(-)
diff --git a/io_uring/futex.c b/io_uring/futex.c
index eea0425f2bcb..f0d80a444f45 100644
--- a/io_uring/futex.c
+++ b/io_uring/futex.c
@@ -154,14 +154,16 @@ int io_futex_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
int io_futex_wait_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
{
+ struct io_futex *iof = io_kiocb_to_cmd(req, struct io_futex);
int ret;
ret = io_futex_prep(req, sqe);
if (unlikely(ret))
return ret;
- /* Mark as inflight, so file exit cancelation will find it */
- io_req_track_inflight(req);
+ /* inflight tracking only needed for mm private hash */
+ if (!(iof->futex_flags & FLAGS_SHARED))
+ io_req_track_inflight(req);
return 0;
}
@@ -186,6 +188,7 @@ int io_futexv_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
{
struct io_futex *iof = io_kiocb_to_cmd(req, struct io_futex);
struct io_futexv_data *ifd;
+ unsigned int i;
int ret;
/* No flags or mask supported for waitv */
@@ -210,8 +213,14 @@ int io_futexv_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
return ret;
}
- /* Mark as inflight, so file exit cancelation will find it */
- io_req_track_inflight(req);
+ /* inflight tracking only needed for mm private hash */
+ for (i = 0; i < iof->futex_nr; i++) {
+ if (!(ifd->futexv[i].w.flags & FLAGS_SHARED)) {
+ io_req_track_inflight(req);
+ break;
+ }
+ }
+
iof->futexv_unqueued = 0;
req->flags |= REQ_F_ASYNC_DATA;
req->async_data = ifd;
--
2.53.0
[-- Attachment #3: 0001-io_uring-futex-don-t-mark-futex-wake-requests-as-inf.patch --]
[-- Type: text/x-patch, Size: 2805 bytes --]
From abd086a38ea8442d87bbedc0ed944fecba964be8 Mon Sep 17 00:00:00 2001
From: Jens Axboe <axboe@kernel.dk>
Date: Thu, 30 Jul 2026 06:50:13 -0600
Subject: [PATCH 1/2] io_uring/futex: don't mark futex wake requests as
inflight
Commit 079afb081c42 ("io_uring/futex: mark wait requests as inflight")
added inflight tracking to ensure that do_exit() ->
io_uring_files_cancel() finds and cancels pending futex waits before the
mm goes away, as a private futex wait depends on the mm private futex
hash staying alive for the duration of the request. However, as
io_futex_prep() is shared between FUTEX_WAIT and FUTEX_WAKE, wake
requests got marked as inflight as well.
A futex wake executes fully inline at issue time and never depends on
the mm staying alive after completion, hence there's no need to track
it. Kill it.
Cc: stable@vger.kernel.org
Fixes: 079afb081c42 ("io_uring/futex: mark wait requests as inflight")
Reported-by: Chengfeng Lin <lin2530632123@gmail.com>
Link: https://lore.kernel.org/io-uring/CANGjgdn=R_qyUdE=j9za+vkmqcxacbP-84OHXF4nZ4ho9qRyVg@mail.gmail.com/
Signed-off-by: Jens Axboe <axboe@kernel.dk>
---
io_uring/futex.c | 11 +++++++++++
io_uring/futex.h | 1 +
io_uring/opdef.c | 2 +-
3 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/io_uring/futex.c b/io_uring/futex.c
index 906701b3c5c6..eea0425f2bcb 100644
--- a/io_uring/futex.c
+++ b/io_uring/futex.c
@@ -149,6 +149,17 @@ int io_futex_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
!futex_validate_input(iof->futex_flags, iof->futex_mask))
return -EINVAL;
+ return 0;
+}
+
+int io_futex_wait_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
+{
+ int ret;
+
+ ret = io_futex_prep(req, sqe);
+ if (unlikely(ret))
+ return ret;
+
/* Mark as inflight, so file exit cancelation will find it */
io_req_track_inflight(req);
return 0;
diff --git a/io_uring/futex.h b/io_uring/futex.h
index d789fcf715e3..987db3f2c6d9 100644
--- a/io_uring/futex.h
+++ b/io_uring/futex.h
@@ -3,6 +3,7 @@
#include "cancel.h"
int io_futex_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe);
+int io_futex_wait_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe);
int io_futexv_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe);
int io_futex_wait(struct io_kiocb *req, unsigned int issue_flags);
int io_futexv_wait(struct io_kiocb *req, unsigned int issue_flags);
diff --git a/io_uring/opdef.c b/io_uring/opdef.c
index 4e58eb1344ea..cf3aa2242cd7 100644
--- a/io_uring/opdef.c
+++ b/io_uring/opdef.c
@@ -467,7 +467,7 @@ const struct io_issue_def io_issue_defs[] = {
},
[IORING_OP_FUTEX_WAIT] = {
#if defined(CONFIG_FUTEX)
- .prep = io_futex_prep,
+ .prep = io_futex_wait_prep,
.issue = io_futex_wait,
#else
.prep = io_eopnotsupp_prep,
--
2.53.0
next prev parent reply other threads:[~2026-07-30 15:49 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-30 10:42 [REGRESSION] io_uring/futex: scalar wait/wake slowdown after 079afb081c42 Chengfeng Lin
2026-07-30 14:34 ` Jens Axboe
2026-07-30 15:49 ` Jens Axboe [this message]
2026-07-31 8:31 ` Chengfeng Lin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=aa84c27c-d9c5-469c-9e95-9f8dd3fe47cb@kernel.dk \
--to=axboe@kernel.dk \
--cc=asml.silence@gmail.com \
--cc=io-uring@vger.kernel.org \
--cc=lin2530632123@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=regressions@lists.linux.dev \
--cc=rtm@csail.mit.edu \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.