On Fri, 2021-08-27 at 10:44 +0100, David Howells wrote: > Convert the netfs helper library to use folios throughout, convert the afs > filesystem to use folios in its file I/O paths and convert the ceph > filesystem to use just enough folios to compile. > > With these changes, afs passes -g quick xfstests. > > Signed-off-by: David Howells <dhowells@xxxxxxxxxx> > cc: Matthew Wilcox (Oracle) <willy@xxxxxxxxxxxxx> > cc: Jeff Layton <jlayton@xxxxxxxxxx> > cc: Marc Dionne <marc.dionne@xxxxxxxxxxxx> > cc: Ilya Dryomov <idryomov@xxxxxxxxx> > cc: linux-afs@xxxxxxxxxxxxxxxxxxx > cc: ceph-devel@xxxxxxxxxxxxxxx > cc: linux-cachefs@xxxxxxxxxx > Link: https://lore.kernel.org/r/2408234.1628687271@xxxxxxxxxxxxxxxxxxxxxx/ # rfc > Link: https://lore.kernel.org/r/162877311459.3085614.10601478228012245108.stgit@xxxxxxxxxxxxxxxxxxxxxx/ > Link: https://lore.kernel.org/r/162981153551.1901565.3124454657133703341.stgit@xxxxxxxxxxxxxxxxxxxxxx/ > --- > > fs/afs/file.c | 70 +++++---- > fs/afs/internal.h | 46 +++--- > fs/afs/write.c | 332 +++++++++++++++++++++----------------------- > fs/ceph/addr.c | 80 ++++++----- > fs/netfs/read_helper.c | 165 +++++++++++----------- > include/linux/netfs.h | 12 +- > include/trace/events/afs.h | 21 +-- > 7 files changed, 363 insertions(+), 363 deletions(-) > > diff --git a/fs/afs/file.c b/fs/afs/file.c > index daa9106b9654..2c07958cde95 100644 > --- a/fs/afs/file.c > +++ b/fs/afs/file.c > @@ -315,23 +315,24 @@ static void afs_req_issue_op(struct netfs_read_subrequest *subreq) > > static int afs_symlink_readpage(struct file *file, struct page *page) > { > - struct afs_vnode *vnode = AFS_FS_I(page->mapping->host); > + struct afs_vnode *vnode = AFS_FS_I(page_mapping(page)->host); > struct afs_read *fsreq; > + struct folio *folio = page_folio(page); > int ret; > > fsreq = afs_alloc_read(GFP_NOFS); > if (!fsreq) > return -ENOMEM; > > - fsreq->pos = page->index * PAGE_SIZE; > - fsreq->len = PAGE_SIZE; > + fsreq->pos = folio_file_pos(folio); > + fsreq->len = folio_size(folio); > fsreq->vnode = vnode; > fsreq->iter = &fsreq->def_iter; > iov_iter_xarray(&fsreq->def_iter, READ, &page->mapping->i_pages, > fsreq->pos, fsreq->len); > > ret = afs_fetch_data(fsreq->vnode, fsreq); > - page_endio(page, false, ret); > + page_endio(&folio->page, false, ret); > return ret; > } > > @@ -355,7 +356,7 @@ static int afs_begin_cache_operation(struct netfs_read_request *rreq) > } > > static int afs_check_write_begin(struct file *file, loff_t pos, unsigned len, > - struct page *page, void **_fsdata) > + struct folio *folio, void **_fsdata) > { > struct afs_vnode *vnode = AFS_FS_I(file_inode(file)); > > @@ -378,7 +379,9 @@ const struct netfs_read_request_ops afs_req_ops = { > > static int afs_readpage(struct file *file, struct page *page) > { > - return netfs_readpage(file, page, &afs_req_ops, NULL); > + struct folio *folio = page_folio(page); > + > + return netfs_readpage(file, folio, &afs_req_ops, NULL); > } > > static void afs_readahead(struct readahead_control *ractl) > @@ -390,29 +393,29 @@ static void afs_readahead(struct readahead_control *ractl) > * Adjust the dirty region of the page on truncation or full invalidation, > * getting rid of the markers altogether if the region is entirely invalidated. > */ > -static void afs_invalidate_dirty(struct page *page, unsigned int offset, > +static void afs_invalidate_dirty(struct folio *folio, unsigned int offset, > unsigned int length) > { > - struct afs_vnode *vnode = AFS_FS_I(page->mapping->host); > + struct afs_vnode *vnode = AFS_FS_I(folio_inode(folio)); > unsigned long priv; > unsigned int f, t, end = offset + length; > > - priv = page_private(page); > + priv = (unsigned long)folio_get_private(folio); > > /* we clean up only if the entire page is being invalidated */ > - if (offset == 0 && length == thp_size(page)) > + if (offset == 0 && length == folio_size(folio)) > goto full_invalidate; > > /* If the page was dirtied by page_mkwrite(), the PTE stays writable > * and we don't get another notification to tell us to expand it > * again. > */ > - if (afs_is_page_dirty_mmapped(priv)) > + if (afs_is_folio_dirty_mmapped(priv)) > return; > > /* We may need to shorten the dirty region */ > - f = afs_page_dirty_from(page, priv); > - t = afs_page_dirty_to(page, priv); > + f = afs_folio_dirty_from(folio, priv); > + t = afs_folio_dirty_to(folio, priv); > > if (t <= offset || f >= end) > return; /* Doesn't overlap */ > @@ -430,17 +433,17 @@ static void afs_invalidate_dirty(struct page *page, unsigned int offset, > if (f == t) > goto undirty; > > - priv = afs_page_dirty(page, f, t); > - set_page_private(page, priv); > - trace_afs_page_dirty(vnode, tracepoint_string("trunc"), page); > + priv = afs_folio_dirty(folio, f, t); > + folio_change_private(folio, (void *)priv); > + trace_afs_folio_dirty(vnode, tracepoint_string("trunc"), folio); > return; > > undirty: > - trace_afs_page_dirty(vnode, tracepoint_string("undirty"), page); > - clear_page_dirty_for_io(page); > + trace_afs_folio_dirty(vnode, tracepoint_string("undirty"), folio); > + folio_clear_dirty_for_io(folio); > full_invalidate: > - trace_afs_page_dirty(vnode, tracepoint_string("inval"), page); > - detach_page_private(page); > + trace_afs_folio_dirty(vnode, tracepoint_string("inval"), folio); > + folio_detach_private(folio); > } > > /* > @@ -451,14 +454,16 @@ static void afs_invalidate_dirty(struct page *page, unsigned int offset, > static void afs_invalidatepage(struct page *page, unsigned int offset, > unsigned int length) > { > - _enter("{%lu},%u,%u", page->index, offset, length); > + struct folio *folio = page_folio(page); > + > + _enter("{%lu},%u,%u", folio_index(folio), offset, length); > > BUG_ON(!PageLocked(page)); > > if (PagePrivate(page)) > - afs_invalidate_dirty(page, offset, length); > + afs_invalidate_dirty(folio, offset, length); > > - wait_on_page_fscache(page); > + folio_wait_fscache(folio); > _leave(""); > } > > @@ -468,30 +473,31 @@ static void afs_invalidatepage(struct page *page, unsigned int offset, > */ > static int afs_releasepage(struct page *page, gfp_t gfp_flags) > { > - struct afs_vnode *vnode = AFS_FS_I(page->mapping->host); > + struct folio *folio = page_folio(page); > + struct afs_vnode *vnode = AFS_FS_I(folio_inode(folio)); > > _enter("{{%llx:%llu}[%lu],%lx},%x", > - vnode->fid.vid, vnode->fid.vnode, page->index, page->flags, > + vnode->fid.vid, vnode->fid.vnode, folio_index(folio), folio->flags, > gfp_flags); > > /* deny if page is being written to the cache and the caller hasn't > * elected to wait */ > #ifdef CONFIG_AFS_FSCACHE > - if (PageFsCache(page)) { > + if (folio_test_fscache(folio)) { > if (!(gfp_flags & __GFP_DIRECT_RECLAIM) || !(gfp_flags & __GFP_FS)) > return false; > - wait_on_page_fscache(page); > + folio_wait_fscache(folio); > } > #endif > > - if (PagePrivate(page)) { > - trace_afs_page_dirty(vnode, tracepoint_string("rel"), page); > - detach_page_private(page); > + if (folio_test_private(folio)) { > + trace_afs_folio_dirty(vnode, tracepoint_string("rel"), folio); > + folio_detach_private(folio); > } > > - /* indicate that the page can be released */ > + /* Indicate that the folio can be released */ > _leave(" = T"); > - return 1; > + return true; > } > > /* > diff --git a/fs/afs/internal.h b/fs/afs/internal.h > index beaef0387e85..f16cc7d7ae1d 100644 > --- a/fs/afs/internal.h > +++ b/fs/afs/internal.h > @@ -867,59 +867,59 @@ struct afs_vnode_cache_aux { > } __packed; > > /* > - * We use page->private to hold the amount of the page that we've written to, > + * We use folio->private to hold the amount of the folio that we've written to, > * splitting the field into two parts. However, we need to represent a range > - * 0...PAGE_SIZE, so we reduce the resolution if the size of the page > + * 0...FOLIO_SIZE, so we reduce the resolution if the size of the folio > * exceeds what we can encode. > */ > #ifdef CONFIG_64BIT > -#define __AFS_PAGE_PRIV_MASK 0x7fffffffUL > -#define __AFS_PAGE_PRIV_SHIFT 32 > -#define __AFS_PAGE_PRIV_MMAPPED 0x80000000UL > +#define __AFS_FOLIO_PRIV_MASK 0x7fffffffUL > +#define __AFS_FOLIO_PRIV_SHIFT 32 > +#define __AFS_FOLIO_PRIV_MMAPPED 0x80000000UL > #else > -#define __AFS_PAGE_PRIV_MASK 0x7fffUL > -#define __AFS_PAGE_PRIV_SHIFT 16 > -#define __AFS_PAGE_PRIV_MMAPPED 0x8000UL > +#define __AFS_FOLIO_PRIV_MASK 0x7fffUL > +#define __AFS_FOLIO_PRIV_SHIFT 16 > +#define __AFS_FOLIO_PRIV_MMAPPED 0x8000UL > #endif > > -static inline unsigned int afs_page_dirty_resolution(struct page *page) > +static inline unsigned int afs_folio_dirty_resolution(struct folio *folio) > { > - int shift = thp_order(page) + PAGE_SHIFT - (__AFS_PAGE_PRIV_SHIFT - 1); > + int shift = folio_shift(folio) - (__AFS_FOLIO_PRIV_SHIFT - 1); > return (shift > 0) ? shift : 0; > } > > -static inline size_t afs_page_dirty_from(struct page *page, unsigned long priv) > +static inline size_t afs_folio_dirty_from(struct folio *folio, unsigned long priv) > { > - unsigned long x = priv & __AFS_PAGE_PRIV_MASK; > + unsigned long x = priv & __AFS_FOLIO_PRIV_MASK; > > /* The lower bound is inclusive */ > - return x << afs_page_dirty_resolution(page); > + return x << afs_folio_dirty_resolution(folio); > } > > -static inline size_t afs_page_dirty_to(struct page *page, unsigned long priv) > +static inline size_t afs_folio_dirty_to(struct folio *folio, unsigned long priv) > { > - unsigned long x = (priv >> __AFS_PAGE_PRIV_SHIFT) & __AFS_PAGE_PRIV_MASK; > + unsigned long x = (priv >> __AFS_FOLIO_PRIV_SHIFT) & __AFS_FOLIO_PRIV_MASK; > > /* The upper bound is immediately beyond the region */ > - return (x + 1) << afs_page_dirty_resolution(page); > + return (x + 1) << afs_folio_dirty_resolution(folio); > } > > -static inline unsigned long afs_page_dirty(struct page *page, size_t from, size_t to) > +static inline unsigned long afs_folio_dirty(struct folio *folio, size_t from, size_t to) > { > - unsigned int res = afs_page_dirty_resolution(page); > + unsigned int res = afs_folio_dirty_resolution(folio); > from >>= res; > to = (to - 1) >> res; > - return (to << __AFS_PAGE_PRIV_SHIFT) | from; > + return (to << __AFS_FOLIO_PRIV_SHIFT) | from; > } > > -static inline unsigned long afs_page_dirty_mmapped(unsigned long priv) > +static inline unsigned long afs_folio_dirty_mmapped(unsigned long priv) > { > - return priv | __AFS_PAGE_PRIV_MMAPPED; > + return priv | __AFS_FOLIO_PRIV_MMAPPED; > } > > -static inline bool afs_is_page_dirty_mmapped(unsigned long priv) > +static inline bool afs_is_folio_dirty_mmapped(unsigned long priv) > { > - return priv & __AFS_PAGE_PRIV_MMAPPED; > + return priv & __AFS_FOLIO_PRIV_MMAPPED; > } > > #include <trace/events/afs.h> > diff --git a/fs/afs/write.c b/fs/afs/write.c > index 5c977deeeee0..4e19672e634d 100644 > --- a/fs/afs/write.c > +++ b/fs/afs/write.c > @@ -32,7 +32,7 @@ int afs_write_begin(struct file *file, struct address_space *mapping, > struct page **_page, void **fsdata) > { > struct afs_vnode *vnode = AFS_FS_I(file_inode(file)); > - struct page *page; > + struct folio *folio; > unsigned long priv; > unsigned f, from; > unsigned t, to; > @@ -46,12 +46,12 @@ int afs_write_begin(struct file *file, struct address_space *mapping, > * file. We need to do this before we get a lock on the page in case > * there's more than one writer competing for the same cache block. > */ > - ret = netfs_write_begin(file, mapping, pos, len, flags, &page, fsdata, > + ret = netfs_write_begin(file, mapping, pos, len, flags, &folio, fsdata, > &afs_req_ops, NULL); > if (ret < 0) > return ret; > > - index = page->index; > + index = folio_index(folio); > from = pos - index * PAGE_SIZE; > to = from + len; > > @@ -59,14 +59,14 @@ int afs_write_begin(struct file *file, struct address_space *mapping, > /* See if this page is already partially written in a way that we can > * merge the new write with. > */ > - if (PagePrivate(page)) { > - priv = page_private(page); > - f = afs_page_dirty_from(page, priv); > - t = afs_page_dirty_to(page, priv); > + if (folio_test_private(folio)) { > + priv = (unsigned long)folio_get_private(folio); > + f = afs_folio_dirty_from(folio, priv); > + t = afs_folio_dirty_to(folio, priv); > ASSERTCMP(f, <=, t); > > - if (PageWriteback(page)) { > - trace_afs_page_dirty(vnode, tracepoint_string("alrdy"), page); > + if (folio_test_writeback(folio)) { > + trace_afs_folio_dirty(vnode, tracepoint_string("alrdy"), folio); > goto flush_conflicting_write; > } > /* If the file is being filled locally, allow inter-write > @@ -78,7 +78,7 @@ int afs_write_begin(struct file *file, struct address_space *mapping, > goto flush_conflicting_write; > } > > - *_page = page; > + *_page = &folio->page; > _leave(" = 0"); > return 0; > > @@ -87,17 +87,17 @@ int afs_write_begin(struct file *file, struct address_space *mapping, > */ > flush_conflicting_write: > _debug("flush conflict"); > - ret = write_one_page(page); > + ret = folio_write_one(folio); > if (ret < 0) > goto error; > > - ret = lock_page_killable(page); > + ret = folio_lock_killable(folio); > if (ret < 0) > goto error; > goto try_again; > > error: > - put_page(page); > + folio_put(folio); > _leave(" = %d", ret); > return ret; > } > @@ -109,14 +109,15 @@ int afs_write_end(struct file *file, struct address_space *mapping, > loff_t pos, unsigned len, unsigned copied, > struct page *page, void *fsdata) > { > + struct folio *folio = page_folio(page); > struct afs_vnode *vnode = AFS_FS_I(file_inode(file)); > unsigned long priv; > - unsigned int f, from = pos & (thp_size(page) - 1); > + unsigned int f, from = pos & (folio_size(folio) - 1); > unsigned int t, to = from + copied; > loff_t i_size, maybe_i_size; > > _enter("{%llx:%llu},{%lx}", > - vnode->fid.vid, vnode->fid.vnode, page->index); > + vnode->fid.vid, vnode->fid.vnode, folio_index(folio)); > > if (!PageUptodate(page)) { > if (copied < len) { > @@ -141,29 +142,29 @@ int afs_write_end(struct file *file, struct address_space *mapping, > write_sequnlock(&vnode->cb_lock); > } > > - if (PagePrivate(page)) { > - priv = page_private(page); > - f = afs_page_dirty_from(page, priv); > - t = afs_page_dirty_to(page, priv); > + if (folio_test_private(folio)) { > + priv = (unsigned long)folio_get_private(folio); > + f = afs_folio_dirty_from(folio, priv); > + t = afs_folio_dirty_to(folio, priv); > if (from < f) > f = from; > if (to > t) > t = to; > - priv = afs_page_dirty(page, f, t); > - set_page_private(page, priv); > - trace_afs_page_dirty(vnode, tracepoint_string("dirty+"), page); > + priv = afs_folio_dirty(folio, f, t); > + folio_change_private(folio, (void *)priv); > + trace_afs_folio_dirty(vnode, tracepoint_string("dirty+"), folio); > } else { > - priv = afs_page_dirty(page, from, to); > - attach_page_private(page, (void *)priv); > - trace_afs_page_dirty(vnode, tracepoint_string("dirty"), page); > + priv = afs_folio_dirty(folio, from, to); > + folio_attach_private(folio, (void *)priv); > + trace_afs_folio_dirty(vnode, tracepoint_string("dirty"), folio); > } > > - if (set_page_dirty(page)) > - _debug("dirtied %lx", page->index); > + if (folio_mark_dirty(folio)) > + _debug("dirtied %lx", folio_index(folio)); > > out: > - unlock_page(page); > - put_page(page); > + folio_unlock(folio); > + folio_put(folio); > return copied; > } > > @@ -174,40 +175,32 @@ static void afs_kill_pages(struct address_space *mapping, > loff_t start, loff_t len) > { > struct afs_vnode *vnode = AFS_FS_I(mapping->host); > - struct pagevec pv; > - unsigned int loop, psize; > + struct folio *folio; > + pgoff_t index = start / PAGE_SIZE; > + pgoff_t last = (start + len - 1) / PAGE_SIZE, next; > > _enter("{%llx:%llu},%llx @%llx", > vnode->fid.vid, vnode->fid.vnode, len, start); > > - pagevec_init(&pv); > - > do { > - _debug("kill %llx @%llx", len, start); > - > - pv.nr = find_get_pages_contig(mapping, start / PAGE_SIZE, > - PAGEVEC_SIZE, pv.pages); > - if (pv.nr == 0) > - break; > + _debug("kill %lx (to %lx)", index, last); > > - for (loop = 0; loop < pv.nr; loop++) { > - struct page *page = pv.pages[loop]; > + folio = filemap_get_folio(mapping, index); > + if (!folio) { > + next = index + 1; > + continue; > + } > > - if (page->index * PAGE_SIZE >= start + len) > - break; > + next = folio_next_index(folio); > > - psize = thp_size(page); > - start += psize; > - len -= psize; > - ClearPageUptodate(page); > - end_page_writeback(page); > - lock_page(page); > - generic_error_remove_page(mapping, page); > - unlock_page(page); > - } > + folio_clear_uptodate(folio); > + folio_end_writeback(folio); > + folio_lock(folio); > + generic_error_remove_page(mapping, &folio->page); > + folio_unlock(folio); > + folio_put(folio); > > - __pagevec_release(&pv); > - } while (len > 0); > + } while (index = next, index <= last); > > _leave(""); > } > @@ -220,37 +213,26 @@ static void afs_redirty_pages(struct writeback_control *wbc, > loff_t start, loff_t len) > { > struct afs_vnode *vnode = AFS_FS_I(mapping->host); > - struct pagevec pv; > - unsigned int loop, psize; > + struct folio *folio; > + pgoff_t index = start / PAGE_SIZE; > + pgoff_t last = (start + len - 1) / PAGE_SIZE, next; > > _enter("{%llx:%llu},%llx @%llx", > vnode->fid.vid, vnode->fid.vnode, len, start); > > - pagevec_init(&pv); > - > do { > _debug("redirty %llx @%llx", len, start); > > - pv.nr = find_get_pages_contig(mapping, start / PAGE_SIZE, > - PAGEVEC_SIZE, pv.pages); > - if (pv.nr == 0) > - break; > - > - for (loop = 0; loop < pv.nr; loop++) { > - struct page *page = pv.pages[loop]; > - > - if (page->index * PAGE_SIZE >= start + len) > - break; > - > - psize = thp_size(page); > - start += psize; > - len -= psize; > - redirty_page_for_writepage(wbc, page); > - end_page_writeback(page); > + folio = filemap_get_folio(mapping, index); > + if (!folio) { > + next = index + 1; > + continue; > } > > - __pagevec_release(&pv); > - } while (len > 0); > + folio_redirty_for_writepage(wbc, folio); > + folio_end_writeback(folio); > + folio_put(folio); > + } while (index = next, index <= last); > > _leave(""); > } > @@ -261,7 +243,7 @@ static void afs_redirty_pages(struct writeback_control *wbc, > static void afs_pages_written_back(struct afs_vnode *vnode, loff_t start, unsigned int len) > { > struct address_space *mapping = vnode->vfs_inode.i_mapping; > - struct page *page; > + struct folio *folio; > pgoff_t end; > > XA_STATE(xas, &mapping->i_pages, start / PAGE_SIZE); > @@ -272,15 +254,16 @@ static void afs_pages_written_back(struct afs_vnode *vnode, loff_t start, unsign > rcu_read_lock(); > > end = (start + len - 1) / PAGE_SIZE; > - xas_for_each(&xas, page, end) { > - if (!PageWriteback(page)) { > - kdebug("bad %x @%llx page %lx %lx", len, start, page->index, end); > - ASSERT(PageWriteback(page)); > + xas_for_each(&xas, folio, end) { > + if (!folio_test_writeback(folio)) { > + kdebug("bad %x @%llx page %lx %lx", > + len, start, folio_index(folio), end); > + ASSERT(folio_test_writeback(folio)); > } > > - trace_afs_page_dirty(vnode, tracepoint_string("clear"), page); > - detach_page_private(page); > - page_endio(page, true, 0); > + trace_afs_folio_dirty(vnode, tracepoint_string("clear"), folio); > + folio_detach_private(folio); > + page_endio(&folio->page, true, 0); > } > > rcu_read_unlock(); > @@ -437,7 +420,7 @@ static void afs_extend_writeback(struct address_space *mapping, > unsigned int *_len) > { > struct pagevec pvec; > - struct page *page; > + struct folio *folio; > unsigned long priv; > unsigned int psize, filler = 0; > unsigned int f, t; > @@ -456,37 +439,37 @@ static void afs_extend_writeback(struct address_space *mapping, > */ > rcu_read_lock(); > > - xas_for_each(&xas, page, ULONG_MAX) { > + xas_for_each(&xas, folio, ULONG_MAX) { > stop = true; > - if (xas_retry(&xas, page)) > + if (xas_retry(&xas, folio)) > continue; > - if (xa_is_value(page)) > + if (xa_is_value(folio)) > break; > - if (page->index != index) > + if (folio_index(folio) != index) > break; > > - if (!page_cache_get_speculative(page)) { > + if (!folio_try_get_rcu(folio)) { > xas_reset(&xas); > continue; > } > > /* Has the page moved or been split? */ > - if (unlikely(page != xas_reload(&xas))) > + if (unlikely(folio != xas_reload(&xas))) > break; > > - if (!trylock_page(page)) > + if (!folio_trylock(folio)) > break; > - if (!PageDirty(page) || PageWriteback(page)) { > - unlock_page(page); > + if (!folio_test_dirty(folio) || folio_test_writeback(folio)) { > + folio_unlock(folio); > break; > } > > - psize = thp_size(page); > - priv = page_private(page); > - f = afs_page_dirty_from(page, priv); > - t = afs_page_dirty_to(page, priv); > + psize = folio_size(folio); > + priv = (unsigned long)folio_get_private(folio); > + f = afs_folio_dirty_from(folio, priv); > + t = afs_folio_dirty_to(folio, priv); > if (f != 0 && !new_content) { > - unlock_page(page); > + folio_unlock(folio); > break; > } > > @@ -497,8 +480,8 @@ static void afs_extend_writeback(struct address_space *mapping, > else if (t == psize || new_content) > stop = false; > > - index += thp_nr_pages(page); > - if (!pagevec_add(&pvec, page)) > + index += folio_nr_pages(folio); > + if (!pagevec_add(&pvec, &folio->page)) > break; > if (stop) > break; > @@ -515,16 +498,16 @@ static void afs_extend_writeback(struct address_space *mapping, > break; > > for (i = 0; i < pagevec_count(&pvec); i++) { > - page = pvec.pages[i]; > - trace_afs_page_dirty(vnode, tracepoint_string("store+"), page); > + folio = page_folio(pvec.pages[i]); > + trace_afs_folio_dirty(vnode, tracepoint_string("store+"), folio); > > - if (!clear_page_dirty_for_io(page)) > + if (!folio_clear_dirty_for_io(folio)) > BUG(); > - if (test_set_page_writeback(page)) > + if (folio_start_writeback(folio)) > BUG(); > > - *_count -= thp_nr_pages(page); > - unlock_page(page); > + *_count -= folio_nr_pages(folio); > + folio_unlock(folio); > } > > pagevec_release(&pvec); > @@ -538,10 +521,10 @@ static void afs_extend_writeback(struct address_space *mapping, > * Synchronously write back the locked page and any subsequent non-locked dirty > * pages. > */ > -static ssize_t afs_write_back_from_locked_page(struct address_space *mapping, > - struct writeback_control *wbc, > - struct page *page, > - loff_t start, loff_t end) > +static ssize_t afs_write_back_from_locked_folio(struct address_space *mapping, > + struct writeback_control *wbc, > + struct folio *folio, > + loff_t start, loff_t end) > { > struct afs_vnode *vnode = AFS_FS_I(mapping->host); > struct iov_iter iter; > @@ -552,22 +535,22 @@ static ssize_t afs_write_back_from_locked_page(struct address_space *mapping, > long count = wbc->nr_to_write; > int ret; > > - _enter(",%lx,%llx-%llx", page->index, start, end); > + _enter(",%lx,%llx-%llx", folio_index(folio), start, end); > > - if (test_set_page_writeback(page)) > + if (folio_start_writeback(folio)) > BUG(); > > - count -= thp_nr_pages(page); > + count -= folio_nr_pages(folio); > > /* Find all consecutive lockable dirty pages that have contiguous > * written regions, stopping when we find a page that is not > * immediately lockable, is not dirty or is missing, or we reach the > * end of the range. > */ > - priv = page_private(page); > - offset = afs_page_dirty_from(page, priv); > - to = afs_page_dirty_to(page, priv); > - trace_afs_page_dirty(vnode, tracepoint_string("store"), page); > + priv = (unsigned long)folio_get_private(folio); > + offset = afs_folio_dirty_from(folio, priv); > + to = afs_folio_dirty_to(folio, priv); > + trace_afs_folio_dirty(vnode, tracepoint_string("store"), folio); > > len = to - offset; > start += offset; > @@ -580,7 +563,7 @@ static ssize_t afs_write_back_from_locked_page(struct address_space *mapping, > max_len = min_t(unsigned long long, max_len, i_size - start); > > if (len < max_len && > - (to == thp_size(page) || new_content)) > + (to == folio_size(folio) || new_content)) > afs_extend_writeback(mapping, vnode, &count, > start, max_len, new_content, &len); > len = min_t(loff_t, len, max_len); > @@ -590,7 +573,7 @@ static ssize_t afs_write_back_from_locked_page(struct address_space *mapping, > * set; the first page is still locked at this point, but all the rest > * have been unlocked. > */ > - unlock_page(page); > + folio_unlock(folio); > > if (start < i_size) { > _debug("write back %x @%llx [%llx]", len, start, i_size); > @@ -651,16 +634,17 @@ static ssize_t afs_write_back_from_locked_page(struct address_space *mapping, > * write a page back to the server > * - the caller locked the page for us > */ > -int afs_writepage(struct page *page, struct writeback_control *wbc) > +int afs_writepage(struct page *subpage, struct writeback_control *wbc) > { > + struct folio *folio = page_folio(subpage); > ssize_t ret; > loff_t start; > > - _enter("{%lx},", page->index); > + _enter("{%lx},", folio_index(folio)); > > - start = page->index * PAGE_SIZE; > - ret = afs_write_back_from_locked_page(page->mapping, wbc, page, > - start, LLONG_MAX - start); > + start = folio_index(folio) * PAGE_SIZE; > + ret = afs_write_back_from_locked_folio(folio_file_mapping(folio), wbc, > + folio, start, LLONG_MAX - start); > if (ret < 0) { > _leave(" = %zd", ret); > return ret; > @@ -677,7 +661,8 @@ static int afs_writepages_region(struct address_space *mapping, > struct writeback_control *wbc, > loff_t start, loff_t end, loff_t *_next) > { > - struct page *page; > + struct folio *folio; > + struct page *head_page; > ssize_t ret; > int n; > > @@ -687,13 +672,14 @@ static int afs_writepages_region(struct address_space *mapping, > pgoff_t index = start / PAGE_SIZE; > > n = find_get_pages_range_tag(mapping, &index, end / PAGE_SIZE, > - PAGECACHE_TAG_DIRTY, 1, &page); > + PAGECACHE_TAG_DIRTY, 1, &head_page); > if (!n) > break; > > - start = (loff_t)page->index * PAGE_SIZE; /* May regress with THPs */ > + folio = page_folio(head_page); > + start = folio_file_pos(folio); /* May regress with THPs */ > > - _debug("wback %lx", page->index); > + _debug("wback %lx", folio_index(folio)); > > /* At this point we hold neither the i_pages lock nor the > * page lock: the page may be truncated or invalidated > @@ -701,37 +687,38 @@ static int afs_writepages_region(struct address_space *mapping, > * back from swapper_space to tmpfs file mapping > */ > if (wbc->sync_mode != WB_SYNC_NONE) { > - ret = lock_page_killable(page); > + ret = folio_lock_killable(folio); > if (ret < 0) { > - put_page(page); > + folio_put(folio); > return ret; > } > } else { > - if (!trylock_page(page)) { > - put_page(page); > + if (!folio_trylock(folio)) { > + folio_put(folio); > return 0; > } > } > > - if (page->mapping != mapping || !PageDirty(page)) { > - start += thp_size(page); > - unlock_page(page); > - put_page(page); > + if (folio_file_mapping(folio) != mapping || > + !folio_test_dirty(folio)) { > + start += folio_size(folio); > + folio_unlock(folio); > + folio_put(folio); > continue; > } > > - if (PageWriteback(page)) { > - unlock_page(page); > + if (folio_test_writeback(folio)) { > + folio_unlock(folio); > if (wbc->sync_mode != WB_SYNC_NONE) > - wait_on_page_writeback(page); > - put_page(page); > + folio_wait_writeback(folio); > + folio_put(folio); > continue; > } > > - if (!clear_page_dirty_for_io(page)) > + if (!folio_clear_dirty_for_io(folio)) > BUG(); > - ret = afs_write_back_from_locked_page(mapping, wbc, page, start, end); > - put_page(page); > + ret = afs_write_back_from_locked_folio(mapping, wbc, folio, start, end); > + folio_put(folio); > if (ret < 0) { > _leave(" = %zd", ret); > return ret; > @@ -840,14 +827,13 @@ int afs_fsync(struct file *file, loff_t start, loff_t end, int datasync) > vm_fault_t afs_page_mkwrite(struct vm_fault *vmf) > { > struct folio *folio = page_folio(vmf->page); > - struct page *page = &folio->page; > struct file *file = vmf->vma->vm_file; > struct inode *inode = file_inode(file); > struct afs_vnode *vnode = AFS_FS_I(inode); > unsigned long priv; > vm_fault_t ret = VM_FAULT_RETRY; > > - _enter("{{%llx:%llu}},{%lx}", vnode->fid.vid, vnode->fid.vnode, page->index); > + _enter("{{%llx:%llu}},{%lx}", vnode->fid.vid, vnode->fid.vnode, folio_index(folio)); > > sb_start_pagefault(inode->i_sb); > > @@ -855,18 +841,18 @@ vm_fault_t afs_page_mkwrite(struct vm_fault *vmf) > * be modified. We then assume the entire page will need writing back. > */ > #ifdef CONFIG_AFS_FSCACHE > - if (PageFsCache(page) && > - wait_on_page_fscache_killable(page) < 0) > + if (folio_test_fscache(folio) && > + folio_wait_fscache_killable(folio) < 0) > goto out; > #endif > > if (folio_wait_writeback_killable(folio)) > goto out; > > - if (lock_page_killable(page) < 0) > + if (folio_lock_killable(folio) < 0) > goto out; > > - /* We mustn't change page->private until writeback is complete as that > + /* We mustn't change folio->private until writeback is complete as that > * details the portion of the page we need to write back and we might > * need to redirty the page if there's a problem. > */ > @@ -875,14 +861,14 @@ vm_fault_t afs_page_mkwrite(struct vm_fault *vmf) > goto out; > } > > - priv = afs_page_dirty(page, 0, thp_size(page)); > - priv = afs_page_dirty_mmapped(priv); > - if (PagePrivate(page)) { > - set_page_private(page, priv); > - trace_afs_page_dirty(vnode, tracepoint_string("mkwrite+"), page); > + priv = afs_folio_dirty(folio, 0, folio_size(folio)); > + priv = afs_folio_dirty_mmapped(priv); > + if (folio_test_private(folio)) { > + folio_change_private(folio, (void *)priv); > + trace_afs_folio_dirty(vnode, tracepoint_string("mkwrite+"), folio); > } else { > - attach_page_private(page, (void *)priv); > - trace_afs_page_dirty(vnode, tracepoint_string("mkwrite"), page); > + folio_attach_private(folio, (void *)priv); > + trace_afs_folio_dirty(vnode, tracepoint_string("mkwrite"), folio); > } > file_update_time(file); > > @@ -923,38 +909,38 @@ void afs_prune_wb_keys(struct afs_vnode *vnode) > /* > * Clean up a page during invalidation. > */ > -int afs_launder_page(struct page *page) > +int afs_launder_page(struct page *subpage) > { > - struct address_space *mapping = page->mapping; > - struct afs_vnode *vnode = AFS_FS_I(mapping->host); > + struct folio *folio = page_folio(subpage); > + struct afs_vnode *vnode = AFS_FS_I(folio_inode(folio)); > struct iov_iter iter; > struct bio_vec bv[1]; > unsigned long priv; > unsigned int f, t; > int ret = 0; > > - _enter("{%lx}", page->index); > + _enter("{%lx}", folio_index(folio)); > > - priv = page_private(page); > - if (clear_page_dirty_for_io(page)) { > + priv = (unsigned long)folio_get_private(folio); > + if (folio_clear_dirty_for_io(folio)) { > f = 0; > - t = thp_size(page); > - if (PagePrivate(page)) { > - f = afs_page_dirty_from(page, priv); > - t = afs_page_dirty_to(page, priv); > + t = folio_size(folio); > + if (folio_test_private(folio)) { > + f = afs_folio_dirty_from(folio, priv); > + t = afs_folio_dirty_to(folio, priv); > } > > - bv[0].bv_page = page; > + bv[0].bv_page = &folio->page; > bv[0].bv_offset = f; > bv[0].bv_len = t - f; > iov_iter_bvec(&iter, WRITE, bv, 1, bv[0].bv_len); > > - trace_afs_page_dirty(vnode, tracepoint_string("launder"), page); > - ret = afs_store_data(vnode, &iter, page_offset(page) + f, true); > + trace_afs_folio_dirty(vnode, tracepoint_string("launder"), folio); > + ret = afs_store_data(vnode, &iter, folio_pos(folio) + f, true); > } > > - trace_afs_page_dirty(vnode, tracepoint_string("laundered"), page); > - detach_page_private(page); > - wait_on_page_fscache(page); > + trace_afs_folio_dirty(vnode, tracepoint_string("laundered"), folio); > + folio_detach_private(folio); > + folio_wait_fscache(folio); > return ret; > } > diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c > index a1e2813731d1..49aa41555baf 100644 > --- a/fs/ceph/addr.c > +++ b/fs/ceph/addr.c > @@ -63,7 +63,7 @@ > (CONGESTION_ON_THRESH(congestion_kb) >> 2)) > > static int ceph_netfs_check_write_begin(struct file *file, loff_t pos, unsigned int len, > - struct page *page, void **_fsdata); > + struct folio *folio, void **_fsdata); > > static inline struct ceph_snap_context *page_snap_context(struct page *page) > { > @@ -317,13 +317,14 @@ static const struct netfs_read_request_ops ceph_netfs_read_ops = { > }; > > /* read a single page, without unlocking it. */ > -static int ceph_readpage(struct file *file, struct page *page) > +static int ceph_readpage(struct file *file, struct page *subpage) > { > + struct folio *folio = page_folio(subpage); > struct inode *inode = file_inode(file); > struct ceph_inode_info *ci = ceph_inode(inode); > struct ceph_vino vino = ceph_vino(inode); > - u64 off = page_offset(page); > - u64 len = thp_size(page); > + size_t len = folio_size(folio); > + u64 off = folio_file_pos(folio); > > if (ci->i_inline_version != CEPH_INLINE_NONE) { > /* > @@ -331,19 +332,19 @@ static int ceph_readpage(struct file *file, struct page *page) > * into page cache while getting Fcr caps. > */ > if (off == 0) { > - unlock_page(page); > + folio_unlock(folio); > return -EINVAL; > } > - zero_user_segment(page, 0, thp_size(page)); > - SetPageUptodate(page); > - unlock_page(page); > + zero_user_segment(&folio->page, 0, folio_size(folio)); > + folio_mark_uptodate(folio); > + folio_unlock(folio); > return 0; > } > > - dout("readpage ino %llx.%llx file %p off %llu len %llu page %p index %lu\n", > - vino.ino, vino.snap, file, off, len, page, page->index); > + dout("readpage ino %llx.%llx file %p off %llu len %zu folio %p index %lu\n", > + vino.ino, vino.snap, file, off, len, folio, folio_index(folio)); > > - return netfs_readpage(file, page, &ceph_netfs_read_ops, NULL); > + return netfs_readpage(file, folio, &ceph_netfs_read_ops, NULL); > } > > static void ceph_readahead(struct readahead_control *ractl) > @@ -1187,18 +1188,18 @@ ceph_find_incompatible(struct page *page) > } > > static int ceph_netfs_check_write_begin(struct file *file, loff_t pos, unsigned int len, > - struct page *page, void **_fsdata) > + struct folio *folio, void **_fsdata) > { > struct inode *inode = file_inode(file); > struct ceph_inode_info *ci = ceph_inode(inode); > struct ceph_snap_context *snapc; > > - snapc = ceph_find_incompatible(page); > + snapc = ceph_find_incompatible(folio_page(folio, 0)); > if (snapc) { > int r; > > - unlock_page(page); > - put_page(page); > + folio_unlock(folio); > + folio_put(folio); > if (IS_ERR(snapc)) > return PTR_ERR(snapc); > > @@ -1216,12 +1217,12 @@ static int ceph_netfs_check_write_begin(struct file *file, loff_t pos, unsigned > * clean, or already dirty within the same snap context. > */ > static int ceph_write_begin(struct file *file, struct address_space *mapping, > - loff_t pos, unsigned len, unsigned flags, > + loff_t pos, unsigned len, unsigned aop_flags, > struct page **pagep, void **fsdata) > { > struct inode *inode = file_inode(file); > struct ceph_inode_info *ci = ceph_inode(inode); > - struct page *page = NULL; > + struct folio *folio = NULL; > pgoff_t index = pos >> PAGE_SHIFT; > int r; > > @@ -1230,39 +1231,43 @@ static int ceph_write_begin(struct file *file, struct address_space *mapping, > * for inline_version sent to the MDS. > */ > if (ci->i_inline_version != CEPH_INLINE_NONE) { > - page = grab_cache_page_write_begin(mapping, index, flags); > - if (!page) > + unsigned int fgp_flags = FGP_LOCK | FGP_WRITE | FGP_CREAT | FGP_STABLE; > + if (aop_flags & AOP_FLAG_NOFS) > + fgp_flags |= FGP_NOFS; > + folio = __filemap_get_folio(mapping, index, fgp_flags, > + mapping_gfp_mask(mapping)); > + if (!folio) > return -ENOMEM; > > /* > * The inline_version on a new inode is set to 1. If that's the > - * case, then the page is brand new and isn't yet Uptodate. > + * case, then the folio is brand new and isn't yet Uptodate. > */ > r = 0; > if (index == 0 && ci->i_inline_version != 1) { > - if (!PageUptodate(page)) { > + if (!folio_test_uptodate(folio)) { > WARN_ONCE(1, "ceph: write_begin called on still-inlined inode (inline_version %llu)!\n", > ci->i_inline_version); > r = -EINVAL; > } > goto out; > } > - zero_user_segment(page, 0, thp_size(page)); > - SetPageUptodate(page); > + zero_user_segment(&folio->page, 0, folio_size(folio)); > + folio_mark_uptodate(folio); > goto out; > } > > - r = netfs_write_begin(file, inode->i_mapping, pos, len, 0, &page, NULL, > + r = netfs_write_begin(file, inode->i_mapping, pos, len, 0, &folio, NULL, > &ceph_netfs_read_ops, NULL); > out: > if (r == 0) > - wait_on_page_fscache(page); > + folio_wait_fscache(folio); > if (r < 0) { > - if (page) > - put_page(page); > + if (folio) > + folio_put(folio); > } else { > - WARN_ON_ONCE(!PageLocked(page)); > - *pagep = page; > + WARN_ON_ONCE(!folio_test_locked(folio)); > + *pagep = &folio->page; > } > return r; > } > @@ -1273,32 +1278,33 @@ static int ceph_write_begin(struct file *file, struct address_space *mapping, > */ > static int ceph_write_end(struct file *file, struct address_space *mapping, > loff_t pos, unsigned len, unsigned copied, > - struct page *page, void *fsdata) > + struct page *subpage, void *fsdata) > { > + struct folio *folio = page_folio(subpage); > struct inode *inode = file_inode(file); > bool check_cap = false; > > - dout("write_end file %p inode %p page %p %d~%d (%d)\n", file, > - inode, page, (int)pos, (int)copied, (int)len); > + dout("write_end file %p inode %p folio %p %d~%d (%d)\n", file, > + inode, folio, (int)pos, (int)copied, (int)len); > > /* zero the stale part of the page if we did a short copy */ > - if (!PageUptodate(page)) { > + if (!folio_test_uptodate(folio)) { > if (copied < len) { > copied = 0; > goto out; > } > - SetPageUptodate(page); > + folio_mark_uptodate(folio); > } > > /* did file size increase? */ > if (pos+copied > i_size_read(inode)) > check_cap = ceph_inode_set_size(inode, pos+copied); > > - set_page_dirty(page); > + folio_mark_dirty(folio); > > out: > - unlock_page(page); > - put_page(page); > + folio_unlock(folio); > + folio_put(folio); > > if (check_cap) > ceph_check_caps(ceph_inode(inode), CHECK_CAPS_AUTHONLY, NULL); > diff --git a/fs/netfs/read_helper.c b/fs/netfs/read_helper.c > index 0b6cd3b8734c..2ad91f9e2a45 100644 > --- a/fs/netfs/read_helper.c > +++ b/fs/netfs/read_helper.c > @@ -230,7 +230,7 @@ static void netfs_rreq_completed(struct netfs_read_request *rreq, bool was_async > > /* > * Deal with the completion of writing the data to the cache. We have to clear > - * the PG_fscache bits on the pages involved and release the caller's ref. > + * the PG_fscache bits on the folios involved and release the caller's ref. > * > * May be called in softirq mode and we inherit a ref from the caller. > */ > @@ -238,7 +238,7 @@ static void netfs_rreq_unmark_after_write(struct netfs_read_request *rreq, > bool was_async) > { > struct netfs_read_subrequest *subreq; > - struct page *page; > + struct folio *folio; > pgoff_t unlocked = 0; > bool have_unlocked = false; > > @@ -247,14 +247,14 @@ static void netfs_rreq_unmark_after_write(struct netfs_read_request *rreq, > list_for_each_entry(subreq, &rreq->subrequests, rreq_link) { > XA_STATE(xas, &rreq->mapping->i_pages, subreq->start / PAGE_SIZE); > > - xas_for_each(&xas, page, (subreq->start + subreq->len - 1) / PAGE_SIZE) { > + xas_for_each(&xas, folio, (subreq->start + subreq->len - 1) / PAGE_SIZE) { > /* We might have multiple writes from the same huge > - * page, but we mustn't unlock a page more than once. > + * folio, but we mustn't unlock a folio more than once. > */ > - if (have_unlocked && page->index <= unlocked) > + if (have_unlocked && folio_index(folio) <= unlocked) > continue; > - unlocked = page->index; > - end_page_fscache(page); > + unlocked = folio_index(folio); > + folio_end_fscache(folio); > have_unlocked = true; > } > } > @@ -367,18 +367,17 @@ static void netfs_rreq_write_to_cache(struct netfs_read_request *rreq, > } > > /* > - * Unlock the pages in a read operation. We need to set PG_fscache on any > - * pages we're going to write back before we unlock them. > + * Unlock the folios in a read operation. We need to set PG_fscache on any > + * folios we're going to write back before we unlock them. > */ > static void netfs_rreq_unlock(struct netfs_read_request *rreq) > { > struct netfs_read_subrequest *subreq; > - struct page *page; > + struct folio *folio; > unsigned int iopos, account = 0; > pgoff_t start_page = rreq->start / PAGE_SIZE; > pgoff_t last_page = ((rreq->start + rreq->len) / PAGE_SIZE) - 1; > bool subreq_failed = false; > - int i; > > XA_STATE(xas, &rreq->mapping->i_pages, start_page); > > @@ -403,9 +402,9 @@ static void netfs_rreq_unlock(struct netfs_read_request *rreq) > trace_netfs_rreq(rreq, netfs_rreq_trace_unlock); > > rcu_read_lock(); > - xas_for_each(&xas, page, last_page) { > - unsigned int pgpos = (page->index - start_page) * PAGE_SIZE; > - unsigned int pgend = pgpos + thp_size(page); > + xas_for_each(&xas, folio, last_page) { > + unsigned int pgpos = (folio_index(folio) - start_page) * PAGE_SIZE; > + unsigned int pgend = pgpos + folio_size(folio); > bool pg_failed = false; > > for (;;) { > @@ -414,7 +413,7 @@ static void netfs_rreq_unlock(struct netfs_read_request *rreq) > break; > } > if (test_bit(NETFS_SREQ_WRITE_TO_CACHE, &subreq->flags)) > - set_page_fscache(page); > + folio_start_fscache(folio); > pg_failed |= subreq_failed; > if (pgend < iopos + subreq->len) > break; > @@ -433,17 +432,16 @@ static void netfs_rreq_unlock(struct netfs_read_request *rreq) > } > > if (!pg_failed) { > - for (i = 0; i < thp_nr_pages(page); i++) > - flush_dcache_page(page); > - SetPageUptodate(page); > + flush_dcache_folio(folio); > + folio_mark_uptodate(folio); > } > > - if (!test_bit(NETFS_RREQ_DONT_UNLOCK_PAGES, &rreq->flags)) { > - if (page->index == rreq->no_unlock_page && > - test_bit(NETFS_RREQ_NO_UNLOCK_PAGE, &rreq->flags)) > + if (!test_bit(NETFS_RREQ_DONT_UNLOCK_FOLIOS, &rreq->flags)) { > + if (folio_index(folio) == rreq->no_unlock_folio && > + test_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, &rreq->flags)) > _debug("no unlock"); > else > - unlock_page(page); > + folio_unlock(folio); > } > } > rcu_read_unlock(); > @@ -876,7 +874,6 @@ void netfs_readahead(struct readahead_control *ractl, > void *netfs_priv) > { > struct netfs_read_request *rreq; > - struct page *page; > unsigned int debug_index = 0; > int ret; > > @@ -911,11 +908,11 @@ void netfs_readahead(struct readahead_control *ractl, > > } while (rreq->submitted < rreq->len); > > - /* Drop the refs on the pages here rather than in the cache or > + /* Drop the refs on the folios here rather than in the cache or > * filesystem. The locks will be dropped in netfs_rreq_unlock(). > */ > - while ((page = readahead_page(ractl))) > - put_page(page); > + while (readahead_folio(ractl)) > + ; > > /* If we decrement nr_rd_ops to 0, the ref belongs to us. */ > if (atomic_dec_and_test(&rreq->nr_rd_ops)) > @@ -935,7 +932,7 @@ EXPORT_SYMBOL(netfs_readahead); > /** > * netfs_readpage - Helper to manage a readpage request > * @file: The file to read from > - * @page: The page to read > + * @folio: The folio to read > * @ops: The network filesystem's operations for the helper to use > * @netfs_priv: Private netfs data to be retained in the request > * > @@ -950,7 +947,7 @@ EXPORT_SYMBOL(netfs_readahead); > * This is usable whether or not caching is enabled. > */ > int netfs_readpage(struct file *file, > - struct page *page, > + struct folio *folio, > const struct netfs_read_request_ops *ops, > void *netfs_priv) > { > @@ -958,23 +955,23 @@ int netfs_readpage(struct file *file, > unsigned int debug_index = 0; > int ret; > > - _enter("%lx", page_index(page)); > + _enter("%lx", folio_index(folio)); > > rreq = netfs_alloc_read_request(ops, netfs_priv, file); > if (!rreq) { > if (netfs_priv) > - ops->cleanup(netfs_priv, page_file_mapping(page)); > - unlock_page(page); > + ops->cleanup(netfs_priv, folio_file_mapping(folio)); > + folio_unlock(folio); > return -ENOMEM; > } > - rreq->mapping = page_file_mapping(page); > - rreq->start = page_file_offset(page); > - rreq->len = thp_size(page); > + rreq->mapping = folio_file_mapping(folio); > + rreq->start = folio_file_pos(folio); > + rreq->len = folio_size(folio); > > if (ops->begin_cache_operation) { > ret = ops->begin_cache_operation(rreq); > if (ret == -ENOMEM || ret == -EINTR || ret == -ERESTARTSYS) { > - unlock_page(page); > + folio_unlock(folio); > goto out; > } > } > @@ -1012,40 +1009,40 @@ int netfs_readpage(struct file *file, > EXPORT_SYMBOL(netfs_readpage); > > /** > - * netfs_skip_page_read - prep a page for writing without reading first > - * @page: page being prepared > + * netfs_skip_folio_read - prep a folio for writing without reading first > + * @folio: The folio being prepared > * @pos: starting position for the write > * @len: length of write > * > * In some cases, write_begin doesn't need to read at all: > - * - full page write > - * - write that lies in a page that is completely beyond EOF > - * - write that covers the the page from start to EOF or beyond it > + * - full folio write > + * - write that lies in a folio that is completely beyond EOF > + * - write that covers the folio from start to EOF or beyond it > * > * If any of these criteria are met, then zero out the unwritten parts > - * of the page and return true. Otherwise, return false. > + * of the folio and return true. Otherwise, return false. > */ > -static bool netfs_skip_page_read(struct page *page, loff_t pos, size_t len) > +static bool netfs_skip_folio_read(struct folio *folio, loff_t pos, size_t len) > { > - struct inode *inode = page->mapping->host; > + struct inode *inode = folio_inode(folio); > loff_t i_size = i_size_read(inode); > - size_t offset = offset_in_thp(page, pos); > + size_t offset = offset_in_folio(folio, pos); > > - /* Full page write */ > - if (offset == 0 && len >= thp_size(page)) > + /* Full folio write */ > + if (offset == 0 && len >= folio_size(folio)) > return true; > > - /* pos beyond last page in the file */ > + /* pos beyond last folio in the file */ > if (pos - offset >= i_size) > goto zero_out; > > - /* Write that covers from the start of the page to EOF or beyond */ > + /* Write that covers from the start of the folio to EOF or beyond */ > if (offset == 0 && (pos + len) >= i_size) > goto zero_out; > > return false; > zero_out: > - zero_user_segments(page, 0, offset, offset + len, thp_size(page)); > + zero_user_segments(&folio->page, 0, offset, offset + len, folio_size(folio)); > return true; > } > > @@ -1054,9 +1051,9 @@ static bool netfs_skip_page_read(struct page *page, loff_t pos, size_t len) > * @file: The file to read from > * @mapping: The mapping to read from > * @pos: File position at which the write will begin > - * @len: The length of the write (may extend beyond the end of the page chosen) > - * @flags: AOP_* flags > - * @_page: Where to put the resultant page > + * @len: The length of the write (may extend beyond the end of the folio chosen) > + * @aop_flags: AOP_* flags > + * @_folio: Where to put the resultant folio > * @_fsdata: Place for the netfs to store a cookie > * @ops: The network filesystem's operations for the helper to use > * @netfs_priv: Private netfs data to be retained in the request > @@ -1072,37 +1069,41 @@ static bool netfs_skip_page_read(struct page *page, loff_t pos, size_t len) > * issue_op, is mandatory. > * > * The check_write_begin() operation can be provided to check for and flush > - * conflicting writes once the page is grabbed and locked. It is passed a > + * conflicting writes once the folio is grabbed and locked. It is passed a > * pointer to the fsdata cookie that gets returned to the VM to be passed to > * write_end. It is permitted to sleep. It should return 0 if the request > - * should go ahead; unlock the page and return -EAGAIN to cause the page to be > - * regot; or return an error. > + * should go ahead; unlock the folio and return -EAGAIN to cause the folio to > + * be regot; or return an error. > * > * This is usable whether or not caching is enabled. > */ > int netfs_write_begin(struct file *file, struct address_space *mapping, > - loff_t pos, unsigned int len, unsigned int flags, > - struct page **_page, void **_fsdata, > + loff_t pos, unsigned int len, unsigned int aop_flags, > + struct folio **_folio, void **_fsdata, > const struct netfs_read_request_ops *ops, > void *netfs_priv) > { > struct netfs_read_request *rreq; > - struct page *page, *xpage; > + struct folio *folio; > struct inode *inode = file_inode(file); > - unsigned int debug_index = 0; > + unsigned int debug_index = 0, fgp_flags; > pgoff_t index = pos >> PAGE_SHIFT; > int ret; > > DEFINE_READAHEAD(ractl, file, NULL, mapping, index); > > retry: > - page = grab_cache_page_write_begin(mapping, index, flags); > - if (!page) > + fgp_flags = FGP_LOCK | FGP_WRITE | FGP_CREAT | FGP_STABLE; > + if (aop_flags & AOP_FLAG_NOFS) > + fgp_flags |= FGP_NOFS; > + folio = __filemap_get_folio(mapping, index, fgp_flags, > + mapping_gfp_mask(mapping)); > + if (!folio) > return -ENOMEM; > > if (ops->check_write_begin) { > /* Allow the netfs (eg. ceph) to flush conflicts. */ > - ret = ops->check_write_begin(file, pos, len, page, _fsdata); > + ret = ops->check_write_begin(file, pos, len, folio, _fsdata); > if (ret < 0) { > trace_netfs_failure(NULL, NULL, ret, netfs_fail_check_write_begin); > if (ret == -EAGAIN) > @@ -1111,28 +1112,28 @@ int netfs_write_begin(struct file *file, struct address_space *mapping, > } > } > > - if (PageUptodate(page)) > - goto have_page; > + if (folio_test_uptodate(folio)) > + goto have_folio; > > /* If the page is beyond the EOF, we want to clear it - unless it's > * within the cache granule containing the EOF, in which case we need > * to preload the granule. > */ > if (!ops->is_cache_enabled(inode) && > - netfs_skip_page_read(page, pos, len)) { > + netfs_skip_folio_read(folio, pos, len)) { > netfs_stat(&netfs_n_rh_write_zskip); > - goto have_page_no_wait; > + goto have_folio_no_wait; > } > > ret = -ENOMEM; > rreq = netfs_alloc_read_request(ops, netfs_priv, file); > if (!rreq) > goto error; > - rreq->mapping = page->mapping; > - rreq->start = page_offset(page); > - rreq->len = thp_size(page); > - rreq->no_unlock_page = page->index; > - __set_bit(NETFS_RREQ_NO_UNLOCK_PAGE, &rreq->flags); > + rreq->mapping = folio_file_mapping(folio); > + rreq->start = folio_file_pos(folio); > + rreq->len = folio_size(folio); > + rreq->no_unlock_folio = folio_index(folio); > + __set_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, &rreq->flags); > netfs_priv = NULL; > > if (ops->begin_cache_operation) { > @@ -1147,14 +1148,14 @@ int netfs_write_begin(struct file *file, struct address_space *mapping, > /* Expand the request to meet caching requirements and download > * preferences. > */ > - ractl._nr_pages = thp_nr_pages(page); > + ractl._nr_pages = folio_nr_pages(folio); > netfs_rreq_expand(rreq, &ractl); > netfs_get_read_request(rreq); > > - /* We hold the page locks, so we can drop the references */ > - while ((xpage = readahead_page(&ractl))) > - if (xpage != page) > - put_page(xpage); > + /* We hold the folio locks, so we can drop the references */ > + folio_get(folio); > + while (readahead_folio(&ractl)) > + ; > > > > atomic_set(&rreq->nr_rd_ops, 1); > do { > @@ -1184,22 +1185,22 @@ int netfs_write_begin(struct file *file, struct address_space *mapping, > if (ret < 0) > goto error; > > -have_page: > - ret = wait_on_page_fscache_killable(page); > +have_folio: > + ret = folio_wait_fscache_killable(folio); > if (ret < 0) > goto error; > -have_page_no_wait: > +have_folio_no_wait: > if (netfs_priv) > ops->cleanup(netfs_priv, mapping); > - *_page = page; > + *_folio = folio; > _leave(" = 0"); > return 0; > > error_put: > netfs_put_read_request(rreq, false); > error: > - unlock_page(page); > - put_page(page); > + folio_unlock(folio); > + folio_put(folio); > if (netfs_priv) > ops->cleanup(netfs_priv, mapping); > _leave(" = %d", ret); > diff --git a/include/linux/netfs.h b/include/linux/netfs.h > index 113b5fa9280c..cb2aee4258ac 100644 > --- a/include/linux/netfs.h > +++ b/include/linux/netfs.h > @@ -166,13 +166,13 @@ struct netfs_read_request { > short error; /* 0 or error that occurred */ > loff_t i_size; /* Size of the file */ > loff_t start; /* Start position */ > - pgoff_t no_unlock_page; /* Don't unlock this page after read */ > + pgoff_t no_unlock_folio; /* Don't unlock this folio after read */ > refcount_t usage; > unsigned long flags; > #define NETFS_RREQ_INCOMPLETE_IO 0 /* Some ioreqs terminated short or with error */ > #define NETFS_RREQ_WRITE_TO_CACHE 1 /* Need to write to the cache */ > -#define NETFS_RREQ_NO_UNLOCK_PAGE 2 /* Don't unlock no_unlock_page on completion */ > -#define NETFS_RREQ_DONT_UNLOCK_PAGES 3 /* Don't unlock the pages on completion */ > +#define NETFS_RREQ_NO_UNLOCK_FOLIO 2 /* Don't unlock no_unlock_folio on completion */ > +#define NETFS_RREQ_DONT_UNLOCK_FOLIOS 3 /* Don't unlock the folios on completion */ > #define NETFS_RREQ_FAILED 4 /* The request failed */ > #define NETFS_RREQ_IN_PROGRESS 5 /* Unlocked when the request completes */ > const struct netfs_read_request_ops *netfs_ops; > @@ -190,7 +190,7 @@ struct netfs_read_request_ops { > void (*issue_op)(struct netfs_read_subrequest *subreq); > bool (*is_still_valid)(struct netfs_read_request *rreq); > int (*check_write_begin)(struct file *file, loff_t pos, unsigned len, > - struct page *page, void **_fsdata); > + struct folio *folio, void **_fsdata); > void (*done)(struct netfs_read_request *rreq); > void (*cleanup)(struct address_space *mapping, void *netfs_priv); > }; > @@ -240,11 +240,11 @@ extern void netfs_readahead(struct readahead_control *, > const struct netfs_read_request_ops *, > void *); > extern int netfs_readpage(struct file *, > - struct page *, > + struct folio *, > const struct netfs_read_request_ops *, > void *); > extern int netfs_write_begin(struct file *, struct address_space *, > - loff_t, unsigned int, unsigned int, struct page **, > + loff_t, unsigned int, unsigned int, struct folio **, > void **, > const struct netfs_read_request_ops *, > void *); > diff --git a/include/trace/events/afs.h b/include/trace/events/afs.h > index 3ccf591b2374..d3d8abf3f8df 100644 > --- a/include/trace/events/afs.h > +++ b/include/trace/events/afs.h > @@ -955,31 +955,32 @@ TRACE_EVENT(afs_dir_check_failed, > __entry->vnode, __entry->off, __entry->i_size) > ); > > -TRACE_EVENT(afs_page_dirty, > - TP_PROTO(struct afs_vnode *vnode, const char *where, struct page *page), > +TRACE_EVENT(afs_folio_dirty, > + TP_PROTO(struct afs_vnode *vnode, const char *where, struct folio *folio), > > - TP_ARGS(vnode, where, page), > + TP_ARGS(vnode, where, folio), > > TP_STRUCT__entry( > __field(struct afs_vnode *, vnode ) > __field(const char *, where ) > - __field(pgoff_t, page ) > + __field(pgoff_t, index ) > __field(unsigned long, from ) > __field(unsigned long, to ) > ), > > TP_fast_assign( > + unsigned long priv = (unsigned long)folio_get_private(folio); > __entry->vnode = vnode; > __entry->where = where; > - __entry->page = page->index; > - __entry->from = afs_page_dirty_from(page, page->private); > - __entry->to = afs_page_dirty_to(page, page->private); > - __entry->to |= (afs_is_page_dirty_mmapped(page->private) ? > - (1UL << (BITS_PER_LONG - 1)) : 0); > + __entry->index = folio_index(folio); > + __entry->from = afs_folio_dirty_from(folio, priv); > + __entry->to = afs_folio_dirty_to(folio, priv); > + __entry->to |= (afs_is_folio_dirty_mmapped(priv) ? > + (1UL << (BITS_PER_LONG - 1)) : 0); > ), > > TP_printk("vn=%p %lx %s %lx-%lx%s", > - __entry->vnode, __entry->page, __entry->where, > + __entry->vnode, __entry->index, __entry->where, > __entry->from, > __entry->to & ~(1UL << (BITS_PER_LONG - 1)), > __entry->to & (1UL << (BITS_PER_LONG - 1)) ? " M" : "") > > The conversion looks fairly straightforward substituting folios for pages in this code. You can add this for the ceph and netfs parts (I'm less well-versed in the AFS bits): Reviewed-by: Jeff Layton <jlayton@xxxxxxxxxx> FWIW, I've also done some testing with a pile that contained this series and it seems to behave as expected. -- Linux-cachefs mailing list Linux-cachefs@xxxxxxxxxx https://listman.redhat.com/mailman/listinfo/linux-cachefs