Add .unaccount_folio callback to allow filesystems to do accounting-related
updates to the inode or struct address_space mapping, when the folio is
about to be removed from the filemap/page_cache.

.free_folio cannot be used since .free_folio cannot assume that struct
address_space mapping still exists.

>From the name, .invalidate_folio and .release_folio seem suitable, but
those are meant only to handle freeing of a folio's private
data. .release_folio is also not called in the truncation path.

An alternative would be to add a more general callback and call that from
filemap_remove_folio() and delete_from_page_cache_batch(). .unaccount_folio
was chosen as it is more specific to the how guest_memfd will be using this
callback in later patches. Also, .unaccount_folio only needs a single call
site.

This further refactoring was considered:

if (mapping->a_ops->unaccount_folio &&
    mapping->a_ops->unaccount_folio(folio))
        ... do generic page_cache unaccounting ...

but that was abandoned since a hugetlb folio may not have an associated
mapping.

Signed-off-by: Ackerley Tng <[email protected]>
---
 Documentation/filesystems/vfs.rst | 8 ++++++++
 include/linux/fs.h                | 1 +
 mm/filemap.c                      | 3 +++
 3 files changed, 12 insertions(+)

diff --git a/Documentation/filesystems/vfs.rst 
b/Documentation/filesystems/vfs.rst
index 670ba66b60e49..5ed5c43d5768b 100644
--- a/Documentation/filesystems/vfs.rst
+++ b/Documentation/filesystems/vfs.rst
@@ -809,6 +809,7 @@ cache in your filesystem.  The following members are 
defined:
                sector_t (*bmap)(struct address_space *, sector_t);
                void (*invalidate_folio) (struct folio *, size_t start, size_t 
len);
                bool (*release_folio)(struct folio *, gfp_t);
+               void (*unaccount_folio)(struct folio *folio);
                void (*free_folio)(struct folio *);
                ssize_t (*direct_IO)(struct kiocb *, struct iov_iter *iter);
                int (*migrate_folio)(struct mapping *, struct folio *dst,
@@ -967,6 +968,13 @@ cache in your filesystem.  The following members are 
defined:
        its release_folio will need to ensure this.  Possibly it can
        clear the uptodate flag if it cannot free private data yet.
 
+``unaccount_folio``
+       unaccount_folio is called under inode lock and struct
+       address_space's xa_lock, just before the folio is removed from
+       the page cache in order to allow updating any kind of
+       accounting on the inode or address_space mapping while the
+       address_space mapping still exists.
+
 ``free_folio``
        free_folio is called once the folio is no longer visible in the
        page cache in order to allow the cleanup of any private data.
diff --git a/include/linux/fs.h b/include/linux/fs.h
index a01621fa636a6..c71f327032142 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -422,6 +422,7 @@ struct address_space_operations {
        sector_t (*bmap)(struct address_space *, sector_t);
        void (*invalidate_folio) (struct folio *, size_t offset, size_t len);
        bool (*release_folio)(struct folio *, gfp_t);
+       void (*unaccount_folio)(struct folio *folio);
        void (*free_folio)(struct folio *folio);
        ssize_t (*direct_IO)(struct kiocb *, struct iov_iter *iter);
        /*
diff --git a/mm/filemap.c b/mm/filemap.c
index ebd75684cb0a7..ff957929e6087 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -176,6 +176,9 @@ static void filemap_unaccount_folio(struct address_space 
*mapping,
                }
        }
 
+       if (unlikely(mapping->a_ops->unaccount_folio))
+               mapping->a_ops->unaccount_folio(folio);
+
        /* hugetlb folios do not participate in page cache accounting. */
        if (folio_test_hugetlb(folio))
                return;

-- 
2.53.0.414.gf7e9f6c205-goog


Reply via email to