summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2025-07-01 19:38:45 +0300
committerChristian Brauner <brauner@kernel.org>2025-07-01 23:37:14 +0300
commit2e0658940d90a3dc130bb3b7f75bae9f4100e01f (patch)
tree05a4c9ce1ec6849705661c3a35ef745cb0f69dda
parent74ee76bea4b445c023d04806e0bcd78a912fd30b (diff)
downloadlinux-2e0658940d90a3dc130bb3b7f75bae9f4100e01f.tar.xz
netfs: Fix i_size updating
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> Link: https://lore.kernel.org/20250701163852.2171681-11-dhowells@redhat.com Reviewed-by: Paulo Alcantara (Red Hat) <pc@manguebit.org> 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 Signed-off-by: Christian Brauner <brauner@kernel.org>
-rw-r--r--fs/netfs/buffered_write.c2
-rw-r--r--fs/netfs/direct_write.c8
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 fa9a5bf3c6d5..3efa5894b2c0 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);
}
/*