On Fri 31-07-26 22:13:30, Zi Yan wrote:
> 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]

One comment regarding the generic infrastructure below.

> 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;

Thanks for the patch! Currently there's the invariant that the returned
folio is still inside the _index .. _index+_nr_pages range. I think when we
are providing a generic helper, we should keep that to make code more
robust for the future when more people start using it.

What I'd suggest doing is add bool in struct readahead_control telling
whether the last folio (batch) was taken from the head or tail of the
range, advance _nr_pages and _index accordingly in the functions returning
folios (probably hide this in a helper function __readahead_advance()
because it will be used in 3 places) and maybe call this new function
readahead_folio_last() instead of _reverse() (but I have only a slight
preference here so .._reverse() is ok with me if other people prefer it).

                                                                Honza
-- 
Jan Kara <[email protected]>
SUSE Labs, CR

Reply via email to