linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] netfs: Fix i_size updating
@ 2025-06-26 12:32 David Howells
  2025-06-26 12:44 ` [PATCH] netfs: Merge i_size update functions David Howells
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: David Howells @ 2025-06-26 12:32 UTC (permalink / raw)
  To: Christian Brauner, Steve French
  Cc: dhowells, Paulo Alcantara, linux-cifs, netfs, linux-fsdevel,
	linux-kernel

Fix the updating of i_size, particularly in regard to the completion of DIO
writes and especially async DIO writes by using a lock.

The bug is triggered occasionally by the generic/207 xfstest as it chucks a
bunch of AIO DIO writes at the filesystem and then checks that fstat()
returns a reasonable st_size as each completes.

The problem is that netfs is trying to do "if new_size > inode->i_size,
update inode->i_size" sort of thing but without a lock around it.

This can be seen with cifs, but shouldn't be seen with kafs because kafs
serialises modification ops on the client whereas cifs sends the requests
to the server as they're generated and lets the server order them.

Fixes: 153a9961b551 ("netfs: Implement unbuffered/DIO write support")
Signed-off-by: David Howells <dhowells@redhat.com>
cc: Steve French <sfrench@samba.org>
cc: Paulo Alcantara <pc@manguebit.org>
cc: linux-cifs@vger.kernel.org
cc: netfs@lists.linux.dev
cc: linux-fsdevel@vger.kernel.org
---
 fs/netfs/buffered_write.c |    2 ++
 fs/netfs/direct_write.c   |    8 ++++++--
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/fs/netfs/buffered_write.c b/fs/netfs/buffered_write.c
index 72a3e6db2524..b87ef3fe4ea4 100644
--- a/fs/netfs/buffered_write.c
+++ b/fs/netfs/buffered_write.c
@@ -64,6 +64,7 @@ static void netfs_update_i_size(struct netfs_inode *ctx, struct inode *inode,
 		return;
 	}
 
+	spin_lock(&inode->i_lock);
 	i_size_write(inode, pos);
 #if IS_ENABLED(CONFIG_FSCACHE)
 	fscache_update_cookie(ctx->cache, NULL, &pos);
@@ -77,6 +78,7 @@ static void netfs_update_i_size(struct netfs_inode *ctx, struct inode *inode,
 					DIV_ROUND_UP(pos, SECTOR_SIZE),
 					inode->i_blocks + add);
 	}
+	spin_unlock(&inode->i_lock);
 }
 
 /**
diff --git a/fs/netfs/direct_write.c b/fs/netfs/direct_write.c
index c0797d6c72c9..9df297a555f1 100644
--- a/fs/netfs/direct_write.c
+++ b/fs/netfs/direct_write.c
@@ -14,13 +14,17 @@ static void netfs_cleanup_dio_write(struct netfs_io_request *wreq)
 	struct inode *inode = wreq->inode;
 	unsigned long long end = wreq->start + wreq->transferred;
 
-	if (!wreq->error &&
-	    i_size_read(inode) < end) {
+	if (wreq->error || end <= i_size_read(inode))
+		return;
+
+	spin_lock(&inode->i_lock);
+	if (end > i_size_read(inode)) {
 		if (wreq->netfs_ops->update_i_size)
 			wreq->netfs_ops->update_i_size(inode, end);
 		else
 			i_size_write(inode, end);
 	}
+	spin_unlock(&inode->i_lock);
 }
 
 /*


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH] netfs: Merge i_size update functions
  2025-06-26 12:32 [PATCH] netfs: Fix i_size updating David Howells
@ 2025-06-26 12:44 ` David Howells
  2025-07-01 11:41   ` Christian Brauner
  2025-06-26 13:37 ` [PATCH] netfs: Fix i_size updating Paulo Alcantara
  2025-07-01 11:42 ` Christian Brauner
  2 siblings, 1 reply; 6+ messages in thread
From: David Howells @ 2025-06-26 12:44 UTC (permalink / raw)
  Cc: dhowells, Christian Brauner, Steve French, Paulo Alcantara,
	linux-cifs, netfs, linux-fsdevel, linux-kernel

Here's a follow up patch to the previous one, though this would be for next -
and assuming it's okay to do the i_blocks update in the DIO case which it
currently lacks.

David
---
Netfslib has two functions for updating the i_size after a write: one for
buffered writes into the pagecache and one for direct/unbuffered writes.
However, what needs to be done is much the same in both cases, so merge
them together.

This does raise one question, though: should updating the i_size after a
direct write do the same estimated update of i_blocks as is done for
buffered writes.

Also get rid of the cleanup function pointer from netfs_io_request as it's
only used for direct write to update i_size; instead do the i_size setting
directly from write collection.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Steve French <sfrench@samba.org>
cc: Paulo Alcantara <pc@manguebit.org>
cc: linux-cifs@vger.kernel.org
cc: netfs@lists.linux.dev
cc: linux-fsdevel@vger.kernel.org
---
 fs/netfs/buffered_write.c |   36 +++++++++++++++++++++---------------
 fs/netfs/direct_write.c   |   19 -------------------
 fs/netfs/internal.h       |    6 ++++++
 fs/netfs/write_collect.c  |    6 ++++--
 include/linux/netfs.h     |    1 -
 5 files changed, 31 insertions(+), 37 deletions(-)

diff --git a/fs/netfs/buffered_write.c b/fs/netfs/buffered_write.c
index b87ef3fe4ea4..f27ea5099a68 100644
--- a/fs/netfs/buffered_write.c
+++ b/fs/netfs/buffered_write.c
@@ -53,30 +53,38 @@ static struct folio *netfs_grab_folio_for_write(struct address_space *mapping,
  * data written into the pagecache until we can find out from the server what
  * the values actually are.
  */
-static void netfs_update_i_size(struct netfs_inode *ctx, struct inode *inode,
-				loff_t i_size, loff_t pos, size_t copied)
+void netfs_update_i_size(struct netfs_inode *ctx, struct inode *inode,
+			 loff_t pos, size_t copied)
 {
+	loff_t i_size, end = pos + copied;
 	blkcnt_t add;
 	size_t gap;
 
+	if (end <= i_size_read(inode))
+		return;
+
 	if (ctx->ops->update_i_size) {
-		ctx->ops->update_i_size(inode, pos);
+		ctx->ops->update_i_size(inode, end);
 		return;
 	}
 
 	spin_lock(&inode->i_lock);
-	i_size_write(inode, pos);
+
+	i_size = i_size_read(inode);
+	if (end > i_size) {
+		i_size_write(inode, end);
 #if IS_ENABLED(CONFIG_FSCACHE)
-	fscache_update_cookie(ctx->cache, NULL, &pos);
+		fscache_update_cookie(ctx->cache, NULL, &end);
 #endif
 
-	gap = SECTOR_SIZE - (i_size & (SECTOR_SIZE - 1));
-	if (copied > gap) {
-		add = DIV_ROUND_UP(copied - gap, SECTOR_SIZE);
+		gap = SECTOR_SIZE - (i_size & (SECTOR_SIZE - 1));
+		if (copied > gap) {
+			add = DIV_ROUND_UP(copied - gap, SECTOR_SIZE);
 
-		inode->i_blocks = min_t(blkcnt_t,
-					DIV_ROUND_UP(pos, SECTOR_SIZE),
-					inode->i_blocks + add);
+			inode->i_blocks = min_t(blkcnt_t,
+						DIV_ROUND_UP(end, SECTOR_SIZE),
+						inode->i_blocks + add);
+		}
 	}
 	spin_unlock(&inode->i_lock);
 }
@@ -113,7 +121,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter,
 	struct folio *folio = NULL, *writethrough = NULL;
 	unsigned int bdp_flags = (iocb->ki_flags & IOCB_NOWAIT) ? BDP_ASYNC : 0;
 	ssize_t written = 0, ret, ret2;
-	loff_t i_size, pos = iocb->ki_pos;
+	loff_t pos = iocb->ki_pos;
 	size_t max_chunk = mapping_max_folio_size(mapping);
 	bool maybe_trouble = false;
 
@@ -346,10 +354,8 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter,
 		flush_dcache_folio(folio);
 
 		/* Update the inode size if we moved the EOF marker */
+		netfs_update_i_size(ctx, inode, pos, copied);
 		pos += copied;
-		i_size = i_size_read(inode);
-		if (pos > i_size)
-			netfs_update_i_size(ctx, inode, i_size, pos, copied);
 		written += copied;
 
 		if (likely(!wreq)) {
diff --git a/fs/netfs/direct_write.c b/fs/netfs/direct_write.c
index 9df297a555f1..a16660ab7f83 100644
--- a/fs/netfs/direct_write.c
+++ b/fs/netfs/direct_write.c
@@ -9,24 +9,6 @@
 #include <linux/uio.h>
 #include "internal.h"
 
-static void netfs_cleanup_dio_write(struct netfs_io_request *wreq)
-{
-	struct inode *inode = wreq->inode;
-	unsigned long long end = wreq->start + wreq->transferred;
-
-	if (wreq->error || end <= i_size_read(inode))
-		return;
-
-	spin_lock(&inode->i_lock);
-	if (end > i_size_read(inode)) {
-		if (wreq->netfs_ops->update_i_size)
-			wreq->netfs_ops->update_i_size(inode, end);
-		else
-			i_size_write(inode, end);
-	}
-	spin_unlock(&inode->i_lock);
-}
-
 /*
  * Perform an unbuffered write where we may have to do an RMW operation on an
  * encrypted file.  This can also be used for direct I/O writes.
@@ -102,7 +84,6 @@ ssize_t netfs_unbuffered_write_iter_locked(struct kiocb *iocb, struct iov_iter *
 	if (async)
 		wreq->iocb = iocb;
 	wreq->len = iov_iter_count(&wreq->buffer.iter);
-	wreq->cleanup = netfs_cleanup_dio_write;
 	ret = netfs_unbuffered_write(wreq, is_sync_kiocb(iocb), wreq->len);
 	if (ret < 0) {
 		_debug("begin = %zd", ret);
diff --git a/fs/netfs/internal.h b/fs/netfs/internal.h
index e13ed767aec0..d4f16fefd965 100644
--- a/fs/netfs/internal.h
+++ b/fs/netfs/internal.h
@@ -27,6 +27,12 @@ void netfs_cache_read_terminated(void *priv, ssize_t transferred_or_error);
 int netfs_prefetch_for_write(struct file *file, struct folio *folio,
 			     size_t offset, size_t len);
 
+/*
+ * buffered_write.c
+ */
+void netfs_update_i_size(struct netfs_inode *ctx, struct inode *inode,
+			 loff_t pos, size_t copied);
+
 /*
  * main.c
  */
diff --git a/fs/netfs/write_collect.c b/fs/netfs/write_collect.c
index dedfdf80eccc..0f3a36852a4d 100644
--- a/fs/netfs/write_collect.c
+++ b/fs/netfs/write_collect.c
@@ -393,8 +393,10 @@ bool netfs_write_collection(struct netfs_io_request *wreq)
 		ictx->ops->invalidate_cache(wreq);
 	}
 
-	if (wreq->cleanup)
-		wreq->cleanup(wreq);
+	if ((wreq->origin == NETFS_UNBUFFERED_WRITE ||
+	     wreq->origin == NETFS_DIO_WRITE) &&
+	    !wreq->error)
+		netfs_update_i_size(ictx, &ictx->inode, wreq->start, wreq->transferred);
 
 	if (wreq->origin == NETFS_DIO_WRITE &&
 	    wreq->mapping->nrpages) {
diff --git a/include/linux/netfs.h b/include/linux/netfs.h
index 8b5bf6e393f6..f43f075852c0 100644
--- a/include/linux/netfs.h
+++ b/include/linux/netfs.h
@@ -279,7 +279,6 @@ struct netfs_io_request {
 #define NETFS_RREQ_USE_PGPRIV2		31	/* [DEPRECATED] Use PG_private_2 to mark
 						 * write to cache on read */
 	const struct netfs_request_ops *netfs_ops;
-	void (*cleanup)(struct netfs_io_request *req);
 };
 
 /*


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH] netfs: Fix i_size updating
  2025-06-26 12:32 [PATCH] netfs: Fix i_size updating David Howells
  2025-06-26 12:44 ` [PATCH] netfs: Merge i_size update functions David Howells
@ 2025-06-26 13:37 ` Paulo Alcantara
  2025-06-26 14:07   ` Steve French
  2025-07-01 11:42 ` Christian Brauner
  2 siblings, 1 reply; 6+ messages in thread
From: Paulo Alcantara @ 2025-06-26 13:37 UTC (permalink / raw)
  To: David Howells, Christian Brauner, Steve French
  Cc: dhowells, linux-cifs, netfs, linux-fsdevel, linux-kernel

David Howells <dhowells@redhat.com> writes:

> Fix the updating of i_size, particularly in regard to the completion of DIO
> writes and especially async DIO writes by using a lock.
>
> The bug is triggered occasionally by the generic/207 xfstest as it chucks a
> bunch of AIO DIO writes at the filesystem and then checks that fstat()
> returns a reasonable st_size as each completes.
>
> The problem is that netfs is trying to do "if new_size > inode->i_size,
> update inode->i_size" sort of thing but without a lock around it.
>
> This can be seen with cifs, but shouldn't be seen with kafs because kafs
> serialises modification ops on the client whereas cifs sends the requests
> to the server as they're generated and lets the server order them.
>
> Fixes: 153a9961b551 ("netfs: Implement unbuffered/DIO write support")
> Signed-off-by: David Howells <dhowells@redhat.com>
> cc: Steve French <sfrench@samba.org>
> cc: Paulo Alcantara <pc@manguebit.org>
> cc: linux-cifs@vger.kernel.org
> cc: netfs@lists.linux.dev
> cc: linux-fsdevel@vger.kernel.org
> ---
>  fs/netfs/buffered_write.c |    2 ++
>  fs/netfs/direct_write.c   |    8 ++++++--
>  2 files changed, 8 insertions(+), 2 deletions(-)

Reviewed-by: Paulo Alcantara (Red Hat) <pc@manguebit.org>

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] netfs: Fix i_size updating
  2025-06-26 13:37 ` [PATCH] netfs: Fix i_size updating Paulo Alcantara
@ 2025-06-26 14:07   ` Steve French
  0 siblings, 0 replies; 6+ messages in thread
From: Steve French @ 2025-06-26 14:07 UTC (permalink / raw)
  To: Paulo Alcantara
  Cc: David Howells, Christian Brauner, Steve French, linux-cifs, netfs,
	linux-fsdevel, linux-kernel

added to for-next pending testing

On Thu, Jun 26, 2025 at 8:39 AM Paulo Alcantara <pc@manguebit.org> wrote:
>
> David Howells <dhowells@redhat.com> writes:
>
> > Fix the updating of i_size, particularly in regard to the completion of DIO
> > writes and especially async DIO writes by using a lock.
> >
> > The bug is triggered occasionally by the generic/207 xfstest as it chucks a
> > bunch of AIO DIO writes at the filesystem and then checks that fstat()
> > returns a reasonable st_size as each completes.
> >
> > The problem is that netfs is trying to do "if new_size > inode->i_size,
> > update inode->i_size" sort of thing but without a lock around it.
> >
> > This can be seen with cifs, but shouldn't be seen with kafs because kafs
> > serialises modification ops on the client whereas cifs sends the requests
> > to the server as they're generated and lets the server order them.
> >
> > Fixes: 153a9961b551 ("netfs: Implement unbuffered/DIO write support")
> > Signed-off-by: David Howells <dhowells@redhat.com>
> > cc: Steve French <sfrench@samba.org>
> > cc: Paulo Alcantara <pc@manguebit.org>
> > cc: linux-cifs@vger.kernel.org
> > cc: netfs@lists.linux.dev
> > cc: linux-fsdevel@vger.kernel.org
> > ---
> >  fs/netfs/buffered_write.c |    2 ++
> >  fs/netfs/direct_write.c   |    8 ++++++--
> >  2 files changed, 8 insertions(+), 2 deletions(-)
>
> Reviewed-by: Paulo Alcantara (Red Hat) <pc@manguebit.org>
>


-- 
Thanks,

Steve

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] netfs: Merge i_size update functions
  2025-06-26 12:44 ` [PATCH] netfs: Merge i_size update functions David Howells
