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