diff options
author | Peter Zijlstra <a.p.zijlstra@chello.nl> | 2011-03-22 19:32:49 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-03-22 20:44:03 -0400 |
commit | 01d8b20dec5f4019283e244aba50ba86fe6ead6e (patch) | |
tree | 738a2e675547de61f74d6f4019dd5830c40446dd /mm | |
parent | 83813267c699ab11cc65a6d9d0f42db42f0862b3 (diff) |
mm: simplify anon_vma refcounts
This patch changes the anon_vma refcount to be 0 when the object is free.
It does this by adding 1 ref to being in use in the anon_vma structure
(iow. the anon_vma->head list is not empty).
This allows a simpler release scheme without having to check both the
refcount and the list as well as avoids taking a ref for each entry on the
list.
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Reviewed-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Acked-by: Hugh Dickins <hughd@google.com>
Acked-by: Mel Gorman <mel@csn.ul.ie>
Acked-by: Rik van Riel <riel@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm')
-rw-r--r-- | mm/rmap.c | 76 |
1 files changed, 28 insertions, 48 deletions
@@ -67,11 +67,24 @@ static struct kmem_cache *anon_vma_chain_cachep; | |||
67 | 67 | ||
68 | static inline struct anon_vma *anon_vma_alloc(void) | 68 | static inline struct anon_vma *anon_vma_alloc(void) |
69 | { | 69 | { |
70 | return kmem_cache_alloc(anon_vma_cachep, GFP_KERNEL); | 70 | struct anon_vma *anon_vma; |
71 | |||
72 | anon_vma = kmem_cache_alloc(anon_vma_cachep, GFP_KERNEL); | ||
73 | if (anon_vma) { | ||
74 | atomic_set(&anon_vma->refcount, 1); | ||
75 | /* | ||
76 | * Initialise the anon_vma root to point to itself. If called | ||
77 | * from fork, the root will be reset to the parents anon_vma. | ||
78 | */ | ||
79 | anon_vma->root = anon_vma; | ||
80 | } | ||
81 | |||
82 | return anon_vma; | ||
71 | } | 83 | } |
72 | 84 | ||
73 | void anon_vma_free(struct anon_vma *anon_vma) | 85 | static inline void anon_vma_free(struct anon_vma *anon_vma) |
74 | { | 86 | { |
87 | VM_BUG_ON(atomic_read(&anon_vma->refcount)); | ||
75 | kmem_cache_free(anon_vma_cachep, anon_vma); | 88 | kmem_cache_free(anon_vma_cachep, anon_vma); |
76 | } | 89 | } |
77 | 90 | ||
@@ -133,11 +146,6 @@ int anon_vma_prepare(struct vm_area_struct *vma) | |||
133 | if (unlikely(!anon_vma)) | 146 | if (unlikely(!anon_vma)) |
134 | goto out_enomem_free_avc; | 147 | goto out_enomem_free_avc; |
135 | allocated = anon_vma; | 148 | allocated = anon_vma; |
136 | /* | ||
137 | * This VMA had no anon_vma yet. This anon_vma is | ||
138 | * the root of any anon_vma tree that might form. | ||
139 | */ | ||
140 | anon_vma->root = anon_vma; | ||
141 | } | 149 | } |
142 | 150 | ||
143 | anon_vma_lock(anon_vma); | 151 | anon_vma_lock(anon_vma); |
@@ -156,7 +164,7 @@ int anon_vma_prepare(struct vm_area_struct *vma) | |||
156 | anon_vma_unlock(anon_vma); | 164 | anon_vma_unlock(anon_vma); |
157 | 165 | ||
158 | if (unlikely(allocated)) | 166 | if (unlikely(allocated)) |
159 | anon_vma_free(allocated); | 167 | put_anon_vma(allocated); |
160 | if (unlikely(avc)) | 168 | if (unlikely(avc)) |
161 | anon_vma_chain_free(avc); | 169 | anon_vma_chain_free(avc); |
162 | } | 170 | } |
@@ -241,9 +249,9 @@ int anon_vma_fork(struct vm_area_struct *vma, struct vm_area_struct *pvma) | |||
241 | */ | 249 | */ |
242 | anon_vma->root = pvma->anon_vma->root; | 250 | anon_vma->root = pvma->anon_vma->root; |
243 | /* | 251 | /* |
244 | * With KSM refcounts, an anon_vma can stay around longer than the | 252 | * With refcounts, an anon_vma can stay around longer than the |
245 | * process it belongs to. The root anon_vma needs to be pinned | 253 | * process it belongs to. The root anon_vma needs to be pinned until |
246 | * until this anon_vma is freed, because the lock lives in the root. | 254 | * this anon_vma is freed, because the lock lives in the root. |
247 | */ | 255 | */ |
248 | get_anon_vma(anon_vma->root); | 256 | get_anon_vma(anon_vma->root); |
249 | /* Mark this anon_vma as the one where our new (COWed) pages go. */ | 257 | /* Mark this anon_vma as the one where our new (COWed) pages go. */ |
@@ -253,7 +261,7 @@ int anon_vma_fork(struct vm_area_struct *vma, struct vm_area_struct *pvma) | |||
253 | return 0; | 261 | return 0; |
254 | 262 | ||
255 | out_error_free_anon_vma: | 263 | out_error_free_anon_vma: |
256 | anon_vma_free(anon_vma); | 264 | put_anon_vma(anon_vma); |
257 | out_error: | 265 | out_error: |
258 | unlink_anon_vmas(vma); | 266 | unlink_anon_vmas(vma); |
259 | return -ENOMEM; | 267 | return -ENOMEM; |
@@ -272,15 +280,11 @@ static void anon_vma_unlink(struct anon_vma_chain *anon_vma_chain) | |||
272 | list_del(&anon_vma_chain->same_anon_vma); | 280 | list_del(&anon_vma_chain->same_anon_vma); |
273 | 281 | ||
274 | /* We must garbage collect the anon_vma if it's empty */ | 282 | /* We must garbage collect the anon_vma if it's empty */ |
275 | empty = list_empty(&anon_vma->head) && !atomic_read(&anon_vma->refcount); | 283 | empty = list_empty(&anon_vma->head); |
276 | anon_vma_unlock(anon_vma); | 284 | anon_vma_unlock(anon_vma); |
277 | 285 | ||
278 | if (empty) { | 286 | if (empty) |
279 | /* We no longer need the root anon_vma */ | 287 | put_anon_vma(anon_vma); |
280 | if (anon_vma->root != anon_vma) | ||
281 | put_anon_vma(anon_vma->root); | ||
282 | anon_vma_free(anon_vma); | ||
283 | } | ||
284 | } | 288 | } |
285 | 289 | ||
286 | void unlink_anon_vmas(struct vm_area_struct *vma) | 290 | void unlink_anon_vmas(struct vm_area_struct *vma) |
@@ -1486,38 +1490,14 @@ int try_to_munlock(struct page *page) | |||
1486 | return try_to_unmap_file(page, TTU_MUNLOCK); | 1490 | return try_to_unmap_file(page, TTU_MUNLOCK); |
1487 | } | 1491 | } |
1488 | 1492 | ||
1489 | /* | 1493 | void __put_anon_vma(struct anon_vma *anon_vma) |
1490 | * Drop an anon_vma refcount, freeing the anon_vma and anon_vma->root | ||
1491 | * if necessary. Be careful to do all the tests under the lock. Once | ||
1492 | * we know we are the last user, nobody else can get a reference and we | ||
1493 | * can do the freeing without the lock. | ||
1494 | */ | ||
1495 | void put_anon_vma(struct anon_vma *anon_vma) | ||
1496 | { | 1494 | { |
1497 | BUG_ON(atomic_read(&anon_vma->refcount) <= 0); | 1495 | struct anon_vma *root = anon_vma->root; |
1498 | if (atomic_dec_and_lock(&anon_vma->refcount, &anon_vma->root->lock)) { | ||
1499 | struct anon_vma *root = anon_vma->root; | ||
1500 | int empty = list_empty(&anon_vma->head); | ||
1501 | int last_root_user = 0; | ||
1502 | int root_empty = 0; | ||
1503 | 1496 | ||
1504 | /* | 1497 | if (root != anon_vma && atomic_dec_and_test(&root->refcount)) |
1505 | * The refcount on a non-root anon_vma got dropped. Drop | 1498 | anon_vma_free(root); |
1506 | * the refcount on the root and check if we need to free it. | ||
1507 | */ | ||
1508 | if (empty && anon_vma != root) { | ||
1509 | BUG_ON(atomic_read(&root->refcount) <= 0); | ||
1510 | last_root_user = atomic_dec_and_test(&root->refcount); | ||
1511 | root_empty = list_empty(&root->head); | ||
1512 | } | ||
1513 | anon_vma_unlock(anon_vma); | ||
1514 | 1499 | ||
1515 | if (empty) { | 1500 | anon_vma_free(anon_vma); |
1516 | anon_vma_free(anon_vma); | ||
1517 | if (root_empty && last_root_user) | ||
1518 | anon_vma_free(root); | ||
1519 | } | ||
1520 | } | ||
1521 | } | 1501 | } |
1522 | 1502 | ||
1523 | #ifdef CONFIG_MIGRATION | 1503 | #ifdef CONFIG_MIGRATION |