diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 94398e024d3a..5dad4b6ec6d6 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -61,6 +61,7 @@ unsigned long transparent_hugepage_flags __read_mostly = static struct shrinker deferred_split_shrinker; static atomic_t huge_zero_refcount; +static DEFINE_SPINLOCK(huge_zero_lock); struct page *huge_zero_page __read_mostly; unsigned long huge_zero_pfn __read_mostly = ~0UL; @@ -91,7 +92,8 @@ bool transparent_hugepage_active(struct vm_area_struct *vma) static bool get_huge_zero_page(void) { struct page *zero_page; -retry: + + /* Paired with atomic_set_release(). */ if (likely(atomic_inc_not_zero(&huge_zero_refcount))) return true; @@ -102,17 +104,22 @@ static bool get_huge_zero_page(void) return false; } count_vm_event(THP_ZERO_PAGE_ALLOC); - preempt_disable(); - if (cmpxchg(&huge_zero_page, NULL, zero_page)) { - preempt_enable(); - __free_pages(zero_page, compound_order(zero_page)); - goto retry; - } - WRITE_ONCE(huge_zero_pfn, page_to_pfn(zero_page)); - /* We take additional reference here. It will be put back by shrinker */ - atomic_set(&huge_zero_refcount, 2); - preempt_enable(); + /* Paired with critical section in shrink_huge_zero_page_scan(). */ + spin_lock(&huge_zero_lock); + if (huge_zero_page) { + /* Somebody else already installed it. */ + atomic_inc(&huge_zero_refcount); + spin_unlock(&huge_zero_lock); + __free_pages(zero_page, compound_order(zero_page)); + return true; + } + WRITE_ONCE(huge_zero_page, zero_page); + WRITE_ONCE(huge_zero_pfn, page_to_pfn(zero_page)); + /* Paired with atomic_inc_not_zero(). +1 for shrinker pin. */ + atomic_set_release(&huge_zero_refcount, 2); + spin_unlock(&huge_zero_lock); + return true; } @@ -155,15 +162,24 @@ static unsigned long shrink_huge_zero_page_count(struct shrinker *shrink, static unsigned long shrink_huge_zero_page_scan(struct shrinker *shrink, struct shrink_control *sc) { - if (atomic_cmpxchg(&huge_zero_refcount, 1, 0) == 1) { - struct page *zero_page = xchg(&huge_zero_page, NULL); - BUG_ON(zero_page == NULL); - WRITE_ONCE(huge_zero_pfn, ~0UL); - __free_pages(zero_page, compound_order(zero_page)); - return HPAGE_PMD_NR; + struct page *zero_page; + + /* Paired with critical section in get_huge_zero_page(). */ + spin_lock(&huge_zero_lock); + /* Paired with atomic_inc_not_zero() in get_huge_zero_page(). */ + if (atomic_cmpxchg(&huge_zero_refcount, 1, 0) != 1) { + spin_unlock(&huge_zero_lock); + return 0; } - return 0; + zero_page = huge_zero_page; + VM_WARN_ON_ONCE(!zero_page); + WRITE_ONCE(huge_zero_page, NULL); + WRITE_ONCE(huge_zero_pfn, ~0UL); + spin_unlock(&huge_zero_lock); + + __free_pages(zero_page, compound_order(zero_page)); + return HPAGE_PMD_NR; } static struct shrinker huge_zero_page_shrinker = {