summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
authorBob Peterson <rpeterso@redhat.com>2022-01-18 09:30:18 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2022-04-13 21:00:54 +0200
commitf349d7f9ee6d6c9cf9edadb0e1c5e9de6eda7466 (patch)
tree45dae036cbebdf6bde1b1db252892b13148cf1ab /fs
parent3f53715fd55c4616fc25ac0e45ca7331d8fdf7f1 (diff)
gfs2: Fix gfs2_release for non-writers regression
[ Upstream commit d3add1a9519dcacd6e644ecac741c56cf18b67f5 ] When a file is opened for writing, the vfs code (do_dentry_open) calls get_write_access for the inode, thus incrementing the inode's write count. That writer normally then creates a multi-block reservation for the inode (i_res) that can be re-used by other writers, which speeds up writes for applications that stupidly loop on open/write/close. When the writes are all done, the multi-block reservation should be deleted when the file is closed by the last "writer." Commit 0ec9b9ea4f83 broke that concept when it moved the call to gfs2_rs_delete before the check for FMODE_WRITE. Non-writers have no business removing the multi-block reservations of writers. In fact, if someone opens and closes the file for RO while a writer has a multi-block reservation, the RO closer will delete the reservation midway through the write, and this results in: kernel BUG at fs/gfs2/rgrp.c:677! (or thereabouts) which is: BUG_ON(rs->rs_requested); from function gfs2_rs_deltree. This patch moves the check back inside the check for FMODE_WRITE. Fixes: 0ec9b9ea4f83 ("gfs2: Check for active reservation in gfs2_release") Cc: stable@vger.kernel.org # v5.12+ Signed-off-by: Bob Peterson <rpeterso@redhat.com> Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/gfs2/file.c7
1 files changed, 4 insertions, 3 deletions
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
index 59318b1eaa60..7bd7581aa682 100644
--- a/fs/gfs2/file.c
+++ b/fs/gfs2/file.c
@@ -716,10 +716,11 @@ static int gfs2_release(struct inode *inode, struct file *file)
kfree(file->private_data);
file->private_data = NULL;
- if (gfs2_rs_active(&ip->i_res))
- gfs2_rs_delete(ip, &inode->i_writecount);
- if (file->f_mode & FMODE_WRITE)
+ if (file->f_mode & FMODE_WRITE) {
+ if (gfs2_rs_active(&ip->i_res))
+ gfs2_rs_delete(ip, &inode->i_writecount);
gfs2_qa_put(ip);
+ }
return 0;
}