diff options
-rw-r--r-- | mm/rmap.c | 86 |
1 files changed, 82 insertions, 4 deletions
@@ -86,6 +86,29 @@ static inline struct anon_vma *anon_vma_alloc(void) | |||
86 | static inline void anon_vma_free(struct anon_vma *anon_vma) | 86 | static inline void anon_vma_free(struct anon_vma *anon_vma) |
87 | { | 87 | { |
88 | VM_BUG_ON(atomic_read(&anon_vma->refcount)); | 88 | VM_BUG_ON(atomic_read(&anon_vma->refcount)); |
89 | |||
90 | /* | ||
91 | * Synchronize against page_lock_anon_vma() such that | ||
92 | * we can safely hold the lock without the anon_vma getting | ||
93 | * freed. | ||
94 | * | ||
95 | * Relies on the full mb implied by the atomic_dec_and_test() from | ||
96 | * put_anon_vma() against the acquire barrier implied by | ||
97 | * mutex_trylock() from page_lock_anon_vma(). This orders: | ||
98 | * | ||
99 | * page_lock_anon_vma() VS put_anon_vma() | ||
100 | * mutex_trylock() atomic_dec_and_test() | ||
101 | * LOCK MB | ||
102 | * atomic_read() mutex_is_locked() | ||
103 | * | ||
104 | * LOCK should suffice since the actual taking of the lock must | ||
105 | * happen _before_ what follows. | ||
106 | */ | ||
107 | if (mutex_is_locked(&anon_vma->root->mutex)) { | ||
108 | anon_vma_lock(anon_vma); | ||
109 | anon_vma_unlock(anon_vma); | ||
110 | } | ||
111 | |||
89 | kmem_cache_free(anon_vma_cachep, anon_vma); | 112 | kmem_cache_free(anon_vma_cachep, anon_vma); |
90 | } | 113 | } |
91 | 114 | ||
@@ -372,20 +395,75 @@ out: | |||
372 | return anon_vma; | 395 | return anon_vma; |
373 | } | 396 | } |
374 | 397 | ||
398 | /* | ||
399 | * Similar to page_get_anon_vma() except it locks the anon_vma. | ||
400 | * | ||
401 | * Its a little more complex as it tries to keep the fast path to a single | ||
402 | * atomic op -- the trylock. If we fail the trylock, we fall back to getting a | ||
403 | * reference like with page_get_anon_vma() and then block on the mutex. | ||
404 | */ | ||
375 | struct anon_vma *page_lock_anon_vma(struct page *page) | 405 | struct anon_vma *page_lock_anon_vma(struct page *page) |
376 | { | 406 | { |
377 | struct anon_vma *anon_vma = page_get_anon_vma(page); | 407 | struct anon_vma *anon_vma = NULL; |
408 | unsigned long anon_mapping; | ||
378 | 409 | ||
379 | if (anon_vma) | 410 | rcu_read_lock(); |
380 | anon_vma_lock(anon_vma); | 411 | anon_mapping = (unsigned long) ACCESS_ONCE(page->mapping); |
412 | if ((anon_mapping & PAGE_MAPPING_FLAGS) != PAGE_MAPPING_ANON) | ||
413 | goto out; | ||
414 | if (!page_mapped(page)) | ||
415 | goto out; | ||
416 | |||
417 | anon_vma = (struct anon_vma *) (anon_mapping - PAGE_MAPPING_ANON); | ||
418 | if (mutex_trylock(&anon_vma->root->mutex)) { | ||
419 | /* | ||
420 | * If we observe a !0 refcount, then holding the lock ensures | ||
421 | * the anon_vma will not go away, see __put_anon_vma(). | ||
422 | */ | ||
423 | if (!atomic_read(&anon_vma->refcount)) { | ||
424 | anon_vma_unlock(anon_vma); | ||
425 | anon_vma = NULL; | ||
426 | } | ||
427 | goto out; | ||
428 | } | ||
429 | |||
430 | /* trylock failed, we got to sleep */ | ||
431 | if (!atomic_inc_not_zero(&anon_vma->refcount)) { | ||
432 | anon_vma = NULL; | ||
433 | goto out; | ||
434 | } | ||
381 | 435 | ||
436 | if (!page_mapped(page)) { | ||
437 | put_anon_vma(anon_vma); | ||
438 | anon_vma = NULL; | ||
439 | goto out; | ||
440 | } | ||
441 | |||
442 | /* we pinned the anon_vma, its safe to sleep */ | ||
443 | rcu_read_unlock(); | ||
444 | anon_vma_lock(anon_vma); | ||
445 | |||
446 | if (atomic_dec_and_test(&anon_vma->refcount)) { | ||
447 | /* | ||
448 | * Oops, we held the last refcount, release the lock | ||
449 | * and bail -- can't simply use put_anon_vma() because | ||
450 | * we'll deadlock on the anon_vma_lock() recursion. | ||
451 | */ | ||
452 | anon_vma_unlock(anon_vma); | ||
453 | __put_anon_vma(anon_vma); | ||
454 | anon_vma = NULL; | ||
455 | } | ||
456 | |||
457 | return anon_vma; | ||
458 | |||
459 | out: | ||
460 | rcu_read_unlock(); | ||
382 | return anon_vma; | 461 | return anon_vma; |
383 | } | 462 | } |
384 | 463 | ||
385 | void page_unlock_anon_vma(struct anon_vma *anon_vma) | 464 | void page_unlock_anon_vma(struct anon_vma *anon_vma) |
386 | { | 465 | { |
387 | anon_vma_unlock(anon_vma); | 466 | anon_vma_unlock(anon_vma); |
388 | put_anon_vma(anon_vma); | ||
389 | } | 467 | } |
390 | 468 | ||
391 | /* | 469 | /* |