]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
btrfs: fix unnecessary flush on close when truncating zero-sized files
authorDave Chen <davechen@synology.com>
Mon, 23 Mar 2026 03:43:22 +0000 (11:43 +0800)
committerDavid Sterba <dsterba@suse.com>
Tue, 7 Apr 2026 16:56:06 +0000 (18:56 +0200)
In btrfs_setsize(), when a file is truncated to size 0, the
BTRFS_INODE_FLUSH_ON_CLOSE flag is unconditionally set to ensure
pending writes get flushed on close. This flag was designed to protect
the "truncate-then-rewrite" pattern, where an application truncates a
file with existing data down to zero and writes new content, ensuring
the new data reach disk on close.

However, when a file already has a size of 0 (e.g. a newly created
file opened with O_CREAT | O_TRUNC), oldsize and newsize are both 0.
In this case, setting BTRFS_INODE_FLUSH_ON_CLOSE is unnecessary because
no "good data" was truncated away. The subsequent filemap_flush() in
btrfs_release_file() then triggers avoidable writeback that disrupts
the normal delayed writeback batching, adding I/O overhead.

This comes from a real workload. A backup service creates temporary
files via mkstemp(), closes them, and later reopens them with O_TRUNC
for writing. The O_TRUNC is defensive.  The file creation and usage is
done by a different component, so removing the unneeded truncation is
not straightforward.  This pattern repeats for a large number of files
each close() triggers an unnecessary filemap_flush().

Signed-off-by: Dave Chen <davechen@synology.com>
Signed-off-by: Robbie Ko <robbieko@synology.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/inode.c

index 8d97a8ad3858bf9e4d26266d95ef369249843568..1a4e6a9239aed303113d318035d0ccca18e3a180 100644 (file)
@@ -5442,7 +5442,7 @@ static int btrfs_setsize(struct inode *inode, struct iattr *attr)
                 * zero. Make sure any new writes to the file get on disk
                 * on close.
                 */
-               if (newsize == 0)
+               if (newsize == 0 && oldsize != 0)
                        set_bit(BTRFS_INODE_FLUSH_ON_CLOSE,
                                &BTRFS_I(inode)->runtime_flags);