Commit 8f251a3d authored by Mike Kravetz's avatar Mike Kravetz Committed by Linus Torvalds

hugetlb: convert page_huge_active() HPageMigratable flag

Use the new hugetlb page specific flag HPageMigratable to replace the
page_huge_active interfaces.  By it's name, page_huge_active implied that
a huge page was on the active list.  However, that is not really what code
checking the flag wanted to know.  It really wanted to determine if the
huge page could be migrated.  This happens when the page is actually added
to the page cache and/or task page table.  This is the reasoning behind
the name change.

The VM_BUG_ON_PAGE() calls in the *_huge_active() interfaces are not
really necessary as we KNOW the page is a hugetlb page.  Therefore, they
are removed.

The routine page_huge_active checked for PageHeadHuge before testing the
active bit.  This is unnecessary in the case where we hold a reference or
lock and know it is a hugetlb head page.  page_huge_active is also called
without holding a reference or lock (scan_movable_pages), and can race
with code freeing the page.  The extra check in page_huge_active shortened
the race window, but did not prevent the race.  Offline code calling
scan_movable_pages already deals with these races, so removing the check
is acceptable.  Add comment to racy code.

[songmuchun@bytedance.com: remove set_page_huge_active() declaration from include/linux/hugetlb.h]
  Link: https://lkml.kernel.org/r/CAMZfGtUda+KoAZscU0718TN61cSFwp4zy=y2oZ=+6Z2TAZZwng@mail.gmail.com

