Commit 820ef9df authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] handle pages which alter their ->mapping

Patch from Hugh Dickins <hugh@veritas.com>

tmpfs failed fsx+swapout tests after many hours, a page found zeroed.
Not a truncate problem, but mirror image of earlier truncate problems:
swap goes through mpage_writepages, which must therefore allow for a
sudden swizzle back to file identity.

Second time this caught us, so I've audited the tree for other places
which might be surprised by such swizzling.  The only others I found
were (perhaps) in the parisc and sparc64 flush_dcache_page called
from do_generic_mapping_read on a looped tmpfs file which is also
mmapped; but that's a very marginal case, I wanted to understand it
better before making any edit, and now realize that hch's sendfile
in loop eliminates it (now go through do_shmem_file_read instead:
similar but crucially this locks the page when raising its count,
which is enough to keep vmscan from interfering).
parent 77fe2d67
...@@ -587,12 +587,19 @@ mpage_writepages(struct address_space *mapping, ...@@ -587,12 +587,19 @@ mpage_writepages(struct address_space *mapping,
page_cache_get(page); page_cache_get(page);
write_unlock(&mapping->page_lock); write_unlock(&mapping->page_lock);
/*
* At this point we hold neither mapping->page_lock nor
* lock on the page itself: the page may be truncated or
* invalidated (changing page->mapping to NULL), or even
* swizzled back from swapper_space to tmpfs file mapping.
*/
lock_page(page); lock_page(page);
if (sync) if (sync)
wait_on_page_writeback(page); wait_on_page_writeback(page);
if (page->mapping && !PageWriteback(page) && if (page->mapping == mapping && !PageWriteback(page) &&
test_clear_page_dirty(page)) { test_clear_page_dirty(page)) {
if (writepage) { if (writepage) {
ret = (*writepage)(page); ret = (*writepage)(page);
......
...@@ -613,6 +613,7 @@ int __set_page_dirty_nobuffers(struct page *page) ...@@ -613,6 +613,7 @@ int __set_page_dirty_nobuffers(struct page *page)
if (mapping) { if (mapping) {
write_lock(&mapping->page_lock); write_lock(&mapping->page_lock);
if (page->mapping) { /* Race with truncate? */ if (page->mapping) { /* Race with truncate? */
BUG_ON(page->mapping != mapping);
if (!mapping->backing_dev_info->memory_backed) if (!mapping->backing_dev_info->memory_backed)
inc_page_state(nr_dirty); inc_page_state(nr_dirty);
list_del(&page->list); list_del(&page->list);
......
...@@ -30,6 +30,7 @@ get_swap_bio(int gfp_flags, struct page *page, bio_end_io_t end_io) ...@@ -30,6 +30,7 @@ get_swap_bio(int gfp_flags, struct page *page, bio_end_io_t end_io)
struct swap_info_struct *sis; struct swap_info_struct *sis;
swp_entry_t entry; swp_entry_t entry;
BUG_ON(!PageSwapCache(page));
entry.val = page->index; entry.val = page->index;
sis = get_swap_info_struct(swp_type(entry)); sis = get_swap_info_struct(swp_type(entry));
......
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