From: Hans Reiser 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 --- 25-akpm/mm/truncate.c | 7 +++++++ 25-akpm/mm/vmscan.c | 5 +++++ 2 files changed, 12 insertions(+) diff -puN mm/truncate.c~reiser4-reget-page-mapping mm/truncate.c --- 25/mm/truncate.c~reiser4-reget-page-mapping 2004-12-27 00:30:54.432455568 -0800 +++ 25-akpm/mm/truncate.c 2004-12-27 00:30:54.438454656 -0800 @@ -75,6 +75,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 --- 25/mm/vmscan.c~reiser4-reget-page-mapping 2004-12-27 00:30:54.434455264 -0800 +++ 25-akpm/mm/vmscan.c 2004-12-27 00:30:54.439454504 -0800 @@ -470,6 +470,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; } _