summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Brauner <brauner@kernel.org>2026-09-10 17:48:04 +0200
committerChristian Brauner <brauner@kernel.org>2026-09-14 15:58:06 +0200
commitd99d38540bf0084df2ac30dc72a2d14eaa4d2db2 (patch)
tree2b97d61f873f965b5da05d8062b3a34ede9957ab
parent7e6ad3fe9136fefed8fea8ac6d3a14fe2808a58d (diff)
downloadlinux-next-d99d38540bf0084df2ac30dc72a2d14eaa4d2db2.tar.gz
linux-next-d99d38540bf0084df2ac30dc72a2d14eaa4d2db2.zip
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 4a9d4b024a31 ("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> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
-rw-r--r--fs/file.c4
1 files changed, 2 insertions, 2 deletions
diff --git a/fs/file.c b/fs/file.c
index 636a87e527b3..b2b466dce7fb 100644
--- a/fs/file.c
+++ b/fs/file.c
@@ -489,7 +489,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
@@ -509,7 +509,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();
}
}