| From 7aeda370440125daa5e8a7c2c28e9d0e30699784 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Fri, 5 Sep 2025 12:06:23 -0400 |
| Subject: NFS: Serialise O_DIRECT i/o and truncate() |
| |
| From: Trond Myklebust <trond.myklebust@hammerspace.com> |
| |
| [ Upstream commit 9eb90f435415c7da4800974ed943e39b5578ee7f ] |
| |
| Ensure that all O_DIRECT reads and writes are complete, and prevent the |
| initiation of new i/o until the setattr operation that will truncate the |
| file is complete. |
| |
| Fixes: a5864c999de6 ("NFS: Do not serialise O_DIRECT reads and writes") |
| Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| fs/nfs/inode.c | 4 +++- |
| fs/nfs/internal.h | 10 ++++++++++ |
| fs/nfs/io.c | 13 ++----------- |
| 3 files changed, 15 insertions(+), 12 deletions(-) |
| |
| diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c |
| index 7e7dd2aab449d..5cd5e4226db36 100644 |
| --- a/fs/nfs/inode.c |
| +++ b/fs/nfs/inode.c |
| @@ -645,8 +645,10 @@ nfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, |
| trace_nfs_setattr_enter(inode); |
| |
| /* Write all dirty data */ |
| - if (S_ISREG(inode->i_mode)) |
| + if (S_ISREG(inode->i_mode)) { |
| + nfs_file_block_o_direct(NFS_I(inode)); |
| nfs_sync_inode(inode); |
| + } |
| |
| fattr = nfs_alloc_fattr_with_label(NFS_SERVER(inode)); |
| if (fattr == NULL) { |
| diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h |
| index e78f43a137231..bde81e0abf0ae 100644 |
| --- a/fs/nfs/internal.h |
| +++ b/fs/nfs/internal.h |
| @@ -474,6 +474,16 @@ static inline bool nfs_file_io_is_buffered(struct nfs_inode *nfsi) |
| return test_bit(NFS_INO_ODIRECT, &nfsi->flags) == 0; |
| } |
| |
| +/* Must be called with exclusively locked inode->i_rwsem */ |
| +static inline void nfs_file_block_o_direct(struct nfs_inode *nfsi) |
| +{ |
| + if (test_bit(NFS_INO_ODIRECT, &nfsi->flags)) { |
| + clear_bit(NFS_INO_ODIRECT, &nfsi->flags); |
| + inode_dio_wait(&nfsi->vfs_inode); |
| + } |
| +} |
| + |
| + |
| /* namespace.c */ |
| #define NFS_PATH_CANONICAL 1 |
| extern char *nfs_path(char **p, struct dentry *dentry, |
| diff --git a/fs/nfs/io.c b/fs/nfs/io.c |
| index 3388faf2acb9f..d275b0a250bf3 100644 |
| --- a/fs/nfs/io.c |
| +++ b/fs/nfs/io.c |
| @@ -14,15 +14,6 @@ |
| |
| #include "internal.h" |
| |
| -/* Call with exclusively locked inode->i_rwsem */ |
| -static void nfs_block_o_direct(struct nfs_inode *nfsi, struct inode *inode) |
| -{ |
| - if (test_bit(NFS_INO_ODIRECT, &nfsi->flags)) { |
| - clear_bit(NFS_INO_ODIRECT, &nfsi->flags); |
| - inode_dio_wait(inode); |
| - } |
| -} |
| - |
| /** |
| * nfs_start_io_read - declare the file is being used for buffered reads |
| * @inode: file inode |
| @@ -57,7 +48,7 @@ nfs_start_io_read(struct inode *inode) |
| err = down_write_killable(&inode->i_rwsem); |
| if (err) |
| return err; |
| - nfs_block_o_direct(nfsi, inode); |
| + nfs_file_block_o_direct(nfsi); |
| downgrade_write(&inode->i_rwsem); |
| |
| return 0; |
| @@ -90,7 +81,7 @@ nfs_start_io_write(struct inode *inode) |
| |
| err = down_write_killable(&inode->i_rwsem); |
| if (!err) |
| - nfs_block_o_direct(NFS_I(inode), inode); |
| + nfs_file_block_o_direct(NFS_I(inode)); |
| return err; |
| } |
| |
| -- |
| 2.51.0 |
| |