Skip to content

Commit 5bebe8d

Browse files
committed
mm/huge_memory: Fix initialization of huge zero folio
The recent fix to properly initialize the tags of the huge zero folio had an unfortunate not-so-subtle side effect: it caused the actual *contents* of the huge zero folio to not be initialized at all when the hardware didn't support the memory tagging. The reason was the unfortunate semantics of tag_clear_highpage(): on hardware that didn't do the tagging, it would silently just not do anything at all. And since this is done only on arm64 with MTE support, that basically meant most hardware. It wasn't necessarily immediately obvious since the huge zero page isn't necessarily very heavily used - or because it might already be zero because all-zeroes is the most common pattern. But it ends up causing random odd user space failures when you do hit it. The unfortunate semantics have been around for a while, but became a real bug only when we started actively using __GFP_ZEROTAGS in the generic get_huge_zero_folio() function - before that, it had only ever been used in code that checked that the hardware supported it. Fix this by simply changing the semantics of tag_clear_highpage() to return whether it actually successfully did something or not. While at it, also make it initialize multiple pages in one go, since that's actually what the only caller wants it to do and it simplifies the whole logic. Fixes: adfb660 ("mm/huge_memory: initialise the tags of the huge zero folio") Link: https://lore.kernel.org/all/[email protected]/ Reviewed-by: David Hildenbrand (Red Hat) <[email protected]> Reported-and-tested-by: David Wang <[email protected]> Reported-and-tested-by: Carlos Llamas <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent e7c375b commit 5bebe8d

File tree

4 files changed

+19
-21
lines changed

4 files changed

+19
-21
lines changed

arch/arm64/include/asm/page.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ struct folio *vma_alloc_zeroed_movable_folio(struct vm_area_struct *vma,
3333
unsigned long vaddr);
3434
#define vma_alloc_zeroed_movable_folio vma_alloc_zeroed_movable_folio
3535

36-
void tag_clear_highpage(struct page *to);
37-
#define __HAVE_ARCH_TAG_CLEAR_HIGHPAGE
36+
bool tag_clear_highpages(struct page *to, int numpages);
37+
#define __HAVE_ARCH_TAG_CLEAR_HIGHPAGES
3838

3939
#define clear_user_page(page, vaddr, pg) clear_page(page)
4040
#define copy_user_page(to, from, vaddr, pg) copy_page(to, from)

arch/arm64/mm/fault.c

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -967,20 +967,21 @@ struct folio *vma_alloc_zeroed_movable_folio(struct vm_area_struct *vma,
967967
return vma_alloc_folio(flags, 0, vma, vaddr);
968968
}
969969

970-
void tag_clear_highpage(struct page *page)
970+
bool tag_clear_highpages(struct page *page, int numpages)
971971
{
972972
/*
973973
* Check if MTE is supported and fall back to clear_highpage().
974974
* get_huge_zero_folio() unconditionally passes __GFP_ZEROTAGS and
975-
* post_alloc_hook() will invoke tag_clear_highpage().
975+
* post_alloc_hook() will invoke tag_clear_highpages().
976976
*/
977-
if (!system_supports_mte()) {
978-
clear_highpage(page);
979-
return;
980-
}
977+
if (!system_supports_mte())
978+
return false;
981979

982-
/* Newly allocated page, shouldn't have been tagged yet */
983-
WARN_ON_ONCE(!try_page_mte_tagging(page));
984-
mte_zero_clear_page_tags(page_address(page));
985-
set_page_mte_tagged(page);
980+
/* Newly allocated pages, shouldn't have been tagged yet */
981+
for (int i = 0; i < numpages; i++, page++) {
982+
WARN_ON_ONCE(!try_page_mte_tagging(page));
983+
mte_zero_clear_page_tags(page_address(page));
984+
set_page_mte_tagged(page);
985+
}
986+
return true;
986987
}

include/linux/highmem.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,10 +249,12 @@ static inline void clear_highpage_kasan_tagged(struct page *page)
249249
kunmap_local(kaddr);
250250
}
251251

252-
#ifndef __HAVE_ARCH_TAG_CLEAR_HIGHPAGE
252+
#ifndef __HAVE_ARCH_TAG_CLEAR_HIGHPAGES
253253

254-
static inline void tag_clear_highpage(struct page *page)
254+
/* Return false to let people know we did not initialize the pages */
255+
static inline bool tag_clear_highpages(struct page *page, int numpages)
255256
{
257+
return false;
256258
}
257259

258260
#endif

mm/page_alloc.c

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1822,14 +1822,9 @@ inline void post_alloc_hook(struct page *page, unsigned int order,
18221822
* If memory tags should be zeroed
18231823
* (which happens only when memory should be initialized as well).
18241824
*/
1825-
if (zero_tags) {
1826-
/* Initialize both memory and memory tags. */
1827-
for (i = 0; i != 1 << order; ++i)
1828-
tag_clear_highpage(page + i);
1825+
if (zero_tags)
1826+
init = !tag_clear_highpages(page, 1 << order);
18291827

1830-
/* Take note that memory was initialized by the loop above. */
1831-
init = false;
1832-
}
18331828
if (!should_skip_kasan_unpoison(gfp_flags) &&
18341829
kasan_unpoison_pages(page, order, init)) {
18351830
/* Take note that memory was initialized by KASAN. */

0 commit comments

Comments
 (0)