reiser4 keeps its meta-data pages in the page cache, attached to a special
"fake" inode. Meta-data blocks have "znodes" attached to them (reiser4 analog
of buffer_head) and initially don't have real disk block numbers assigned.
Later meta-data blocks can be "relocated" to decrease fragmentation. As a
result, their pages cannot be easily indexed by block number. Instead reiser4
indexes pages of fake inode by some function of znode address. This looks
weird, but it works. The only problem is that there is a race involving
->releasepage(): there is a window when znode has already been freed by
reiser4_releasepage(), but its page still exists (albeit locked). If at this
moment another znode is allocated at the same address as one just destroyed,
then some other thread can acquire a reference to lingering page (because it
is indexed by address of znode), and prevent shrink_list() from freeing it.
To avoid this, reiser4_releasepage() removes page from radix-tree manually.
This requires re-checking page->mapping after calling try_to_release_page().
Signed-off-by: Andrew Morton <
[email protected]>
---
diff -puN mm/truncate.c~reiser4-reget-page-mapping mm/truncate.c
mm/truncate.c | 7 +++++++
mm/vmscan.c | 5 +++++
2 files changed, 12 insertions(+)
diff -puN mm/truncate.c~reiser4-reget-page-mapping mm/truncate.c
--- linux-2.6.11-rc3-mm2/mm/truncate.c~reiser4-reget-page-mapping 2005-02-17 14:36:51.000000000 +0300
+++ linux-2.6.11-rc3-mm2-vs/mm/truncate.c 2005-02-17 14:36:51.000000000 +0300
@@ -76,6 +76,13 @@ invalidate_complete_page(struct address_
if (PagePrivate(page) && !try_to_release_page(page, 0))
return 0;
+ /*
+ * file system may manually remove page from the page
+ * cache in ->releasepage(). Check for this.
+ */
+ if (page->mapping != mapping)
+ return 0;
+
write_lock_irq(&mapping->tree_lock);
if (PageDirty(page)) {
write_unlock_irq(&mapping->tree_lock);
diff -puN mm/vmscan.c~reiser4-reget-page-mapping mm/vmscan.c
--- linux-2.6.11-rc3-mm2/mm/vmscan.c~reiser4-reget-page-mapping 2005-02-17 14:36:51.000000000 +0300
+++ linux-2.6.11-rc3-mm2-vs/mm/vmscan.c 2005-02-17 14:36:51.000000000 +0300
@@ -480,6 +480,11 @@ static int shrink_list(struct list_head
if (PagePrivate(page)) {
if (!try_to_release_page(page, sc->gfp_mask))
goto activate_locked;
+ /*
+ * file system may manually remove page from the page
+ * cache in ->releasepage(). Check for this.
+ */
+ mapping = page_mapping(page);
if (!mapping && page_count(page) == 1)
goto free_it;
}
_