[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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.