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