@ 2025-07-01 11:41   ` Christian Brauner
  0 siblings, 0 replies; 6+ messages in thread
From: Christian Brauner @ 2025-07-01 11:41 UTC (permalink / raw)
  To: David Howells
  Cc: Steve French, Paulo Alcantara, linux-cifs, netfs, linux-fsdevel,
	linux-kernel

On Thu, Jun 26, 2025 at 01:44:36PM +0100, David Howells wrote:
> Here's a follow up patch to the previous one, though this would be for next -
> and assuming it's okay to do the i_blocks update in the DIO case which it
> currently lacks.
> 
> David
> ---
> Netfslib has two functions for updating the i_size after a write: one for
> buffered writes into the pagecache and one for direct/unbuffered writes.
> However, what needs to be done is much the same in both cases, so merge
> them together.
> 
> This does raise one question, though: should updating the i_size after a
> direct write do the same estimated update of i_blocks as is done for
> buffered writes.
> 
> Also get rid of the cleanup function pointer from netfs_io_request as it's
> only used for direct write to update i_size; instead do the i_size setting
> directly from write collection.
> 
> Signed-off-by: David Howells <dhowells@redhat.com>
> cc: Steve French <sfrench@samba.org>
> cc: Paulo Alcantara <pc@manguebit.org>
> cc: linux-cifs@vger.kernel.org
> cc: netfs@lists.linux.dev
> cc: linux-fsdevel@vger.kernel.org
> ---

I have one big ask. Please please please put your comments after the
commit message --- separator. Otherwise git/b4 will take your top
comment as the commmit message and discard the actual commit message.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] netfs: Fix i_size updating
  2025-06-26 12:32 [PATCH] netfs: Fix i_size updating David Howells
  2025-06-26 12:44 ` [PATCH] netfs: Merge i_size update functions David Howells
  2025-06-26 13:37 ` [PATCH] netfs: Fix i_size updating Paulo Alcantara
@ 2025-07-01 11:42 ` Christian Brauner
  2 siblings, 0 replies; 6+ messages in thread
