diff options
author | Peter Zijlstra <a.p.zijlstra@chello.nl> | 2011-05-24 20:12:13 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-05-25 11:39:20 -0400 |
commit | 88c22088bf235f50b09a10bd9f022b0472bcb6b5 (patch) | |
tree | 8859c39a8e48bf41b5bd48e9e279571ca56ec1b0 /mm | |
parent | 2b575eb64f7a9c701fb4bfdb12388ac547f6c2b6 (diff) |
mm: optimize page_lock_anon_vma() fast-path
Optimize the page_lock_anon_vma() fast path to be one atomic op, instead
of two.
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Reviewed-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: David Miller <davem@davemloft.net>
Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
Cc: Russell King <rmk@arm.linux.org.uk>
Cc: Paul Mundt <lethal@linux-sh.org>
Cc: Jeff Dike <jdike@addtoit.com>
Cc: Richard Weinberger <richard@nod.at>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Hugh Dickins <hughd@google.com>
Cc: Mel Gorman <mel@csn.ul.ie>
Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Cc: Nick Piggin <npiggin@kernel.dk>
Cc: Namhyung Kim <namhyung@gmail.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 | 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 | /* |