Page cache indices are in units of PAGE_SIZE, not in units of the folio size. Revert the change in nfs_grow_file(), and pass the inode to nfs_folio_length() so it can be reimplemented in terms of folio_mkwrite_check_truncate() which handles this correctly. Fixes: 0c493b5cf16e ("NFS: Convert buffered writes to use folios") Signed-off-by: Matthew Wilcox (Oracle) <willy@xxxxxxxxxxxxx> Cc: Trond Myklebust <trond.myklebust@xxxxxxxxxxxxxxx> Cc: Anna Schumaker <Anna.Schumaker@xxxxxxxxxx> Cc: Christoph Hellwig <hch@xxxxxxxxxxxxx> --- fs/nfs/file.c | 6 +++--- fs/nfs/internal.h | 16 +++++----------- fs/nfs/read.c | 2 +- fs/nfs/write.c | 9 +++++---- include/linux/pagemap.h | 4 ++-- 5 files changed, 16 insertions(+), 21 deletions(-) diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 6bd127e6683d..723d78bbfe3f 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -301,7 +301,7 @@ EXPORT_SYMBOL_GPL(nfs_file_fsync); static bool nfs_folio_is_full_write(struct folio *folio, loff_t pos, unsigned int len) { - unsigned int pglen = nfs_folio_length(folio); + unsigned int pglen = nfs_folio_length(folio, folio->mapping->host); unsigned int offset = offset_in_folio(folio, pos); unsigned int end = offset + len; @@ -386,7 +386,7 @@ static int nfs_write_end(struct file *file, struct address_space *mapping, */ if (!folio_test_uptodate(folio)) { size_t fsize = folio_size(folio); - unsigned pglen = nfs_folio_length(folio); + unsigned pglen = nfs_folio_length(folio, mapping->host); unsigned end = offset + copied; if (pglen == 0) { @@ -610,7 +610,7 @@ static vm_fault_t nfs_vm_page_mkwrite(struct vm_fault *vmf) folio_wait_writeback(folio); - pagelen = nfs_folio_length(folio); + pagelen = nfs_folio_length(folio, inode); if (pagelen == 0) goto out_unlock; diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 9f0f4534744b..3b0236e67257 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -819,19 +819,13 @@ unsigned int nfs_page_length(struct page *page) /* * Determine the number of bytes of data the page contains */ -static inline size_t nfs_folio_length(struct folio *folio) +static inline size_t nfs_folio_length(struct folio *folio, struct inode *inode) { - loff_t i_size = i_size_read(folio_file_mapping(folio)->host); + ssize_t ret = folio_mkwrite_check_truncate(folio, inode); - if (i_size > 0) { - pgoff_t index = folio_index(folio) >> folio_order(folio); - pgoff_t end_index = (i_size - 1) >> folio_shift(folio); - if (index < end_index) - return folio_size(folio); - if (index == end_index) - return offset_in_folio(folio, i_size - 1) + 1; - } - return 0; + if (ret < 0) + ret = 0; + return ret; } /* diff --git a/fs/nfs/read.c b/fs/nfs/read.c index a142287d86f6..ba3bb496f832 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -296,7 +296,7 @@ int nfs_read_add_folio(struct nfs_pageio_descriptor *pgio, unsigned int len, aligned_len; int error; - len = nfs_folio_length(folio); + len = nfs_folio_length(folio, inode); if (len == 0) return nfs_return_empty_folio(folio); diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 2329cbb0e446..7713ce7c5b3a 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -278,8 +278,8 @@ static void nfs_grow_file(struct folio *folio, unsigned int offset, spin_lock(&inode->i_lock); i_size = i_size_read(inode); - end_index = ((i_size - 1) >> folio_shift(folio)) << folio_order(folio); - if (i_size > 0 && folio_index(folio) < end_index) + end_index = (i_size - 1) >> PAGE_SHIFT; + if (i_size > 0 && folio->index < end_index) goto out; end = folio_file_pos(folio) + (loff_t)offset + (loff_t)count; if (i_size >= end) @@ -358,7 +358,8 @@ nfs_page_group_search_locked(struct nfs_page *head, unsigned int page_offset) */ static bool nfs_page_group_covers_page(struct nfs_page *req) { - unsigned int len = nfs_folio_length(nfs_page_to_folio(req)); + struct folio *folio = nfs_page_to_folio(req); + unsigned int len = nfs_folio_length(folio, folio->mapping->host); struct nfs_page *tmp; unsigned int pos = 0; @@ -1356,7 +1357,7 @@ int nfs_update_folio(struct file *file, struct folio *folio, struct nfs_open_context *ctx = nfs_file_open_context(file); struct address_space *mapping = folio_file_mapping(folio); struct inode *inode = mapping->host; - unsigned int pagelen = nfs_folio_length(folio); + unsigned int pagelen = nfs_folio_length(folio, inode); int status = 0; nfs_inc_stats(inode, NFSIOS_VFSUPDATEPAGE); diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index c6aaceed0de6..df57d7361a9a 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -212,8 +212,8 @@ enum mapping_flags { AS_FOLIO_ORDER_MAX = 21, /* Bits 16-25 are used for FOLIO_ORDER */ }; -#define AS_FOLIO_ORDER_MIN_MASK 0x001f0000 -#define AS_FOLIO_ORDER_MAX_MASK 0x03e00000 +#define AS_FOLIO_ORDER_MIN_MASK (31 << AS_FOLIO_ORDER_MIN) +#define AS_FOLIO_ORDER_MAX_MASK (31 << AS_FOLIO_ORDER_MAX) #define AS_FOLIO_ORDER_MASK (AS_FOLIO_ORDER_MIN_MASK | AS_FOLIO_ORDER_MAX_MASK) /** -- 2.43.0