fs: make close_files() synchronous

When the last reference to a descriptor table is dropped close_files()
closes every file but punts the actual work to task work. For an exiting
task that task work only runs in exit_task_work().

Before commit 4a9d4b024a ("switch fput to task_work_add") fput() was
synchronous everywhere and exit released its files in exit_files().

The deferral made fput() safe from any context. And exit_files()
offloaded to task work as a side-effect. And that has downsides.

Oleg and Neil noticed that some time ago. A task that exits with a big
descriptor table ends up queueing a very large number of files on task
work. That leaves a list for any later task_work_cancel() to search
under ->pi_lock and costs a lot of atomics too.

Let close_files() close right away. Flush and put each file inline the
way close(2) does. The final __fput() runs during the table walk now
instead of from task_work_run() in exit_task_work(). One difference is
the order: task work ran the final __fput()s in reverse and now they run
in table order.

Every put of a dying table is synchronous now:

- exit_files()
- copy_process()
- close_range(CLOSE_RANGE_UNSHARE)
- unshare(2)
- exec

Kernel threads don't own a file descriptor table and exec already splats
were they to exec. kthreadd and every kthread share init_files and
init_task pins that forever.

Link: https://patch.msgid.link/20260910-work-coredump-unlock-self-v4-6-a5c1800dc930@kernel.org
Reviewed-by: NeilBrown <neil@brown.name>
Reviewed-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
This commit is contained in:
Christian Brauner 2026-09-10 17:48:04 +02:00
parent 2b256ebe82
commit c8d9b40eab
No known key found for this signature in database
GPG Key ID: 91C61BC06578DCA2

View File

@ -549,7 +549,7 @@ int unshare_fd(unsigned long unshare_flags, struct files_struct **new_fdp)
return 0;
}
static struct fdtable *close_files(struct files_struct * files)
static struct fdtable *close_files(struct files_struct *files)
{
/*
* It is safe to dereference the fd table without RCU or
@ -569,7 +569,7 @@ static struct fdtable *close_files(struct files_struct * files)
if (set & 1) {
struct file *file = fdt->fd[i];
if (file) {
filp_close(file, files);
filp_close_sync(file, files);
cond_resched();
}
}