[Devel] [PATCH RHEL8 COMMIT] fs/fuse: avoid triggering BUG_ON at file close
Konstantin Khorenko
khorenko at virtuozzo.com
Tue Apr 27 17:08:11 MSK 2021
The commit is pushed to "branch-rh8-4.18.0-240.1.1.vz8.5.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh8-4.18.0-240.1.1.vz8.5.22
------>
commit 7b8be41539c610f995aaaf8bcfe8518f564b4a83
Author: Alexey Kuznetsov <kuznet at virtuozzo.com>
Date: Tue Apr 27 17:08:11 2021 +0300
fs/fuse: avoid triggering BUG_ON at file close
The problem is that our implementation of vstorage file close
is not compatible with voluntary taking a reference with fuse file.
Natural and better fix would be global changing i_ops->fiemap method
to pass file in addition to inode. Nevertheless, this change is global
and can be done only in future kernel versions.
Also, replace BUG_ON with WARN_ON. It was not a fatal logical condition,
kernel can proceed in case we still miss something,
it is just the places which could violate strict vstorage close semantics.
https://pmc.acronis.com/browse/VSTOR-43056
Signed-off-by: Alexey Kuznetsov <kuznet at acronis.com>
Cherry-picked from vz7 commit ("386d9fd72108 fs/fuse: avoid triggering BUG_ON at
file close")
Signed-off-by: Ildar Ismagilov <ildar.ismagilov at virtuozzo.com>
---
fs/fuse/file.c | 27 +++++++++++++--------------
fs/fuse/fuse_i.h | 3 ++-
2 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index a25c2d5d2e25..fb55c7968b48 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -386,7 +386,7 @@ void fuse_release_common(struct file *file, bool isdir)
* fuse file release is synchronous
*/
if (ff->fc->close_wait) {
- BUG_ON(refcount_read(&ff->count) != 1);
+ WARN_ON(refcount_read(&ff->count) != 1);
}
/*
@@ -413,6 +413,10 @@ static int fuse_release(struct inode *inode, struct file *file)
struct fuse_file *ff = file->private_data;
struct fuse_inode *fi = get_fuse_inode(inode);
+ set_bit(FUSE_S_CLOSING, &ff->ff_state);
+ spin_lock(&fi->lock);
+ spin_unlock(&fi->lock);
+
if (ff->fc->writeback_cache) {
if (file->f_mode & FMODE_WRITE) {
write_inode_now(inode, 1);
@@ -3874,28 +3878,23 @@ static int fuse_request_fiemap(struct inode *inode, u32 cur_max,
err = 0;
spin_lock(&fi->lock);
/* Kernel API is weird in this place, we have to find a file associated with this inode.
- * This problem is similar to writepage routines, but it is much worse. When doing
+ * This problem is similar to writepage routines, but it is worse. When doing
* writepage, we have some file open for write and can take any file from write_files
* and it is safe to keep it with get/put. But here we can have the file open for read
- * and no files open for write. Even worse, if we select a random file open for write,
- * it can be closed by user from another thread and its close will violate close_wait requirement.
- * So, we have to search for a read-only file first and fallback to writable only
- * if there is no such file.
+ * and no files open for write. So, we have to take a file from rw_files.
*/
if (!list_empty(&fi->rw_files)) {
struct fuse_file *t_ff;
list_for_each_entry(t_ff, &fi->rw_files, rw_entry) {
- if (list_empty(&t_ff->write_entry)) {
+ if (!test_bit(FUSE_S_CLOSING, &t_ff->ff_state)) {
ff = t_ff;
break;
}
}
- if (!ff)
- ff = list_entry(fi->rw_files.next, struct fuse_file, rw_entry);
- fuse_file_get(ff);
- inarg.fh = ff->fh;
- } else if (!list_empty(&fi->write_files)) {
+ }
+ if (!ff && !list_empty(&fi->write_files))
ff = list_entry(fi->write_files.next, struct fuse_file, write_entry);
+ if (ff) {
fuse_file_get(ff);
inarg.fh = ff->fh;
} else {
@@ -3917,7 +3916,7 @@ static int fuse_request_fiemap(struct inode *inode, u32 cur_max,
ap.pages = fuse_pages_alloc(fc->max_pages, GFP_KERNEL, &ap.descs);
if (!ap.pages) {
- fuse_file_put(ff, false, false);
+ fuse_release_ff(inode, ff);
return -ENOMEM;
}
@@ -4001,7 +4000,7 @@ static int fuse_request_fiemap(struct inode *inode, u32 cur_max,
__free_page(ap.pages[allocated]);
ap.pages[allocated] = NULL;
}
- fuse_file_put(ff, false, false);
+ fuse_release_ff(inode, ff);
return err;
}
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 2d6a7d43a08e..99d871dc641b 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -257,7 +257,8 @@ struct fuse_file {
/** FUSE file states (ff_state) */
enum {
/** Any fops on given ff should fail immediately */
- FUSE_S_FAIL_IMMEDIATELY,
+ FUSE_S_FAIL_IMMEDIATELY = 0,
+ FUSE_S_CLOSING = 1
};
/** One input argument of a request */
More information about the Devel
mailing list