alloc_hugetlb_folio() calls hugepage_subpool_get_pages() when map_chg is set. For subpools with max_hpages, that increments used_hpages. If the later hugetlb cgroup charge fails, the unwind must undo that charge even when gbl_chg > 0. hugepage_subpool_put_pages() can also restore rsv_hpages if concurrent frees move used_hpages below min_hpages between the get and put. When that happens on the gbl_chg > 0 path, restore the matching global reservation as well. Skip the gbl_chg > 0 put when max_hpages is unset. For a min_size-only subpool, get_pages() did not change subpool state and put_pages() would create a false reservation. Signed-off-by: Zhao Li --- Changes in v2: - Handle rsv_hpages restoration when racing frees cross min_hpages. - Skip gbl_chg > 0 put_pages() when max_hpages is unset. mm/hugetlb.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index f24bf49be047e..4065d66fdcb5c 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -3026,12 +3026,23 @@ struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma, h_cg); out_subpool_put: /* - * put page to subpool iff the quota of subpool's rsv_hpages is used - * during hugepage_subpool_get_pages. + * map_chg means hugepage_subpool_get_pages() succeeded above. + * If max_hpages accounting was touched, undo it. If racing frees + * moved the subpool below min_hpages, the put path may restore a + * subpool reservation. Restore the matching global reservation too. */ - if (map_chg && !gbl_chg) { - gbl_reserve = hugepage_subpool_put_pages(spool, 1); - hugetlb_acct_memory(h, -gbl_reserve); + if (map_chg) { + if (!gbl_chg) { + gbl_reserve = hugepage_subpool_put_pages(spool, 1); + hugetlb_acct_memory(h, -gbl_reserve); + } else if (spool && spool->max_hpages != -1) { + gbl_reserve = hugepage_subpool_put_pages(spool, 1); + if (!gbl_reserve) { + spin_lock_irq(&hugetlb_lock); + h->resv_huge_pages++; + spin_unlock_irq(&hugetlb_lock); + } + } } -- 2.50.1 (Apple Git-155)