aboutsummaryrefslogtreecommitdiffstats
path: root/mm
diff options
context:
space:
mode:
authorNaoya Horiguchi <n-horiguchi@ah.jp.nec.com>2015-04-15 19:14:38 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2015-04-15 19:35:19 -0400
commitbcc54222309c70ebcb6c69c156fba4a13dee0a3b (patch)
tree2d947c66bdc0391d5294dd5fdd87cbabfa032020 /mm
parent822fc61367f062d36c5b5a4d517e9bd2b65a741f (diff)
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>
Diffstat (limited to 'mm')
-rw-r--r--mm/hugetlb.c41
-rw-r--r--mm/memory-failure.c14
2 files changed, 50 insertions, 5 deletions
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 995c8d65a95c..05407831016b 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -924,6 +924,31 @@ struct hstate *size_to_hstate(unsigned long size)
924 return NULL; 924 return NULL;
925} 925}
926 926
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 */
933bool 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 */
940static void set_page_huge_active(struct page *page)
941{
942 VM_BUG_ON_PAGE(!PageHeadHuge(page), page);
943 SetPagePrivate(&page[1]);
944}
945
946static void clear_page_huge_active(struct page *page)
947{
948 VM_BUG_ON_PAGE(!PageHeadHuge(page), page);
949 ClearPagePrivate(&page[1]);
950}
951
927void free_huge_page(struct page *page) 952void free_huge_page(struct page *page)
928{ 953{
929 /* 954 /*
@@ -952,6 +977,7 @@ void free_huge_page(struct page *page)
952 restore_reserve = true; 977 restore_reserve = true;
953 978
954 spin_lock(&hugetlb_lock); 979 spin_lock(&hugetlb_lock);
980 clear_page_huge_active(page);
955 hugetlb_cgroup_uncharge_page(hstate_index(h), 981 hugetlb_cgroup_uncharge_page(hstate_index(h),
956 pages_per_huge_page(h), page); 982 pages_per_huge_page(h), page);
957 if (restore_reserve) 983 if (restore_reserve)
@@ -2972,6 +2998,7 @@ retry_avoidcopy:
2972 copy_user_huge_page(new_page, old_page, address, vma, 2998 copy_user_huge_page(new_page, old_page, address, vma,
2973 pages_per_huge_page(h)); 2999 pages_per_huge_page(h));
2974 __SetPageUptodate(new_page); 3000 __SetPageUptodate(new_page);
3001 set_page_huge_active(new_page);
2975 3002
2976 mmun_start = address & huge_page_mask(h); 3003 mmun_start = address & huge_page_mask(h);
2977 mmun_end = mmun_start + huge_page_size(h); 3004 mmun_end = mmun_start + huge_page_size(h);
@@ -3084,6 +3111,7 @@ retry:
3084 } 3111 }
3085 clear_huge_page(page, address, pages_per_huge_page(h)); 3112 clear_huge_page(page, address, pages_per_huge_page(h));
3086 __SetPageUptodate(page); 3113 __SetPageUptodate(page);
3114 set_page_huge_active(page);
3087 3115
3088 if (vma->vm_flags & VM_MAYSHARE) { 3116 if (vma->vm_flags & VM_MAYSHARE) {
3089 int err; 3117 int err;
@@ -3913,19 +3941,26 @@ int dequeue_hwpoisoned_huge_page(struct page *hpage)
3913 3941
3914bool isolate_huge_page(struct page *page, struct list_head *list) 3942bool isolate_huge_page(struct page *page, struct list_head *list)
3915{ 3943{
3944 bool ret = true;
3945
3916 VM_BUG_ON_PAGE(!PageHead(page), page); 3946 VM_BUG_ON_PAGE(!PageHead(page), page);
3917 if (!get_page_unless_zero(page))
3918 return false;
3919 spin_lock(&hugetlb_lock); 3947 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);
3920 list_move_tail(&page->lru, list); 3953 list_move_tail(&page->lru, list);
3954unlock:
3921 spin_unlock(&hugetlb_lock); 3955 spin_unlock(&hugetlb_lock);
3922 return true; 3956 return ret;
3923} 3957}
3924 3958
3925void putback_active_hugepage(struct page *page) 3959void putback_active_hugepage(struct page *page)
3926{ 3960{
3927 VM_BUG_ON_PAGE(!PageHead(page), page); 3961 VM_BUG_ON_PAGE(!PageHead(page), page);
3928 spin_lock(&hugetlb_lock); 3962 spin_lock(&hugetlb_lock);
3963 set_page_huge_active(page);
3929 list_move_tail(&page->lru, &(page_hstate(page))->hugepage_activelist); 3964 list_move_tail(&page->lru, &(page_hstate(page))->hugepage_activelist);
3930 spin_unlock(&hugetlb_lock); 3965 spin_unlock(&hugetlb_lock);
3931 put_page(page); 3966 put_page(page);
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 5fd8931d8c31..d9359b770cd9 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -1586,8 +1586,18 @@ static int soft_offline_huge_page(struct page *page, int flags)
1586 } 1586 }
1587 unlock_page(hpage); 1587 unlock_page(hpage);
1588 1588
1589 /* Keep page count to indicate a given hugepage is isolated. */ 1589 ret = isolate_huge_page(hpage, &pagelist);
1590 list_move(&hpage->lru, &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
1591 ret = migrate_pages(&pagelist, new_page, NULL, MPOL_MF_MOVE_ALL, 1601 ret = migrate_pages(&pagelist, new_page, NULL, MPOL_MF_MOVE_ALL,
1592 MIGRATE_SYNC, MR_MEMORY_FAILURE); 1602 MIGRATE_SYNC, MR_MEMORY_FAILURE);
1593 if (ret) { 1603 if (ret) {