Skip to content

Commit d99e314

Browse files
Matthew Wilcox (Oracle)akpm00
authored andcommitted
mm: turn folio_test_hugetlb into a PageType
The current folio_test_hugetlb() can be fooled by a concurrent folio split into returning true for a folio which has never belonged to hugetlbfs. This can't happen if the caller holds a refcount on it, but we have a few places (memory-failure, compaction, procfs) which do not and should not take a speculative reference. Since hugetlb pages do not use individual page mapcounts (they are always fully mapped and use the entire_mapcount field to record the number of mappings), the PageType field is available now that page_mapcount() ignores the value in this field. In compaction and with CONFIG_DEBUG_VM enabled, the current implementation can result in an oops, as reported by Luis. This happens since 9c5ccf2 ("mm: remove HUGETLB_PAGE_DTOR") effectively added some VM_BUG_ON() checks in the PageHuge() testing path. [[email protected]: update vmcoreinfo] Link: https://lkml.kernel.org/r/[email protected] Link: https://lkml.kernel.org/r/[email protected] Fixes: 9c5ccf2 ("mm: remove HUGETLB_PAGE_DTOR") Signed-off-by: Matthew Wilcox (Oracle) <[email protected]> Reviewed-by: David Hildenbrand <[email protected]> Acked-by: Vlastimil Babka <[email protected]> Reported-by: Luis Chamberlain <[email protected]> Closes: https://bugzilla.kernel.org/show_bug.cgi?id=218227 Cc: Miaohe Lin <[email protected]> Cc: Muchun Song <[email protected]> Cc: Oscar Salvador <[email protected]> Cc: <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent fd1a745 commit d99e314

File tree

4 files changed

+39
-59
lines changed

4 files changed

+39
-59
lines changed

include/linux/page-flags.h

Lines changed: 33 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,6 @@ enum pageflags {
190190

191191
/* At least one page in this folio has the hwpoison flag set */
192192
PG_has_hwpoisoned = PG_error,
193-
PG_hugetlb = PG_active,
194193
PG_large_rmappable = PG_workingset, /* anon or file-backed */
195194
};
196195

@@ -876,29 +875,6 @@ TESTPAGEFLAG_FALSE(LargeRmappable, large_rmappable)
876875

877876
#define PG_head_mask ((1UL << PG_head))
878877

879-
#ifdef CONFIG_HUGETLB_PAGE
880-
int PageHuge(const struct page *page);
881-
SETPAGEFLAG(HugeTLB, hugetlb, PF_SECOND)
882-
CLEARPAGEFLAG(HugeTLB, hugetlb, PF_SECOND)
883-
884-
/**
885-
* folio_test_hugetlb - Determine if the folio belongs to hugetlbfs
886-
* @folio: The folio to test.
887-
*
888-
* Context: Any context. Caller should have a reference on the folio to
889-
* prevent it from being turned into a tail page.
890-
* Return: True for hugetlbfs folios, false for anon folios or folios
891-
* belonging to other filesystems.
892-
*/
893-
static inline bool folio_test_hugetlb(const struct folio *folio)
894-
{
895-
return folio_test_large(folio) &&
896-
test_bit(PG_hugetlb, const_folio_flags(folio, 1));
897-
}
898-
#else
899-
TESTPAGEFLAG_FALSE(Huge, hugetlb)
900-
#endif
901-
902878
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
903879
/*
904880
* PageHuge() only returns true for hugetlbfs pages, but not for
@@ -954,18 +930,6 @@ PAGEFLAG_FALSE(HasHWPoisoned, has_hwpoisoned)
954930
TESTSCFLAG_FALSE(HasHWPoisoned, has_hwpoisoned)
955931
#endif
956932

957-
/*
958-
* Check if a page is currently marked HWPoisoned. Note that this check is
959-
* best effort only and inherently racy: there is no way to synchronize with
960-
* failing hardware.
961-
*/
962-
static inline bool is_page_hwpoison(struct page *page)
963-
{
964-
if (PageHWPoison(page))
965-
return true;
966-
return PageHuge(page) && PageHWPoison(compound_head(page));
967-
}
968-
969933
/*
970934
* For pages that are never mapped to userspace (and aren't PageSlab),
971935
* page_type may be used. Because it is initialised to -1, we invert the
@@ -982,6 +946,7 @@ static inline bool is_page_hwpoison(struct page *page)
982946
#define PG_offline 0x00000100
983947
#define PG_table 0x00000200
984948
#define PG_guard 0x00000400
949+
#define PG_hugetlb 0x00000800
985950

986951
#define PageType(page, flag) \
987952
((page->page_type & (PAGE_TYPE_BASE | flag)) == PAGE_TYPE_BASE)
@@ -1076,6 +1041,37 @@ PAGE_TYPE_OPS(Table, table, pgtable)
10761041
*/
10771042
PAGE_TYPE_OPS(Guard, guard, guard)
10781043

