Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Peter Zijlstra and committed by
Linus Torvalds
01d8b20d 83813267

+37 -50
+9 -2
include/linux/rmap.h
··· 73 73 atomic_inc(&anon_vma->refcount); 74 74 } 75 75 76 - void put_anon_vma(struct anon_vma *); 76 + void __put_anon_vma(struct anon_vma *anon_vma); 77 + 78 + static inline void put_anon_vma(struct anon_vma *anon_vma) 79 + { 80 + if (atomic_dec_and_test(&anon_vma->refcount)) 81 + __put_anon_vma(anon_vma); 82 + } 77 83 78 84 static inline struct anon_vma *page_anon_vma(struct page *page) 79 85 { ··· 122 116 int anon_vma_clone(struct vm_area_struct *, struct vm_area_struct *); 123 117 int anon_vma_fork(struct vm_area_struct *, struct vm_area_struct *); 124 118 void __anon_vma_link(struct vm_area_struct *); 125 - void anon_vma_free(struct anon_vma *); 126 119 127 120 static inline void anon_vma_merge(struct vm_area_struct *vma, 128 121 struct vm_area_struct *next) ··· 129 124 VM_BUG_ON(vma->anon_vma != next->anon_vma); 130 125 unlink_anon_vmas(next); 131 126 } 127 + 128 + struct anon_vma *page_get_anon_vma(struct page *page); 132 129 133 130 /* 134 131 * rmap interfaces called when adding or removing pte of page
+28 -48
mm/rmap.c
··· 67 67 68 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 88 kmem_cache_free(anon_vma_cachep, anon_vma); 76 89 } 77 90 ··· 146 133 if (unlikely(!anon_vma)) 147 134 goto out_enomem_free_avc; 148 135 allocated = anon_vma; 149 - /* 150 - * This VMA had no anon_vma yet. This anon_vma is 151 - * the root of any anon_vma tree that might form. 152 - */ 153 - anon_vma->root = anon_vma; 154 136 } 155 137 156 138 anon_vma_lock(anon_vma); ··· 164 156 anon_vma_unlock(anon_vma); 165 157 166 158 if (unlikely(allocated)) 167 - anon_vma_free(allocated); 159 + put_anon_vma(allocated); 168 160 if (unlikely(avc)) 169 161 anon_vma_chain_free(avc); 170 162 } ··· 249 241 */ 250 242 anon_vma->root = pvma->anon_vma->root; 251 243 /* 252 - * With KSM refcounts, an anon_vma can stay around longer than the 253 - * process it belongs to. The root anon_vma needs to be pinned 254 - * until this anon_vma is freed, because the lock lives in the root. 244 + * With refcounts, an anon_vma can stay around longer than the 245 + * process it belongs to. The root anon_vma needs to be pinned until 246 + * this anon_vma is freed, because the lock lives in the root. 255 247 */ 256 248 get_anon_vma(anon_vma->root); 257 249 /* Mark this anon_vma as the one where our new (COWed) pages go. */ ··· 261 253 return 0; 262 254 263 255 out_error_free_anon_vma: 264 - anon_vma_free(anon_vma); 256 + put_anon_vma(anon_vma); 265 257 out_error: 266 258 unlink_anon_vmas(vma); 267 259 return -ENOMEM; ··· 280 272 list_del(&anon_vma_chain->same_anon_vma); 281 273 282 274 /* We must garbage collect the anon_vma if it's empty */ 283 - empty = list_empty(&anon_vma->head) && !atomic_read(&anon_vma->refcount); 275 + empty = list_empty(&anon_vma->head); 284 276 anon_vma_unlock(anon_vma); 285 277 286 - if (empty) { 287 - /* We no longer need the root anon_vma */ 288 - if (anon_vma->root != anon_vma) 289 - put_anon_vma(anon_vma->root); 290 - anon_vma_free(anon_vma); 291 - } 278 + if (empty) 279 + put_anon_vma(anon_vma); 292 280 } 293 281 294 282 void unlink_anon_vmas(struct vm_area_struct *vma) ··· 1490 1486 return try_to_unmap_file(page, TTU_MUNLOCK); 1491 1487 } 1492 1488 1493 - /* 1494 - * Drop an anon_vma refcount, freeing the anon_vma and anon_vma->root 1495 - * if necessary. Be careful to do all the tests under the lock. Once 1496 - * we know we are the last user, nobody else can get a reference and we 1497 - * can do the freeing without the lock. 1498 - */ 1499 - void put_anon_vma(struct anon_vma *anon_vma) 1489 + void __put_anon_vma(struct anon_vma *anon_vma) 1500 1490 { 1501 - BUG_ON(atomic_read(&anon_vma->refcount) <= 0); 1502 - if (atomic_dec_and_lock(&anon_vma->refcount, &anon_vma->root->lock)) { 1503 - struct anon_vma *root = anon_vma->root; 1504 - int empty = list_empty(&anon_vma->head); 1505 - int last_root_user = 0; 1506 - int root_empty = 0; 1491 + struct anon_vma *root = anon_vma->root; 1507 1492 1508 - /* 1509 - * The refcount on a non-root anon_vma got dropped. Drop 1510 - * the refcount on the root and check if we need to free it. 1511 - */ 1512 - if (empty && anon_vma != root) { 1513 - BUG_ON(atomic_read(&root->refcount) <= 0); 1514 - last_root_user = atomic_dec_and_test(&root->refcount); 1515 - root_empty = list_empty(&root->head); 1516 - } 1517 - anon_vma_unlock(anon_vma); 1493 + if (root != anon_vma && atomic_dec_and_test(&root->refcount)) 1494 + anon_vma_free(root); 1518 1495 1519 - if (empty) { 1520 - anon_vma_free(anon_vma); 1521 - if (root_empty && last_root_user) 1522 - anon_vma_free(root); 1523 - } 1524 - } 1496 + anon_vma_free(anon_vma); 1525 1497 } 1526 1498 1527 1499 #ifdef CONFIG_MIGRATION