The commit is pushed to "branch-rh10-6.12.0-55.13.1.2.x.vz10-ovz" and will
appear at [email protected]:openvz/vzkernel.git
after rh10-6.12.0-55.13.1.2.26.vz10
------>
commit acd31bc9817a263ceced79bf43c56c58ef65a46d
Author: Alexey Kuznetsov <[email protected]>
Date: Mon Dec 22 18:08:32 2025 +0800
fs/fuse: vstorage specific file attribute maintenance
rhel10 has a few of changes which broke inode attributes consistency.
An explanation is needed. Long ago we merged our support of writeback cache
to mainstream in hope it is maintained w/o our further efforts.
This did not work. Not that it did not, it is maintained, unfortunately
it has very few, if any, users but us, nobody actually needs for fuse
to perform decently. AFAIK ntfs-3g was the only backend using it,
if someone knows another backend, please, tell me,
we need to keep track of them.
Actually, changes in rhel10 are not invalid, they are even good, but
they rely on assumption that backend fs has no external impact.
If fs is mounted, only this instanse of fuse can change it.
The assumption is fair, but this is not actually the thing
we need. Our fs is kind of hybrid: we must behave as genuine
writeback_cache fs on files which are open by us, but we still behave
as not cacheing fuse when file is not open. Obviously, mainstream
is not able to maintain such mode.
So, fix it. In fact this patch is 99% pure cleanup, it undoes our
previous hacks and reduces differences of mainstream
and replaces them with oneliner, see function fuse_get_cache_mask().
Also, partial audit of our use of fc->writeback_cache flag has
been done and in places where it actually had nothing to do with
mainstream writeback_cache, they are replaced with our private
flag fc->close_wait, which is strictly specific to vstorage.
Remember, if this mode still has other users we have no right
to violate their assumptions.
Also, while audit an old bug has been discovered. After an inode
is revoked we did some efforts to refresh its attributes, neverthless
the procedure was incorrect and actually we always had the same
problem that we got in rhel10 after plain reopen.
https://virtuozzo.atlassian.net/browse/VSTOR-120894
Signed-off-by: Alexey Kuznetsov <[email protected]>
Feature: vStorage
---
fs/fuse/dir.c | 28 ++++++----------------------
fs/fuse/file.c | 13 +++++--------
fs/fuse/fuse_i.h | 2 --
fs/fuse/inode.c | 15 +++++++++++----
4 files changed, 22 insertions(+), 36 deletions(-)
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 5355ee72eadea..6637914c8f282 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -721,7 +721,7 @@ static int fuse_create_open(struct mnt_idmap *idmap, struct
inode *dir,
invalidate_inode_pages2(inode->i_mapping);
}
- if (fm->fc->writeback_cache) {
+ if (fm->fc->close_wait) {
struct fuse_inode *fi = get_fuse_inode(inode);
bool need_open;
@@ -1274,8 +1274,7 @@ static int fuse_do_statx(struct mnt_idmap *idmap, struct
inode *inode,
}
static int fuse_do_getattr(struct mnt_idmap *idmap, struct inode *inode,
- struct kstat *stat, struct file *file,
- int get_size_form_attr)
+ struct kstat *stat, struct file *file)
{
int err;
struct fuse_getattr_in inarg;
@@ -1314,28 +1313,13 @@ static int fuse_do_getattr(struct mnt_idmap *idmap,
struct inode *inode,
fuse_change_attributes(inode, &outarg.attr, NULL,
ATTR_TIMEOUT(&outarg),
attr_version);
- if (get_size_form_attr)
- stat->size = outarg.attr.size;
- else if (stat)
+ if (stat)
fuse_fillattr(idmap, inode, &outarg.attr, stat);
}
}
return err;
}
-int fuse_getattr_size(struct inode *inode, struct file *file, u64 *size)
-{
- struct kstat stat;
- int err;
-
- err = fuse_do_getattr(&nop_mnt_idmap, inode, &stat, file, 1);
- if (err)
- return err;
-
- *size = stat.size;
- return 0;
-}
-
static int fuse_update_get_attr(struct mnt_idmap *idmap, struct inode *inode,
struct file *file, struct kstat *stat,
u32 request_mask, unsigned int flags)
@@ -1376,7 +1360,7 @@ static int fuse_update_get_attr(struct mnt_idmap *idmap,
struct inode *inode,
goto retry;
}
} else {
- err = fuse_do_getattr(idmap, inode, stat, file, 0);
+ err = fuse_do_getattr(idmap, inode, stat, file);
}
} else if (stat) {
generic_fillattr(idmap, request_mask, inode, stat);
@@ -1545,7 +1529,7 @@ static int fuse_perm_getattr(struct inode *inode, int
mask)
return -ECHILD;
forget_all_cached_acls(inode);
- return fuse_do_getattr(&nop_mnt_idmap, inode, NULL, NULL, 0);
+ return fuse_do_getattr(&nop_mnt_idmap, inode, NULL, NULL);
}
/*
@@ -2138,7 +2122,7 @@ static int fuse_setattr(struct mnt_idmap *idmap, struct
dentry *entry,
* ia_mode calculation may have used stale i_mode.
* Refresh and recalculate.
*/
- ret = fuse_do_getattr(idmap, inode, NULL, file, 0);
+ ret = fuse_do_getattr(idmap, inode, NULL, file);
if (ret)
return ret;
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 5722cd4414318..0860996c19ad3 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -360,17 +360,17 @@ static int fuse_open(struct inode *inode, struct file
*file)
if (is_wb_truncate || dax_truncate)
inode_unlock(inode);
- if (!err && fc->writeback_cache) {
+ if (!err && fc->close_wait) {
struct fuse_inode *fi = get_fuse_inode(inode);
- u64 size;
inode_lock(inode);
spin_lock(&fi->lock);
- if (++fi->num_openers == 1) {
+ if (++fi->num_openers == 1 || fi->i_size_unstable) {
fi->i_size_unstable = 1;
+ fi->inval_mask = ~0;
spin_unlock(&fi->lock);
- err = fuse_getattr_size(inode, file, &size);
+ err = fuse_update_attributes(inode, file, ~0);
if (!err && fc->kio.op && fc->kio.op->file_open)
err = fc->kio.op->file_open(file, inode);
@@ -379,12 +379,9 @@ static int fuse_open(struct inode *inode, struct file
*file)
fi->i_size_unstable = 0;
if (err)
fi->num_openers--;
- else
- i_size_write(inode, size);
}
- if (fc->close_wait)
- file->f_mode |= FMODE_NOWAIT;
+ file->f_mode |= FMODE_NOWAIT;
spin_unlock(&fi->lock);
inode_unlock(inode);
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index d6c7c5fd2bdcc..853bf12e282d9 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -1495,8 +1495,6 @@ void fuse_update_ctime(struct inode *inode);
int fuse_update_attributes(struct inode *inode, struct file *file, u32 mask);
-int fuse_getattr_size(struct inode *inode, struct file *file, u64 *size);
-
void fuse_flush_writepages(struct inode *inode);
void fuse_set_nowrite(struct inode *inode);
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index c067c5922b5df..f167d275885bf 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -321,6 +321,13 @@ u32 fuse_get_cache_mask(struct inode *inode)
if (!fc->writeback_cache || !S_ISREG(inode->i_mode))
return 0;
+ if (fc->close_wait) {
+ struct fuse_inode *fi = get_fuse_inode(inode);
+
+ if (!fi->num_openers || fi->i_size_unstable)
+ return 0;
+ }
+
return STATX_MTIME | STATX_CTIME | STATX_SIZE;
}
@@ -362,18 +369,17 @@ void fuse_change_attributes(struct inode *inode, struct
fuse_attr *attr,
old_mtime = inode_get_mtime(inode);
fuse_change_attributes_common(inode, attr, sx, attr_valid, cache_mask);
- oldsize = inode->i_size;
+ oldsize = i_size_read(inode);
/*
* In case of writeback_cache enabled, the cached writes beyond EOF
* extend local i_size without keeping userspace server in sync. So,
* attr->size coming from server can be stale. We cannot trust it.
*/
- if (!(cache_mask & STATX_SIZE) ||
- !fi->num_openers || fi->i_size_unstable)
+ if (!(cache_mask & STATX_SIZE))
i_size_write(inode, attr->size);
spin_unlock(&fi->lock);
- if (!cache_mask && S_ISREG(inode->i_mode)) {
+ if (!cache_mask && S_ISREG(inode->i_mode) && !fc->close_wait) {
bool inval = false;
if (oldsize != attr->size) {
@@ -624,6 +630,7 @@ int fuse_invalidate_files(struct fuse_conn *fc, u64 nodeid)
set_bit(FUSE_S_FAIL_IMMEDIATELY, &ff->ff_state);
spin_unlock(&ff->lock);
}
+ fi->i_size_unstable = 1;
spin_unlock(&fi->lock);
/* let them see FUSE_S_FAIL_IMMEDIATELY */
_______________________________________________
Devel mailing list
[email protected]
https://lists.openvz.org/mailman/listinfo/devel