diff options
author | Hugh Dickins <hugh.dickins@tiscali.co.uk> | 2009-12-14 20:59:29 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-12-15 11:53:19 -0500 |
commit | 80e148226028257ec0a1909d99b2c40d0ffe17f2 (patch) | |
tree | 1e3cae42144f5c80e215ba254e01bd6847ba1b36 /mm/rmap.c | |
parent | 4035c07a895974d0ac06a56fe870ad293fc451a7 (diff) |
ksm: share anon page without allocating
When ksm pages were unswappable, it made no sense to include them in mem
cgroup accounting; but now that they are swappable (although I see no
strict logical connection) the principle of least surprise implies that
they should be accounted (with the usual dissatisfaction, that a shared
page is accounted to only one of the cgroups using it).
This patch was intended to add mem cgroup accounting where necessary; but
turned inside out, it now avoids allocating a ksm page, instead upgrading
an anon page to ksm - which brings its existing mem cgroup accounting with
it. Thus mem cgroups don't appear in the patch at all.
This upgrade from PageAnon to PageKsm takes place under page lock (via a
somewhat hacky NULL kpage interface), and audit showed only one place
which needed to cope with the race - page_referenced() is sometimes used
without page lock, so page_lock_anon_vma() needs an ACCESS_ONCE() to be
sure of getting anon_vma and flags together (no problem if the page goes
ksm an instant after, the integrity of that anon_vma list is unaffected).
Signed-off-by: Hugh Dickins <hugh.dickins@tiscali.co.uk>
Cc: Izik Eidus <ieidus@redhat.com>
Cc: Andrea Arcangeli <aarcange@redhat.com>
Cc: Chris Wright <chrisw@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/rmap.c')
-rw-r--r-- | mm/rmap.c | 6 |
1 files changed, 4 insertions, 2 deletions
@@ -204,7 +204,7 @@ struct anon_vma *page_lock_anon_vma(struct page *page) | |||
204 | unsigned long anon_mapping; | 204 | unsigned long anon_mapping; |
205 | 205 | ||
206 | rcu_read_lock(); | 206 | rcu_read_lock(); |
207 | anon_mapping = (unsigned long) page->mapping; | 207 | anon_mapping = (unsigned long) ACCESS_ONCE(page->mapping); |
208 | if ((anon_mapping & PAGE_MAPPING_FLAGS) != PAGE_MAPPING_ANON) | 208 | if ((anon_mapping & PAGE_MAPPING_FLAGS) != PAGE_MAPPING_ANON) |
209 | goto out; | 209 | goto out; |
210 | if (!page_mapped(page)) | 210 | if (!page_mapped(page)) |
@@ -666,7 +666,9 @@ static void __page_check_anon_rmap(struct page *page, | |||
666 | * @address: the user virtual address mapped | 666 | * @address: the user virtual address mapped |
667 | * | 667 | * |
668 | * The caller needs to hold the pte lock, and the page must be locked in | 668 | * The caller needs to hold the pte lock, and the page must be locked in |
669 | * the anon_vma case: to serialize mapping,index checking after setting. | 669 | * the anon_vma case: to serialize mapping,index checking after setting, |
670 | * and to ensure that PageAnon is not being upgraded racily to PageKsm | ||
671 | * (but PageKsm is never downgraded to PageAnon). | ||
670 | */ | 672 | */ |
671 | void page_add_anon_rmap(struct page *page, | 673 | void page_add_anon_rmap(struct page *page, |
672 | struct vm_area_struct *vma, unsigned long address) | 674 | struct vm_area_struct *vma, unsigned long address) |