Skip to content

Commit d8f5f7e

Browse files
mjkravetzakpm00
authored andcommitted
hugetlb: set hugetlb page flag before optimizing vmemmap
Currently, vmemmap optimization of hugetlb pages is performed before the hugetlb flag (previously hugetlb destructor) is set identifying it as a hugetlb folio. This means there is a window of time where an ordinary folio does not have all associated vmemmap present. The core mm only expects vmemmap to be potentially optimized for hugetlb and device dax. This can cause problems in code such as memory error handling that may want to write to tail struct pages. There is only one call to perform hugetlb vmemmap optimization today. To fix this issue, simply set the hugetlb flag before that call. There was a similar issue in the free hugetlb path that was previously addressed. The two routines that optimize or restore hugetlb vmemmap should only be passed hugetlb folios/pages. To catch any callers not following this rule, add VM_WARN_ON calls to the routines. In the hugetlb free code paths, some calls could be made to restore vmemmap after clearing the hugetlb flag. This was 'safe' as in these cases vmemmap was already present and the call was a NOOP. However, for consistency these calls where eliminated so that we can add the VM_WARN_ON checks. Link: https://lkml.kernel.org/r/[email protected] Fixes: f41f2ed ("mm: hugetlb: free the vmemmap pages associated with each HugeTLB page") Signed-off-by: Mike Kravetz <[email protected]> Reviewed-by: Muchun Song <[email protected]> Cc: James Houghton <[email protected]> Cc: Miaohe Lin <[email protected]> Cc: Michal Hocko <[email protected]> Cc: Naoya Horiguchi <[email protected]> Cc: Usama Arif <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent dd34d9f commit d8f5f7e

File tree

2 files changed

+25
-9
lines changed

2 files changed

+25
-9
lines changed

mm/hugetlb.c

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1720,7 +1720,12 @@ static void __update_and_free_hugetlb_folio(struct hstate *h,
17201720
if (folio_test_hugetlb_raw_hwp_unreliable(folio))
17211721
return;
17221722

1723-
if (hugetlb_vmemmap_restore(h, &folio->page)) {
1723+
/*
1724+
* If folio is not vmemmap optimized (!clear_dtor), then the folio
1725+
* is no longer identified as a hugetlb page. hugetlb_vmemmap_restore
1726+
* can only be passed hugetlb pages and will BUG otherwise.
1727+
*/
1728+
if (clear_dtor && hugetlb_vmemmap_restore(h, &folio->page)) {
17241729
spin_lock_irq(&hugetlb_lock);
17251730
/*
17261731
* If we cannot allocate vmemmap pages, just refuse to free the
@@ -1930,9 +1935,9 @@ static void __prep_account_new_huge_page(struct hstate *h, int nid)
19301935

19311936
static void __prep_new_hugetlb_folio(struct hstate *h, struct folio *folio)
19321937
{
1938+
folio_set_hugetlb(folio);
19331939
hugetlb_vmemmap_optimize(h, &folio->page);
19341940
INIT_LIST_HEAD(&folio->lru);
1935-
folio_set_hugetlb(folio);
19361941
hugetlb_set_folio_subpool(folio, NULL);
19371942
set_hugetlb_cgroup(folio, NULL);
19381943
set_hugetlb_cgroup_rsvd(folio, NULL);
@@ -3580,13 +3585,21 @@ static int demote_free_hugetlb_folio(struct hstate *h, struct folio *folio)
35803585
remove_hugetlb_folio_for_demote(h, folio, false);
35813586
spin_unlock_irq(&hugetlb_lock);
35823587

3583-
rc = hugetlb_vmemmap_restore(h, &folio->page);
3584-
if (rc) {
3585-
/* Allocation of vmemmmap failed, we can not demote folio */
3586-
spin_lock_irq(&hugetlb_lock);
3587-
folio_ref_unfreeze(folio, 1);
3588-
add_hugetlb_folio(h, folio, false);
3589-
return rc;
3588+
/*
3589+
* If vmemmap already existed for folio, the remove routine above would
3590+
* have cleared the hugetlb folio flag. Hence the folio is technically
3591+
* no longer a hugetlb folio. hugetlb_vmemmap_restore can only be
3592+
* passed hugetlb folios and will BUG otherwise.
3593+
*/
3594+
if (folio_test_hugetlb(folio)) {
3595+
rc = hugetlb_vmemmap_restore(h, &folio->page);
3596+
if (rc) {
3597+
/* Allocation of vmemmmap failed, we can not demote folio */
3598+
spin_lock_irq(&hugetlb_lock);
3599+
folio_ref_unfreeze(folio, 1);
3600+
add_hugetlb_folio(h, folio, false);
3601+
return rc;
3602+
}
35903603
}
35913604

35923605
/*

mm/hugetlb_vmemmap.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <linux/pgtable.h>
1414
#include <linux/moduleparam.h>
1515
#include <linux/bootmem_info.h>
16+
#include <linux/mmdebug.h>
1617
#include <asm/pgalloc.h>
1718
#include <asm/tlbflush.h>
1819
#include "hugetlb_vmemmap.h"
@@ -456,6 +457,7 @@ int hugetlb_vmemmap_restore(const struct hstate *h, struct page *head)
456457
unsigned long vmemmap_start = (unsigned long)head, vmemmap_end;
457458
unsigned long vmemmap_reuse;
458459

460+
VM_WARN_ON_ONCE(!PageHuge(head));
459461
if (!HPageVmemmapOptimized(head))
460462
return 0;
461463

@@ -550,6 +552,7 @@ void hugetlb_vmemmap_optimize(const struct hstate *h, struct page *head)
550552
unsigned long vmemmap_start = (unsigned long)head, vmemmap_end;
551553
unsigned long vmemmap_reuse;
552554

555+
VM_WARN_ON_ONCE(!PageHuge(head));
553556
if (!vmemmap_should_optimize(h, head))
554557
return;
555558

0 commit comments

Comments
 (0)