1044+
#ifdef CONFIG_HUGETLB_PAGE
1045+
FOLIO_TYPE_OPS(hugetlb, hugetlb)
1046+
#else
1047+
FOLIO_TEST_FLAG_FALSE(hugetlb)
1048+
#endif
1049+
1050+
/**
1051+
* PageHuge - Determine if the page belongs to hugetlbfs
1052+
* @page: The page to test.
1053+
*
1054+
* Context: Any context.
1055+
* Return: True for hugetlbfs pages, false for anon pages or pages
1056+
* belonging to other filesystems.
1057+
*/
1058+
static inline bool PageHuge(const struct page *page)
1059+
{
1060+
return folio_test_hugetlb(page_folio(page));
1061+
}
1062+
1063+
/*
1064+
* Check if a page is currently marked HWPoisoned. Note that this check is
1065+
* best effort only and inherently racy: there is no way to synchronize with
1066+
* failing hardware.
1067+
*/
1068+
static inline bool is_page_hwpoison(struct page *page)
1069+
{
1070+
if (PageHWPoison(page))
1071+
return true;
1072+
return PageHuge(page) && PageHWPoison(compound_head(page));
1073+
}
1074+
10791075
extern bool is_free_buddy_page(struct page *page);
10801076

10811077
PAGEFLAG(Isolated, isolated, PF_ANY);
@@ -1142,7 +1138,7 @@ static __always_inline void __ClearPageAnonExclusive(struct page *page)
11421138
*/
11431139
#define PAGE_FLAGS_SECOND \
11441140
(0xffUL /* order */ | 1UL << PG_has_hwpoisoned | \
1145-
1UL << PG_hugetlb | 1UL << PG_large_rmappable)
1141+
1UL << PG_large_rmappable)
11461142

11471143
#define PAGE_FLAGS_PRIVATE \
11481144
(1UL << PG_private | 1UL << PG_private_2)

include/trace/events/mmflags.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ IF_HAVE_PG_ARCH_X(arch_3)
135135
#define DEF_PAGETYPE_NAME(_name) { PG_##_name, __stringify(_name) }
136136

137137
#define __def_pagetype_names \
138+
DEF_PAGETYPE_NAME(hugetlb), \
138139
DEF_PAGETYPE_NAME(offline), \
139140
DEF_PAGETYPE_NAME(guard), \
140141
DEF_PAGETYPE_NAME(table), \

kernel/vmcore_info.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -205,11 +205,10 @@ static int __init crash_save_vmcoreinfo_init(void)
205205
VMCOREINFO_NUMBER(PG_head_mask);
206206
#define PAGE_BUDDY_MAPCOUNT_VALUE (~PG_buddy)
207207
VMCOREINFO_NUMBER(PAGE_BUDDY_MAPCOUNT_VALUE);
208-
#ifdef CONFIG_HUGETLB_PAGE
209-
VMCOREINFO_NUMBER(PG_hugetlb);
208+
#define PAGE_HUGETLB_MAPCOUNT_VALUE (~PG_hugetlb)
209+
VMCOREINFO_NUMBER(PAGE_HUGETLB_MAPCOUNT_VALUE);
210210
#define PAGE_OFFLINE_MAPCOUNT_VALUE (~PG_offline)
211211
VMCOREINFO_NUMBER(PAGE_OFFLINE_MAPCOUNT_VALUE);
212-
#endif
213212

214213
#ifdef CONFIG_KALLSYMS
215214
VMCOREINFO_SYMBOL(kallsyms_names);

mm/hugetlb.c

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1624,7 +1624,7 @@ static inline void __clear_hugetlb_destructor(struct hstate *h,
16241624
{
16251625
lockdep_assert_held(&hugetlb_lock);
16261626

1627-
folio_clear_hugetlb(folio);
1627+
__folio_clear_hugetlb(folio);
16281628
}
16291629

16301630
/*
@@ -1711,7 +1711,7 @@ static void add_hugetlb_folio(struct hstate *h, struct folio *folio,
17111711
h->surplus_huge_pages_node[nid]++;
17121712
}
17131713

1714-
folio_set_hugetlb(folio);
1714+
__folio_set_hugetlb(folio);
17151715
folio_change_private(folio, NULL);
17161716
/*
17171717
* We have to set hugetlb_vmemmap_optimized again as above
@@ -2049,7 +2049,7 @@ static void __prep_account_new_huge_page(struct hstate *h, int nid)
20492049

20502050
static void init_new_hugetlb_folio(struct hstate *h, struct folio *folio)
20512051
{
2052-
folio_set_hugetlb(folio);
2052+
__folio_set_hugetlb(folio);
20532053
INIT_LIST_HEAD(&folio->lru);
20542054
hugetlb_set_folio_subpool(folio, NULL);
20552055
set_hugetlb_cgroup(folio, NULL);
@@ -2159,22 +2159,6 @@ static bool prep_compound_gigantic_folio_for_demote(struct folio *folio,
21592159
return __prep_compound_gigantic_folio(folio, order, true);
21602160
}
21612161

2162-
/*
2163-
* PageHuge() only returns true for hugetlbfs pages, but not for normal or
2164-
* transparent huge pages. See the PageTransHuge() documentation for more
2165-
* details.
2166-
*/
2167-
int PageHuge(const struct page *page)
2168-
{
2169-
const struct folio *folio;
2170-
2171-
if (!PageCompound(page))
2172-
return 0;
2173-
folio = page_folio(page);
2174-
return folio_test_hugetlb(folio);
2175-
}
2176-
EXPORT_SYMBOL_GPL(PageHuge);
2177-
21782162
/*
21792163
* Find and lock address space (mapping) in write mode.
21802164
*

0 commit comments

Comments
 (0)