Skip to content

Commit bcc5422

Browse files
Naoya Horiguchitorvalds
authored andcommitted
mm: hugetlb: introduce page_huge_active
We are not safe from calling isolate_huge_page() on a hugepage concurrently, which can make the victim hugepage in invalid state and results in BUG_ON(). The root problem of this is that we don't have any information on struct page (so easily accessible) about hugepages' activeness. Note that hugepages' activeness means just being linked to hstate->hugepage_activelist, which is not the same as normal pages' activeness represented by PageActive flag. Normal pages are isolated by isolate_lru_page() which prechecks PageLRU before isolation, so let's do similarly for hugetlb with a new paeg_huge_active(). set/clear_page_huge_active() should be called within hugetlb_lock. But hugetlb_cow() and hugetlb_no_page() don't do this, being justified because in these functions set_page_huge_active() is called right after the hugepage is allocated and no other thread tries to isolate it. [akpm@linux-foundation.org: s/PageHugeActive/page_huge_active/, make it return bool] [fengguang.wu@intel.com: set_page_huge_active() can be static] Signed-off-by: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com> Cc: Hugh Dickins <hughd@google.com> Reviewed-by: Michal Hocko <mhocko@suse.cz> Cc: Mel Gorman <mgorman@suse.de> Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: David Rientjes <rientjes@google.com> Signed-off-by: Fengguang Wu <fengguang.wu@intel.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
1 parent 822fc61 commit bcc5422

File tree

2 files changed

+50
-5
lines changed

2 files changed

+50
-5
lines changed

mm/hugetlb.c

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -924,6 +924,31 @@ struct hstate *size_to_hstate(unsigned long size)
924924
return NULL;
925925
}
926926

927+
/*
928+
* Test to determine whether the hugepage is "active/in-use" (i.e. being linked
929+
* to hstate->hugepage_activelist.)
930+
*
931+
* This function can be called for tail pages, but never returns true for them.
932+
*/
933+
bool page_huge_active(struct page *page)
934+
{
935+
VM_BUG_ON_PAGE(!PageHuge(page), page);
936+
return PageHead(page) && PagePrivate(&page[1]);
937+
}
938+
939+
/* never called for tail page */
940+
static void set_page_huge_active(struct page *page)
941+
{
942+
VM_BUG_ON_PAGE(!PageHeadHuge(page), page);
943+
SetPagePrivate(&page[1]);
944+
}
945+
946+
static void clear_page_huge_active(struct page *page)
947+
{
948+
VM_BUG_ON_PAGE(!PageHeadHuge(page), page);
949+
ClearPagePrivate(&page[1]);
950+
}
951+
927952
void free_huge_page(struct page *page)
928953
{
929954
/*
@@ -952,6 +977,7 @@ void free_huge_page(struct page *page)
952977
restore_reserve = true;
953978

954979
spin_lock(&hugetlb_lock);
980+
clear_page_huge_active(page);
955981
hugetlb_cgroup_uncharge_page(hstate_index(h),
956982
pages_per_huge_page(h), page);
957983
if (restore_reserve)
@@ -2972,6 +2998,7 @@ static int hugetlb_cow(struct mm_struct *mm, struct vm_area_struct *vma,
29722998
copy_user_huge_page(new_page, old_page, address, vma,
29732999
pages_per_huge_page(h));
29743000
__SetPageUptodate(new_page);
3001+
set_page_huge_active(new_page);
29753002

29763003
mmun_start = address & huge_page_mask(h);
29773004
mmun_end = mmun_start + huge_page_size(h);
@@ -3084,6 +3111,7 @@ static int hugetlb_no_page(struct mm_struct *mm, struct vm_area_struct *vma,
30843111
}
30853112
clear_huge_page(page, address, pages_per_huge_page(h));
30863113
__SetPageUptodate(page);
3114+
set_page_huge_active(page);
30873115

30883116
if (vma->vm_flags & VM_MAYSHARE) {
30893117
int err;
@@ -3913,19 +3941,26 @@ int dequeue_hwpoisoned_huge_page(struct page *hpage)
39133941

39143942
bool isolate_huge_page(struct page *page, struct list_head *list)
39153943
{
3944+
bool ret = true;
3945+
39163946
VM_BUG_ON_PAGE(!PageHead(page), page);
3917-
if (!get_page_unless_zero(page))
3918-
return false;
39193947
spin_lock(&hugetlb_lock);
3948+
if (!page_huge_active(page) || !get_page_unless_zero(page)) {
3949+
ret = false;
3950+
goto unlock;
3951+
}
3952+
clear_page_huge_active(page);
39203953
list_move_tail(&page->lru, list);
3954+
unlock:
39213955
spin_unlock(&hugetlb_lock);
3922-
return true;
3956+
return ret;
39233957
}
39243958

39253959
void putback_active_hugepage(struct page *page)
39263960
{
39273961
VM_BUG_ON_PAGE(!PageHead(page), page);
39283962
spin_lock(&hugetlb_lock);
3963+
set_page_huge_active(page);
39293964
list_move_tail(&page->lru, &(page_hstate(page))->hugepage_activelist);
39303965
spin_unlock(&hugetlb_lock);
39313966
put_page(page);

mm/memory-failure.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1586,8 +1586,18 @@ static int soft_offline_huge_page(struct page *page, int flags)
15861586
}
15871587
unlock_page(hpage);
15881588

1589-
/* Keep page count to indicate a given hugepage is isolated. */
1590-
list_move(&hpage->lru, &pagelist);
1589+
ret = isolate_huge_page(hpage, &pagelist);
1590+
if (ret) {
1591+
/*
1592+
* get_any_page() and isolate_huge_page() takes a refcount each,
1593+
* so need to drop one here.
1594+
*/
1595+
put_page(hpage);
1596+
} else {
1597+
pr_info("soft offline: %#lx hugepage failed to isolate\n", pfn);
1598+
return -EBUSY;
1599+
}
1600+
15911601
ret = migrate_pages(&pagelist, new_page, NULL, MPOL_MF_MOVE_ALL,
15921602
MIGRATE_SYNC, MR_MEMORY_FAILURE);
15931603
if (ret) {

0 commit comments

Comments
 (0)