[PATCH RFC 2/7] fs: make close_files() synchronous
Christian Brauner <[email protected]>
| Newsgroups | gmane.linux.file-systems |
|---|---|
| Message-ID | <[email protected]> |
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 walks the task work list under ->pi_lock.
Note, today closing the files runs after exit_fs(), disassociate_ctty()
and exit_task_namespaces(). So ->flush() and ->release() get a task that
has already lost its fs, its namespaces and its controlling terminal
even though the files were closed long before that.
So let close_files() close right away. No locks are held, the path
already sleeps, close() does the same thing already. Kernel threads can
just keep deferring. ->flush() and ->release() now run before exit_fs()
and disassociate_ctty().
Signed-off-by: Christian Brauner (Amutable) <[email protected]>
---
fs/file.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/fs/file.c b/fs/file.c
index 628ca07dc4b1..e2d4b72c2e26 100644
--- a/fs/file.c
+++ b/fs/file.c
@@ -480,6 +480,11 @@ static struct fdtable *close_files(struct files_struct * files)
*/
struct fdtable *fdt = rcu_dereference_raw(files->fdt);
unsigned int i, j = 0;
+ /*
+ * A kernel thread that might be needed to make progress on some
+ * umount must not run __fput() itself, see __fput_sync().
+ */
+ bool sync = !(current->flags & PF_KTHREAD);
for (;;) {
unsigned long set;
@@ -491,7 +496,10 @@ static struct fdtable *close_files(struct files_struct * files)
if (set & 1) {
struct file *file = fdt->fd[i];
if (file) {
- filp_close(file, files);
+ if (sync)
+ filp_close_sync(file, files);
+ else
+ filp_close(file, files);
cond_resched();
}
}
--
2.53.0