[PATCH RFC 07/14] fs/erofs: mm/pagemap: add readahead_folio_reverse() to avoid folio->private
Zi Yan <[email protected]> Fri, 31 Jul 2026 22:13:30 -0400
| Newsgroups | org.ozlabs.lists.linux-erofs,org.kernel.vger.linux-fsdevel,org.kernel.vger.linux-kernel,org.kvack.linux-mm |
|---|---|
| Message-ID | <[email protected]> |
erofs needs to traverse readahead folios in reverse order to achieve maximum performance by 1. reading all folios from readahead_folio(); 2. storing the prior folio pointer in folio->private; 3. traverse from the last folio to the first one. Add readahead_folio_reverse() to achieve the same function without using folio->private. It prepares for a future commit that replaces PG_private checks with !folio->private checks. After switching the checks, erofs's use of folio->private without bumping folio refcount can cause unexpected outcomes, e.g., in filemap_release_folio(), try_to_free_buffers() becomes reachable. No funtional change intended. Assisted-by: Claude:claude-opus-4-8 Assisted-by: Codex:gpt-5 Signed-off-by: Zi Yan <[email protected]> To: Gao Xiang <[email protected]> To: Chao Yu <[email protected]> To: "Matthew Wilcox (Oracle)" <[email protected]> To: Jan Kara <[email protected]> Cc: Yue Hu <[email protected]> Cc: Jeffle Xu <[email protected]> Cc: Sandeep Dhavale <[email protected]> Cc: Hongbo Li <[email protected]> Cc: Chunhai Guo <[email protected]> Cc: [email protected] Cc: [email protected] Cc: [email protected] Cc: [email protected] --- fs/erofs/zdata.c | 11 ++--------- include/linux/pagemap.h | 31 +++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c index 74520e9102596..b59f2745a8e72 100644 --- a/fs/erofs/zdata.c +++ b/fs/erofs/zdata.c @@ -1902,21 +1902,14 @@ static void z_erofs_readahead(struct readahead_control *rac) struct inode *realinode = erofs_real_inode(sharedinode, &need_iput); Z_EROFS_DEFINE_FRONTEND(f, realinode, sharedinode, readahead_pos(rac)); unsigned int nrpages = readahead_count(rac); - struct folio *head = NULL, *folio; + struct folio *folio; int err; trace_erofs_readahead(realinode, readahead_index(rac), nrpages, false); z_erofs_pcluster_readmore(&f, rac, true); - while ((folio = readahead_folio(rac))) { - folio->private = head; - head = folio; - } /* traverse in reverse order for best metadata I/O performance */ - while (head) { - folio = head; - head = folio_get_private(folio); - + while ((folio = readahead_folio_reverse(rac))) { err = z_erofs_scan_folio(&f, folio, true); if (err && err != -EINTR) erofs_err(realinode->i_sb, "readahead error at folio %lu @ nid %llu", diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index 4e8b2b29f6d3e..90904a4d173b7 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -1549,6 +1549,37 @@ static inline struct folio *readahead_folio(struct readahead_control *ractl) return folio; } +/** + * readahead_folio_reverse - Get the next folio to read, from the tail. + * @ractl: The current readahead request. + * + * Like readahead_folio(), but walks the range back-to-front. The folio is + * returned locked with its refcount dropped; the caller unlocks it once I/O + * completes. Compound folios are returned once, at their head index. + * + * Context: The folio is locked. + * Return: A pointer to the next folio, or %NULL when done. + */ +static inline struct folio *readahead_folio_reverse(struct readahead_control *ractl) +{ + struct folio *folio; + + if (!ractl->_nr_pages) + return NULL; + + /* xa_load() follows sibling entries, so a tail index returns the head */ + folio = xa_load(&ractl->mapping->i_pages, + ractl->_index + ractl->_nr_pages - 1); + VM_WARN_ON_ONCE_FOLIO(!folio_test_locked(folio), folio); + + /* Shrink the window from the tail down to this folio's head index */ + ractl->_nr_pages = folio->index - ractl->_index; + ractl->_batch_count = 0; + + folio_put(folio); + return folio; +} + static inline unsigned int __readahead_batch(struct readahead_control *rac, struct page **array, unsigned int array_sz) { -- 2.53.0