From: Christian Brauner @ 2025-07-01 11:42 UTC (permalink / raw)
  To: David Howells
  Cc: Christian Brauner, Paulo Alcantara, linux-cifs, netfs,
	linux-fsdevel, linux-kernel, Steve French

On Thu, 26 Jun 2025 13:32:57 +0100, David Howells wrote:
> Fix the updating of i_size, particularly in regard to the completion of DIO
> writes and especially async DIO writes by using a lock.
> 
> The bug is triggered occasionally by the generic/207 xfstest as it chucks a
> bunch of AIO DIO writes at the filesystem and then checks that fstat()
> returns a reasonable st_size as each completes.
> 
> [...]

Applied to the vfs.fixes branch of the vfs/vfs.git tree.
Patches in the vfs.fixes branch should appear in linux-next soon.

Please report any outstanding bugs that were missed during review in a
new review to the original patch series allowing us to drop it.

It's encouraged to provide Acked-bys and Reviewed-bys even though the
patch has now been applied. If possible patch trailers will be updated.

Note that commit hashes shown below are subject to change due to rebase,
trailer updates or similar. If in doubt, please check the listed branch.

tree:   https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git
branch: vfs.fixes

[1/1] netfs: Fix i_size updating
      https://git.kernel.org/vfs/vfs/c/9754a8f2d5b5
[1/1] netfs: Merge i_size update functions
      https://git.kernel.org/vfs/vfs/c/871cb1a5a294

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2025-07-01 11:43 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-06-26 12:32 [PATCH] netfs: Fix i_size updating David Howells
2025-06-26 12:44 ` [PATCH] netfs: Merge i_size update functions David Howells
2025-07-01 11:41   ` Christian Brauner
2025-06-26 13:37 ` [PATCH] netfs: Fix i_size updating Paulo Alcantara
2025-06-26 14:07   ` Steve French
2025-07-01 11:42 ` Christian Brauner

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).