* Re: [BUG] RCU hang with io_uring nvme polling
From: Ben Carey @ 2026-06-26 17:41 UTC (permalink / raw)
To: Keith Busch; +Cc: Jens Axboe, io-uring, linux-kernel
In-Reply-To: <CA+KFGSoyCSRzgamm-38oyAtEsqd7wZZ8awL79P40x7a819EK4w@mail.gmail.com>
On Fri, Jun 26, 2026 at 12:48 PM Keith Busch <kbusch@kernel.org> wrote:
>
> On Fri, Jun 26, 2026 at 10:35:56AM -0600, Jens Axboe wrote:
> > Yes, it's a bad configuration. I bet it's as simple as:
> >
> > https://lore.kernel.org/linux-block/20260617155051.1266079-1-anuj20.g@samsung.com/
>
> Yep, that's definitely the same problem. Thanks, I hadn't seen that
> thread yet.
(Resending this, didn't enable plain-text mode)
Jens, Keith,
I appreciate you both for the quick responses.
After putting that patch into the kernel, the fio job ran, but most of the
I/O's completed after about 2 milliseconds (makes sense, given the offset.)
Part of me wonders if there's a race between the NVMe driver and the hardware
controller, but I can't back this up right now.
Ben Carey
^ permalink raw reply
* Re: [BUG] RCU hang with io_uring nvme polling
From: Keith Busch @ 2026-06-26 16:48 UTC (permalink / raw)
To: Jens Axboe; +Cc: Ben Carey, io-uring, linux-kernel
In-Reply-To: <94614dd9-9351-4a64-83dc-4fc87e377e59@kernel.dk>
On Fri, Jun 26, 2026 at 10:35:56AM -0600, Jens Axboe wrote:
> Yes, it's a bad configuration. I bet it's as simple as:
>
> https://lore.kernel.org/linux-block/20260617155051.1266079-1-anuj20.g@samsung.com/
Yep, that's definitely the same problem. Thanks, I hadn't seen that
thread yet.
^ permalink raw reply
* Re: [BUG] RCU hang with io_uring nvme polling
From: Jens Axboe @ 2026-06-26 16:35 UTC (permalink / raw)
To: Keith Busch; +Cc: Ben Carey, io-uring, linux-kernel
In-Reply-To: <aj6p3kZy1a8Mf68S@kbusch-mbp>
On 6/26/26 10:33 AM, Keith Busch wrote:
> On Fri, Jun 26, 2026 at 10:06:49AM -0600, Jens Axboe wrote:
>> Ah good catch, I missed that. Should've grepped! In general, IO should
>> either get polled, or if the device is misbehaving, then timeouts will
>> catch it. That said, haven't looked at the actual report yet, will do
>> so next week (unless you beat me to it...?)
>
> I'll give it a shot!
>
> The test has 1 polling queue with 2 jobs dispatching. One of the job's
> polled the completions for both. The other job is polling for no reason
> at all with nothing outstanding. The only thing that can break us out of
> that loop now is need_resched(), but that appears to never return true.
Yes, it's a bad configuration. I bet it's as simple as:
https://lore.kernel.org/linux-block/20260617155051.1266079-1-anuj20.g@samsung.com/
but in practice nobody should configure a single poll queue and run
multiple jobs, particularly not when the object is framed around "energy
efficiency" as this configuration is pretty much guaranteed to waste 2
cores, with most of the time going towards spinning on a lock rather
than doing potentially useful work.
--
Jens Axboe
^ permalink raw reply
* Re: [BUG] RCU hang with io_uring nvme polling
From: Keith Busch @ 2026-06-26 16:33 UTC (permalink / raw)
To: Jens Axboe; +Cc: Ben Carey, io-uring, linux-kernel
In-Reply-To: <1932a509-4e27-485e-8e09-1da67e0082c8@kernel.dk>
On Fri, Jun 26, 2026 at 10:06:49AM -0600, Jens Axboe wrote:
> Ah good catch, I missed that. Should've grepped! In general, IO should
> either get polled, or if the device is misbehaving, then timeouts will
> catch it. That said, haven't looked at the actual report yet, will do
> so next week (unless you beat me to it...?)
I'll give it a shot!
The test has 1 polling queue with 2 jobs dispatching. One of the job's
polled the completions for both. The other job is polling for no reason
at all with nothing outstanding. The only thing that can break us out of
that loop now is need_resched(), but that appears to never return true.
^ permalink raw reply
* Re: [BUG] RCU hang with io_uring nvme polling
From: Jens Axboe @ 2026-06-26 16:06 UTC (permalink / raw)
To: Keith Busch; +Cc: Ben Carey, io-uring, linux-kernel
In-Reply-To: <aj6jQyJd3zmZFcwx@kbusch-mbp>
On 6/26/26 10:05 AM, Keith Busch wrote:
> On Fri, Jun 26, 2026 at 09:17:35AM -0600, Jens Axboe wrote:
>> On 6/26/26 9:09 AM, Ben Carey wrote:
>>> From a running QEMU image with the latest kernel:
>>> 1. Attach GDB to the running instance.
>>> 2. Enable io polling via sysfs (echo 1 > /sys/block/nvme0n1/queue/io_poll).
>>
>> That's not how that works at all. You need to setup poll queues on the
>> nvme driver side, using the nvme.poll_queues=XX kernel parameter, or if
>> using nvme as a module, load the module with poll_queues=XX where XX is
>> the number of poll queues. You're not doing any polled IO as-is, and the
>> above should also have dumped a dmesg message about how that does
>> absolutely nothing.
>>
>> That said, it should still work, just not doing polled IO. I'll take a
>> look sometime next week, OOO right now.
>
> Yeah, the sysfs attribute does nothing, but Ben mentioned they had the
> correct kernel command line:
>
> BOOT_IMAGE=/vmlinuz-7.1.0-g3996771b8f75 root=/dev/mapper/ubuntu--vg-ubuntu--lv \
> ro nvme.poll_queues=1 nokaslr
>
> So they did enable polling, but the "echo" step is just confusing and
> unnecessary.
>
> I tried out the test, and there does appear to be a problem here, so I'm
> looking into it.
Ah good catch, I missed that. Should've grepped! In general, IO should
either get polled, or if the device is misbehaving, then timeouts will
catch it. That said, haven't looked at the actual report yet, will do
so next week (unless you beat me to it...?)
--
Jens Axboe
^ permalink raw reply
* Re: [BUG] RCU hang with io_uring nvme polling
From: Keith Busch @ 2026-06-26 16:05 UTC (permalink / raw)
To: Jens Axboe; +Cc: Ben Carey, io-uring, linux-kernel
In-Reply-To: <85d1f999-7778-4c74-9d72-b8ac8500de31@kernel.dk>
On Fri, Jun 26, 2026 at 09:17:35AM -0600, Jens Axboe wrote:
> On 6/26/26 9:09 AM, Ben Carey wrote:
> > From a running QEMU image with the latest kernel:
> > 1. Attach GDB to the running instance.
> > 2. Enable io polling via sysfs (echo 1 > /sys/block/nvme0n1/queue/io_poll).
>
> That's not how that works at all. You need to setup poll queues on the
> nvme driver side, using the nvme.poll_queues=XX kernel parameter, or if
> using nvme as a module, load the module with poll_queues=XX where XX is
> the number of poll queues. You're not doing any polled IO as-is, and the
> above should also have dumped a dmesg message about how that does
> absolutely nothing.
>
> That said, it should still work, just not doing polled IO. I'll take a
> look sometime next week, OOO right now.
Yeah, the sysfs attribute does nothing, but Ben mentioned they had the
correct kernel command line:
BOOT_IMAGE=/vmlinuz-7.1.0-g3996771b8f75 root=/dev/mapper/ubuntu--vg-ubuntu--lv \
ro nvme.poll_queues=1 nokaslr
So they did enable polling, but the "echo" step is just confusing and
unnecessary.
I tried out the test, and there does appear to be a problem here, so I'm
looking into it.
^ permalink raw reply
* Re: [BUG] RCU hang with io_uring nvme polling
From: Jens Axboe @ 2026-06-26 15:17 UTC (permalink / raw)
To: Ben Carey, io-uring; +Cc: linux-kernel
In-Reply-To: <20260626150946.287781-1-benjamin.james.carey3@gmail.com>
On 6/26/26 9:09 AM, Ben Carey wrote:
> From: benjamin.james.carey3@gmail.com
>
> Hello, whomever this may concern.
>
> I am working in a lab researching energy efficiency of I/O servicing and
> completion mechanisms, and we have encountered an issue when using io_uring and
> completing I/O requests while polling NVMe drives.
>
> Description
> ===========
>
> When using fio to run io_uring test benches for energy consumption analysis
> on our lab server, we're encountering strange kernel locking behaviors as
> numjobs increases.
>
> This issue occurs on our workloads the poll for I/O completion. Specifically,
> whenever the numjobs parameter scales to beyond the nvme.poll_queues
> parameter, the job takes much longer to complete or doesn't complete at all.
>
> Notably, this issue occurs also on a QEMU image mimicking our setup. Using GDB
> to read dmesg output we get the following:
>
> ...
> rcu: INFO: rcu_preempt detected stalls on CPUs/tasks:
> rcu: Tasks blocked on level-0 rcu_node (CPUs 0-7): P1070
> rcu: (detected by 7, t=252035 jiffies, g=1985, q=25149 ncpus=8)
> task:fio state:R running task stack:13296 pid:1070 tgid:1070 ppid:1068 task_flags:0x400140 flags:0x00080000
> Call Trace:
> ...
> ? blk_hctx_poll+0x34/0x80
> blk_mq_poll+0x2b/0x40
> bio_poll+0x94/0x180
> iocb_bio_iopoll+0x31/0x50
> io_uring_classic_poll+0x20/0x40
> io_do_iopoll+0x233/0x430
> ? io_issue_sqe+0x2f/0x560
> ? io_submit_sqes+0x270/0x820
> __do_sys_io_uring_enter+0x228/0x770
> ? handle_softirqs+0xc7/0x250
> __x64_sys_io_uring_enter+0x21/0x30
> x64_sys_call+0x17c8/0x1dd0
> do_syscall_64+0xe0/0x5a0
> entry_SYSCALL_64_after_hwframe+0x77/0x7f
>
> Expected behavior
> =================
>
> fio job completes after specified runtime.
>
> Actual behavior
> ===============
>
> fio job never completes, system becomes less responsive (if the number of poll
> queues and jobs are high) and RCU stall checker detects stalls.
>
> Observations
> ============
>
> After some minimal investigation we found this notable function being called as
> the callback for q->mq_ops->poll:
>
> static int nvme_poll(struct blk_mq_hw_ctx *hctx, struct io_comp_batch *iob)
> {
> struct nvme_queue *nvmeq = hctx->driver_data;
> bool found;
>
> if (!test_bit(NVMEQ_POLLED, &nvmeq->flags) ||
> !nvme_cqe_pending(nvmeq))
> return 0;
>
> spin_lock(&nvmeq->cq_poll_lock);
> found = nvme_poll_cq(nvmeq, iob);
> spin_unlock(&nvmeq->cq_poll_lock);
>
> return found;
> }
>
> This function, when stuck on the RCU loop, always returns 0. It also always
> calls the helper function nvme_cqe_pending.
>
> Following this are some items that may help in reproducing this issue.
>
> Steps to reproduce
> ==================
> From a running QEMU image with the latest kernel:
> 1. Attach GDB to the running instance.
> 2. Enable io polling via sysfs (echo 1 > /sys/block/nvme0n1/queue/io_poll).
That's not how that works at all. You need to setup poll queues on the
nvme driver side, using the nvme.poll_queues=XX kernel parameter, or if
using nvme as a module, load the module with poll_queues=XX where XX is
the number of poll queues. You're not doing any polled IO as-is, and the
above should also have dumped a dmesg message about how that does
absolutely nothing.
That said, it should still work, just not doing polled IO. I'll take a
look sometime next week, OOO right now.
--
Jens Axboe
^ permalink raw reply
* [BUG] RCU hang with io_uring nvme polling
From: Ben Carey @ 2026-06-26 15:09 UTC (permalink / raw)
To: io-uring; +Cc: linux-kernel, axboe, stable, benjamin.james.carey3
From: benjamin.james.carey3@gmail.com
Hello, whomever this may concern.
I am working in a lab researching energy efficiency of I/O servicing and
completion mechanisms, and we have encountered an issue when using io_uring and
completing I/O requests while polling NVMe drives.
Description
===========
When using fio to run io_uring test benches for energy consumption analysis
on our lab server, we're encountering strange kernel locking behaviors as
numjobs increases.
This issue occurs on our workloads the poll for I/O completion. Specifically,
whenever the numjobs parameter scales to beyond the nvme.poll_queues
parameter, the job takes much longer to complete or doesn't complete at all.
Notably, this issue occurs also on a QEMU image mimicking our setup. Using GDB
to read dmesg output we get the following:
...
rcu: INFO: rcu_preempt detected stalls on CPUs/tasks:
rcu: Tasks blocked on level-0 rcu_node (CPUs 0-7): P1070
rcu: (detected by 7, t=252035 jiffies, g=1985, q=25149 ncpus=8)
task:fio state:R running task stack:13296 pid:1070 tgid:1070 ppid:1068 task_flags:0x400140 flags:0x00080000
Call Trace:
...
? blk_hctx_poll+0x34/0x80
blk_mq_poll+0x2b/0x40
bio_poll+0x94/0x180
iocb_bio_iopoll+0x31/0x50
io_uring_classic_poll+0x20/0x40
io_do_iopoll+0x233/0x430
? io_issue_sqe+0x2f/0x560
? io_submit_sqes+0x270/0x820
__do_sys_io_uring_enter+0x228/0x770
? handle_softirqs+0xc7/0x250
__x64_sys_io_uring_enter+0x21/0x30
x64_sys_call+0x17c8/0x1dd0
do_syscall_64+0xe0/0x5a0
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Expected behavior
=================
fio job completes after specified runtime.
Actual behavior
===============
fio job never completes, system becomes less responsive (if the number of poll
queues and jobs are high) and RCU stall checker detects stalls.
Observations
============
After some minimal investigation we found this notable function being called as
the callback for q->mq_ops->poll:
static int nvme_poll(struct blk_mq_hw_ctx *hctx, struct io_comp_batch *iob)
{
struct nvme_queue *nvmeq = hctx->driver_data;
bool found;
if (!test_bit(NVMEQ_POLLED, &nvmeq->flags) ||
!nvme_cqe_pending(nvmeq))
return 0;
spin_lock(&nvmeq->cq_poll_lock);
found = nvme_poll_cq(nvmeq, iob);
spin_unlock(&nvmeq->cq_poll_lock);
return found;
}
This function, when stuck on the RCU loop, always returns 0. It also always
calls the helper function nvme_cqe_pending.
Following this are some items that may help in reproducing this issue.
Steps to reproduce
==================
From a running QEMU image with the latest kernel:
1. Attach GDB to the running instance.
2. Enable io polling via sysfs (echo 1 > /sys/block/nvme0n1/queue/io_poll).
3. Execute the fio job below.
4. After 1-2 minutes, observe RCU stalls.
Offending fio job
=================
fio --bs=1K --direct=1 --iodepth=1 --runtime=1 --rw=randread --time_based \
--ioengine=io_uring --hipri=1 --fixedbufs=0 --registerfiles=0 \
--sqthread_poll=0 \
--numjobs=2 --name=job0 --output-format=json --clocksource=clock_gettime \
--filename=/dev/nvme0n1
Kernel config
=============
Start with x86_defconfig
The following options are enabled for ease of debugging with GDB and QEMU.
In "Kernel Hacking" do the following:
- Set "Compile-time checks and compiler options -> Debug options" to "Rely on
the toolchain's implicit default DWARF version."
- Set "Compile-time checks and compiler options -> Provide GDB scripts for
debugging" to Yes.
- Set "x86 Debugging -> Choose kernel unwinder" to "Frame pointer unwinder."
In "Processor types and features" do the following:
- Set "Randomize the address of the kernel image (KASLR)" to No.
The following options are enabled to support NVMe over PCIe.
In "Device Drivers" do the following:
- Set "PCI Support -> PCI Endpoint support" to Yes.
- Set "NVMe Support -> NVM Express block device" to Module.
- Set "NVMe Support -> NVMe Target Support" to Module.
- Set "NVMe Support -> NVMe PCI Endpoint Function target support" to Module.
Kernel command line
===================
BOOT_IMAGE=/vmlinuz-7.1.0-g3996771b8f75 root=/dev/mapper/ubuntu--vg-ubuntu--lv \
ro nvme.poll_queues=1 nokaslr \
crashkernel=2G-4G:320M,4G-32G:512M,32G-64G:1024M,64G-128G:2048M,128G-:4096M
(nokaslr may be unneeded.)
QEMU command line
=================
qemu-system-x86_64 \
-m 4G -enable-kvm -monitor stdio -s -S -smp 8 \
-device nvme,serial=deadbeef,drive=nvm \
-drive file=disk.img,index=0,media=disk,if=virtio \
-drive file=nvme.img,index=1,media=disk,if=none,id=nvm \
-chardev socket,path=/tmp/port1,server=on,wait=off,id=port1-char \
-device virtio-serial \
-device virtserialport,id=port1,chardev=port1-char,name=org.fedoraproject.port.0 \
-net user,hostfwd=tcp::10022-:22,hostfwd=tcp::45455-:45455 \
-net nic
For us, disk.img and nvme.img are created via:
dd if=/dev/zero of=disk.img bs=4K count=5000000
dd if=/dev/zero of=nvme.img bs=4K count=2000000
We then format disk.img with ext4.
To install a test userspace we download an ISO of Ubuntu Server 26.04 and
append the filename as a parameter to the QEMU task. After installing it.
If you all think there's a better mailing list to which this should be sent,
please let me know. Also, please let me know if there are other details about
how to reproduce this issue or the system on which this issue appears, or if
you have any other questions.
Best wishes,
Benjamin Carey
^ permalink raw reply
* Re: [PATCH v2] scsi: bsg: read io_uring command fields once
From: Yang Xiuwei @ 2026-06-26 1:34 UTC (permalink / raw)
To: James E . J . Bottomley, Martin K . Petersen
Cc: Yang Xiuwei, Rahul Chandelkar, Jens Axboe, FUJITA Tomonori,
linux-scsi, linux-block, io-uring, Bart Van Assche,
Caleb Sander Mateos
In-Reply-To: <55f36cc5-a013-4960-8787-fbdf4b4d0c20@kernel.dk>
Hi James, Martin,
Following Jens's feedback — would you prefer picking up v2 as-is, or
a narrowed follow-up? Happy to help Rahul with the latter if useful.
I also have a separate GFP_NOWAIT fix for the same path. Happy to
rebase once this lands, or send a combined series if you prefer.
Thanks,
Yang Xiuwei
^ permalink raw reply
* Re: [PATCH v6] io_uring/register: add IORING_REGISTER_CLONE_FILES opcode
From: Gabriel Krisman Bertazi @ 2026-06-25 20:33 UTC (permalink / raw)
To: Harshal Chavan, harshal24.chavan
Cc: axboe, gregkh, gustavoars, io-uring, kees, linux-hardening,
linux-kernel
In-Reply-To: <20260624124019.4521-1-harshal24.chavan@gmail.com>
Harshal Chavan <harshal24.chavan@gmail.com> writes:
> Currently, if an application wants to duplicate registered file
> descriptors from one io_uring instance to another, it must manually
> unregister and re-register them, incurring unnecessary overhead.
>
> Add IORING_REGISTER_CLONE_FILES to allow direct cloning of the file
> table from a source ring to a destination ring. This implementation
> strictly mirrors the io_clone_buffers UAPI, supporting partial offsets
> and the IORING_REGISTER_DST_REPLACE flag.
>
> To ensure lock synchronization safety, destination nodes are strictly
> allocated as new, private io_rsrc_nodes rather than sharing references
> across rings.
>
> Signed-off-by: Harshal Chavan <harshal24.chavan@gmail.com>
>
> ---
> Sorry for the noise on the previous email! I accidentally sent the patch
> before running checkpatch and missed a whitespace error. This v6 corrects it.
>
> v6:
> - Fixed trailing whitespace checkpatch error.
> v5:
> - Added missing spacing in comment (Gabriel).
> - Removed ctx->user and mm_account checks (Gabriel).
> - Used !! for boolean conversion (Gabriel).
> - Moved mutex_unlock unconditionally above the out label (Gabriel).
> - liburing implementation and tests: https://github.com/axboe/liburing/pull/1606
> v4:
> - Updated Signed-off-by to use real name and moved above the scissors line (Greg KH).
> v3:
> - Rewrote the cloning loop to allocate private destination nodes via io_rsrc_node_alloc to fix non-atomic ref lock synchronization (Jens).
> - Maintained partial offset/copy support to mirror io_clone_buffers UAPI (Jens).
> - Gated the replacement free check on ctx->file_table.data.nr (Gabriel).
> - Prevented self-cloning by checking ctx == src_ctx (Gabriel).
> - Removed submitter_task check to allow cross-thread pooling setups (Gabriel).
> v2:
> - Dropped unrelated whitespace formatting changes from v1
> +static int io_clone_file_node(struct io_ring_ctx *ctx,
> + struct io_rsrc_node *src_node,
> + int dst_index,
> + struct io_file_table *new_table)
> +{
> + struct io_rsrc_node *dst_node;
> + struct file *file;
> +
> + dst_node = io_rsrc_node_alloc(ctx, IORING_RSRC_FILE);
> + if (!dst_node)
> + return -ENOMEM;
> +
> + file = io_slot_file(src_node);
> + get_file(file);
> + io_fixed_file_set(dst_node, file);
don't you need to copy the src_node->tag here as well?
I didn't get a chance to run it yet, sorry. I'd suggest you wait for
Jens feedback before pushing the v7 too, so you don't need to keep
iterating drop by drop :)
> +
> + new_table->data.nodes[dst_index] = dst_node;
> + io_file_bitmap_set(new_table, dst_index);
> +
> + return 0;
> +}
> +
> +static int io_clone_files(struct io_ring_ctx *ctx, struct io_ring_ctx *src_ctx,
> + struct io_uring_clone_files *arg)
> +{
> + struct io_file_table new_file_table;
> + unsigned int dst_nr = ctx->file_table.data.nr;
> + unsigned int src_nr = src_ctx->file_table.data.nr;
> + unsigned int new_nr, i;
> +
> + lockdep_assert_held(&ctx->uring_lock);
> + lockdep_assert_held(&src_ctx->uring_lock);
> +
> + if (dst_nr && !(arg->flags & IORING_REGISTER_DST_REPLACE))
> + return -EBUSY;
> +
> + if (!src_nr)
> + return -ENXIO;
> +
> + if (!arg->nr)
> + arg->nr = src_nr;
> + else if (arg->nr > src_nr)
> + return -EINVAL;
> +
> + if (check_add_overflow(arg->src_off, arg->nr, &i) || i > src_nr)
> + return -EINVAL;
> + if (check_add_overflow(arg->dst_off, arg->nr, &i))
> + return -EINVAL;
> +
> + new_nr = max(dst_nr, arg->dst_off + arg->nr);
> + if (new_nr > IORING_MAX_FIXED_FILES)
> + return -EINVAL;
> +
> + memset(&new_file_table, 0, sizeof(new_file_table));
> + if (!io_alloc_file_tables(ctx, &new_file_table, new_nr))
> + return -ENOMEM;
> +
> + /* Copy original nodes from before the cloned range */
> + for (i = 0; i < min(arg->dst_off, dst_nr); i++) {
> + struct io_rsrc_node *src_node = io_rsrc_node_lookup(&ctx->file_table.data, i);
> +
> + if (!src_node)
> + continue;
> + if (io_clone_file_node(ctx, src_node, i, &new_file_table))
> + goto out;
> + }
> +
> + /* Copy the actual cloned range from the source ring */
> + for (i = 0; i < arg->nr; i++) {
> + struct io_rsrc_node *src_node = io_rsrc_node_lookup(&src_ctx->file_table.data,
> + arg->src_off + i);
> +
> + if (!src_node)
> + continue;
> + if (io_clone_file_node(ctx, src_node, arg->dst_off + i, &new_file_table))
> + goto out;
> + }
> +
> + /* Copy original nodes from after the cloned range */
> + for (i = arg->dst_off + arg->nr; i < dst_nr; i++) {
> + struct io_rsrc_node *src_node = io_rsrc_node_lookup(&ctx->file_table.data, i);
> +
> + if (!src_node)
> + continue;
> + if (io_clone_file_node(ctx, src_node, i, &new_file_table))
> + goto out;
> + }
> +
> + /* free the old file table if there is any data present */
> + if (dst_nr)
> + io_free_file_tables(ctx, &ctx->file_table);
> +
> + WARN_ON_ONCE(ctx->file_table.data.nr);
> + ctx->file_table = new_file_table;
> + io_file_table_set_alloc_range(ctx, 0, ctx->file_table.data.nr);
> + return 0;
> +
> +out:
> + /* Error Path: Safely destroy whatever we partially built */
> + io_free_file_tables(ctx, &new_file_table);
> + return -ENOMEM;
> +}
> +
> +int io_register_clone_files(struct io_ring_ctx *ctx, void __user *arg)
> +{
> + struct io_uring_clone_files clone_arg;
> + struct io_ring_ctx *src_ctx;
> + bool registered_src;
> + struct file *file;
> + int ret;
> +
> + if (copy_from_user(&clone_arg, arg, sizeof(clone_arg)))
> + return -EFAULT;
> + if (clone_arg.flags &
> + ~(IORING_REGISTER_SRC_REGISTERED | IORING_REGISTER_DST_REPLACE))
> + return -EINVAL;
> +
> + if (memchr_inv(clone_arg.pad, 0, sizeof(clone_arg.pad)))
> + return -EINVAL;
> +
> + registered_src = !!(clone_arg.flags & IORING_REGISTER_SRC_REGISTERED);
> + file = io_uring_ctx_get_file(clone_arg.src_fd, registered_src);
> + if (IS_ERR(file))
> + return PTR_ERR(file);
> +
> + src_ctx = file->private_data;
> + /* Same ring clone is not allowed */
> + if (src_ctx == ctx) {
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + mutex_unlock(&ctx->uring_lock);
> + lock_two_rings(ctx, src_ctx);
> +
> + ret = io_clone_files(ctx, src_ctx, &clone_arg);
> +
> + mutex_unlock(&src_ctx->uring_lock);
> +
> +out:
> + if (!registered_src)
> + fput(file);
> + return ret;
> +}
> +
> void io_vec_free(struct iou_vec *iv)
> {
> if (!iv->iovec)
> diff --git a/io_uring/rsrc.h b/io_uring/rsrc.h
> index 44e3386f7c1c..32f5c47c46af 100644
> --- a/io_uring/rsrc.h
> +++ b/io_uring/rsrc.h
> @@ -75,6 +75,7 @@ int io_prep_reg_iovec(struct io_kiocb *req, struct iou_vec *iv,
> const struct iovec __user *uvec, size_t uvec_segs);
>
> int io_register_clone_buffers(struct io_ring_ctx *ctx, void __user *arg);
> +int io_register_clone_files(struct io_ring_ctx *ctx, void __user *arg);
> int io_sqe_buffers_unregister(struct io_ring_ctx *ctx);
> int io_sqe_buffers_register(struct io_ring_ctx *ctx, void __user *arg,
> unsigned int nr_args, u64 __user *tags);
> --
> 2.54.0
>
--
Gabriel Krisman Bertazi
^ permalink raw reply
* Re: [GIT PULL] io_uring fixes for 7.2-rc1
From: pr-tracker-bot @ 2026-06-25 17:10 UTC (permalink / raw)
To: Jens Axboe; +Cc: Linus Torvalds, io-uring
In-Reply-To: <8762b3ca-0f33-4aa1-9d81-76dcbd222676@kernel.dk>
The pull request you sent on Thu, 25 Jun 2026 06:08:47 -0600:
> https://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux.git tags/io_uring-7.2-20260625
has been merged into torvalds/linux.git:
https://git.kernel.org/torvalds/c/c58ddac1aa507b71cb5a95a95c641bdd73a3f075
Thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/prtracker.html
^ permalink raw reply
* [PATCH for-next] io_uring: remove redundant NULL check before kfree
From: Subasri S @ 2026-06-25 14:37 UTC (permalink / raw)
To: Jens Axboe; +Cc: io-uring, linux-kernel, Subasri S
Remove the unnecessary NULL pointer check before calling kfree()
in io_uring.c, as kfree() handles NULL arguments internally.
Reported by ifnullfree.cocci Coccinelle semantic
patch script.
Signed-off-by: Subasri S <subasris1210@gmail.com>
---
io_uring/io_uring.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c
index 1ea2fca34a36..ac499722aaee 100644
--- a/io_uring/io_uring.c
+++ b/io_uring/io_uring.c
@@ -1114,8 +1114,7 @@ static void io_free_batch_list(struct io_ring_ctx *ctx,
if ((req->flags & REQ_F_POLLED) && req->apoll) {
struct async_poll *apoll = req->apoll;
- if (apoll->double_poll)
- kfree(apoll->double_poll);
+ kfree(apoll->double_poll);
io_cache_free(&ctx->apoll_cache, apoll);
req->flags &= ~REQ_F_POLLED;
}
--
2.43.0
^ permalink raw reply related
* [GIT PULL] io_uring fixes for 7.2-rc1
From: Jens Axboe @ 2026-06-25 12:08 UTC (permalink / raw)
To: Linus Torvalds; +Cc: io-uring
Hi Linus,
A few io_uring fixes that came in during the merge window. This contains:
- Fix a file reference leak in the nop opcode when used with
IOSQE_FIXED_FILE.
- Preserve the SQ array entries when resizing the ring via the register
path.
- Preserve the partial result for an iopoll request rather than
overwriting it.
- Don't audit log IORING_OP_RECV_ZC.
- Bound io_pin_pages() by the page array byte size in the memmap path.
- Follow-up cleanup to the task_work mpscq conversion, getting rid of
the now-unnecessary tw_pending tracking for the !DEFER_TASKRUN path.
- Switch a system_unbound_wq user over to system_dfl_wq
Please pull!
The following changes since commit 8b308f96484e37d92d2fc6b72b091f60496c000e:
Merge tag 'linux_kselftest-next-7.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest (2026-06-16 16:49:07 +0530)
are available in the Git repository at:
https://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux.git tags/io_uring-7.2-20260625
for you to fetch changes up to 3996771b8f759729cba0a28007438c085f814d61:
io_uring/memmap: bound io_pin_pages() by page array byte size (2026-06-22 15:12:54 -0600)
----------------------------------------------------------------
io_uring-7.2-20260625
----------------------------------------------------------------
Deepanshu Kartikey (1):
io_uring/memmap: bound io_pin_pages() by page array byte size
Jens Axboe (1):
io_uring: get rid of tw_pending for !DEFER task work
Michael Wigham (1):
io_uring/rw: preserve partial result for iopoll
Nathan Chancellor (1):
io_uring: Use system_dfl_wq instead of system_unbound_wq
Ricardo Robaina (1):
io_uring, audit: don't log IORING_OP_RECV_ZC
Vasileios Almpanis (1):
io_uring/nop: fix file reference leak with IOSQE_FIXED_FILE
guzebing (1):
io_uring/register: preserve SQ array entries on resize
include/linux/io_uring_types.h | 2 --
io_uring/memmap.c | 2 +-
io_uring/mpscq.h | 9 +++++++++
io_uring/nop.c | 8 ++++----
io_uring/opdef.c | 1 +
io_uring/register.c | 31 +++++++++++++++++++++----------
io_uring/rw.c | 12 ++++++------
io_uring/tw.c | 21 +++++++++------------
8 files changed, 51 insertions(+), 35 deletions(-)
--
Jens Axboe
^ permalink raw reply
* Re: [PATCH v2] scsi: bsg: read io_uring command fields once
From: Jens Axboe @ 2026-06-25 12:06 UTC (permalink / raw)
To: Yang Xiuwei, James E . J . Bottomley, Martin K . Petersen
Cc: Rahul Chandelkar, FUJITA Tomonori, linux-scsi, linux-block,
io-uring, Bart Van Assche, Caleb Sander Mateos
In-Reply-To: <20260626020000.0000000-1-yangxiuwei@kylinos.cn>
On 6/24/26 9:25 PM, Yang Xiuwei wrote:
> Hi James, Martin,
>
> Friendly ping on v2 ? anything else needed before pick-up?
It'll fix the issue, but it also just applies READ_ONCE() everywhere.
Which is fine, but most of them don't really matter. For example, yes
you could race on the timeout if the application is being stupid or
silly, but it doesn't matter one bit. Similarly with a bunch of others.
I'll leave that up to the SCSI folks to decide how they want to do it.
--
Jens Axboe
^ permalink raw reply
* Re: [PATCH v3 1/7] list: Add mutable iterator variants
From: Jani Nikula @ 2026-06-25 11:00 UTC (permalink / raw)
To: Kaitao Cheng, David Laight, Christian König,
David Hildenbrand (Arm), Alexei Starovoitov
Cc: Andrew Morton, David Hildenbrand, Jens Axboe, Tejun Heo,
Alexander Viro, Christian Brauner, Daniel Borkmann,
Andrii Nakryiko, Johannes Weiner, Peter Zijlstra, Ingo Molnar,
Arnaldo Carvalho de Melo, Namhyung Kim, Thomas Gleixner,
Juri Lelli, Vincent Guittot, Paul Moore, Andy Shevchenko,
Paul E. McKenney, Shakeel Butt, David Howells, Simona Vetter,
Randy Dunlap, Luca Ceresoli, Philipp Stanner, linux-block,
linux-kernel, cgroups, linux-ntfs-dev, linux-fsdevel, io-uring,
audit, bpf, netdev, dri-devel, linux-perf-users,
linux-trace-kernel, kexec, live-patching, linux-modules,
linux-crypto, linux-pm, rcu, sched-ext, linux-mm, virtualization,
damon, llvm, Kaitao Cheng, Muchun Song
In-Reply-To: <0ed6b5c3-e955-46e2-9fc6-075a0dfd1c4f@linux.dev>
On Thu, 25 Jun 2026, Kaitao Cheng <kaitao.cheng@linux.dev> wrote:
> 在 2026/6/24 22:23, David Laight 写道:
>> On Wed, 24 Jun 2026 15:23:47 +0200
>> Christian König <christian.koenig@amd.com> wrote:
>>> On 6/24/26 15:14, Kaitao Cheng wrote:
>>>> 在 2026/6/22 16:42, David Laight 写道:
>>>>> On Mon, 22 Jun 2026 12:05:31 +0800
>>>>> Kaitao Cheng <kaitao.cheng@linux.dev> wrote:
>>>>>
>>>>>> From: Kaitao Cheng <chengkaitao@kylinos.cn>
>>>>>>
>>>>>> The list_for_each*_safe() helpers are used when the loop body may
>>>>>> remove the current entry. Their API exposes the temporary cursor at
>>>>>> every call site, even though most users only need it for the iterator
>>>>>> implementation and never reference it in the loop body.
>>>>>>
>>>>>> Add *_mutable() variants for list and hlist iteration. The new helpers
>>>>>> support both forms: callers may keep passing an explicit temporary cursor
>>>>>> when they need to inspect or reset it, or omit it and let the helper use
>>>>>> a unique internal cursor.
>>>>>
>>>>> I'm not really sure 'mutable' means anything either.
>>>>> It is possible to make it valid for the loop body (or even other threads)
>>>>> to delete arbitrary list items - but that needs significant extra overheads.
>>>>>
>>>>> It might be worth doing something that doesn't need the extra variable,
>>>>> but there is little point doing all the churn just to rename things.
>>>>>
>>>>>>
>>>>>> This makes call sites that only mutate the list through the current entry
>>>>>> less noisy, while keeping the existing *_safe() helpers available for
>>>>>> compatibility.
>>>>>>
>>>>>> Signed-off-by: Kaitao Cheng <chengkaitao@kylinos.cn>
>>>>>> ---
>>>>>> include/linux/list.h | 269 +++++++++++++++++++++++++++++++++++++------
>>>>>> 1 file changed, 231 insertions(+), 38 deletions(-)
>>>>>>
>>>>>> diff --git a/include/linux/list.h b/include/linux/list.h
>>>>>> index 09d979976b3b..1081def7cea9 100644
>>>>>> --- a/include/linux/list.h
>>>>>> +++ b/include/linux/list.h
>>>>>> @@ -7,6 +7,7 @@
>>>>>> #include <linux/stddef.h>
>>>>>> #include <linux/poison.h>
>>>>>> #include <linux/const.h>
>>>>>> +#include <linux/args.h>
>>>>>>
>>>>>> #include <asm/barrier.h>
>>>>>>
>>>>>> @@ -763,28 +764,72 @@ static inline void list_splice_tail_init(struct list_head *list,
>>>>>> #define list_for_each_prev(pos, head) \
>>>>>> for (pos = (head)->prev; !list_is_head(pos, (head)); pos = pos->prev)
>>>>>>
>>>>>> -/**
>>>>>> - * list_for_each_safe - iterate over a list safe against removal of list entry
>>>>>> - * @pos: the &struct list_head to use as a loop cursor.
>>>>>> - * @n: another &struct list_head to use as temporary storage
>>>>>> - * @head: the head for your list.
>>>>>> +/*
>>>>>> + * list_for_each_safe is an old interface, use list_for_each_mutable instead.
>>>>>> */
>>>>>> #define list_for_each_safe(pos, n, head) \
>>>>>> for (pos = (head)->next, n = pos->next; \
>>>>>> !list_is_head(pos, (head)); \
>>>>>> pos = n, n = pos->next)
>>>>>>
>>>>>> +#define __list_for_each_mutable_internal(pos, tmp, head) \
>>>>>> + for (typeof(pos) tmp = (pos = (head)->next)->next; \
>>>>>
>>>>> Use auto
>>>>>
>>>>>> + !list_is_head(pos, (head)); \
>>>>>> + pos = tmp, tmp = pos->next)
>>>>>> +
>>>>>> +#define __list_for_each_mutable1(pos, head) \
>>>>>> + __list_for_each_mutable_internal(pos, __UNIQUE_ID(next), head)
>>>>>> +
>>>>>> +#define __list_for_each_mutable2(pos, next, head) \
>>>>>> + list_for_each_safe(pos, next, head)
>>>>>> +
>>>>>> /**
>>>>>> - * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
>>>>>> + * list_for_each_mutable - iterate over a list safe against entry removal
>>>>>> * @pos: the &struct list_head to use as a loop cursor.
>>>>>> - * @n: another &struct list_head to use as temporary storage
>>>>>> - * @head: the head for your list.
>>>>>> + * @...: either (head) or (next, head)
>>>>>> + *
>>>>>> + * next: another &struct list_head to use as optional temporary storage.
>>>>>> + * The temporary cursor is internal unless explicitly supplied by
>>>>>> + * the caller.
>>>>>> + * head: the head for your list.
>>>>>> + */
>>>>>> +#define list_for_each_mutable(pos, ...) \
>>>>>> + CONCATENATE(__list_for_each_mutable, COUNT_ARGS(__VA_ARGS__)) \
>>>>>> + (pos, __VA_ARGS__)
>>>>>
>>>>> The variable argument count logic really just slows down compilation.
>>>>> Maybe there aren't enough copies of this code to make that significant.
>>>>> But just because you can do it doesn't mean it is a gooD idea.
>>>>> I'm also not sure it really adds anything to the readability.
>>>>>
>>>>> And, it you are going to make the middle argument optional there is
>>>>> no need to change the macro name.
>>>>
>>>> Christian König and Jani Nikula also disagree with the variadic-argument
>>>> implementation approach. If we abandon that method, it means we will
>>>> inevitably need to add some new macros. If mutable is not a good name,
>>>> suggestions for better alternatives would be welcome; coming up with a
>>>> suitable name is indeed rather tricky.
>>>
>>> I don't think you need to add a new macro for the specific use case that people want to modify the next element of the iteration.
>>>
>>> If I remember your numbers correctly that is a really corner case and keeping using the existing *_safe() macros for that sounds perfectly fine to me.
>>
>> IIRC currently you have a choice of either:
>> define Item that can't be deleted
>> list_for_each() The current item.
>> list_for_each_safe() The next item.
>> There is also likely to be code that updates the variables to allow
>> for other scenarios.
>>
>> Note that if increase a reference count and release a lock then list_for_each()
>> is likely safer than list_for_each_safe() :-)
>>
>> list.h has 9 variants of the 'safe' loop.
>> The bloat of another 9 is getting excessive.
>>
>> It has to be said that this is one of my least favourite type of list...
>
> Hi Christian König, David Laight, Jani Nikula, David Hildenbrand,
> Andy Shevchenko, Alexei Starovoitov
>
> For ease of discussion, I need to summarize the currently possible
> approaches and briefly describe their respective pros and cons,
> using the list_for_each_entry* interfaces as examples.
>
> 1. Add list_for_each_entry_mutable, while keeping list_for_each_entry
> and list_for_each_entry_safe unchanged. list_for_each_entry_mutable
> would be used specifically for safe deletion scenarios that do not
> need to expose the temporary cursor externally. The code can refer to
> the v1 version.
>
> Pros: Does not depend on immediate per-subsystem adaptation and can be
> merged directly.
> Cons: Requires adding a whole set of mutable interfaces, which makes the
> code somewhat redundant.
Seems fine, and the original _safe naming is ambiguous anyway.
> 2. Directly optimize away the temporary cursor in list_for_each_entry_safe
> and define it inside the loop instead, changing the interface from four
> arguments to three.
>
> Pros: Does not add redundant interfaces.
> Cons: (1) Users need to manually update special cases that use the
> traversal variable of list_for_each_entry_safe, the new
> list_for_each_entry_safe would no longer apply there and would
> need to be open-coded.
> (2) Because the macro arguments changes, all list_for_each_entry_safe
> callers would need to be modified and merged together, making it
> difficult to merge such a large amount of code at once.
This won't fly because there are literally thousands of
list_for_each_entry_safe() users.
> 3. Use a variadic macro approach to optimize list_for_each_entry_safe,
> so that it supports both three and four arguments.
>
> Pros: (1) Does not add redundant interfaces.
> (2) Does not depend on immediate per-subsystem adaptation and can
> be merged directly.
> Cons: (1) Increases compile time.
> (2) Makes the interface harder for users to use.
Basically I'm against any variadic macro tricks where the optional
argument is not the last argument. That's just way too surprising, and
goes against common practice in just about all other languages.
> 4. Optimize list_for_each_entry by defining the temporary cursor internally,
> making it compatible with the functionality of list_for_each_entry_safe.
> The code can refer to the v2 version.
>
> Pros: (1) Does not add redundant interfaces.
> (2) The number of externally visible arguments of list_for_each_entry
> remains unchanged, still three.
> Cons: (1) list_for_each_entry and list_for_each_entry_safe would be merged
> into one, and list_for_each_entry_safe would gradually be deprecated.
> (2) Users need to manually update special cases that use the traversal
> variable of list_for_each_entry, the new list_for_each_entry would no
> longer apply there and would need to be open-coded. There are 15 such
> cases in total.
This sounds good to me, though I take it there's some code size increase
and/or performance penalty?
Maybe the 15 cases are questionable anyway?
> 5. Use a variadic macro approach to optimize list_for_each_entry, so that
> it supports both three and four arguments.
>
> Pros: (1) Does not add redundant interfaces.
> (2) Does not depend on immediate per-subsystem adaptation and can be
> merged directly.
> Cons: (1) Increases compile time.
> (2) list_for_each_entry and list_for_each_entry_safe would be merged
> into one, and list_for_each_entry_safe would gradually be deprecated.
Please don't do the macro tricks.
> 6. Make no changes, keep the current logic unchanged, and close the current
> email discussion.
I like hiding the temporary stuff when possible.
BR,
Jani.
--
Jani Nikula, Intel
^ permalink raw reply
* Re: [PATCH v2] scsi: bsg: read io_uring command fields once
From: Yang Xiuwei @ 2026-06-25 3:25 UTC (permalink / raw)
To: James E . J . Bottomley, Martin K . Petersen
Cc: Yang Xiuwei, Rahul Chandelkar, Jens Axboe, FUJITA Tomonori,
linux-scsi, linux-block, io-uring, Bart Van Assche,
Caleb Sander Mateos
In-Reply-To: <20260527191817.142769-1-rc@rexion.ai>
Hi James, Martin,
Friendly ping on v2 — anything else needed before pick-up?
Thanks,
Yang Xiuwei
^ permalink raw reply
* Re: [PATCH v3 1/7] list: Add mutable iterator variants
From: Kaitao Cheng @ 2026-06-25 3:01 UTC (permalink / raw)
To: David Laight, Christian König, Jani Nikula,
David Hildenbrand (Arm), Alexei Starovoitov
Cc: Andrew Morton, David Hildenbrand, Jens Axboe, Tejun Heo,
Alexander Viro, Christian Brauner, Daniel Borkmann,
Andrii Nakryiko, Johannes Weiner, Peter Zijlstra, Ingo Molnar,
Arnaldo Carvalho de Melo, Namhyung Kim, Thomas Gleixner,
Juri Lelli, Vincent Guittot, Paul Moore, Andy Shevchenko,
Paul E. McKenney, Shakeel Butt, David Howells, Simona Vetter,
Randy Dunlap, Luca Ceresoli, Philipp Stanner, linux-block,
linux-kernel, cgroups, linux-ntfs-dev, linux-fsdevel, io-uring,
audit, bpf, netdev, dri-devel, linux-perf-users,
linux-trace-kernel, kexec, live-patching, linux-modules,
linux-crypto, linux-pm, rcu, sched-ext, linux-mm, virtualization,
damon, llvm, Kaitao Cheng, Muchun Song
In-Reply-To: <20260624152324.3def88ce@pumpkin>
在 2026/6/24 22:23, David Laight 写道:
> On Wed, 24 Jun 2026 15:23:47 +0200
> Christian König <christian.koenig@amd.com> wrote:
>> On 6/24/26 15:14, Kaitao Cheng wrote:
>>> 在 2026/6/22 16:42, David Laight 写道:
>>>> On Mon, 22 Jun 2026 12:05:31 +0800
>>>> Kaitao Cheng <kaitao.cheng@linux.dev> wrote:
>>>>
>>>>> From: Kaitao Cheng <chengkaitao@kylinos.cn>
>>>>>
>>>>> The list_for_each*_safe() helpers are used when the loop body may
>>>>> remove the current entry. Their API exposes the temporary cursor at
>>>>> every call site, even though most users only need it for the iterator
>>>>> implementation and never reference it in the loop body.
>>>>>
>>>>> Add *_mutable() variants for list and hlist iteration. The new helpers
>>>>> support both forms: callers may keep passing an explicit temporary cursor
>>>>> when they need to inspect or reset it, or omit it and let the helper use
>>>>> a unique internal cursor.
>>>>
>>>> I'm not really sure 'mutable' means anything either.
>>>> It is possible to make it valid for the loop body (or even other threads)
>>>> to delete arbitrary list items - but that needs significant extra overheads.
>>>>
>>>> It might be worth doing something that doesn't need the extra variable,
>>>> but there is little point doing all the churn just to rename things.
>>>>
>>>>>
>>>>> This makes call sites that only mutate the list through the current entry
>>>>> less noisy, while keeping the existing *_safe() helpers available for
>>>>> compatibility.
>>>>>
>>>>> Signed-off-by: Kaitao Cheng <chengkaitao@kylinos.cn>
>>>>> ---
>>>>> include/linux/list.h | 269 +++++++++++++++++++++++++++++++++++++------
>>>>> 1 file changed, 231 insertions(+), 38 deletions(-)
>>>>>
>>>>> diff --git a/include/linux/list.h b/include/linux/list.h
>>>>> index 09d979976b3b..1081def7cea9 100644
>>>>> --- a/include/linux/list.h
>>>>> +++ b/include/linux/list.h
>>>>> @@ -7,6 +7,7 @@
>>>>> #include <linux/stddef.h>
>>>>> #include <linux/poison.h>
>>>>> #include <linux/const.h>
>>>>> +#include <linux/args.h>
>>>>>
>>>>> #include <asm/barrier.h>
>>>>>
>>>>> @@ -763,28 +764,72 @@ static inline void list_splice_tail_init(struct list_head *list,
>>>>> #define list_for_each_prev(pos, head) \
>>>>> for (pos = (head)->prev; !list_is_head(pos, (head)); pos = pos->prev)
>>>>>
>>>>> -/**
>>>>> - * list_for_each_safe - iterate over a list safe against removal of list entry
>>>>> - * @pos: the &struct list_head to use as a loop cursor.
>>>>> - * @n: another &struct list_head to use as temporary storage
>>>>> - * @head: the head for your list.
>>>>> +/*
>>>>> + * list_for_each_safe is an old interface, use list_for_each_mutable instead.
>>>>> */
>>>>> #define list_for_each_safe(pos, n, head) \
>>>>> for (pos = (head)->next, n = pos->next; \
>>>>> !list_is_head(pos, (head)); \
>>>>> pos = n, n = pos->next)
>>>>>
>>>>> +#define __list_for_each_mutable_internal(pos, tmp, head) \
>>>>> + for (typeof(pos) tmp = (pos = (head)->next)->next; \
>>>>
>>>> Use auto
>>>>
>>>>> + !list_is_head(pos, (head)); \
>>>>> + pos = tmp, tmp = pos->next)
>>>>> +
>>>>> +#define __list_for_each_mutable1(pos, head) \
>>>>> + __list_for_each_mutable_internal(pos, __UNIQUE_ID(next), head)
>>>>> +
>>>>> +#define __list_for_each_mutable2(pos, next, head) \
>>>>> + list_for_each_safe(pos, next, head)
>>>>> +
>>>>> /**
>>>>> - * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
>>>>> + * list_for_each_mutable - iterate over a list safe against entry removal
>>>>> * @pos: the &struct list_head to use as a loop cursor.
>>>>> - * @n: another &struct list_head to use as temporary storage
>>>>> - * @head: the head for your list.
>>>>> + * @...: either (head) or (next, head)
>>>>> + *
>>>>> + * next: another &struct list_head to use as optional temporary storage.
>>>>> + * The temporary cursor is internal unless explicitly supplied by
>>>>> + * the caller.
>>>>> + * head: the head for your list.
>>>>> + */
>>>>> +#define list_for_each_mutable(pos, ...) \
>>>>> + CONCATENATE(__list_for_each_mutable, COUNT_ARGS(__VA_ARGS__)) \
>>>>> + (pos, __VA_ARGS__)
>>>>
>>>> The variable argument count logic really just slows down compilation.
>>>> Maybe there aren't enough copies of this code to make that significant.
>>>> But just because you can do it doesn't mean it is a gooD idea.
>>>> I'm also not sure it really adds anything to the readability.
>>>>
>>>> And, it you are going to make the middle argument optional there is
>>>> no need to change the macro name.
>>>
>>> Christian König and Jani Nikula also disagree with the variadic-argument
>>> implementation approach. If we abandon that method, it means we will
>>> inevitably need to add some new macros. If mutable is not a good name,
>>> suggestions for better alternatives would be welcome; coming up with a
>>> suitable name is indeed rather tricky.
>>
>> I don't think you need to add a new macro for the specific use case that people want to modify the next element of the iteration.
>>
>> If I remember your numbers correctly that is a really corner case and keeping using the existing *_safe() macros for that sounds perfectly fine to me.
>
> IIRC currently you have a choice of either:
> define Item that can't be deleted
> list_for_each() The current item.
> list_for_each_safe() The next item.
> There is also likely to be code that updates the variables to allow
> for other scenarios.
>
> Note that if increase a reference count and release a lock then list_for_each()
> is likely safer than list_for_each_safe() :-)
>
> list.h has 9 variants of the 'safe' loop.
> The bloat of another 9 is getting excessive.
>
> It has to be said that this is one of my least favourite type of list...
Hi Christian König, David Laight, Jani Nikula, David Hildenbrand,
Andy Shevchenko, Alexei Starovoitov
For ease of discussion, I need to summarize the currently possible
approaches and briefly describe their respective pros and cons,
using the list_for_each_entry* interfaces as examples.
1. Add list_for_each_entry_mutable, while keeping list_for_each_entry
and list_for_each_entry_safe unchanged. list_for_each_entry_mutable
would be used specifically for safe deletion scenarios that do not
need to expose the temporary cursor externally. The code can refer to
the v1 version.
Pros: Does not depend on immediate per-subsystem adaptation and can be
merged directly.
Cons: Requires adding a whole set of mutable interfaces, which makes the
code somewhat redundant.
2. Directly optimize away the temporary cursor in list_for_each_entry_safe
and define it inside the loop instead, changing the interface from four
arguments to three.
Pros: Does not add redundant interfaces.
Cons: (1) Users need to manually update special cases that use the
traversal variable of list_for_each_entry_safe, the new
list_for_each_entry_safe would no longer apply there and would
need to be open-coded.
(2) Because the macro arguments changes, all list_for_each_entry_safe
callers would need to be modified and merged together, making it
difficult to merge such a large amount of code at once.
3. Use a variadic macro approach to optimize list_for_each_entry_safe,
so that it supports both three and four arguments.
Pros: (1) Does not add redundant interfaces.
(2) Does not depend on immediate per-subsystem adaptation and can
be merged directly.
Cons: (1) Increases compile time.
(2) Makes the interface harder for users to use.
4. Optimize list_for_each_entry by defining the temporary cursor internally,
making it compatible with the functionality of list_for_each_entry_safe.
The code can refer to the v2 version.
Pros: (1) Does not add redundant interfaces.
(2) The number of externally visible arguments of list_for_each_entry
remains unchanged, still three.
Cons: (1) list_for_each_entry and list_for_each_entry_safe would be merged
into one, and list_for_each_entry_safe would gradually be deprecated.
(2) Users need to manually update special cases that use the traversal
variable of list_for_each_entry, the new list_for_each_entry would no
longer apply there and would need to be open-coded. There are 15 such
cases in total.
5. Use a variadic macro approach to optimize list_for_each_entry, so that
it supports both three and four arguments.
Pros: (1) Does not add redundant interfaces.
(2) Does not depend on immediate per-subsystem adaptation and can be
merged directly.
Cons: (1) Increases compile time.
(2) list_for_each_entry and list_for_each_entry_safe would be merged
into one, and list_for_each_entry_safe would gradually be deprecated.
6. Make no changes, keep the current logic unchanged, and close the current
email discussion.
Which of the six solutions above do people prefer?
--
Thanks
Kaitao Cheng
^ permalink raw reply
* Re: [PATCH v3 1/7] list: Add mutable iterator variants
From: David Laight @ 2026-06-24 14:23 UTC (permalink / raw)
To: Christian König
Cc: Kaitao Cheng, Andrew Morton, David Hildenbrand, Jens Axboe,
Tejun Heo, Alexander Viro, Christian Brauner, Alexei Starovoitov,
Daniel Borkmann, Andrii Nakryiko, Johannes Weiner, Peter Zijlstra,
Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim,
Thomas Gleixner, Juri Lelli, Vincent Guittot, Paul Moore,
Andy Shevchenko, Paul E. McKenney, Shakeel Butt, David Howells,
Simona Vetter, Randy Dunlap, Luca Ceresoli, Philipp Stanner,
linux-block, linux-kernel, cgroups, linux-ntfs-dev, linux-fsdevel,
io-uring, audit, bpf, netdev, dri-devel, linux-perf-users,
linux-trace-kernel, kexec, live-patching, linux-modules,
linux-crypto, linux-pm, rcu, sched-ext, linux-mm, virtualization,
damon, llvm, Kaitao Cheng
In-Reply-To: <cf8467c7-b98f-44a5-9cf9-60b43b5da711@amd.com>
On Wed, 24 Jun 2026 15:23:47 +0200
Christian König <christian.koenig@amd.com> wrote:
> On 6/24/26 15:14, Kaitao Cheng wrote:
> >
> >
> > 在 2026/6/22 16:42, David Laight 写道:
> >> On Mon, 22 Jun 2026 12:05:31 +0800
> >> Kaitao Cheng <kaitao.cheng@linux.dev> wrote:
> >>
> >>> From: Kaitao Cheng <chengkaitao@kylinos.cn>
> >>>
> >>> The list_for_each*_safe() helpers are used when the loop body may
> >>> remove the current entry. Their API exposes the temporary cursor at
> >>> every call site, even though most users only need it for the iterator
> >>> implementation and never reference it in the loop body.
> >>>
> >>> Add *_mutable() variants for list and hlist iteration. The new helpers
> >>> support both forms: callers may keep passing an explicit temporary cursor
> >>> when they need to inspect or reset it, or omit it and let the helper use
> >>> a unique internal cursor.
> >>
> >> I'm not really sure 'mutable' means anything either.
> >> It is possible to make it valid for the loop body (or even other threads)
> >> to delete arbitrary list items - but that needs significant extra overheads.
> >>
> >> It might be worth doing something that doesn't need the extra variable,
> >> but there is little point doing all the churn just to rename things.
> >>
> >>>
> >>> This makes call sites that only mutate the list through the current entry
> >>> less noisy, while keeping the existing *_safe() helpers available for
> >>> compatibility.
> >>>
> >>> Signed-off-by: Kaitao Cheng <chengkaitao@kylinos.cn>
> >>> ---
> >>> include/linux/list.h | 269 +++++++++++++++++++++++++++++++++++++------
> >>> 1 file changed, 231 insertions(+), 38 deletions(-)
> >>>
> >>> diff --git a/include/linux/list.h b/include/linux/list.h
> >>> index 09d979976b3b..1081def7cea9 100644
> >>> --- a/include/linux/list.h
> >>> +++ b/include/linux/list.h
> >>> @@ -7,6 +7,7 @@
> >>> #include <linux/stddef.h>
> >>> #include <linux/poison.h>
> >>> #include <linux/const.h>
> >>> +#include <linux/args.h>
> >>>
> >>> #include <asm/barrier.h>
> >>>
> >>> @@ -763,28 +764,72 @@ static inline void list_splice_tail_init(struct list_head *list,
> >>> #define list_for_each_prev(pos, head) \
> >>> for (pos = (head)->prev; !list_is_head(pos, (head)); pos = pos->prev)
> >>>
> >>> -/**
> >>> - * list_for_each_safe - iterate over a list safe against removal of list entry
> >>> - * @pos: the &struct list_head to use as a loop cursor.
> >>> - * @n: another &struct list_head to use as temporary storage
> >>> - * @head: the head for your list.
> >>> +/*
> >>> + * list_for_each_safe is an old interface, use list_for_each_mutable instead.
> >>> */
> >>> #define list_for_each_safe(pos, n, head) \
> >>> for (pos = (head)->next, n = pos->next; \
> >>> !list_is_head(pos, (head)); \
> >>> pos = n, n = pos->next)
> >>>
> >>> +#define __list_for_each_mutable_internal(pos, tmp, head) \
> >>> + for (typeof(pos) tmp = (pos = (head)->next)->next; \
> >>
> >> Use auto
> >>
> >>> + !list_is_head(pos, (head)); \
> >>> + pos = tmp, tmp = pos->next)
> >>> +
> >>> +#define __list_for_each_mutable1(pos, head) \
> >>> + __list_for_each_mutable_internal(pos, __UNIQUE_ID(next), head)
> >>> +
> >>> +#define __list_for_each_mutable2(pos, next, head) \
> >>> + list_for_each_safe(pos, next, head)
> >>> +
> >>> /**
> >>> - * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
> >>> + * list_for_each_mutable - iterate over a list safe against entry removal
> >>> * @pos: the &struct list_head to use as a loop cursor.
> >>> - * @n: another &struct list_head to use as temporary storage
> >>> - * @head: the head for your list.
> >>> + * @...: either (head) or (next, head)
> >>> + *
> >>> + * next: another &struct list_head to use as optional temporary storage.
> >>> + * The temporary cursor is internal unless explicitly supplied by
> >>> + * the caller.
> >>> + * head: the head for your list.
> >>> + */
> >>> +#define list_for_each_mutable(pos, ...) \
> >>> + CONCATENATE(__list_for_each_mutable, COUNT_ARGS(__VA_ARGS__)) \
> >>> + (pos, __VA_ARGS__)
> >>
> >> The variable argument count logic really just slows down compilation.
> >> Maybe there aren't enough copies of this code to make that significant.
> >> But just because you can do it doesn't mean it is a gooD idea.
> >> I'm also not sure it really adds anything to the readability.
> >>
> >> And, it you are going to make the middle argument optional there is
> >> no need to change the macro name.
> >
> > Christian König and Jani Nikula also disagree with the variadic-argument
> > implementation approach. If we abandon that method, it means we will
> > inevitably need to add some new macros. If mutable is not a good name,
> > suggestions for better alternatives would be welcome; coming up with a
> > suitable name is indeed rather tricky.
>
> I don't think you need to add a new macro for the specific use case that people want to modify the next element of the iteration.
>
> If I remember your numbers correctly that is a really corner case and keeping using the existing *_safe() macros for that sounds perfectly fine to me.
IIRC currently you have a choice of either:
define Item that can't be deleted
list_for_each() The current item.
list_for_each_safe() The next item.
There is also likely to be code that updates the variables to allow
for other scenarios.
Note that if increase a reference count and release a lock then list_for_each()
is likely safer than list_for_each_safe() :-)
list.h has 9 variants of the 'safe' loop.
The bloat of another 9 is getting excessive.
It has to be said that this is one of my least favourite type of list...
David
>
> Regards,
> Christian.
^ permalink raw reply
* Re: [PATCH v3] io_uring: annotate remote tasks for kcoverage
From: Jens Axboe @ 2026-06-24 14:16 UTC (permalink / raw)
To: Jann Horn, robert; +Cc: io-uring, Dmitry Vyukov, Andrey Konovalov, kasan-dev
In-Reply-To: <CAG48ez02Sio8ZENVK3gUWM+8j6NgG9LxtnDV=v+FSqsqs_KfnA@mail.gmail.com>
On 6/23/26 10:37 AM, Jann Horn wrote:
> On Tue, May 26, 2026 at 6:49 PM Robert Femmer <robert@fmmr.tech> wrote:
>> Fuzzers use coverage information to guide generation of test cases
>> towards new or interesting code paths. Syzkaller, specifically, makes
>> use kcoverage (CONFIG_KCOV). Coverage information is not collected for
>> kernel tasks unless annotated by kcov_remote_start and kcov_remote_stop.
>> This patch annotates io-uring's work queue and sqpoll tasks.
>
> I think this is a useful change overall.
Agree, mostly waiting on Andrey and Robert to hash out the details and
we can get this landed for 7.3. On vacation the next weeks, not much
going on on my end, work wise.
--
Jens Axboe
^ permalink raw reply
* Re: [PATCH v3 1/7] list: Add mutable iterator variants
From: Christian König @ 2026-06-24 13:23 UTC (permalink / raw)
To: Kaitao Cheng, David Laight
Cc: Andrew Morton, David Hildenbrand, Jens Axboe, Tejun Heo,
Alexander Viro, Christian Brauner, Alexei Starovoitov,
Daniel Borkmann, Andrii Nakryiko, Johannes Weiner, Peter Zijlstra,
Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim,
Thomas Gleixner, Juri Lelli, Vincent Guittot, Paul Moore,
Andy Shevchenko, Paul E. McKenney, Shakeel Butt, David Howells,
Simona Vetter, Randy Dunlap, Luca Ceresoli, Philipp Stanner,
linux-block, linux-kernel, cgroups, linux-ntfs-dev, linux-fsdevel,
io-uring, audit, bpf, netdev, dri-devel, linux-perf-users,
linux-trace-kernel, kexec, live-patching, linux-modules,
linux-crypto, linux-pm, rcu, sched-ext, linux-mm, virtualization,
damon, llvm, Kaitao Cheng
In-Reply-To: <351a6b67-b394-4c58-aee2-88b6c8089ad5@linux.dev>
On 6/24/26 15:14, Kaitao Cheng wrote:
>
>
> 在 2026/6/22 16:42, David Laight 写道:
>> On Mon, 22 Jun 2026 12:05:31 +0800
>> Kaitao Cheng <kaitao.cheng@linux.dev> wrote:
>>
>>> From: Kaitao Cheng <chengkaitao@kylinos.cn>
>>>
>>> The list_for_each*_safe() helpers are used when the loop body may
>>> remove the current entry. Their API exposes the temporary cursor at
>>> every call site, even though most users only need it for the iterator
>>> implementation and never reference it in the loop body.
>>>
>>> Add *_mutable() variants for list and hlist iteration. The new helpers
>>> support both forms: callers may keep passing an explicit temporary cursor
>>> when they need to inspect or reset it, or omit it and let the helper use
>>> a unique internal cursor.
>>
>> I'm not really sure 'mutable' means anything either.
>> It is possible to make it valid for the loop body (or even other threads)
>> to delete arbitrary list items - but that needs significant extra overheads.
>>
>> It might be worth doing something that doesn't need the extra variable,
>> but there is little point doing all the churn just to rename things.
>>
>>>
>>> This makes call sites that only mutate the list through the current entry
>>> less noisy, while keeping the existing *_safe() helpers available for
>>> compatibility.
>>>
>>> Signed-off-by: Kaitao Cheng <chengkaitao@kylinos.cn>
>>> ---
>>> include/linux/list.h | 269 +++++++++++++++++++++++++++++++++++++------
>>> 1 file changed, 231 insertions(+), 38 deletions(-)
>>>
>>> diff --git a/include/linux/list.h b/include/linux/list.h
>>> index 09d979976b3b..1081def7cea9 100644
>>> --- a/include/linux/list.h
>>> +++ b/include/linux/list.h
>>> @@ -7,6 +7,7 @@
>>> #include <linux/stddef.h>
>>> #include <linux/poison.h>
>>> #include <linux/const.h>
>>> +#include <linux/args.h>
>>>
>>> #include <asm/barrier.h>
>>>
>>> @@ -763,28 +764,72 @@ static inline void list_splice_tail_init(struct list_head *list,
>>> #define list_for_each_prev(pos, head) \
>>> for (pos = (head)->prev; !list_is_head(pos, (head)); pos = pos->prev)
>>>
>>> -/**
>>> - * list_for_each_safe - iterate over a list safe against removal of list entry
>>> - * @pos: the &struct list_head to use as a loop cursor.
>>> - * @n: another &struct list_head to use as temporary storage
>>> - * @head: the head for your list.
>>> +/*
>>> + * list_for_each_safe is an old interface, use list_for_each_mutable instead.
>>> */
>>> #define list_for_each_safe(pos, n, head) \
>>> for (pos = (head)->next, n = pos->next; \
>>> !list_is_head(pos, (head)); \
>>> pos = n, n = pos->next)
>>>
>>> +#define __list_for_each_mutable_internal(pos, tmp, head) \
>>> + for (typeof(pos) tmp = (pos = (head)->next)->next; \
>>
>> Use auto
>>
>>> + !list_is_head(pos, (head)); \
>>> + pos = tmp, tmp = pos->next)
>>> +
>>> +#define __list_for_each_mutable1(pos, head) \
>>> + __list_for_each_mutable_internal(pos, __UNIQUE_ID(next), head)
>>> +
>>> +#define __list_for_each_mutable2(pos, next, head) \
>>> + list_for_each_safe(pos, next, head)
>>> +
>>> /**
>>> - * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
>>> + * list_for_each_mutable - iterate over a list safe against entry removal
>>> * @pos: the &struct list_head to use as a loop cursor.
>>> - * @n: another &struct list_head to use as temporary storage
>>> - * @head: the head for your list.
>>> + * @...: either (head) or (next, head)
>>> + *
>>> + * next: another &struct list_head to use as optional temporary storage.
>>> + * The temporary cursor is internal unless explicitly supplied by
>>> + * the caller.
>>> + * head: the head for your list.
>>> + */
>>> +#define list_for_each_mutable(pos, ...) \
>>> + CONCATENATE(__list_for_each_mutable, COUNT_ARGS(__VA_ARGS__)) \
>>> + (pos, __VA_ARGS__)
>>
>> The variable argument count logic really just slows down compilation.
>> Maybe there aren't enough copies of this code to make that significant.
>> But just because you can do it doesn't mean it is a gooD idea.
>> I'm also not sure it really adds anything to the readability.
>>
>> And, it you are going to make the middle argument optional there is
>> no need to change the macro name.
>
> Christian König and Jani Nikula also disagree with the variadic-argument
> implementation approach. If we abandon that method, it means we will
> inevitably need to add some new macros. If mutable is not a good name,
> suggestions for better alternatives would be welcome; coming up with a
> suitable name is indeed rather tricky.
I don't think you need to add a new macro for the specific use case that people want to modify the next element of the iteration.
If I remember your numbers correctly that is a really corner case and keeping using the existing *_safe() macros for that sounds perfectly fine to me.
Regards,
Christian.
^ permalink raw reply
* Re: [PATCH v3 1/7] list: Add mutable iterator variants
From: Kaitao Cheng @ 2026-06-24 13:14 UTC (permalink / raw)
To: David Laight
Cc: Andrew Morton, David Hildenbrand, Jens Axboe, Tejun Heo,
Alexander Viro, Christian Brauner, Alexei Starovoitov,
Daniel Borkmann, Andrii Nakryiko, Johannes Weiner, Peter Zijlstra,
Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim,
Thomas Gleixner, Juri Lelli, Vincent Guittot, Paul Moore,
Andy Shevchenko, Paul E. McKenney, Shakeel Butt,
Christian König, David Howells, Simona Vetter, Randy Dunlap,
Luca Ceresoli, Philipp Stanner, linux-block, linux-kernel,
cgroups, linux-ntfs-dev, linux-fsdevel, io-uring, audit, bpf,
netdev, dri-devel, linux-perf-users, linux-trace-kernel, kexec,
live-patching, linux-modules, linux-crypto, linux-pm, rcu,
sched-ext, linux-mm, virtualization, damon, llvm, Kaitao Cheng
In-Reply-To: <20260622094242.64531b9a@pumpkin>
在 2026/6/22 16:42, David Laight 写道:
> On Mon, 22 Jun 2026 12:05:31 +0800
> Kaitao Cheng <kaitao.cheng@linux.dev> wrote:
>
>> From: Kaitao Cheng <chengkaitao@kylinos.cn>
>>
>> The list_for_each*_safe() helpers are used when the loop body may
>> remove the current entry. Their API exposes the temporary cursor at
>> every call site, even though most users only need it for the iterator
>> implementation and never reference it in the loop body.
>>
>> Add *_mutable() variants for list and hlist iteration. The new helpers
>> support both forms: callers may keep passing an explicit temporary cursor
>> when they need to inspect or reset it, or omit it and let the helper use
>> a unique internal cursor.
>
> I'm not really sure 'mutable' means anything either.
> It is possible to make it valid for the loop body (or even other threads)
> to delete arbitrary list items - but that needs significant extra overheads.
>
> It might be worth doing something that doesn't need the extra variable,
> but there is little point doing all the churn just to rename things.
>
>>
>> This makes call sites that only mutate the list through the current entry
>> less noisy, while keeping the existing *_safe() helpers available for
>> compatibility.
>>
>> Signed-off-by: Kaitao Cheng <chengkaitao@kylinos.cn>
>> ---
>> include/linux/list.h | 269 +++++++++++++++++++++++++++++++++++++------
>> 1 file changed, 231 insertions(+), 38 deletions(-)
>>
>> diff --git a/include/linux/list.h b/include/linux/list.h
>> index 09d979976b3b..1081def7cea9 100644
>> --- a/include/linux/list.h
>> +++ b/include/linux/list.h
>> @@ -7,6 +7,7 @@
>> #include <linux/stddef.h>
>> #include <linux/poison.h>
>> #include <linux/const.h>
>> +#include <linux/args.h>
>>
>> #include <asm/barrier.h>
>>
>> @@ -763,28 +764,72 @@ static inline void list_splice_tail_init(struct list_head *list,
>> #define list_for_each_prev(pos, head) \
>> for (pos = (head)->prev; !list_is_head(pos, (head)); pos = pos->prev)
>>
>> -/**
>> - * list_for_each_safe - iterate over a list safe against removal of list entry
>> - * @pos: the &struct list_head to use as a loop cursor.
>> - * @n: another &struct list_head to use as temporary storage
>> - * @head: the head for your list.
>> +/*
>> + * list_for_each_safe is an old interface, use list_for_each_mutable instead.
>> */
>> #define list_for_each_safe(pos, n, head) \
>> for (pos = (head)->next, n = pos->next; \
>> !list_is_head(pos, (head)); \
>> pos = n, n = pos->next)
>>
>> +#define __list_for_each_mutable_internal(pos, tmp, head) \
>> + for (typeof(pos) tmp = (pos = (head)->next)->next; \
>
> Use auto
>
>> + !list_is_head(pos, (head)); \
>> + pos = tmp, tmp = pos->next)
>> +
>> +#define __list_for_each_mutable1(pos, head) \
>> + __list_for_each_mutable_internal(pos, __UNIQUE_ID(next), head)
>> +
>> +#define __list_for_each_mutable2(pos, next, head) \
>> + list_for_each_safe(pos, next, head)
>> +
>> /**
>> - * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
>> + * list_for_each_mutable - iterate over a list safe against entry removal
>> * @pos: the &struct list_head to use as a loop cursor.
>> - * @n: another &struct list_head to use as temporary storage
>> - * @head: the head for your list.
>> + * @...: either (head) or (next, head)
>> + *
>> + * next: another &struct list_head to use as optional temporary storage.
>> + * The temporary cursor is internal unless explicitly supplied by
>> + * the caller.
>> + * head: the head for your list.
>> + */
>> +#define list_for_each_mutable(pos, ...) \
>> + CONCATENATE(__list_for_each_mutable, COUNT_ARGS(__VA_ARGS__)) \
>> + (pos, __VA_ARGS__)
>
> The variable argument count logic really just slows down compilation.
> Maybe there aren't enough copies of this code to make that significant.
> But just because you can do it doesn't mean it is a gooD idea.
> I'm also not sure it really adds anything to the readability.
>
> And, it you are going to make the middle argument optional there is
> no need to change the macro name.
Christian König and Jani Nikula also disagree with the variadic-argument
implementation approach. If we abandon that method, it means we will
inevitably need to add some new macros. If mutable is not a good name,
suggestions for better alternatives would be welcome; coming up with a
suitable name is indeed rather tricky.
--
Thanks
Kaitao Cheng
^ permalink raw reply
* Re: [PATCH v3 0/7] Prepare mutable list iterators to cache cursor state
From: Kaitao Cheng @ 2026-06-24 13:05 UTC (permalink / raw)
To: Jani Nikula, Andrew Morton, David Hildenbrand, Jens Axboe,
Tejun Heo, Alexander Viro, Christian Brauner, Alexei Starovoitov,
Daniel Borkmann, Andrii Nakryiko, Johannes Weiner, Peter Zijlstra,
Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim,
Thomas Gleixner, Juri Lelli, Vincent Guittot, Paul Moore,
Andy Shevchenko, Paul E. McKenney, Shakeel Butt,
Christian König
Cc: David Howells, Simona Vetter, Randy Dunlap, Luca Ceresoli,
Philipp Stanner, linux-block, linux-kernel, cgroups,
linux-ntfs-dev, linux-fsdevel, io-uring, audit, bpf, netdev,
dri-devel, linux-perf-users, linux-trace-kernel, kexec,
live-patching, linux-modules, linux-crypto, linux-pm, rcu,
sched-ext, linux-mm, virtualization, damon, llvm, chengkaitao
In-Reply-To: <88f34c7fa5a3d1700cc8005818751d6aa31f09df@intel.com>
在 2026/6/22 16:37, Jani Nikula 写道:
> On Mon, 22 Jun 2026, Kaitao Cheng <kaitao.cheng@linux.dev> wrote:
>> Add *_mutable() iterator variants for list, hlist and llist. The new
>> helpers are variadic and support both forms. In the common case, the
>> caller omits the temporary cursor and the macro creates a unique internal
>> cursor with typeof(pos) and __UNIQUE_ID(). If a loop really needs an
>> explicit temporary cursor, the caller can still pass it and the helper
>> keeps the existing *_safe() behaviour.
>>
>> For example, a call site may use the shorter form:
>>
>> list_for_each_entry_mutable(pos, head, member)
>>
>> or keep the explicit temporary cursor form:
>>
>> list_for_each_entry_mutable(pos, tmp, head, member)
>
> I'm unconvinced it's a good idea to allow two forms with macro trickery,
> *especially* when it's not the last argument you can omit. I think it's
> a footgun.
>
> IMO stick with the first form only, and there'll always be the _safe
> variant that can be used when the temp pointer is needed.
Could we go back to the v1 version? What do you think of that
implementation approach?
https://lore.kernel.org/all/20260529082149.76764-1-kaitao.cheng@linux.dev/
--
Thanks
Kaitao Cheng
^ permalink raw reply
* Re: [PATCH v3 0/7] Prepare mutable list iterators to cache cursor state
From: Kaitao Cheng @ 2026-06-24 12:58 UTC (permalink / raw)
To: David Hildenbrand (Arm), Alexei Starovoitov
Cc: Andrew Morton, Jens Axboe, Tejun Heo, Alexander Viro,
Christian Brauner, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, Johannes Weiner, Peter Zijlstra, Ingo Molnar,
Arnaldo Carvalho de Melo, Namhyung Kim, Thomas Gleixner,
Juri Lelli, Vincent Guittot, Paul Moore, Andy Shevchenko,
Paul E. McKenney, Shakeel Butt, Christian König,
David Howells, Simona Vetter, Randy Dunlap, Luca Ceresoli,
Philipp Stanner, linux-block, LKML,
open list:CONTROL GROUP (CGROUP), linux-ntfs-dev, Linux-Fsdevel,
io-uring, audit, bpf, Network Development, dri-devel,
linux-perf-use., linux-trace-kernel, kexec, live-patching,
linux-modules, Linux Crypto Mailing List, Linux Power Management,
rcu, sched-ext, linux-mm, virtualization, damon,
clang-built-linux, chengkaitao
In-Reply-To: <8f98a3a6-f97b-4673-964f-fb09c8879e2e@kernel.org>
在 2026/6/22 19:27, David Hildenbrand (Arm) 写道:
> On 6/22/26 07:28, Alexei Starovoitov wrote:
>> On Sun, Jun 21, 2026 at 9:06 PM Kaitao Cheng <kaitao.cheng@linux.dev> wrote:
>>>
>>> From: chengkaitao <chengkaitao@kylinos.cn>
>>>
>>> The list_for_each*_safe() helpers are used when the loop body may remove
>>> the current entry. Their current interface, however, forces every caller
>>> to define a temporary cursor outside the macro and pass it in, even when
>>> the caller never uses that cursor directly. For most call sites this
>>> extra cursor is just boilerplate required by the macro implementation.
>>>
>>> This is awkward because the saved next pointer is an internal detail of
>>> the iteration. Callers that only remove or move the current entry do not
>>> need to spell it out.
>>>
>>> The _safe() suffix has also caused confusion. Christian Koenig pointed
>>> out that the name is easy to read as a thread-safe variant, especially
>>> for beginners, even though it only means that the iterator keeps enough
>>> state to tolerate removal of the current entry. He suggested _mutable()
>>> as a clearer description of what the loop permits.
>>>
>>> Add *_mutable() iterator variants for list, hlist and llist. The new
>>> helpers are variadic and support both forms. In the common case, the
>>> caller omits the temporary cursor and the macro creates a unique internal
>>> cursor with typeof(pos) and __UNIQUE_ID(). If a loop really needs an
>>> explicit temporary cursor, the caller can still pass it and the helper
>>> keeps the existing *_safe() behaviour.
>>>
>>> For example, a call site may use the shorter form:
>>>
>>> list_for_each_entry_mutable(pos, head, member)
>>>
>>> or keep the explicit temporary cursor form:
>>>
>>> list_for_each_entry_mutable(pos, tmp, head, member)
>>>
>>> The existing *_safe() helpers remain available for compatibility. This
>>> series only converts users in mm, block, kernel, init and io_uring. If
>>> this approach looks acceptable, the remaining users can be converted in
>>> follow-up series.
>>>
>>> Changes in v3 (Christian König, Andy Shevchenko):
>>> - Convert safe list walks to mutable iterators
>>>
>>> Changes in v2 (Muchun Song, Andy Shevchenko):
>>> - Drop the list_for_each_entry_mutable*() helpers from v1 and make the
>>> cursor change directly in the existing list_for_each_entry*() helpers.
>>> - Open-code special list walks that rely on updating the loop cursor in
>>> the body, preserving their existing traversal semantics.
>>>
>>> Link to v2:
>>> https://lore.kernel.org/all/20260609061347.93688-1-kaitao.cheng@linux.dev/
>>>
>>> Link to v1:
>>> https://lore.kernel.org/all/20260529082149.76764-1-kaitao.cheng@linux.dev/
>>>
>>> Kaitao Cheng (7):
>>> list: Add mutable iterator variants
>>> llist: Add mutable iterator variants
>>> mm: Use mutable list iterators
>>> block: Use mutable list iterators
>>> kernel: Use mutable list iterators
>>> initramfs: Use mutable list iterator
>>> io_uring: Use mutable list iterators
>>>
>>> block/bfq-iosched.c | 17 +-
>>> block/blk-cgroup.c | 12 +-
>>> block/blk-flush.c | 4 +-
>>> block/blk-iocost.c | 18 +-
>>> block/blk-mq.c | 8 +-
>>> block/blk-throttle.c | 4 +-
>>> block/kyber-iosched.c | 4 +-
>>> block/partitions/ldm.c | 8 +-
>>> block/sed-opal.c | 4 +-
>>> include/linux/list.h | 269 ++++++++++++++++++++++++----
>>> include/linux/llist.h | 81 +++++++--
>>> init/initramfs.c | 5 +-
>>> io_uring/cancel.c | 6 +-
>>> io_uring/poll.c | 3 +-
>>> io_uring/rw.c | 4 +-
>>> io_uring/timeout.c | 8 +-
>>> io_uring/uring_cmd.c | 3 +-
>>> kernel/audit_tree.c | 4 +-
>>> kernel/audit_watch.c | 16 +-
>>> kernel/auditfilter.c | 4 +-
>>> kernel/auditsc.c | 4 +-
>>> kernel/bpf/arena.c | 10 +-
>>> kernel/bpf/arraymap.c | 8 +-
>>> kernel/bpf/bpf_local_storage.c | 3 +-
>>> kernel/bpf/bpf_lru_list.c | 25 ++-
>>> kernel/bpf/btf.c | 18 +-
>>> kernel/bpf/cgroup.c | 7 +-
>>> kernel/bpf/cpumap.c | 4 +-
>>> kernel/bpf/devmap.c | 10 +-
>>> kernel/bpf/helpers.c | 8 +-
>>> kernel/bpf/local_storage.c | 4 +-
>>> kernel/bpf/memalloc.c | 16 +-
>>> kernel/bpf/offload.c | 8 +-
>>> kernel/bpf/states.c | 4 +-
>>> kernel/bpf/stream.c | 4 +-
>>> kernel/bpf/verifier.c | 6 +-
>>> kernel/cgroup/cgroup-v1.c | 4 +-
>>> kernel/cgroup/cgroup.c | 54 +++---
>>> kernel/cgroup/dmem.c | 12 +-
>>> kernel/cgroup/rdma.c | 8 +-
>>> kernel/events/core.c | 44 +++--
>>> kernel/events/uprobes.c | 12 +-
>>> kernel/exit.c | 8 +-
>>> kernel/fail_function.c | 4 +-
>>> kernel/gcov/clang.c | 4 +-
>>> kernel/irq_work.c | 4 +-
>>> kernel/kexec_core.c | 4 +-
>>> kernel/kprobes.c | 16 +-
>>> kernel/livepatch/core.c | 4 +-
>>> kernel/livepatch/core.h | 4 +-
>>> kernel/liveupdate/kho_block.c | 4 +-
>>> kernel/liveupdate/luo_flb.c | 4 +-
>>> kernel/locking/rwsem.c | 2 +-
>>> kernel/locking/test-ww_mutex.c | 2 +-
>>> kernel/module/main.c | 11 +-
>>> kernel/padata.c | 4 +-
>>> kernel/power/snapshot.c | 8 +-
>>> kernel/power/wakelock.c | 4 +-
>>> kernel/printk/printk.c | 11 +-
>>> kernel/ptrace.c | 4 +-
>>> kernel/rcu/rcutorture.c | 3 +-
>>> kernel/rcu/tasks.h | 9 +-
>>> kernel/rcu/tree.c | 6 +-
>>> kernel/resource.c | 4 +-
>>> kernel/sched/core.c | 4 +-
>>> kernel/sched/ext.c | 22 +--
>>> kernel/sched/fair.c | 28 +--
>>> kernel/sched/topology.c | 4 +-
>>> kernel/sched/wait.c | 4 +-
>>> kernel/seccomp.c | 4 +-
>>> kernel/signal.c | 11 +-
>>> kernel/smp.c | 4 +-
>>> kernel/taskstats.c | 8 +-
>>> kernel/time/clockevents.c | 6 +-
>>> kernel/time/clocksource.c | 4 +-
>>> kernel/time/posix-cpu-timers.c | 4 +-
>>> kernel/time/posix-timers.c | 3 +-
>>> kernel/torture.c | 3 +-
>>> kernel/trace/bpf_trace.c | 4 +-
>>> kernel/trace/ftrace.c | 49 +++--
>>> kernel/trace/ring_buffer.c | 25 ++-
>>> kernel/trace/trace.c | 12 +-
>>> kernel/trace/trace_dynevent.c | 6 +-
>>> kernel/trace/trace_dynevent.h | 5 +-
>>> kernel/trace/trace_events.c | 35 ++--
>>> kernel/trace/trace_events_filter.c | 4 +-
>>> kernel/trace/trace_events_hist.c | 8 +-
>>> kernel/trace/trace_events_trigger.c | 17 +-
>>> kernel/trace/trace_events_user.c | 16 +-
>>> kernel/trace/trace_stat.c | 4 +-
>>> kernel/user-return-notifier.c | 3 +-
>>> kernel/workqueue.c | 16 +-
>>> mm/backing-dev.c | 8 +-
>>> mm/balloon.c | 8 +-
>>> mm/cma.c | 4 +-
>>> mm/compaction.c | 4 +-
>>> mm/damon/core.c | 4 +-
>>> mm/damon/sysfs-schemes.c | 4 +-
>>> mm/dmapool.c | 4 +-
>>> mm/huge_memory.c | 8 +-
>>> mm/hugetlb.c | 56 +++---
>>> mm/hugetlb_vmemmap.c | 16 +-
>>> mm/khugepaged.c | 14 +-
>>> mm/kmemleak.c | 7 +-
>>> mm/ksm.c | 25 +--
>>> mm/list_lru.c | 4 +-
>>> mm/memcontrol-v1.c | 8 +-
>>> mm/memory-failure.c | 12 +-
>>> mm/memory-tiers.c | 4 +-
>>> mm/migrate.c | 23 ++-
>>> mm/mmu_notifier.c | 9 +-
>>> mm/page_alloc.c | 8 +-
>>> mm/page_reporting.c | 2 +-
>>> mm/percpu.c | 11 +-
>>> mm/pgtable-generic.c | 4 +-
>>> mm/rmap.c | 10 +-
>>> mm/shmem.c | 9 +-
>>> mm/slab_common.c | 14 +-
>>> mm/slub.c | 33 ++--
>>> mm/swapfile.c | 4 +-
>>> mm/userfaultfd.c | 12 +-
>>> mm/vmalloc.c | 24 +--
>>> mm/vmscan.c | 7 +-
>>> mm/zsmalloc.c | 4 +-
>>> 124 files changed, 875 insertions(+), 681 deletions(-)
>>
>> Not sure what you were thinking, but this diff stat
>> is not landable.
>
> Agreed. If we decide we want this, I guess we should target per-subsystem
> conversions.
>
> If this goes through the MM tree, I would even appreciate doing this on a per-MM
> component granularity.
>
> (unless we have some magic "Linus converts all of them" script, which I doubt we
> will have)
I strongly agree with the point above.
> Is there a way forward to replace list_for_each_*_safe entirely, possibly just
> reusing the old name but simply the parameter?
David Laight, Christian König, and Jani Nikula do not agree with using
clever macro syntax to support both calling forms at the same time,
so for now it is not possible to keep the original macro name and only
simplify the parameter. I may revert to the v1 version and ask everyone
for their opinions again.
--
Thanks
Kaitao Cheng
^ permalink raw reply
* [PATCH v6] io_uring/register: add IORING_REGISTER_CLONE_FILES opcode
From: Harshal Chavan @ 2026-06-24 12:40 UTC (permalink / raw)
To: harshal24.chavan
Cc: axboe, gregkh, gustavoars, io-uring, kees, krisman,
linux-hardening, linux-kernel
In-Reply-To: <20260624073921.11037-1-harshal24.chavan@gmail.com>
Currently, if an application wants to duplicate registered file
descriptors from one io_uring instance to another, it must manually
unregister and re-register them, incurring unnecessary overhead.
Add IORING_REGISTER_CLONE_FILES to allow direct cloning of the file
table from a source ring to a destination ring. This implementation
strictly mirrors the io_clone_buffers UAPI, supporting partial offsets
and the IORING_REGISTER_DST_REPLACE flag.
To ensure lock synchronization safety, destination nodes are strictly
allocated as new, private io_rsrc_nodes rather than sharing references
across rings.
Signed-off-by: Harshal Chavan <harshal24.chavan@gmail.com>
---
Sorry for the noise on the previous email! I accidentally sent the patch
before running checkpatch and missed a whitespace error. This v6 corrects it.
v6:
- Fixed trailing whitespace checkpatch error.
v5:
- Added missing spacing in comment (Gabriel).
- Removed ctx->user and mm_account checks (Gabriel).
- Used !! for boolean conversion (Gabriel).
- Moved mutex_unlock unconditionally above the out label (Gabriel).
- liburing implementation and tests: https://github.com/axboe/liburing/pull/1606
v4:
- Updated Signed-off-by to use real name and moved above the scissors line (Greg KH).
v3:
- Rewrote the cloning loop to allocate private destination nodes via io_rsrc_node_alloc to fix non-atomic ref lock synchronization (Jens).
- Maintained partial offset/copy support to mirror io_clone_buffers UAPI (Jens).
- Gated the replacement free check on ctx->file_table.data.nr (Gabriel).
- Prevented self-cloning by checking ctx == src_ctx (Gabriel).
- Removed submitter_task check to allow cross-thread pooling setups (Gabriel).
v2:
- Dropped unrelated whitespace formatting changes from v1
---
include/uapi/linux/io_uring.h | 12 +++
io_uring/register.c | 6 ++
io_uring/rsrc.c | 145 ++++++++++++++++++++++++++++++++++
io_uring/rsrc.h | 1 +
4 files changed, 164 insertions(+)
diff --git a/include/uapi/linux/io_uring.h b/include/uapi/linux/io_uring.h
index 909fb7aea638..67fcc40f8dfc 100644
--- a/include/uapi/linux/io_uring.h
+++ b/include/uapi/linux/io_uring.h
@@ -723,6 +723,9 @@ enum io_uring_register_op {
/* register bpf filtering programs */
IORING_REGISTER_BPF_FILTER = 37,
+ /* clone file descriptors from another ring */
+ IORING_REGISTER_CLONE_FILES = 38,
+
/* this goes last */
IORING_REGISTER_LAST,
@@ -854,6 +857,15 @@ struct io_uring_clone_buffers {
__u32 pad[3];
};
+struct io_uring_clone_files {
+ __u32 src_fd;
+ __u32 flags;
+ __u32 src_off;
+ __u32 dst_off;
+ __u32 nr;
+ __u32 pad[3];
+};
+
struct io_uring_buf {
__u64 addr;
__u32 len;
diff --git a/io_uring/register.c b/io_uring/register.c
index dce5e2f9cf77..bbc8c506ea2d 100644
--- a/io_uring/register.c
+++ b/io_uring/register.c
@@ -924,6 +924,12 @@ static int __io_uring_register(struct io_ring_ctx *ctx, unsigned opcode,
break;
ret = io_register_clone_buffers(ctx, arg);
break;
+ case IORING_REGISTER_CLONE_FILES:
+ ret = -EINVAL;
+ if (!arg || nr_args != 1)
+ break;
+ ret = io_register_clone_files(ctx, arg);
+ break;
case IORING_REGISTER_ZCRX_IFQ:
ret = -EINVAL;
if (!arg || nr_args != 1)
diff --git a/io_uring/rsrc.c b/io_uring/rsrc.c
index 650303626be6..1d58c256b3a5 100644
--- a/io_uring/rsrc.c
+++ b/io_uring/rsrc.c
@@ -1303,6 +1303,151 @@ int io_register_clone_buffers(struct io_ring_ctx *ctx, void __user *arg)
return ret;
}
+static int io_clone_file_node(struct io_ring_ctx *ctx,
+ struct io_rsrc_node *src_node,
+ int dst_index,
+ struct io_file_table *new_table)
+{
+ struct io_rsrc_node *dst_node;
+ struct file *file;
+
+ dst_node = io_rsrc_node_alloc(ctx, IORING_RSRC_FILE);
+ if (!dst_node)
+ return -ENOMEM;
+
+ file = io_slot_file(src_node);
+ get_file(file);
+ io_fixed_file_set(dst_node, file);
+
+ new_table->data.nodes[dst_index] = dst_node;
+ io_file_bitmap_set(new_table, dst_index);
+
+ return 0;
+}
+
+static int io_clone_files(struct io_ring_ctx *ctx, struct io_ring_ctx *src_ctx,
+ struct io_uring_clone_files *arg)
+{
+ struct io_file_table new_file_table;
+ unsigned int dst_nr = ctx->file_table.data.nr;
+ unsigned int src_nr = src_ctx->file_table.data.nr;
+ unsigned int new_nr, i;
+
+ lockdep_assert_held(&ctx->uring_lock);
+ lockdep_assert_held(&src_ctx->uring_lock);
+
+ if (dst_nr && !(arg->flags & IORING_REGISTER_DST_REPLACE))
+ return -EBUSY;
+
+ if (!src_nr)
+ return -ENXIO;
+
+ if (!arg->nr)
+ arg->nr = src_nr;
+ else if (arg->nr > src_nr)
+ return -EINVAL;
+
+ if (check_add_overflow(arg->src_off, arg->nr, &i) || i > src_nr)
+ return -EINVAL;
+ if (check_add_overflow(arg->dst_off, arg->nr, &i))
+ return -EINVAL;
+
+ new_nr = max(dst_nr, arg->dst_off + arg->nr);
+ if (new_nr > IORING_MAX_FIXED_FILES)
+ return -EINVAL;
+
+ memset(&new_file_table, 0, sizeof(new_file_table));
+ if (!io_alloc_file_tables(ctx, &new_file_table, new_nr))
+ return -ENOMEM;
+
+ /* Copy original nodes from before the cloned range */
+ for (i = 0; i < min(arg->dst_off, dst_nr); i++) {
+ struct io_rsrc_node *src_node = io_rsrc_node_lookup(&ctx->file_table.data, i);
+
+ if (!src_node)
+ continue;
+ if (io_clone_file_node(ctx, src_node, i, &new_file_table))
+ goto out;
+ }
+
+ /* Copy the actual cloned range from the source ring */
+ for (i = 0; i < arg->nr; i++) {
+ struct io_rsrc_node *src_node = io_rsrc_node_lookup(&src_ctx->file_table.data,
+ arg->src_off + i);
+
+ if (!src_node)
+ continue;
+ if (io_clone_file_node(ctx, src_node, arg->dst_off + i, &new_file_table))
+ goto out;
+ }
+
+ /* Copy original nodes from after the cloned range */
+ for (i = arg->dst_off + arg->nr; i < dst_nr; i++) {
+ struct io_rsrc_node *src_node = io_rsrc_node_lookup(&ctx->file_table.data, i);
+
+ if (!src_node)
+ continue;
+ if (io_clone_file_node(ctx, src_node, i, &new_file_table))
+ goto out;
+ }
+
+ /* free the old file table if there is any data present */
+ if (dst_nr)
+ io_free_file_tables(ctx, &ctx->file_table);
+
+ WARN_ON_ONCE(ctx->file_table.data.nr);
+ ctx->file_table = new_file_table;
+ io_file_table_set_alloc_range(ctx, 0, ctx->file_table.data.nr);
+ return 0;
+
+out:
+ /* Error Path: Safely destroy whatever we partially built */
+ io_free_file_tables(ctx, &new_file_table);
+ return -ENOMEM;
+}
+
+int io_register_clone_files(struct io_ring_ctx *ctx, void __user *arg)
+{
+ struct io_uring_clone_files clone_arg;
+ struct io_ring_ctx *src_ctx;
+ bool registered_src;
+ struct file *file;
+ int ret;
+
+ if (copy_from_user(&clone_arg, arg, sizeof(clone_arg)))
+ return -EFAULT;
+ if (clone_arg.flags &
+ ~(IORING_REGISTER_SRC_REGISTERED | IORING_REGISTER_DST_REPLACE))
+ return -EINVAL;
+
+ if (memchr_inv(clone_arg.pad, 0, sizeof(clone_arg.pad)))
+ return -EINVAL;
+
+ registered_src = !!(clone_arg.flags & IORING_REGISTER_SRC_REGISTERED);
+ file = io_uring_ctx_get_file(clone_arg.src_fd, registered_src);
+ if (IS_ERR(file))
+ return PTR_ERR(file);
+
+ src_ctx = file->private_data;
+ /* Same ring clone is not allowed */
+ if (src_ctx == ctx) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ mutex_unlock(&ctx->uring_lock);
+ lock_two_rings(ctx, src_ctx);
+
+ ret = io_clone_files(ctx, src_ctx, &clone_arg);
+
+ mutex_unlock(&src_ctx->uring_lock);
+
+out:
+ if (!registered_src)
+ fput(file);
+ return ret;
+}
+
void io_vec_free(struct iou_vec *iv)
{
if (!iv->iovec)
diff --git a/io_uring/rsrc.h b/io_uring/rsrc.h
index 44e3386f7c1c..32f5c47c46af 100644
--- a/io_uring/rsrc.h
+++ b/io_uring/rsrc.h
@@ -75,6 +75,7 @@ int io_prep_reg_iovec(struct io_kiocb *req, struct iou_vec *iv,
const struct iovec __user *uvec, size_t uvec_segs);
int io_register_clone_buffers(struct io_ring_ctx *ctx, void __user *arg);
+int io_register_clone_files(struct io_ring_ctx *ctx, void __user *arg);
int io_sqe_buffers_unregister(struct io_ring_ctx *ctx);
int io_sqe_buffers_register(struct io_ring_ctx *ctx, void __user *arg,
unsigned int nr_args, u64 __user *tags);
--
2.54.0
^ permalink raw reply related
* Re: [PATCH v3 0/7] Prepare mutable list iterators to cache cursor state
From: Kaitao Cheng @ 2026-06-24 12:29 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Alexei Starovoitov, Andrew Morton, David Hildenbrand, Jens Axboe,
Tejun Heo, Alexander Viro, Christian Brauner, Alexei Starovoitov,
Daniel Borkmann, Andrii Nakryiko, Johannes Weiner, Peter Zijlstra,
Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim,
Thomas Gleixner, Juri Lelli, Vincent Guittot, Paul Moore,
Paul E. McKenney, Shakeel Butt, Christian König,
David Howells, Simona Vetter, Randy Dunlap, Luca Ceresoli,
Philipp Stanner, linux-block, LKML,
open list:CONTROL GROUP (CGROUP), linux-ntfs-dev, Linux-Fsdevel,
io-uring, audit, bpf, Network Development, dri-devel,
linux-perf-use., linux-trace-kernel, kexec, live-patching,
linux-modules, Linux Crypto Mailing List, Linux Power Management,
rcu, sched-ext, linux-mm, virtualization, damon,
clang-built-linux, chengkaitao, Muchun Song
In-Reply-To: <ajkSftEbdGoiJXYs@ashevche-desk.local>
在 2026/6/22 18:46, Andy Shevchenko 写道:
> On Mon, Jun 22, 2026 at 02:15:01PM +0800, Kaitao Cheng wrote:
>> 在 2026/6/22 13:28, Alexei Starovoitov 写道:
>>> On Sun, Jun 21, 2026 at 9:06 PM Kaitao Cheng <kaitao.cheng@linux.dev> wrote:
>
> ...
>
>>>> block/bfq-iosched.c | 17 +-
>>>> block/blk-cgroup.c | 12 +-
>>>> block/blk-flush.c | 4 +-
>>>> block/blk-iocost.c | 18 +-
>>>> block/blk-mq.c | 8 +-
>>>> block/blk-throttle.c | 4 +-
>>>> block/kyber-iosched.c | 4 +-
>>>> block/partitions/ldm.c | 8 +-
>>>> block/sed-opal.c | 4 +-
>>>> include/linux/list.h | 269 ++++++++++++++++++++++++----
>>>> include/linux/llist.h | 81 +++++++--
>>>> init/initramfs.c | 5 +-
>>>> io_uring/cancel.c | 6 +-
>>>> io_uring/poll.c | 3 +-
>>>> io_uring/rw.c | 4 +-
>>>> io_uring/timeout.c | 8 +-
>>>> io_uring/uring_cmd.c | 3 +-
>>>> kernel/audit_tree.c | 4 +-
>>>> kernel/audit_watch.c | 16 +-
>>>> kernel/auditfilter.c | 4 +-
>>>> kernel/auditsc.c | 4 +-
>>>> kernel/bpf/arena.c | 10 +-
>>>> kernel/bpf/arraymap.c | 8 +-
>>>> kernel/bpf/bpf_local_storage.c | 3 +-
>>>> kernel/bpf/bpf_lru_list.c | 25 ++-
>>>> kernel/bpf/btf.c | 18 +-
>>>> kernel/bpf/cgroup.c | 7 +-
>>>> kernel/bpf/cpumap.c | 4 +-
>>>> kernel/bpf/devmap.c | 10 +-
>>>> kernel/bpf/helpers.c | 8 +-
>>>> kernel/bpf/local_storage.c | 4 +-
>>>> kernel/bpf/memalloc.c | 16 +-
>>>> kernel/bpf/offload.c | 8 +-
>>>> kernel/bpf/states.c | 4 +-
>>>> kernel/bpf/stream.c | 4 +-
>>>> kernel/bpf/verifier.c | 6 +-
>>>> kernel/cgroup/cgroup-v1.c | 4 +-
>>>> kernel/cgroup/cgroup.c | 54 +++---
>>>> kernel/cgroup/dmem.c | 12 +-
>>>> kernel/cgroup/rdma.c | 8 +-
>>>> kernel/events/core.c | 44 +++--
>>>> kernel/events/uprobes.c | 12 +-
>>>> kernel/exit.c | 8 +-
>>>> kernel/fail_function.c | 4 +-
>>>> kernel/gcov/clang.c | 4 +-
>>>> kernel/irq_work.c | 4 +-
>>>> kernel/kexec_core.c | 4 +-
>>>> kernel/kprobes.c | 16 +-
>>>> kernel/livepatch/core.c | 4 +-
>>>> kernel/livepatch/core.h | 4 +-
>>>> kernel/liveupdate/kho_block.c | 4 +-
>>>> kernel/liveupdate/luo_flb.c | 4 +-
>>>> kernel/locking/rwsem.c | 2 +-
>>>> kernel/locking/test-ww_mutex.c | 2 +-
>>>> kernel/module/main.c | 11 +-
>>>> kernel/padata.c | 4 +-
>>>> kernel/power/snapshot.c | 8 +-
>>>> kernel/power/wakelock.c | 4 +-
>>>> kernel/printk/printk.c | 11 +-
>>>> kernel/ptrace.c | 4 +-
>>>> kernel/rcu/rcutorture.c | 3 +-
>>>> kernel/rcu/tasks.h | 9 +-
>>>> kernel/rcu/tree.c | 6 +-
>>>> kernel/resource.c | 4 +-
>>>> kernel/sched/core.c | 4 +-
>>>> kernel/sched/ext.c | 22 +--
>>>> kernel/sched/fair.c | 28 +--
>>>> kernel/sched/topology.c | 4 +-
>>>> kernel/sched/wait.c | 4 +-
>>>> kernel/seccomp.c | 4 +-
>>>> kernel/signal.c | 11 +-
>>>> kernel/smp.c | 4 +-
>>>> kernel/taskstats.c | 8 +-
>>>> kernel/time/clockevents.c | 6 +-
>>>> kernel/time/clocksource.c | 4 +-
>>>> kernel/time/posix-cpu-timers.c | 4 +-
>>>> kernel/time/posix-timers.c | 3 +-
>>>> kernel/torture.c | 3 +-
>>>> kernel/trace/bpf_trace.c | 4 +-
>>>> kernel/trace/ftrace.c | 49 +++--
>>>> kernel/trace/ring_buffer.c | 25 ++-
>>>> kernel/trace/trace.c | 12 +-
>>>> kernel/trace/trace_dynevent.c | 6 +-
>>>> kernel/trace/trace_dynevent.h | 5 +-
>>>> kernel/trace/trace_events.c | 35 ++--
>>>> kernel/trace/trace_events_filter.c | 4 +-
>>>> kernel/trace/trace_events_hist.c | 8 +-
>>>> kernel/trace/trace_events_trigger.c | 17 +-
>>>> kernel/trace/trace_events_user.c | 16 +-
>>>> kernel/trace/trace_stat.c | 4 +-
>>>> kernel/user-return-notifier.c | 3 +-
>>>> kernel/workqueue.c | 16 +-
>>>> mm/backing-dev.c | 8 +-
>>>> mm/balloon.c | 8 +-
>>>> mm/cma.c | 4 +-
>>>> mm/compaction.c | 4 +-
>>>> mm/damon/core.c | 4 +-
>>>> mm/damon/sysfs-schemes.c | 4 +-
>>>> mm/dmapool.c | 4 +-
>>>> mm/huge_memory.c | 8 +-
>>>> mm/hugetlb.c | 56 +++---
>>>> mm/hugetlb_vmemmap.c | 16 +-
>>>> mm/khugepaged.c | 14 +-
>>>> mm/kmemleak.c | 7 +-
>>>> mm/ksm.c | 25 +--
>>>> mm/list_lru.c | 4 +-
>>>> mm/memcontrol-v1.c | 8 +-
>>>> mm/memory-failure.c | 12 +-
>>>> mm/memory-tiers.c | 4 +-
>>>> mm/migrate.c | 23 ++-
>>>> mm/mmu_notifier.c | 9 +-
>>>> mm/page_alloc.c | 8 +-
>>>> mm/page_reporting.c | 2 +-
>>>> mm/percpu.c | 11 +-
>>>> mm/pgtable-generic.c | 4 +-
>>>> mm/rmap.c | 10 +-
>>>> mm/shmem.c | 9 +-
>>>> mm/slab_common.c | 14 +-
>>>> mm/slub.c | 33 ++--
>>>> mm/swapfile.c | 4 +-
>>>> mm/userfaultfd.c | 12 +-
>>>> mm/vmalloc.c | 24 +--
>>>> mm/vmscan.c | 7 +-
>>>> mm/zsmalloc.c | 4 +-
>>>> 124 files changed, 875 insertions(+), 681 deletions(-)
>>>
>>> Not sure what you were thinking, but this diff stat
>>> is not landable.
>>
>> [PATCH v3 1/7] and [PATCH v3 2/7] contain the main logic and can
>> be merged directly. They are also compatible with the old API.
>> [PATCH v3 3/7] through [PATCH v3 7/7] are just simple interface
>> replacements and do not change any functional logic. They can be
>> left unmerged for now; individual modules can pick them up later
>> if needed.
>>
>> In v2, Andy Shevchenko mentioned: "If it's done by Linus himself
>> during the day when he prepares -rc1, it's fine."
>
> Yes, but you need to get his blessing first to go with this.
> Have you communicated with him on this?
Not yet, because the overall approach is still not mature. People
have different opinions on the implementation details and on how
to move this forward, so I think we should iterate through a few
versions first before making a final decision.
>> Even so, the
>> changes in this patch series are indeed quite large and touch
>> almost every subsystem. I have only converted part of them for
>> now, so I wanted to send this out first and see what people think.
>
> That's why it's better to provide a script to convert (e.g., coccinelle)
> instead of tons of patches.
I tried writing conversion scripts with Coccinelle, but there were
always cases that got missed. In contrast, I found that using AI
for focused replacements was actually more efficient.
As David Hildenbrand mentioned, "If we decide we want this, I guess
we should target per-subsystem conversions." I would like to provide
the new interface first; adapting each subsystem on demand later may
be easier to achieve.
--
Thanks
Kaitao Cheng
^ permalink raw reply
page: next (older)
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox