Re: Function to do end-writeback in bulk
Matthew Wilcox <[email protected]>
| Newsgroups | dev.linux.lists.netfs |
|---|---|
| Message-ID | <[email protected]> |
On Mon, Aug 24, 2026 at 04:09:20PM +0100, David Howells wrote:
> Can you take a look at the function below? This is what I've come up with for
> the moment for a basic bulk end-writeback (and I'll need something similar for
> bulk unlock). At the moment, it returns true/false depending on whether it
> made progress, but I'm thinking that it might be better if it returns the
> remaining size of any folio that partially overlaps the end position and so
> didn't get unlocked. This would could allow me to set a minimum amount to
> accrue before I call it again. Whether progress was made can also be
> determined by comparing @from before and after.
>
> Thanks,
> David
> ---
> /**
> * folio_end_writeback_range - End writeback for the folios within the range
> * @mapping: The pagecache to modify
> * @from: Pointer to the starting position (updated)
> * @to: The end position (exclusive)
> * @cleaner_func: Function to clean up the folios in the range
> * @cleaner_priv: Private data for the cleaner func
I hate this API. I much prefer the _iter() style:
struct end_writeback_ctrl {
struct xa_state xas;
uoff_t fend;
};
struct folio *end_writeback_iter(struct address_space *mapping,
uoff_t from, uoff_t to,
struct end_writeback_ctrl *ctrl, struct folio *folio)
{
if (!folio) {
ctrl->xas = __XA_STATE(&mapping->i_pages, from / PAGE_SIZE,
0, 0);
folio = xas_find(&ctrl->xas, to / PAGE_SIZE);
} else {
retry:
folio_end_writeback(folio);
folio = xas_next_entry(&ctrl->xas, to / PAGE_SIZE);
}
if (xas_retry(&xas, folio))
goto retry;
ctrl->fend = folio_next_pos(folio);
if (ctrl->fend > to)
folio = NULL;
return folio;
}
(you can embed the rcu_read_lock() / unlock in here too, but probably
better to do it in the caller)
> * Unlock folios that are entirely within in the given range, where @from is
> * included in the range, but @to is excluded from the range.
> *
> * Return: True if at least one folio got cleaned, false otherwise. @from will
> * be updated to point past the last folio cleaned.
> */
> static inline
> bool folio_end_writeback_range(struct address_space *mapping,
> uoff_t *from, uoff_t to,
> void (*cleaner_func)(struct folio *folio,
> void *cleaner_priv),
> void *cleaner_priv)
> {
> struct folio *folio;
> XA_STATE(xas, &mapping->i_pages, *from / PAGE_SIZE);
> bool cleaned = false;
>
> rcu_read_lock();
> xas_for_each(&xas, folio, (to - 1) / PAGE_SIZE) {
> uoff_t fend;
>
> if (xas_retry(&xas, folio))
> continue;
>
> fend = folio_next_pos(folio);
> if (fend > to)
> break;
>
> cleaner_func(folio, cleaner_priv);
> folio_end_writeback(folio);
> *from = fend;
> cleaned = true;
> }
> rcu_read_unlock();
> return cleaned;
> }
>