Gitweb: http://git.kernel.org/linus/ea90002b0fa7bdee86ec22eba1d951f30bf043a6
Commit: ea90002b0fa7bdee86ec22eba1d951f30bf043a6
Parent: 646d87b481dab4ba8301716600dfd276605b0ab0
Author: Linus Torvalds <torvalds@linux-foundation.org>
AuthorDate: Mon Apr 12 12:44:29 2010 -0700
Committer: Linus Torvalds <torvalds@linux-foundation.org>
CommitDate: Mon Apr 12 17:54:13 2010 -0700
anonvma: when setting up page->mapping, we need to pick the _oldest_ anonvma
Otherwise we might be mapping in a page in a new mapping, but that page
(through the swapcache) would later be mapped into an old mapping too.
The page->mapping must be the case that works for everybody, not just
the mapping that happened to page it in first.
Here's the scenario:
- page gets allocated/mapped by process A. Let's call the anon_vma we
associate the page with 'A' to keep it easy to track.
- Process A forks, creating process B. The anon_vma in B is 'B', and has
a chain that looks like 'B' -> 'A'. Everything is fine.
- Swapping happens. The page (with mapping pointing to 'A') gets swapped
out (perhaps not to disk - it's enough to assume that it's just not
mapped any more, and lives entirely in the swap-cache)
- Process B pages it in, which goes like this:
do_swap_page ->
page = lookup_swap_cache(entry);
...
set_pte_at(mm, address, page_table, pte);
page_add_anon_rmap(page, vma, address);
And think about what happens here!
In particular, what happens is that this will now be the "first"
mapping of that page, so page_add_anon_rmap() used to do
if (first)
__page_set_anon_rmap(page, vma, address);
and notice what anon_vma it will use? It will use the anon_vma for
process B!
What happens then? Trivial: process 'A' also pages it in ...