Blob Blame History Raw
From b8f95e5d13f5f0191dcb4b9113113d241636e7cb Mon Sep 17 00:00:00 2001
From: Miklos Szeredi <mszeredi@redhat.com>
Date: Thu, 26 Jul 2018 16:13:11 +0200
Subject: [PATCH] fuse: umount should wait for all requests
References: bsc#1105806
Git-commit: b8f95e5d13f5f0191dcb4b9113113d241636e7cb
Patch-mainline: v4.19-rc1

fuse_abort_conn() does not guarantee that all async requests have actually
finished aborting (i.e. their ->end() function is called).  This could
actually result in still used inodes after umount.

Add a helper to wait until all requests are fully done.  This is done by
looking at the "num_waiting" counter.  When this counter drops to zero, we
can be sure that no more requests are outstanding.

Fixes: 0d8e84b0432b ("fuse: simplify request abort")
Cc: <stable@vger.kernel.org> # v4.2
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Acked-by: Goldwyn Rodrigues <rgoldwyn@suse.com>

---
 fs/fuse/dev.c    |   19 +++++++++++++++++--
 fs/fuse/fuse_i.h |    1 +
 fs/fuse/inode.c  |    2 ++
 3 files changed, 20 insertions(+), 2 deletions(-)

--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -131,6 +131,16 @@ static bool fuse_block_alloc(struct fuse
 	return !fc->initialized || (for_background && fc->blocked);
 }
 
+static void fuse_drop_waiting(struct fuse_conn *fc)
+{
+	if (fc->connected) {
+		atomic_dec(&fc->num_waiting);
+	} else if (atomic_dec_and_test(&fc->num_waiting)) {
+		/* wake up aborters */
+		wake_up_all(&fc->blocked_waitq);
+	}
+}
+
 static struct fuse_req *__fuse_get_req(struct fuse_conn *fc, unsigned npages,
 				       bool for_background)
 {
@@ -171,7 +181,7 @@ static struct fuse_req *__fuse_get_req(s
 	return req;
 
  out:
-	atomic_dec(&fc->num_waiting);
+	fuse_drop_waiting(fc);
 	return ERR_PTR(err);
 }
 
@@ -278,7 +288,7 @@ void fuse_put_request(struct fuse_conn *
 
 		if (test_bit(FR_WAITING, &req->flags)) {
 			__clear_bit(FR_WAITING, &req->flags);
-			atomic_dec(&fc->num_waiting);
+			fuse_drop_waiting(fc);
 		}
 
 		if (req->stolen_file)
@@ -2132,6 +2142,11 @@ void fuse_abort_conn(struct fuse_conn *f
 }
 EXPORT_SYMBOL_GPL(fuse_abort_conn);
 
+void fuse_wait_aborted(struct fuse_conn *fc)
+{
+	wait_event(fc->blocked_waitq, atomic_read(&fc->num_waiting) == 0);
+}
+
 int fuse_dev_release(struct inode *inode, struct file *file)
 {
 	struct fuse_dev *fud = fuse_get_dev(file);
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -852,6 +852,7 @@ void fuse_request_send_background_locked
 
 /* Abort all requests */
 void fuse_abort_conn(struct fuse_conn *fc);
+void fuse_wait_aborted(struct fuse_conn *fc);
 
 /**
  * Invalidate inode attributes
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -394,6 +394,8 @@ static void fuse_put_super(struct super_
 	fuse_send_destroy(fc);
 
 	fuse_abort_conn(fc);
+	fuse_wait_aborted(fc);
+
 	mutex_lock(&fuse_mutex);
 	list_del(&fc->entry);
 	fuse_ctl_remove_conn(fc);