* FAILED: patch "[PATCH] btrfs: do not overwrite NODATASUM flag when removing" failed to apply to 6.6-stable tree
@ 2026-09-03 13:24 gregkh
2026-09-07 1:03 ` [PATCH 6.6.y 1/4] btrfs: move btrfs_is_empty_uuid() from ioctl.c into fs.c Sasha Levin
0 siblings, 1 reply; 5+ messages in thread
From: gregkh @ 2026-09-03 13:24 UTC (permalink / raw)
To: wqu; +Cc: stable
The patch below does not apply to the 6.6-stable tree.
If someone wants it applied there, or to any other stable or longterm
tree, then please email the backport, including the original git commit
id to <stable@vger.kernel.org>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.6.y
git checkout FETCH_HEAD
git cherry-pick -x 15f7c86215e8d5f14b24127fa88af6c79363d50e
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<stable@vger.kernel.org>' --in-reply-to '2026090330-ferocity-flavorful-f5e4@gregkh' --subject-prefix 'PATCH 6.6.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 15f7c86215e8d5f14b24127fa88af6c79363d50e Mon Sep 17 00:00:00 2001
From: Qu Wenruo <wqu@suse.com>
Date: Tue, 9 Jun 2026 08:43:34 +0930
Subject: [PATCH] btrfs: do not overwrite NODATASUM flag when removing
NODATACOW flag
[TEST FAILURE]
The test case generic/628 will fail if MOUNT_OPTIONS is set to
"-o nodatasum":
FSTYP -- btrfs
PLATFORM -- Linux/x86_64 btrfs-vm 7.1.0-rc4-custom+ #383 SMP PREEMPT_DYNAMIC Sat May 30 07:35:42 ACST 2026
MKFS_OPTIONS -- -O bgt -K /dev/mapper/test-scratch1
MOUNT_OPTIONS -- -o nodatasum /dev/mapper/test-scratch1 /mnt/scratch
generic/628 1s ... - output mismatch (see /home/adam/xfstests/results//generic/628.out.bad)
--- tests/generic/628.out 2022-05-11 11:25:30.816666664 +0930
+++ /home/adam/xfstests/results//generic/628.out.bad 2026-06-08 18:56:49.878542927 +0930
@@ -8,8 +8,9 @@
310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/a
310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/d
test reflink flag not set iflag
+XFS_IOC_CLONE: Invalid argument
310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/a
-310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/b
+d41d8cd98f00b204e9800998ecf8427e SCRATCH_MNT/b
...
[CAUSE]
The direct cause is that after "chattr +S", the btrfs inode will lose its
NODATASUM flag inherited from the mount option. E.g.:
# mkfs.btrfs -f $dev
# mount $dev $mnt -o nodatasum
# touch $mnt/foobar
# sync
# btrfs ins dump-tree -t 5 $dev | grep "(257 INODE_ITEM 0) itemoff" -A 3
item 4 key (257 INODE_ITEM 0) itemoff 15879 itemsize 160
generation 9 transid 9 size 0 nbytes 0
block group 0 mode 100644 links 1 uid 0 gid 0 rdev 0
sequence 1 flags 0x1(NODATASUM)
^^^^^^^^^ Proper NODATASUM flag
# chattr +S $mnt/foobar
# sync
# btrfs ins dump-tree -t 5 $dev | grep "(257 INODE_ITEM 0) itemoff" -A 3
item 4 key (257 INODE_ITEM 0) itemoff 15879 itemsize 160
generation 9 transid 10 size 0 nbytes 0
block group 0 mode 100644 links 1 uid 0 gid 0 rdev 0
sequence 2 flags 0x20(SYNC)
^^^^ Only the new SYNC flag
This makes the inode drop the old NODATASUM flag, while the new reflink
destination will still inherit the NODATASUM flag. The mismatching
NODATASUM flags will cause the reflink to fail.
The root cause is that, inside btrfs_fileattr_set() if no FS_NOCOW_FL is
set, we remove both NODATASUM and NODATACOW flag.
However we should not touch NODATASUM flag, as data COW doesn't require
checksum. Only NODATACOW implies NODATASUM, but DATACOW doesn't imply
DATASUM.
The deeper problems are:
- Fileattr API is too binary
It either clears or sets a flag, there is no "do not change" option.
So that why "chattr +S" implies "chattr -C", and is forcing us to
change NODATACOW along with NODATASUM flag.
- No way to change NODATASUM through fileattr API
In fact NODATASUM can only be modified through mount option.
The deeper problems are much harder to attack.
[FIX]
Remove NODATACOW flag when FS_NOCOW_FL is not set, but only remove
NODATASUM if "nodatasum" mount option is not set.
This allows the existing "chattr +C" then "chattr -C" to remove
both NODATACOW and NODATASUM flags on a default mount.
But for a mount with "nodatasum" option, the NODATASUM inode flag will
persist through either "chattr +C" and "chattr -C".
Fixes: 7e97b8daf634 ("btrfs: allow setting NOCOW for a zero sized file via ioctl")
Cc: stable@vger.kernel.org
Reviewed-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 7e97a6aebf9b..32dd7bbd4d63 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -356,14 +356,21 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap,
inode_flags |= BTRFS_INODE_NODATACOW;
}
} else {
- /*
- * Revert back under same assumptions as above
- */
- if (S_ISREG(inode->vfs_inode.i_mode)) {
- if (inode->vfs_inode.i_size == 0)
- inode_flags &= ~(BTRFS_INODE_NODATACOW |
- BTRFS_INODE_NODATASUM);
- } else {
+ /* We can only change NODATACOW for zero-sized regular file. */
+ if (S_ISREG(inode->vfs_inode.i_mode) && (inode->vfs_inode.i_size == 0)) {
+ inode_flags &= ~BTRFS_INODE_NODATACOW;
+ /*
+ * There is currently no way to change NODATASUM flag
+ * through fileattr API. If we unconditionally keep the
+ * current NODATASUM flag, chattr +C then chattr -C will
+ * keep the NODATASUM flag, and no way to remove that
+ * flag.
+ *
+ * So respect the current mount option for NODATASUM flag.
+ */
+ if (!btrfs_test_opt(fs_info, NODATASUM))
+ inode_flags &= ~BTRFS_INODE_NODATASUM;
+ } else if (!S_ISREG(inode->vfs_inode.i_mode)) {
inode_flags &= ~BTRFS_INODE_NODATACOW;
}
}
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 6.6.y 1/4] btrfs: move btrfs_is_empty_uuid() from ioctl.c into fs.c
2026-09-03 13:24 FAILED: patch "[PATCH] btrfs: do not overwrite NODATASUM flag when removing" failed to apply to 6.6-stable tree gregkh
@ 2026-09-07 1:03 ` Sasha Levin
2026-09-07 1:03 ` [PATCH 6.6.y 2/4] btrfs: move BTRFS_BYTES_TO_BLKS() into fs.h Sasha Levin
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: Sasha Levin @ 2026-09-07 1:03 UTC (permalink / raw)
To: stable
Cc: Filipe Manana, Qu Wenruo, Johannes Thumshirn, David Sterba,
Sasha Levin
From: Filipe Manana <fdmanana@suse.com>
[ Upstream commit a5b3f117daead61c3c9c88cd1159d38fa4ad1362 ]
It's a generic helper not specific to ioctls and used in several places,
so move it out from ioctl.c and into fs.c. While at it change its return
type from int to bool and declare the loop variable in the loop itself.
This also slightly reduces the module's size.
Before this change:
$ size fs/btrfs/btrfs.ko
text data bss dec hex filename
1781492 161037 16920 1959449 1de619 fs/btrfs/btrfs.ko
After this change:
$ size fs/btrfs/btrfs.ko
text data bss dec hex filename
1781340 161037 16920 1959297 1de581 fs/btrfs/btrfs.ko
Reviewed-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
Stable-dep-of: 15f7c86215e8 ("btrfs: do not overwrite NODATASUM flag when removing NODATACOW flag")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/btrfs/fs.c | 9 +++++
fs/btrfs/fs.h | 2 +
fs/btrfs/ioctl.c | 96 +++++++++++++++++++++---------------------------
fs/btrfs/ioctl.h | 1 -
4 files changed, 53 insertions(+), 55 deletions(-)
diff --git a/fs/btrfs/fs.c b/fs/btrfs/fs.c
index 31c1648bc0b46..e2b2b0f4c770f 100644
--- a/fs/btrfs/fs.c
+++ b/fs/btrfs/fs.c
@@ -5,6 +5,15 @@
#include "fs.h"
#include "accessors.h"
+bool __pure btrfs_is_empty_uuid(const u8 *uuid)
+{
+ for (int i = 0; i < BTRFS_UUID_SIZE; i++) {
+ if (uuid[i] != 0)
+ return false;
+ }
+ return true;
+}
+
void __btrfs_set_fs_incompat(struct btrfs_fs_info *fs_info, u64 flag,
const char *name)
{
diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h
index b8b9ce8921baf..b35aa4556d96f 100644
--- a/fs/btrfs/fs.h
+++ b/fs/btrfs/fs.h
@@ -888,6 +888,8 @@ void btrfs_exclop_finish(struct btrfs_fs_info *fs_info);
void btrfs_exclop_balance(struct btrfs_fs_info *fs_info,
enum btrfs_exclusive_operation op);
+bool __pure btrfs_is_empty_uuid(const u8 *uuid);
+
/* Compatibility and incompatibility defines */
void __btrfs_set_fs_incompat(struct btrfs_fs_info *fs_info, u64 flag,
const char *name);
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index d17d1eff8eff4..46eb80580d3bd 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -246,15 +246,14 @@ int btrfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
int btrfs_fileattr_set(struct mnt_idmap *idmap,
struct dentry *dentry, struct fileattr *fa)
{
- struct inode *inode = d_inode(dentry);
- struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
- struct btrfs_inode *binode = BTRFS_I(inode);
- struct btrfs_root *root = binode->root;
+ struct btrfs_inode *inode = BTRFS_I(d_inode(dentry));
+ struct btrfs_root *root = inode->root;
+ struct btrfs_fs_info *fs_info = root->fs_info;
struct btrfs_trans_handle *trans;
unsigned int fsflags, old_fsflags;
int ret;
const char *comp = NULL;
- u32 binode_flags;
+ u32 inode_flags;
if (btrfs_root_readonly(root))
return -EROFS;
@@ -262,8 +261,8 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap,
if (fileattr_has_fsx(fa))
return -EOPNOTSUPP;
- fsflags = btrfs_mask_fsflags_for_type(inode, fa->flags);
- old_fsflags = btrfs_inode_flags_to_fsflags(binode);
+ fsflags = btrfs_mask_fsflags_for_type(&inode->vfs_inode, fa->flags);
+ old_fsflags = btrfs_inode_flags_to_fsflags(inode);
ret = check_fsflags(old_fsflags, fsflags);
if (ret)
return ret;
@@ -272,27 +271,27 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap,
if (ret)
return ret;
- binode_flags = binode->flags;
+ inode_flags = inode->flags;
if (fsflags & FS_SYNC_FL)
- binode_flags |= BTRFS_INODE_SYNC;
+ inode_flags |= BTRFS_INODE_SYNC;
else
- binode_flags &= ~BTRFS_INODE_SYNC;
+ inode_flags &= ~BTRFS_INODE_SYNC;
if (fsflags & FS_IMMUTABLE_FL)
- binode_flags |= BTRFS_INODE_IMMUTABLE;
+ inode_flags |= BTRFS_INODE_IMMUTABLE;
else
- binode_flags &= ~BTRFS_INODE_IMMUTABLE;
+ inode_flags &= ~BTRFS_INODE_IMMUTABLE;
if (fsflags & FS_APPEND_FL)
- binode_flags |= BTRFS_INODE_APPEND;
+ inode_flags |= BTRFS_INODE_APPEND;
else
- binode_flags &= ~BTRFS_INODE_APPEND;
+ inode_flags &= ~BTRFS_INODE_APPEND;
if (fsflags & FS_NODUMP_FL)
- binode_flags |= BTRFS_INODE_NODUMP;
+ inode_flags |= BTRFS_INODE_NODUMP;
else
- binode_flags &= ~BTRFS_INODE_NODUMP;
+ inode_flags &= ~BTRFS_INODE_NODUMP;
if (fsflags & FS_NOATIME_FL)
- binode_flags |= BTRFS_INODE_NOATIME;
+ inode_flags |= BTRFS_INODE_NOATIME;
else
- binode_flags &= ~BTRFS_INODE_NOATIME;
+ inode_flags &= ~BTRFS_INODE_NOATIME;
/* If coming from FS_IOC_FSSETXATTR then skip unconverted flags */
if (!fa->flags_valid) {
@@ -304,32 +303,32 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap,
}
if (fsflags & FS_DIRSYNC_FL)
- binode_flags |= BTRFS_INODE_DIRSYNC;
+ inode_flags |= BTRFS_INODE_DIRSYNC;
else
- binode_flags &= ~BTRFS_INODE_DIRSYNC;
+ inode_flags &= ~BTRFS_INODE_DIRSYNC;
if (fsflags & FS_NOCOW_FL) {
- if (S_ISREG(inode->i_mode)) {
+ if (S_ISREG(inode->vfs_inode.i_mode)) {
/*
* It's safe to turn csums off here, no extents exist.
* Otherwise we want the flag to reflect the real COW
* status of the file and will not set it.
*/
- if (inode->i_size == 0)
- binode_flags |= BTRFS_INODE_NODATACOW |
- BTRFS_INODE_NODATASUM;
+ if (inode->vfs_inode.i_size == 0)
+ inode_flags |= BTRFS_INODE_NODATACOW |
+ BTRFS_INODE_NODATASUM;
} else {
- binode_flags |= BTRFS_INODE_NODATACOW;
+ inode_flags |= BTRFS_INODE_NODATACOW;
}
} else {
/*
* Revert back under same assumptions as above
*/
- if (S_ISREG(inode->i_mode)) {
- if (inode->i_size == 0)
- binode_flags &= ~(BTRFS_INODE_NODATACOW |
- BTRFS_INODE_NODATASUM);
+ if (S_ISREG(inode->vfs_inode.i_mode)) {
+ if (inode->vfs_inode.i_size == 0)
+ inode_flags &= ~(BTRFS_INODE_NODATACOW |
+ BTRFS_INODE_NODATASUM);
} else {
- binode_flags &= ~BTRFS_INODE_NODATACOW;
+ inode_flags &= ~BTRFS_INODE_NODATACOW;
}
}
@@ -339,21 +338,21 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap,
* things smaller.
*/
if (fsflags & FS_NOCOMP_FL) {
- binode_flags &= ~BTRFS_INODE_COMPRESS;
- binode_flags |= BTRFS_INODE_NOCOMPRESS;
+ inode_flags &= ~BTRFS_INODE_COMPRESS;
+ inode_flags |= BTRFS_INODE_NOCOMPRESS;
} else if (fsflags & FS_COMPR_FL) {
- if (IS_SWAPFILE(inode))
+ if (IS_SWAPFILE(&inode->vfs_inode))
return -ETXTBSY;
- binode_flags |= BTRFS_INODE_COMPRESS;
- binode_flags &= ~BTRFS_INODE_NOCOMPRESS;
+ inode_flags |= BTRFS_INODE_COMPRESS;
+ inode_flags &= ~BTRFS_INODE_NOCOMPRESS;
comp = btrfs_compress_type2str(fs_info->compress_type);
if (!comp || comp[0] == 0)
comp = btrfs_compress_type2str(BTRFS_COMPRESS_ZLIB);
} else {
- binode_flags &= ~(BTRFS_INODE_COMPRESS | BTRFS_INODE_NOCOMPRESS);
+ inode_flags &= ~(BTRFS_INODE_COMPRESS | BTRFS_INODE_NOCOMPRESS);
}
/*
@@ -365,14 +364,14 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap,
return PTR_ERR(trans);
if (comp) {
- ret = btrfs_set_prop(trans, inode, "btrfs.compression", comp,
+ ret = btrfs_set_prop(trans, &inode->vfs_inode, "btrfs.compression", comp,
strlen(comp), 0);
if (ret) {
btrfs_abort_transaction(trans, ret);
goto out_end_trans;
}
} else {
- ret = btrfs_set_prop(trans, inode, "btrfs.compression", NULL,
+ ret = btrfs_set_prop(trans, &inode->vfs_inode, "btrfs.compression", NULL,
0, 0);
if (ret && ret != -ENODATA) {
btrfs_abort_transaction(trans, ret);
@@ -381,11 +380,11 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap,
}
update_flags:
- binode->flags = binode_flags;
- btrfs_sync_inode_flags_to_i_flags(inode);
- inode_inc_iversion(inode);
- inode_set_ctime_current(inode);
- ret = btrfs_update_inode(trans, root, BTRFS_I(inode));
+ inode->flags = inode_flags;
+ btrfs_sync_inode_flags_to_i_flags(&inode->vfs_inode);
+ inode_inc_iversion(&inode->vfs_inode);
+ inode_set_ctime_current(&inode->vfs_inode);
+ ret = btrfs_update_inode(trans, root, inode);
out_end_trans:
btrfs_end_transaction(trans);
@@ -540,17 +539,6 @@ static noinline int btrfs_ioctl_fitrim(struct btrfs_fs_info *fs_info,
return ret;
}
-int __pure btrfs_is_empty_uuid(u8 *uuid)
-{
- int i;
-
- for (i = 0; i < BTRFS_UUID_SIZE; i++) {
- if (uuid[i])
- return 0;
- }
- return 1;
-}
-
/*
* Calculate the number of transaction items to reserve for creating a subvolume
* or snapshot, not including the inode, directory entries, or parent directory.
diff --git a/fs/btrfs/ioctl.h b/fs/btrfs/ioctl.h
index d51b9a2f2f6e8..21ee96c274819 100644
--- a/fs/btrfs/ioctl.h
+++ b/fs/btrfs/ioctl.h
@@ -10,7 +10,6 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap,
struct dentry *dentry, struct fileattr *fa);
int btrfs_ioctl_get_supported_features(void __user *arg);
void btrfs_sync_inode_flags_to_i_flags(struct inode *inode);
-int __pure btrfs_is_empty_uuid(u8 *uuid);
void btrfs_update_ioctl_balance_args(struct btrfs_fs_info *fs_info,
struct btrfs_ioctl_balance_args *bargs);
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 6.6.y 2/4] btrfs: move BTRFS_BYTES_TO_BLKS() into fs.h
2026-09-07 1:03 ` [PATCH 6.6.y 1/4] btrfs: move btrfs_is_empty_uuid() from ioctl.c into fs.c Sasha Levin
@ 2026-09-07 1:03 ` Sasha Levin
2026-09-07 1:03 ` [PATCH 6.6.y 3/4] btrfs: expose per-inode stable writes flag Sasha Levin
2026-09-07 1:03 ` [PATCH 6.6.y 4/4] btrfs: do not overwrite NODATASUM flag when removing NODATACOW flag Sasha Levin
2 siblings, 0 replies; 5+ messages in thread
From: Sasha Levin @ 2026-09-07 1:03 UTC (permalink / raw)
To: stable
Cc: Filipe Manana, Qu Wenruo, Johannes Thumshirn, David Sterba,
Sasha Levin
From: Filipe Manana <fdmanana@suse.com>
[ Upstream commit a4545b74e2de98989c1d14bc48c52c2f9fa734b6 ]
Currently BTRFS_BYTES_TO_BLKS() is defined in ctree.h but it's not related
at all to the btree data structure, so move it into fs.h.
Reviewed-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
Stable-dep-of: 15f7c86215e8 ("btrfs: do not overwrite NODATASUM flag when removing NODATACOW flag")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/btrfs/ctree.h | 3 ---
fs/btrfs/fs.h | 2 ++
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 1743aa21fa6e5..844e397f22387 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -469,9 +469,6 @@ static inline u32 BTRFS_MAX_XATTR_SIZE(const struct btrfs_fs_info *info)
return BTRFS_MAX_ITEM_SIZE(info) - sizeof(struct btrfs_dir_item);
}
-#define BTRFS_BYTES_TO_BLKS(fs_info, bytes) \
- ((bytes) >> (fs_info)->sectorsize_bits)
-
static inline u64 btrfs_name_hash(const char *name, int len)
{
return crc32c((u32)~1, name, len);
diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h
index b35aa4556d96f..92fce625158bc 100644
--- a/fs/btrfs/fs.h
+++ b/fs/btrfs/fs.h
@@ -861,6 +861,8 @@ static inline u64 btrfs_calc_metadata_size(const struct btrfs_fs_info *fs_info,
#define BTRFS_MAX_EXTENT_ITEM_SIZE(r) ((BTRFS_LEAF_DATA_SIZE(r->fs_info) >> 4) - \
sizeof(struct btrfs_item))
+#define BTRFS_BYTES_TO_BLKS(fs_info, bytes) ((bytes) >> (fs_info)->sectorsize_bits)
+
static inline bool btrfs_is_zoned(const struct btrfs_fs_info *fs_info)
{
return IS_ENABLED(CONFIG_BLK_DEV_ZONED) && fs_info->zone_size > 0;
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 6.6.y 3/4] btrfs: expose per-inode stable writes flag
2026-09-07 1:03 ` [PATCH 6.6.y 1/4] btrfs: move btrfs_is_empty_uuid() from ioctl.c into fs.c Sasha Levin
2026-09-07 1:03 ` [PATCH 6.6.y 2/4] btrfs: move BTRFS_BYTES_TO_BLKS() into fs.h Sasha Levin
@ 2026-09-07 1:03 ` Sasha Levin
2026-09-07 1:03 ` [PATCH 6.6.y 4/4] btrfs: do not overwrite NODATASUM flag when removing NODATACOW flag Sasha Levin
2 siblings, 0 replies; 5+ messages in thread
From: Sasha Levin @ 2026-09-07 1:03 UTC (permalink / raw)
To: stable; +Cc: Qu Wenruo, David Sterba, Sasha Levin
From: Qu Wenruo <wqu@suse.com>
[ Upstream commit ecde48a1a6b3256bd49db8780bf37556b157783c ]
The address space flag AS_STABLE_WRITES determine if FGP_STABLE for will
wait for the folio to finish its writeback.
For btrfs, due to the default data checksum behavior, if we modify the
folio while it's still under writeback, it will cause data checksum
mismatch. Thus for quite some call sites we manually call
folio_wait_writeback() to prevent such problem from happening.
Currently there is only one call site inside btrfs really utilizing
FGP_STABLE, and in that case we also manually call folio_wait_writeback()
to do the waiting.
But it's better to properly expose the stable writes flag to a per-inode
basis, to allow call sites to fully benefit from FGP_STABLE flag.
E.g. for inodes with NODATASUM allowing beginning dirtying the page
without waiting for writeback.
This involves:
- Update the mapping's stable write flag when setting/clearing NODATASUM
inode flag using ioctl
This only works for empty files, so it should be fine.
- Update the mapping's stable write flag when reading an inode from disk
- Remove the explicit folio_wait_writeback() for FGP_BEGINWRITE call
site
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
[ sashal: Reduced backport -- upstream ecde48a1a6b32 touches 4 file(s), this
backport carries 3. Not backported here:
fs/btrfs/btrfs_inode.h
This note is generated from the file lists only; see the resolution record
for the reasoning. ]
Stable-dep-of: 15f7c86215e8 ("btrfs: do not overwrite NODATASUM flag when removing NODATACOW flag")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/btrfs/file.c | 3 +--
fs/btrfs/inode.c | 8 ++++++++
fs/btrfs/ioctl.c | 4 ++++
3 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index a188467a9fb3d..b2865f6987e7f 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -870,7 +870,7 @@ static int prepare_uptodate_page(struct inode *inode,
static fgf_t get_prepare_fgp_flags(bool nowait)
{
- fgf_t fgp_flags = FGP_LOCK | FGP_ACCESSED | FGP_CREAT;
+ fgf_t fgp_flags = FGP_LOCK | FGP_ACCESSED | FGP_CREAT | FGP_STABLE;
if (nowait)
fgp_flags |= FGP_NOWAIT;
@@ -940,7 +940,6 @@ static noinline int prepare_pages(struct inode *inode, struct page **pages,
faili = i - 1;
goto fail;
}
- wait_on_page_writeback(pages[i]);
}
return 0;
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index e843531201bef..6ce7df69aa650 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -3867,6 +3867,10 @@ static int btrfs_read_locked_inode(struct inode *inode,
BTRFS_I(inode)->index_cnt = (u64)-1;
btrfs_inode_split_flags(btrfs_inode_flags(leaf, inode_item),
&BTRFS_I(inode)->flags, &BTRFS_I(inode)->ro_flags);
+ if (BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM)
+ mapping_clear_stable_writes(inode->i_mapping);
+ else
+ mapping_set_stable_writes(inode->i_mapping);
cache_index:
/*
@@ -6342,6 +6346,10 @@ int btrfs_create_new_inode(struct btrfs_trans_handle *trans,
if (btrfs_test_opt(fs_info, NODATACOW))
BTRFS_I(inode)->flags |= BTRFS_INODE_NODATACOW |
BTRFS_INODE_NODATASUM;
+ if (BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM)
+ mapping_clear_stable_writes(inode->i_mapping);
+ else
+ mapping_set_stable_writes(inode->i_mapping);
}
location = &BTRFS_I(inode)->location;
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 46eb80580d3bd..c38d7cd0f34e5 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -381,6 +381,10 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap,
update_flags:
inode->flags = inode_flags;
+ if (inode->flags & BTRFS_INODE_NODATASUM)
+ mapping_clear_stable_writes(inode->vfs_inode.i_mapping);
+ else
+ mapping_set_stable_writes(inode->vfs_inode.i_mapping);
btrfs_sync_inode_flags_to_i_flags(&inode->vfs_inode);
inode_inc_iversion(&inode->vfs_inode);
inode_set_ctime_current(&inode->vfs_inode);
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 6.6.y 4/4] btrfs: do not overwrite NODATASUM flag when removing NODATACOW flag
2026-09-07 1:03 ` [PATCH 6.6.y 1/4] btrfs: move btrfs_is_empty_uuid() from ioctl.c into fs.c Sasha Levin
2026-09-07 1:03 ` [PATCH 6.6.y 2/4] btrfs: move BTRFS_BYTES_TO_BLKS() into fs.h Sasha Levin
2026-09-07 1:03 ` [PATCH 6.6.y 3/4] btrfs: expose per-inode stable writes flag Sasha Levin
@ 2026-09-07 1:03 ` Sasha Levin
2 siblings, 0 replies; 5+ messages in thread
From: Sasha Levin @ 2026-09-07 1:03 UTC (permalink / raw)
To: stable; +Cc: Qu Wenruo, Filipe Manana, David Sterba, Sasha Levin
From: Qu Wenruo <wqu@suse.com>
[ Upstream commit 15f7c86215e8d5f14b24127fa88af6c79363d50e ]
[TEST FAILURE]
The test case generic/628 will fail if MOUNT_OPTIONS is set to
"-o nodatasum":
FSTYP -- btrfs
PLATFORM -- Linux/x86_64 btrfs-vm 7.1.0-rc4-custom+ #383 SMP PREEMPT_DYNAMIC Sat May 30 07:35:42 ACST 2026
MKFS_OPTIONS -- -O bgt -K /dev/mapper/test-scratch1
MOUNT_OPTIONS -- -o nodatasum /dev/mapper/test-scratch1 /mnt/scratch
generic/628 1s ... - output mismatch (see /home/adam/xfstests/results//generic/628.out.bad)
--- tests/generic/628.out 2022-05-11 11:25:30.816666664 +0930
+++ /home/adam/xfstests/results//generic/628.out.bad 2026-06-08 18:56:49.878542927 +0930
@@ -8,8 +8,9 @@
310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/a
310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/d
test reflink flag not set iflag
+XFS_IOC_CLONE: Invalid argument
310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/a
-310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/b
+d41d8cd98f00b204e9800998ecf8427e SCRATCH_MNT/b
...
[CAUSE]
The direct cause is that after "chattr +S", the btrfs inode will lose its
NODATASUM flag inherited from the mount option. E.g.:
# mkfs.btrfs -f $dev
# mount $dev $mnt -o nodatasum
# touch $mnt/foobar
# sync
# btrfs ins dump-tree -t 5 $dev | grep "(257 INODE_ITEM 0) itemoff" -A 3
item 4 key (257 INODE_ITEM 0) itemoff 15879 itemsize 160
generation 9 transid 9 size 0 nbytes 0
block group 0 mode 100644 links 1 uid 0 gid 0 rdev 0
sequence 1 flags 0x1(NODATASUM)
^^^^^^^^^ Proper NODATASUM flag
# chattr +S $mnt/foobar
# sync
# btrfs ins dump-tree -t 5 $dev | grep "(257 INODE_ITEM 0) itemoff" -A 3
item 4 key (257 INODE_ITEM 0) itemoff 15879 itemsize 160
generation 9 transid 10 size 0 nbytes 0
block group 0 mode 100644 links 1 uid 0 gid 0 rdev 0
sequence 2 flags 0x20(SYNC)
^^^^ Only the new SYNC flag
This makes the inode drop the old NODATASUM flag, while the new reflink
destination will still inherit the NODATASUM flag. The mismatching
NODATASUM flags will cause the reflink to fail.
The root cause is that, inside btrfs_fileattr_set() if no FS_NOCOW_FL is
set, we remove both NODATASUM and NODATACOW flag.
However we should not touch NODATASUM flag, as data COW doesn't require
checksum. Only NODATACOW implies NODATASUM, but DATACOW doesn't imply
DATASUM.
The deeper problems are:
- Fileattr API is too binary
It either clears or sets a flag, there is no "do not change" option.
So that why "chattr +S" implies "chattr -C", and is forcing us to
change NODATACOW along with NODATASUM flag.
- No way to change NODATASUM through fileattr API
In fact NODATASUM can only be modified through mount option.
The deeper problems are much harder to attack.
[FIX]
Remove NODATACOW flag when FS_NOCOW_FL is not set, but only remove
NODATASUM if "nodatasum" mount option is not set.
This allows the existing "chattr +C" then "chattr -C" to remove
both NODATACOW and NODATASUM flags on a default mount.
But for a mount with "nodatasum" option, the NODATASUM inode flag will
persist through either "chattr +C" and "chattr -C".
Fixes: 7e97b8daf634 ("btrfs: allow setting NOCOW for a zero sized file via ioctl")
Cc: stable@vger.kernel.org
Reviewed-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/btrfs/ioctl.c | 23 +++++++++++++++--------
1 file changed, 15 insertions(+), 8 deletions(-)
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index c38d7cd0f34e5..27089a49cfd22 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -320,14 +320,21 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap,
inode_flags |= BTRFS_INODE_NODATACOW;
}
} else {
- /*
- * Revert back under same assumptions as above
- */
- if (S_ISREG(inode->vfs_inode.i_mode)) {
- if (inode->vfs_inode.i_size == 0)
- inode_flags &= ~(BTRFS_INODE_NODATACOW |
- BTRFS_INODE_NODATASUM);
- } else {
+ /* We can only change NODATACOW for zero-sized regular file. */
+ if (S_ISREG(inode->vfs_inode.i_mode) && (inode->vfs_inode.i_size == 0)) {
+ inode_flags &= ~BTRFS_INODE_NODATACOW;
+ /*
+ * There is currently no way to change NODATASUM flag
+ * through fileattr API. If we unconditionally keep the
+ * current NODATASUM flag, chattr +C then chattr -C will
+ * keep the NODATASUM flag, and no way to remove that
+ * flag.
+ *
+ * So respect the current mount option for NODATASUM flag.
+ */
+ if (!btrfs_test_opt(fs_info, NODATASUM))
+ inode_flags &= ~BTRFS_INODE_NODATASUM;
+ } else if (!S_ISREG(inode->vfs_inode.i_mode)) {
inode_flags &= ~BTRFS_INODE_NODATACOW;
}
}
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-09-07 1:03 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-03 13:24 FAILED: patch "[PATCH] btrfs: do not overwrite NODATASUM flag when removing" failed to apply to 6.6-stable tree gregkh
2026-09-07 1:03 ` [PATCH 6.6.y 1/4] btrfs: move btrfs_is_empty_uuid() from ioctl.c into fs.c Sasha Levin
2026-09-07 1:03 ` [PATCH 6.6.y 2/4] btrfs: move BTRFS_BYTES_TO_BLKS() into fs.h Sasha Levin
2026-09-07 1:03 ` [PATCH 6.6.y 3/4] btrfs: expose per-inode stable writes flag Sasha Levin
2026-09-07 1:03 ` [PATCH 6.6.y 4/4] btrfs: do not overwrite NODATASUM flag when removing NODATACOW flag Sasha Levin
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.