Link: https://lkml.kernel.org/r/20210122195231.324857-3-mike.kravetz@oracle.comSigned-off-by: default avatarMike Kravetz <mike.kravetz@oracle.com>
Reviewed-by: default avatarOscar Salvador <osalvador@suse.de>
Reviewed-by: default avatarMuchun Song <songmuchun@bytedance.com>
Reviewed-by: default avatarMiaohe Lin <linmiaohe@huawei.com>
Acked-by: default avatarMichal Hocko <mhocko@suse.com>
Cc: David Hildenbrand <david@redhat.com>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent d6995da3
...@@ -735,7 +735,7 @@ static long hugetlbfs_fallocate(struct file *file, int mode, loff_t offset, ...@@ -735,7 +735,7 @@ static long hugetlbfs_fallocate(struct file *file, int mode, loff_t offset,
mutex_unlock(&hugetlb_fault_mutex_table[hash]); mutex_unlock(&hugetlb_fault_mutex_table[hash]);
set_page_huge_active(page); SetHPageMigratable(page);
/* /*
* unlock_page because locked by add_to_page_cache() * unlock_page because locked by add_to_page_cache()
* put_page() due to reference from alloc_huge_page() * put_page() due to reference from alloc_huge_page()
......
...@@ -480,9 +480,13 @@ unsigned long hugetlb_get_unmapped_area(struct file *file, unsigned long addr, ...@@ -480,9 +480,13 @@ unsigned long hugetlb_get_unmapped_area(struct file *file, unsigned long addr,
* HPG_restore_reserve - Set when a hugetlb page consumes a reservation at * HPG_restore_reserve - Set when a hugetlb page consumes a reservation at
* allocation time. Cleared when page is fully instantiated. Free * allocation time. Cleared when page is fully instantiated. Free
* routine checks flag to restore a reservation on error paths. * routine checks flag to restore a reservation on error paths.
* HPG_migratable - Set after a newly allocated page is added to the page
* cache and/or page tables. Indicates the page is a candidate for
* migration.
*/ */
enum hugetlb_page_flags { enum hugetlb_page_flags {
HPG_restore_reserve = 0, HPG_restore_reserve = 0,
HPG_migratable,
__NR_HPAGEFLAGS, __NR_HPAGEFLAGS,
}; };
...@@ -525,6 +529,7 @@ static inline void ClearHPage##uname(struct page *page) \ ...@@ -525,6 +529,7 @@ static inline void ClearHPage##uname(struct page *page) \
* Create functions associated with hugetlb page flags * Create functions associated with hugetlb page flags
*/ */
HPAGEFLAG(RestoreReserve, restore_reserve) HPAGEFLAG(RestoreReserve, restore_reserve)
HPAGEFLAG(Migratable, migratable)
#ifdef CONFIG_HUGETLB_PAGE #ifdef CONFIG_HUGETLB_PAGE
...@@ -838,8 +843,6 @@ static inline void huge_ptep_modify_prot_commit(struct vm_area_struct *vma, ...@@ -838,8 +843,6 @@ static inline void huge_ptep_modify_prot_commit(struct vm_area_struct *vma,
} }
#endif #endif
void set_page_huge_active(struct page *page);
#else /* CONFIG_HUGETLB_PAGE */ #else /* CONFIG_HUGETLB_PAGE */
struct hstate {}; struct hstate {};
......
...@@ -592,15 +592,9 @@ static inline void ClearPageCompound(struct page *page) ...@@ -592,15 +592,9 @@ static inline void ClearPageCompound(struct page *page)
#ifdef CONFIG_HUGETLB_PAGE #ifdef CONFIG_HUGETLB_PAGE
int PageHuge(struct page *page); int PageHuge(struct page *page);
int PageHeadHuge(struct page *page); int PageHeadHuge(struct page *page);
bool page_huge_active(struct page *page);
#else #else
TESTPAGEFLAG_FALSE(Huge) TESTPAGEFLAG_FALSE(Huge)
TESTPAGEFLAG_FALSE(HeadHuge) TESTPAGEFLAG_FALSE(HeadHuge)
static inline bool page_huge_active(struct page *page)
{
return 0;
}
#endif #endif
......
...@@ -1364,30 +1364,6 @@ struct hstate *size_to_hstate(unsigned long size) ...@@ -1364,30 +1364,6 @@ struct hstate *size_to_hstate(unsigned long size)
return NULL; return NULL;
} }
/*
* Test to determine whether the hugepage is "active/in-use" (i.e. being linked
* to hstate->hugepage_activelist.)
*
* This function can be called for tail pages, but never returns true for them.
*/
bool page_huge_active(struct page *page)
{
return PageHeadHuge(page) && PagePrivate(&page[1]);
}
/* never called for tail page */
void set_page_huge_active(struct page *page)
{
VM_BUG_ON_PAGE(!PageHeadHuge(page), page);
SetPagePrivate(&page[1]);
}
static void clear_page_huge_active(struct page *page)
{
VM_BUG_ON_PAGE(!PageHeadHuge(page), page);
ClearPagePrivate(&page[1]);
}
/* /*
* Internal hugetlb specific page flag. Do not use outside of the hugetlb * Internal hugetlb specific page flag. Do not use outside of the hugetlb
* code * code
...@@ -1449,7 +1425,7 @@ static void __free_huge_page(struct page *page) ...@@ -1449,7 +1425,7 @@ static void __free_huge_page(struct page *page)
} }
spin_lock(&hugetlb_lock); spin_lock(&hugetlb_lock);
clear_page_huge_active(page); ClearHPageMigratable(page);
hugetlb_cgroup_uncharge_page(hstate_index(h), hugetlb_cgroup_uncharge_page(hstate_index(h),
pages_per_huge_page(h), page); pages_per_huge_page(h), page);
hugetlb_cgroup_uncharge_page_rsvd(hstate_index(h), hugetlb_cgroup_uncharge_page_rsvd(hstate_index(h),
...@@ -4218,7 +4194,7 @@ static vm_fault_t hugetlb_cow(struct mm_struct *mm, struct vm_area_struct *vma, ...@@ -4218,7 +4194,7 @@ static vm_fault_t hugetlb_cow(struct mm_struct *mm, struct vm_area_struct *vma,
make_huge_pte(vma, new_page, 1)); make_huge_pte(vma, new_page, 1));
page_remove_rmap(old_page, true); page_remove_rmap(old_page, true);
hugepage_add_new_anon_rmap(new_page, vma, haddr); hugepage_add_new_anon_rmap(new_page, vma, haddr);
set_page_huge_active(new_page); SetHPageMigratable(new_page);
/* Make the old page be freed below */ /* Make the old page be freed below */
new_page = old_page; new_page = old_page;
} }
...@@ -4455,12 +4431,12 @@ static vm_fault_t hugetlb_no_page(struct mm_struct *mm, ...@@ -4455,12 +4431,12 @@ static vm_fault_t hugetlb_no_page(struct mm_struct *mm,
spin_unlock(ptl); spin_unlock(ptl);
/* /*
* Only make newly allocated pages active. Existing pages found * Only set HPageMigratable in newly allocated pages. Existing pages
* in the pagecache could be !page_huge_active() if they have been * found in the pagecache may not have HPageMigratableset if they have
* isolated for migration. * been isolated for migration.
*/ */
if (new_page) if (new_page)
set_page_huge_active(page); SetHPageMigratable(page);
unlock_page(page); unlock_page(page);
out: out:
...@@ -4771,7 +4747,7 @@ int hugetlb_mcopy_atomic_pte(struct mm_struct *dst_mm, ...@@ -4771,7 +4747,7 @@ int hugetlb_mcopy_atomic_pte(struct mm_struct *dst_mm,
update_mmu_cache(dst_vma, dst_addr, dst_pte); update_mmu_cache(dst_vma, dst_addr, dst_pte);
spin_unlock(ptl); spin_unlock(ptl);
set_page_huge_active(page); SetHPageMigratable(page);
if (vm_shared) if (vm_shared)
unlock_page(page); unlock_page(page);
ret = 0; ret = 0;
...@@ -5610,12 +5586,13 @@ bool isolate_huge_page(struct page *page, struct list_head *list) ...@@ -5610,12 +5586,13 @@ bool isolate_huge_page(struct page *page, struct list_head *list)
bool ret = true; bool ret = true;
spin_lock(&hugetlb_lock); spin_lock(&hugetlb_lock);
if (!PageHeadHuge(page) || !page_huge_active(page) || if (!PageHeadHuge(page) ||
!HPageMigratable(page) ||
!get_page_unless_zero(page)) { !get_page_unless_zero(page)) {
ret = false; ret = false;
goto unlock; goto unlock;
} }
clear_page_huge_active(page); ClearHPageMigratable(page);
list_move_tail(&page->lru, list); list_move_tail(&page->lru, list);
unlock: unlock:
spin_unlock(&hugetlb_lock); spin_unlock(&hugetlb_lock);
...@@ -5625,7 +5602,7 @@ bool isolate_huge_page(struct page *page, struct list_head *list) ...@@ -5625,7 +5602,7 @@ bool isolate_huge_page(struct page *page, struct list_head *list)
void putback_active_hugepage(struct page *page) void putback_active_hugepage(struct page *page)
{ {
spin_lock(&hugetlb_lock); spin_lock(&hugetlb_lock);
set_page_huge_active(page); SetHPageMigratable(page);
list_move_tail(&page->lru, &(page_hstate(page))->hugepage_activelist); list_move_tail(&page->lru, &(page_hstate(page))->hugepage_activelist);
spin_unlock(&hugetlb_lock); spin_unlock(&hugetlb_lock);
put_page(page); put_page(page);
......
...@@ -1260,7 +1260,14 @@ static int scan_movable_pages(unsigned long start, unsigned long end, ...@@ -1260,7 +1260,14 @@ static int scan_movable_pages(unsigned long start, unsigned long end,
if (!PageHuge(page)) if (!PageHuge(page))
continue; continue;
head = compound_head(page); head = compound_head(page);
if (page_huge_active(head)) /*
* This test is racy as we hold no reference or lock. The
* hugetlb page could have been free'ed and head is no longer
* a hugetlb page before the following check. In such unlikely
* cases false positives and negatives are possible. Calling
* code must deal with these scenarios.
*/
if (HPageMigratable(head))
goto found; goto found;
skip = compound_nr(head) - (page - head); skip = compound_nr(head) - (page - head);
pfn += skip - 1; pfn += skip - 1;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment