Commit 6072d13c authored by Linus Torvalds's avatar Linus Torvalds Committed by Trond Myklebust

Call the filesystem back whenever a page is removed from the page cache

NFS needs to be able to release objects that are stored in the page
cache once the page itself is no longer visible from the page cache.

This patch adds a callback to the address space operations that allows
filesystems to perform page cleanups once the page has been removed
from the page cache.

Original patch by: Linus Torvalds <torvalds@linux-foundation.org>
[trondmy: cover the cases of invalidate_inode_pages2() and
          truncate_inode_pages()]
Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
parent 0aded708
...@@ -173,12 +173,13 @@ prototypes: ...@@ -173,12 +173,13 @@ prototypes:
sector_t (*bmap)(struct address_space *, sector_t); sector_t (*bmap)(struct address_space *, sector_t);
int (*invalidatepage) (struct page *, unsigned long); int (*invalidatepage) (struct page *, unsigned long);
int (*releasepage) (struct page *, int); int (*releasepage) (struct page *, int);
void (*freepage)(struct page *);
int (*direct_IO)(int, struct kiocb *, const struct iovec *iov, int (*direct_IO)(int, struct kiocb *, const struct iovec *iov,
loff_t offset, unsigned long nr_segs); loff_t offset, unsigned long nr_segs);
int (*launder_page) (struct page *); int (*launder_page) (struct page *);
locking rules: locking rules:
All except set_page_dirty may block All except set_page_dirty and freepage may block
BKL PageLocked(page) i_mutex BKL PageLocked(page) i_mutex
writepage: no yes, unlocks (see below) writepage: no yes, unlocks (see below)
...@@ -193,6 +194,7 @@ perform_write: no n/a yes ...@@ -193,6 +194,7 @@ perform_write: no n/a yes
bmap: no bmap: no
invalidatepage: no yes invalidatepage: no yes
releasepage: no yes releasepage: no yes
freepage: no yes
direct_IO: no direct_IO: no
launder_page: no yes launder_page: no yes
...@@ -288,6 +290,9 @@ buffers from the page in preparation for freeing it. It returns zero to ...@@ -288,6 +290,9 @@ buffers from the page in preparation for freeing it. It returns zero to
indicate that the buffers are (or may be) freeable. If ->releasepage is zero, indicate that the buffers are (or may be) freeable. If ->releasepage is zero,
the kernel assumes that the fs has no private interest in the buffers. the kernel assumes that the fs has no private interest in the buffers.
->freepage() is called when the kernel is done dropping the page
from the page cache.
->launder_page() may be called prior to releasing a page if ->launder_page() may be called prior to releasing a page if
it is still found to be dirty. It returns zero if the page was successfully it is still found to be dirty. It returns zero if the page was successfully
cleaned, or an error value if not. Note that in order to prevent the page cleaned, or an error value if not. Note that in order to prevent the page
......
...@@ -534,6 +534,7 @@ struct address_space_operations { ...@@ -534,6 +534,7 @@ struct address_space_operations {
sector_t (*bmap)(struct address_space *, sector_t); sector_t (*bmap)(struct address_space *, sector_t);
int (*invalidatepage) (struct page *, unsigned long); int (*invalidatepage) (struct page *, unsigned long);
int (*releasepage) (struct page *, int); int (*releasepage) (struct page *, int);
void (*freepage)(struct page *);
ssize_t (*direct_IO)(int, struct kiocb *, const struct iovec *iov, ssize_t (*direct_IO)(int, struct kiocb *, const struct iovec *iov,
loff_t offset, unsigned long nr_segs); loff_t offset, unsigned long nr_segs);
struct page* (*get_xip_page)(struct address_space *, sector_t, struct page* (*get_xip_page)(struct address_space *, sector_t,
...@@ -679,6 +680,12 @@ struct address_space_operations { ...@@ -679,6 +680,12 @@ struct address_space_operations {
need to ensure this. Possibly it can clear the PageUptodate need to ensure this. Possibly it can clear the PageUptodate
bit if it cannot free private data yet. bit if it cannot free private data yet.
freepage: freepage is called once the page is no longer visible in
the page cache in order to allow the cleanup of any private
data. Since it may be called by the memory reclaimer, it
should not assume that the original address_space mapping still
exists, and it should not block.
direct_IO: called by the generic read/write routines to perform direct_IO: called by the generic read/write routines to perform
direct_IO - that is IO requests which bypass the page cache direct_IO - that is IO requests which bypass the page cache
and transfer data directly between the storage and the and transfer data directly between the storage and the
......
...@@ -602,6 +602,7 @@ struct address_space_operations { ...@@ -602,6 +602,7 @@ struct address_space_operations {
sector_t (*bmap)(struct address_space *, sector_t); sector_t (*bmap)(struct address_space *, sector_t);
void (*invalidatepage) (struct page *, unsigned long); void (*invalidatepage) (struct page *, unsigned long);
int (*releasepage) (struct page *, gfp_t); int (*releasepage) (struct page *, gfp_t);
void (*freepage)(struct page *);
ssize_t (*direct_IO)(int, struct kiocb *, const struct iovec *iov, ssize_t (*direct_IO)(int, struct kiocb *, const struct iovec *iov,
loff_t offset, unsigned long nr_segs); loff_t offset, unsigned long nr_segs);
int (*get_xip_mem)(struct address_space *, pgoff_t, int, int (*get_xip_mem)(struct address_space *, pgoff_t, int,
......
...@@ -143,13 +143,18 @@ void __remove_from_page_cache(struct page *page) ...@@ -143,13 +143,18 @@ void __remove_from_page_cache(struct page *page)
void remove_from_page_cache(struct page *page) void remove_from_page_cache(struct page *page)
{ {
struct address_space *mapping = page->mapping; struct address_space *mapping = page->mapping;
void (*freepage)(struct page *);
BUG_ON(!PageLocked(page)); BUG_ON(!PageLocked(page));
freepage = mapping->a_ops->freepage;
spin_lock_irq(&mapping->tree_lock); spin_lock_irq(&mapping->tree_lock);
__remove_from_page_cache(page); __remove_from_page_cache(page);
spin_unlock_irq(&mapping->tree_lock); spin_unlock_irq(&mapping->tree_lock);
mem_cgroup_uncharge_cache_page(page); mem_cgroup_uncharge_cache_page(page);
if (freepage)
freepage(page);
} }
EXPORT_SYMBOL(remove_from_page_cache); EXPORT_SYMBOL(remove_from_page_cache);
......
...@@ -390,6 +390,10 @@ invalidate_complete_page2(struct address_space *mapping, struct page *page) ...@@ -390,6 +390,10 @@ invalidate_complete_page2(struct address_space *mapping, struct page *page)
__remove_from_page_cache(page); __remove_from_page_cache(page);
spin_unlock_irq(&mapping->tree_lock); spin_unlock_irq(&mapping->tree_lock);
mem_cgroup_uncharge_cache_page(page); mem_cgroup_uncharge_cache_page(page);
if (mapping->a_ops->freepage)
mapping->a_ops->freepage(page);
page_cache_release(page); /* pagecache ref */ page_cache_release(page); /* pagecache ref */
return 1; return 1;
failed: failed:
......
...@@ -494,9 +494,16 @@ static int __remove_mapping(struct address_space *mapping, struct page *page) ...@@ -494,9 +494,16 @@ static int __remove_mapping(struct address_space *mapping, struct page *page)
spin_unlock_irq(&mapping->tree_lock); spin_unlock_irq(&mapping->tree_lock);
swapcache_free(swap, page); swapcache_free(swap, page);
} else { } else {
void (*freepage)(struct page *);
freepage = mapping->a_ops->freepage;
__remove_from_page_cache(page); __remove_from_page_cache(page);
spin_unlock_irq(&mapping->tree_lock); spin_unlock_irq(&mapping->tree_lock);
mem_cgroup_uncharge_cache_page(page); mem_cgroup_uncharge_cache_page(page);
if (freepage != NULL)
freepage(page);
} }
return 1; return 1;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment