diff options
author | Michel Lespinasse <walken@google.com> | 2012-10-08 19:31:39 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-09 03:22:41 -0400 |
commit | bf181b9f9d8dfbba58b23441ad60d0bc33806d64 (patch) | |
tree | 7ad0caaf8998f31c5d910dcbb768f5a1d381b5f4 /include/linux/mm.h | |
parent | 108d6642ad81bb1d62b401490a334d2c12397517 (diff) |
mm anon rmap: replace same_anon_vma linked list with an interval tree.
When a large VMA (anon or private file mapping) is first touched, which
will populate its anon_vma field, and then split into many regions through
the use of mprotect(), the original anon_vma ends up linking all of the
vmas on a linked list. This can cause rmap to become inefficient, as we
have to walk potentially thousands of irrelevent vmas before finding the
one a given anon page might fall into.
By replacing the same_anon_vma linked list with an interval tree (where
each avc's interval is determined by its vma's start and last pgoffs), we
can make rmap efficient for this use case again.
While the change is large, all of its pieces are fairly simple.
Most places that were walking the same_anon_vma list were looking for a
known pgoff, so they can just use the anon_vma_interval_tree_foreach()
interval tree iterator instead. The exception here is ksm, where the
page's index is not known. It would probably be possible to rework ksm so
that the index would be known, but for now I have decided to keep things
simple and just walk the entirety of the interval tree there.
When updating vma's that already have an anon_vma assigned, we must take
care to re-index the corresponding avc's on their interval tree. This is
done through the use of anon_vma_interval_tree_pre_update_vma() and
anon_vma_interval_tree_post_update_vma(), which remove the avc's from
their interval tree before the update and re-insert them after the update.
The anon_vma stays locked during the update, so there is no chance that
rmap would miss the vmas that are being updated.
Signed-off-by: Michel Lespinasse <walken@google.com>
Cc: Andrea Arcangeli <aarcange@redhat.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Daniel Santos <daniel.santos@pobox.com>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'include/linux/mm.h')
-rw-r--r-- | include/linux/mm.h | 14 |
1 files changed, 14 insertions, 0 deletions
diff --git a/include/linux/mm.h b/include/linux/mm.h index f1d9aaadb566..0cdab4e0f814 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h | |||
@@ -20,6 +20,7 @@ | |||
20 | 20 | ||
21 | struct mempolicy; | 21 | struct mempolicy; |
22 | struct anon_vma; | 22 | struct anon_vma; |
23 | struct anon_vma_chain; | ||
23 | struct file_ra_state; | 24 | struct file_ra_state; |
24 | struct user_struct; | 25 | struct user_struct; |
25 | struct writeback_control; | 26 | struct writeback_control; |
@@ -1377,6 +1378,19 @@ static inline void vma_nonlinear_insert(struct vm_area_struct *vma, | |||
1377 | list_add_tail(&vma->shared.nonlinear, list); | 1378 | list_add_tail(&vma->shared.nonlinear, list); |
1378 | } | 1379 | } |
1379 | 1380 | ||
1381 | void anon_vma_interval_tree_insert(struct anon_vma_chain *node, | ||
1382 | struct rb_root *root); | ||
1383 | void anon_vma_interval_tree_remove(struct anon_vma_chain *node, | ||
1384 | struct rb_root *root); | ||
1385 | struct anon_vma_chain *anon_vma_interval_tree_iter_first( | ||
1386 | struct rb_root *root, unsigned long start, unsigned long last); | ||
1387 | struct anon_vma_chain *anon_vma_interval_tree_iter_next( | ||
1388 | struct anon_vma_chain *node, unsigned long start, unsigned long last); | ||
1389 | |||
1390 | #define anon_vma_interval_tree_foreach(avc, root, start, last) \ | ||
1391 | for (avc = anon_vma_interval_tree_iter_first(root, start, last); \ | ||
1392 | avc; avc = anon_vma_interval_tree_iter_next(avc, start, last)) | ||
1393 | |||
1380 | /* mmap.c */ | 1394 | /* mmap.c */ |
1381 | extern int __vm_enough_memory(struct mm_struct *mm, long pages, int cap_sys_admin); | 1395 | extern int __vm_enough_memory(struct mm_struct *mm, long pages, int cap_sys_admin); |
1382 | extern int vma_adjust(struct vm_area_struct *vma, unsigned long start, | 1396 | extern int vma_adjust(struct vm_area_struct *vma, unsigned long start, |