diff options
Diffstat (limited to 'mm/rmap.c')
-rw-r--r-- | mm/rmap.c | 42 |
1 files changed, 27 insertions, 15 deletions
@@ -337,9 +337,9 @@ void __init anon_vma_init(void) | |||
337 | * that the anon_vma pointer from page->mapping is valid if there is a | 337 | * that the anon_vma pointer from page->mapping is valid if there is a |
338 | * mapcount, we can dereference the anon_vma after observing those. | 338 | * mapcount, we can dereference the anon_vma after observing those. |
339 | */ | 339 | */ |
340 | struct anon_vma *page_lock_anon_vma(struct page *page) | 340 | struct anon_vma *page_get_anon_vma(struct page *page) |
341 | { | 341 | { |
342 | struct anon_vma *anon_vma, *root_anon_vma; | 342 | struct anon_vma *anon_vma = NULL; |
343 | unsigned long anon_mapping; | 343 | unsigned long anon_mapping; |
344 | 344 | ||
345 | rcu_read_lock(); | 345 | rcu_read_lock(); |
@@ -350,30 +350,42 @@ struct anon_vma *page_lock_anon_vma(struct page *page) | |||
350 | goto out; | 350 | goto out; |
351 | 351 | ||
352 | anon_vma = (struct anon_vma *) (anon_mapping - PAGE_MAPPING_ANON); | 352 | anon_vma = (struct anon_vma *) (anon_mapping - PAGE_MAPPING_ANON); |
353 | root_anon_vma = ACCESS_ONCE(anon_vma->root); | 353 | if (!atomic_inc_not_zero(&anon_vma->refcount)) { |
354 | spin_lock(&root_anon_vma->lock); | 354 | anon_vma = NULL; |
355 | goto out; | ||
356 | } | ||
355 | 357 | ||
356 | /* | 358 | /* |
357 | * If this page is still mapped, then its anon_vma cannot have been | 359 | * If this page is still mapped, then its anon_vma cannot have been |
358 | * freed. But if it has been unmapped, we have no security against | 360 | * freed. But if it has been unmapped, we have no security against the |
359 | * the anon_vma structure being freed and reused (for another anon_vma: | 361 | * anon_vma structure being freed and reused (for another anon_vma: |
360 | * SLAB_DESTROY_BY_RCU guarantees that - so the spin_lock above cannot | 362 | * SLAB_DESTROY_BY_RCU guarantees that - so the atomic_inc_not_zero() |
361 | * corrupt): with anon_vma_prepare() or anon_vma_fork() redirecting | 363 | * above cannot corrupt). |
362 | * anon_vma->root before page_unlock_anon_vma() is called to unlock. | ||
363 | */ | 364 | */ |
364 | if (page_mapped(page)) | 365 | if (!page_mapped(page)) { |
365 | return anon_vma; | 366 | put_anon_vma(anon_vma); |
366 | 367 | anon_vma = NULL; | |
367 | spin_unlock(&root_anon_vma->lock); | 368 | } |
368 | out: | 369 | out: |
369 | rcu_read_unlock(); | 370 | rcu_read_unlock(); |
370 | return NULL; | 371 | |
372 | return anon_vma; | ||
373 | } | ||
374 | |||
375 | struct anon_vma *page_lock_anon_vma(struct page *page) | ||
376 | { | ||
377 | struct anon_vma *anon_vma = page_get_anon_vma(page); | ||
378 | |||
379 | if (anon_vma) | ||
380 | anon_vma_lock(anon_vma); | ||
381 | |||
382 | return anon_vma; | ||
371 | } | 383 | } |
372 | 384 | ||
373 | void page_unlock_anon_vma(struct anon_vma *anon_vma) | 385 | void page_unlock_anon_vma(struct anon_vma *anon_vma) |
374 | { | 386 | { |
375 | anon_vma_unlock(anon_vma); | 387 | anon_vma_unlock(anon_vma); |
376 | rcu_read_unlock(); | 388 | put_anon_vma(anon_vma); |
377 | } | 389 | } |
378 | 390 | ||
379 | /* | 391 | /* |