On 6/17/26 2:16 AM, harshal24-chavan wrote:
> diff --git a/io_uring/rsrc.c b/io_uring/rsrc.c
> index 650303626be6..1e4e114ca5a5 100644
> --- a/io_uring/rsrc.c
> +++ b/io_uring/rsrc.c
> @@ -1303,6 +1303,166 @@ int io_register_clone_buffers(struct io_ring_ctx
> *ctx, void __user *arg)
> return ret;
> }
>
> +
> +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;
> + int i, off, nr;
> + unsigned int src_nr;
> +
> + lockdep_assert_held(&ctx->uring_lock);
> + lockdep_assert_held(&src_ctx->uring_lock);
> +
> + /* if offsets are given, must have nr specified too */
> + if (!arg->nr && (arg->dst_off || arg->src_off))
> + return -EINVAL;
Not sure the offsets and partial copies are going to be worth it, but
I'm willing to have my mind changed. But that's a minor thing really.
> + /* not allowed unless REPLACE is set */
> + if (ctx->file_table.data.nr &&
> + !(arg->flags & IORING_REGISTER_DST_REPLACE))
> + return -EBUSY;
> +
> + src_nr = src_ctx->file_table.data.nr;
> + if (!src_nr)
> + return -ENXIO;
> + if (!arg->nr)
> + arg->nr = src_nr;
> + else if (arg->nr > src_nr)
> + return -EINVAL;
> + else if (arg->nr > IORING_MAX_FIXED_FILES)
> + return -EINVAL;
> + if (check_add_overflow(arg->nr, arg->src_off, &off) || off > src_nr)
> + return -EOVERFLOW;
> + if (check_add_overflow(arg->nr, arg->dst_off, &src_nr))
> + return -EOVERFLOW;
> + if (src_nr > IORING_MAX_FIXED_FILES)
> + return -EINVAL;
> + /* Allocate file tables memory {data + bitmap} into new_file_table */
> + memset(&new_file_table, 0, sizeof(new_file_table));
> + if (!io_alloc_file_tables(ctx, &new_file_table,
> + max(src_nr, ctx->file_table.data.nr)))
> + return -ENOMEM;
Also a question whether the destination should've already allocated a
sparse table. This kind of bundles the two into one. In general, as
mention on the GH link, I do think this should work exactly like cloning
buffers. It'd be somewhat confusing if they don't match up, as it's
essentially the same operation, just on a different node type.
> + /* Copy original dst nodes from before the cloned range */
> + for (i = 0; i < min(arg->dst_off, ctx->file_table.data.nr); i++) {
> + struct io_rsrc_node *node = ctx->file_table.data.nodes[i];
> +
> + if (node) {
> + new_file_table.data.nodes[i] = node;
> + node->refs++;
> + io_file_bitmap_set(&new_file_table, i);
> + }
> + }
This definitely won't work - I also mentioned in the GH link that nodes
cannot be shared, you have to allocate new nodes on the destination
side.
> + while (nr--) {
> + struct io_rsrc_node *dst_node, *src_node;
> +
> + src_node = io_rsrc_node_lookup(&src_ctx->file_table.data, i);
> + if (!src_node) {
> + dst_node = NULL;
> + } else {
> + dst_node = io_rsrc_node_alloc(ctx, IORING_RSRC_FILE);
> + if (!dst_node) {
> + io_free_file_tables(ctx, &new_file_table);
> + return -ENOMEM;
> + }
> +
> + struct file *file = io_slot_file(src_node);
> +
> + get_file(file);
> + io_fixed_file_set(dst_node, file);
> + }
> + new_file_table.data.nodes[off] = dst_node;
> + if (dst_node)
> + io_file_bitmap_set(&new_file_table, off);
> +
> + i++;
> + off++;
> + }
Same here, it needs a new node that's private to the destination. Hence
you'd need to _always_ allocate one, assign the file, and get a
reference to it.
The file nodes rely on non-atomic refs when being used, which is
protected by the ctx->uring_lock as that's always held for the fast path
issue. If you just assign the node by reference, now you have two
different rings manipulating the same node in memory, but they don't
agree on synchronization.
--
Jens Axboe