Re: [PATCH v6 05/26] fs/dax: Create a common implementation to break DAX layouts

Dan Williams
2025-01-13T20:11:56Z

Alistair Popple wrote:
> Prior to freeing a block file systems supporting FS DAX must check
> that the associated pages are both unmapped from user-space and not
> undergoing DMA or other access from eg. get_user_pages(). This is
> achieved by unmapping the file range and scanning the FS DAX
> page-cache to see if any pages within the mapping have an elevated
> refcount.
>
> This is done using two functions - dax_layout_busy_page_range() which
> returns a page to wait for the refcount to become idle on. Rather than
> open-code this introduce a common implementation to both unmap and
> wait for the page to become idle.
>
> Signed-off-by: Alistair Popple <apopple@nvidia.com>

After resolving my confusion about retries, you can add:

Reviewed-by: Dan Williams <dan.j.williams@intel.com>

...although some bikeshedding below that can take or leave as you wish.

>
> ---
>
> Changes for v5:
>
> - Don't wait for idle pages on non-DAX mappings
>
> Changes for v4:
>
> - Fixed some build breakage due to missing symbol exports reported by
> John Hubbard (thanks!).
> ---
> fs/dax.c | 33 +++++++++++++++++++++++++++++++++
> fs/ext4/inode.c | 10 +---------
> fs/fuse/dax.c | 27 +++------------------------
> fs/xfs/xfs_inode.c | 23 +++++------------------
> fs/xfs/xfs_inode.h | 2 +-
> include/linux/dax.h | 21 +++++++++++++++++++++
> mm/madvise.c | 8 ++++----
> 7 files changed, 68 insertions(+), 56 deletions(-)
>
> diff --git a/fs/dax.c b/fs/dax.c
> index d010c10..9c3bd07 100644
> --- a/fs/dax.c
> +++ b/fs/dax.c
> @@ -845,6 +845,39 @@ int dax_delete_mapping_entry(struct address_space *mapping, pgoff_t index)
> return ret;
> }
>
> +static int wait_page_idle(struct page *page,
> + void (cb)(struct inode *),
> + struct inode *inode)
> +{
> + return ___wait_var_event(page, page_ref_count(page) == 1,
> + TASK_INTERRUPTIBLE, 0, 0, cb(inode));
> +}
> +
> +/*
> + * Unmaps the inode and waits for any DMA to complete prior to deleting the
> + * DAX mapping entries for the range.
> + */
> +int dax_break_mapping(struct inode *inode, loff_t start, loff_t end,
> + void (cb)(struct inode *))
> +{
> + struct page *page;
> + int error;
> +
> + if (!dax_mapping(inode->i_mapping))
> + return 0;
> +
> + do {
> + page = dax_layout_busy_page_range(inode->i_mapping, start, end);
> + if (!page)
> + break;
> +
> + error = wait_page_idle(page, cb, inode);
> + } while (error == 0);
> +
> + return error;
> +}
> +EXPORT_SYMBOL_GPL(dax_break_mapping);

It is not clear why this is called "mapping" vs "layout". The detail
about the file that is being "broken" is whether there are any live
subscriptions to the "layout" of the file, the pfn storage layout, not
the memory mapping.

For example the bulk of dax_break_layout() is performed after
invalidate_inode_pages() has torn down the memory mapping.