mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Jeff Layton <jlayton@kernel.org>
To: David Sterba <dsterba@suse.com>, Chris Mason <mason@kernel.org>,
	 Chuck Lever <cel@kernel.org>
Cc: linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org,
	 Jeff Layton <jlayton@kernel.org>
Subject: [PATCH 3/4] btrfs: honor FMODE_NOCMTIME in fallocate
Date: Thu, 05 Nov 2026 15:07:44 -0500	[thread overview]
Message-ID: <20261105-delegts-btrfs-v1-3-a42a2b93cdc2@kernel.org> (raw)
In-Reply-To: <20261105-delegts-btrfs-v1-0-a42a2b93cdc2@kernel.org>

btrfs_fallocate() calls file_modified(), which skips the c/mtime update
for FMODE_NOCMTIME files. But the helpers that btrfs_fallocate() calls
update the times anyway:

- btrfs_prealloc_file_range() sets ctime. Its
  insert_prealloc_file_extent() tells btrfs_replace_file_extents() to
  update c/mtime.
- btrfs_fallocate_update_isize() sets ctime.

btrfs_zero_range() also calls these helpers. nfsd sets FMODE_NOCMTIME
while a client holds delegated timestamps. Thus an ALLOCATE from the
holder advances the server's ctime, and the times that the holder
returns cannot move it back.

Pass an update_times flag from btrfs_fallocate() to these helpers. Set
the flag from btrfs_should_update_cmtime(). Relocation still updates the
times.

btrfs_fallocate_update_isize() never incremented i_version. When a size
extension stayed within existing preallocation, only the ctime update
changed the NFS change attribute. Increment i_version unconditionally
there, as btrfs_prealloc_file_range() does.

Fixes: e5e9b24ab8fa ("nfsd: freeze c/mtime updates with outstanding WRITE_ATTRS delegation")
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
 fs/btrfs/btrfs_inode.h |  3 ++-
 fs/btrfs/file.c        | 32 +++++++++++++++++++++-----------
 fs/btrfs/inode.c       | 12 +++++++-----
 fs/btrfs/relocation.c  |  2 +-
 4 files changed, 31 insertions(+), 18 deletions(-)

diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h
index 0782f917fccc..fbcf8ca05fb7 100644
--- a/fs/btrfs/btrfs_inode.h
+++ b/fs/btrfs/btrfs_inode.h
@@ -595,7 +595,8 @@ void btrfs_run_delayed_iputs(struct btrfs_fs_info *fs_info);
 int btrfs_wait_on_delayed_iputs(struct btrfs_fs_info *fs_info);
 int btrfs_prealloc_file_range(struct inode *inode, int mode,
 			      u64 start, u64 num_bytes, u64 min_size,
-			      loff_t actual_len, u64 *alloc_hint);
+			      loff_t actual_len, u64 *alloc_hint,
+			      bool update_times);
 int btrfs_run_delalloc_range(struct btrfs_inode *inode, struct folio *locked_folio,
 			     u64 start, u64 end, struct writeback_control *wbc);
 void btrfs_queue_writepage_fixup(struct btrfs_inode *inode, struct folio *folio);
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index eab20cc6e18e..6ff494846aea 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -2836,7 +2836,8 @@ static int add_falloc_range(struct list_head *head, u64 start, u64 len)
 
 static int btrfs_fallocate_update_isize(struct inode *inode,
 					const u64 end,
-					const int mode)
+					const int mode,
+					bool update_times)
 {
 	struct btrfs_trans_handle *trans;
 	struct btrfs_root *root = BTRFS_I(inode)->root;
@@ -2860,7 +2861,9 @@ static int btrfs_fallocate_update_isize(struct inode *inode,
 	if (IS_ERR(trans))
 		return PTR_ERR(trans);
 
-	inode_set_ctime_current(inode);
+	inode_inc_iversion(inode);
+	if (update_times)
+		inode_set_ctime_current(inode);
 	i_size_write(inode, end);
 	btrfs_inode_safe_disk_i_size_write(BTRFS_I(inode), 0);
 	ret = btrfs_update_inode(trans, BTRFS_I(inode));
@@ -2901,7 +2904,8 @@ static int btrfs_zero_range_check_range_boundary(struct btrfs_inode *inode,
 static int btrfs_zero_range(struct inode *inode,
 			    loff_t offset,
 			    loff_t len,
-			    const int mode)
+			    const int mode,
+			    bool update_times)
 {
 	struct btrfs_fs_info *fs_info = BTRFS_I(inode)->root->fs_info;
 	struct extent_map *em;
@@ -2942,7 +2946,7 @@ static int btrfs_zero_range(struct inode *inode,
 			 */
 			btrfs_free_extent_map(em);
 			ret = btrfs_fallocate_update_isize(inode, offset + len,
-							   mode);
+							   mode, update_times);
 			goto out;
 		}
 		/*
@@ -2968,7 +2972,7 @@ static int btrfs_zero_range(struct inode *inode,
 		if (em->flags & EXTENT_FLAG_PREALLOC) {
 			btrfs_free_extent_map(em);
 			ret = btrfs_fallocate_update_isize(inode, offset + len,
-							   mode);
+							   mode, update_times);
 			goto out;
 		}
 		if (len < sectorsize && em->disk_bytenr != EXTENT_MAP_HOLE) {
@@ -2978,7 +2982,8 @@ static int btrfs_zero_range(struct inode *inode,
 			if (!ret)
 				ret = btrfs_fallocate_update_isize(inode,
 								   offset + len,
-								   mode);
+								   mode,
+								   update_times);
 			return ret;
 		}
 		btrfs_free_extent_map(em);
@@ -3056,7 +3061,8 @@ static int btrfs_zero_range(struct inode *inode,
 		ret = btrfs_prealloc_file_range(inode, mode, alloc_start,
 						alloc_end - alloc_start,
 						fs_info->sectorsize,
-						offset + len, &alloc_hint);
+						offset + len, &alloc_hint,
+						update_times);
 		btrfs_unlock_extent(&BTRFS_I(inode)->io_tree, lockstart, lockend,
 				    &cached_state);
 		/* btrfs_prealloc_file_range releases reserved space on error */
@@ -3065,7 +3071,8 @@ static int btrfs_zero_range(struct inode *inode,
 			goto out;
 		}
 	}
-	ret = btrfs_fallocate_update_isize(inode, offset + len, mode);
+	ret = btrfs_fallocate_update_isize(inode, offset + len, mode,
+					   update_times);
  out:
 	if (ret && space_reserved)
 		btrfs_free_reserved_data_space(BTRFS_I(inode), data_reserved,
@@ -3096,6 +3103,7 @@ static long btrfs_fallocate(struct file *file, int mode,
 	u64 qgroup_reserved = 0;
 	struct extent_map *em;
 	int blocksize = BTRFS_I(inode)->root->fs_info->sectorsize;
+	const bool update_times = btrfs_should_update_cmtime(file);
 	int ret;
 
 	if (btrfs_is_shutdown(inode_to_fs_info(inode)))
@@ -3167,7 +3175,7 @@ static long btrfs_fallocate(struct file *file, int mode,
 		goto out;
 
 	if (mode & FALLOC_FL_ZERO_RANGE) {
-		ret = btrfs_zero_range(inode, offset, len, mode);
+		ret = btrfs_zero_range(inode, offset, len, mode, update_times);
 		btrfs_inode_unlock(BTRFS_I(inode), BTRFS_ILOCK_MMAP);
 		return ret;
 	}
@@ -3232,7 +3240,8 @@ static long btrfs_fallocate(struct file *file, int mode,
 			ret = btrfs_prealloc_file_range(inode, mode,
 					range->start,
 					range->len, blocksize,
-					offset + len, &alloc_hint);
+					offset + len, &alloc_hint,
+					update_times);
 			/*
 			 * btrfs_prealloc_file_range() releases space even
 			 * if it returns an error.
@@ -3260,7 +3269,8 @@ static long btrfs_fallocate(struct file *file, int mode,
 	 * We didn't need to allocate any more space, but we still extended the
 	 * size of the file so we need to update i_size and the inode item.
 	 */
-	ret = btrfs_fallocate_update_isize(inode, actual_end, mode);
+	ret = btrfs_fallocate_update_isize(inode, actual_end, mode,
+					   update_times);
 out_unlock:
 	btrfs_unlock_extent(&BTRFS_I(inode)->io_tree, alloc_start, locked_end,
 			    &cached_state);
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index a6ce83a94fe6..1a6bd5025274 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -9100,7 +9100,7 @@ static int btrfs_symlink(const struct mnt_idmap *idmap, struct inode *dir,
 static struct btrfs_trans_handle *insert_prealloc_file_extent(
 				       struct btrfs_inode *inode,
 				       struct btrfs_key *ins,
-				       u64 file_offset)
+				       u64 file_offset, bool update_times)
 {
 	struct btrfs_file_extent_item stack_fi;
 	struct btrfs_replace_extent_info extent_info;
@@ -9143,7 +9143,7 @@ static struct btrfs_trans_handle *insert_prealloc_file_extent(
 
 	ret = btrfs_replace_file_extents(inode, path, file_offset,
 				     file_offset + len - 1, &extent_info,
-				     true, &trans);
+				     update_times, &trans);
 	btrfs_free_path(path);
 	if (ret)
 		goto free_qgroup;
@@ -9165,7 +9165,8 @@ static struct btrfs_trans_handle *insert_prealloc_file_extent(
 
 int btrfs_prealloc_file_range(struct inode *inode, int mode,
 			      u64 start, u64 num_bytes, u64 min_size,
-			      loff_t actual_len, u64 *alloc_hint)
+			      loff_t actual_len, u64 *alloc_hint,
+			      bool update_times)
 {
 	struct btrfs_fs_info *fs_info = inode_to_fs_info(inode);
 	struct btrfs_trans_handle *trans;
@@ -9206,7 +9207,7 @@ int btrfs_prealloc_file_range(struct inode *inode, int mode,
 
 		last_alloc = ins.offset;
 		trans = insert_prealloc_file_extent(BTRFS_I(inode), &ins,
-						    cur_offset);
+						    cur_offset, update_times);
 		/*
 		 * Now that we inserted the prealloc extent we can finally
 		 * decrement the number of reservations in the block group.
@@ -9246,7 +9247,8 @@ int btrfs_prealloc_file_range(struct inode *inode, int mode,
 		*alloc_hint = ins.objectid + ins.offset;
 
 		inode_inc_iversion(inode);
-		inode_set_ctime_current(inode);
+		if (update_times)
+			inode_set_ctime_current(inode);
 		BTRFS_I(inode)->flags |= BTRFS_INODE_PREALLOC;
 		if (!(mode & FALLOC_FL_KEEP_SIZE) &&
 		    (actual_len > inode->i_size) &&
diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c
index b2ac5b836b21..9af207446848 100644
--- a/fs/btrfs/relocation.c
+++ b/fs/btrfs/relocation.c
@@ -2834,7 +2834,7 @@ static noinline_for_stack int prealloc_file_extent_cluster(struct reloc_control
 		num_bytes = end + 1 - start;
 		ret = btrfs_prealloc_file_range(&inode->vfs_inode, 0, start,
 						num_bytes, num_bytes,
-						end + 1, &alloc_hint);
+						end + 1, &alloc_hint, true);
 		cur_offset = end + 1;
 		btrfs_unlock_extent(&inode->io_tree, start, end, &cached_state);
 		if (ret)

-- 
2.56.0


  parent reply	other threads:[~2026-11-05 20:07 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-05 20:07 [PATCH 0/4] btrfs: fix FMODE_NOCMTIME handling in btrfs Jeff Layton
2026-11-05 20:07 ` [PATCH 1/4] btrfs: honor FMODE_NOCMTIME in btrfs_write_check() Jeff Layton
2026-11-05 20:07 ` [PATCH 2/4] btrfs: honor FMODE_NOCMTIME when punching holes Jeff Layton
2026-11-05 20:07 ` Jeff Layton [this message]
2026-11-05 20:07 ` [PATCH 4/4] btrfs: honor FMODE_NOCMTIME when cloning Jeff Layton

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261105-delegts-btrfs-v1-3-a42a2b93cdc2@kernel.org \
    --to=jlayton@kernel.org \
    --cc=cel@kernel.org \
    --cc=dsterba@suse.com \
    --cc=linux-btrfs@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mason@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®