Hi all,
We have recently encountered a case where aria2c adopts the following
IO pattern when downloading files(We enabled writeback_cache option):
It allocates file space via fallocate. If fallocate is not supported,
it will circularly write 256KB of zero-filled data to the file until it reaches
an enough size, and then truncate the file to the desired size. Subsequently,
it fills non-zero data into the file through random writes.
This causes aria2c to run extremely slowly, which does not meet our
expectations,
because we have enabled writeback_cache, random writes should not be this slow.
After investigation, I found that a readpage operation is performed in every
write_begin callback. This is quite odd, as the file was just fully filled with
zeros via write operations; the file's page cache should all be uptodate,
so there is no need for a readpage. Upon further analysis, I discovered that the
root cause is that truncate has invalidated all the page cache.
I would like to know why the invalidation is performed. After checking the code
commit history, I found that this has been the implementation since FUSE added
support for the writeback cache mode.
Therefore, I can only seek help from the community: What were the
considerations
behind this implementation, and can it be removed?
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -2279,7 +2279,6 @@ int fuse_do_setattr(struct mnt_idmap *idmap,
struct dentry *dentry,
if ((is_truncate || !is_wb) &&
S_ISREG(inode->i_mode) && oldsize != outarg.attr.size) {
truncate_pagecache(inode, outarg.attr.size);
- invalidate_inode_pages2(mapping);
}
clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
Thanks,
Tianci
On Sun, 4 Jan 2026 at 07:51, Zhang Tianci
<zhangtianci.1997@bytedance.com> wrote:
>
> Hi all,
>
> We have recently encountered a case where aria2c adopts the following
> IO pattern when downloading files(We enabled writeback_cache option):
>
> It allocates file space via fallocate. If fallocate is not supported,
> it will circularly write 256KB of zero-filled data to the file until it reaches
> an enough size, and then truncate the file to the desired size. Subsequently,
> it fills non-zero data into the file through random writes.
>
> This causes aria2c to run extremely slowly, which does not meet our
> expectations,
> because we have enabled writeback_cache, random writes should not be this slow.
> After investigation, I found that a readpage operation is performed in every
> write_begin callback. This is quite odd, as the file was just fully filled with
> zeros via write operations; the file's page cache should all be uptodate,
> so there is no need for a readpage. Upon further analysis, I discovered that the
> root cause is that truncate has invalidated all the page cache.
>
> I would like to know why the invalidation is performed. After checking the code
> commit history, I found that this has been the implementation since FUSE added
> support for the writeback cache mode.
This in fact goes back to the very first version committed into Linus' tree:
$ git show d8a5ba45457e4 | grep -C 3 invalidate_inode_pages
+void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr)
+{
+ if (S_ISREG(inode->i_mode) && i_size_read(inode) != attr->size)
+ invalidate_inode_pages(inode->i_mapping);
This pattern was copied into setattr and, since it was harmless, left
there for two centuries.
And because it was there for so long there's a minute chance that some
fuse filesystem is relying on this behavior, so I'm a bit reluctant to
change it.
And fixing this does not in fact fix the underlying issue. Manually
preallocating disk space by writing zero blocks has the size effect of
priming the page cache as well, which makes the random writes faster.
Doing the same with fallocate() does not have this side effect and
would leave the fuse filesystem with the bad performance.
Thanks,
Miklos
Hi Miklos,
On Tue, Jan 27, 2026 at 5:44 PM Miklos Szeredi <miklos@szeredi.hu> wrote:
>
> On Sun, 4 Jan 2026 at 07:51, Zhang Tianci
> <zhangtianci.1997@bytedance.com> wrote:
> >
> > Hi all,
> >
> > We have recently encountered a case where aria2c adopts the following
> > IO pattern when downloading files(We enabled writeback_cache option):
> >
> > It allocates file space via fallocate. If fallocate is not supported,
> > it will circularly write 256KB of zero-filled data to the file until it reaches
> > an enough size, and then truncate the file to the desired size. Subsequently,
> > it fills non-zero data into the file through random writes.
> >
> > This causes aria2c to run extremely slowly, which does not meet our
> > expectations,
> > because we have enabled writeback_cache, random writes should not be this slow.
> > After investigation, I found that a readpage operation is performed in every
> > write_begin callback. This is quite odd, as the file was just fully filled with
> > zeros via write operations; the file's page cache should all be uptodate,
> > so there is no need for a readpage. Upon further analysis, I discovered that the
> > root cause is that truncate has invalidated all the page cache.
> >
> > I would like to know why the invalidation is performed. After checking the code
> > commit history, I found that this has been the implementation since FUSE added
> > support for the writeback cache mode.
>
> This in fact goes back to the very first version committed into Linus' tree:
>
> $ git show d8a5ba45457e4 | grep -C 3 invalidate_inode_pages
> +void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr)
> +{
> + if (S_ISREG(inode->i_mode) && i_size_read(inode) != attr->size)
> + invalidate_inode_pages(inode->i_mapping);
>
> This pattern was copied into setattr and, since it was harmless, left
> there for two centuries.
Thanks for sharing your memories.
>
> And because it was there for so long there's a minute chance that some
> fuse filesystem is relying on this behavior, so I'm a bit reluctant to
> change it.
>
> And fixing this does not in fact fix the underlying issue. Manually
> preallocating disk space by writing zero blocks has the size effect of
> priming the page cache as well, which makes the random writes faster.
> Doing the same with fallocate() does not have this side effect and
> would leave the fuse filesystem with the bad performance.
Yep, I agree. I'm just wondering if we can or should optimize for this
specific case.
Thanks,
Tianci
© 2016 - 2026 Red Hat, Inc.