__hugetlb_acct_memory() today does more than just memory accounting. When there's insufficient HugeTLB pages, __hugetlb_acct_memory() will attempt to get surplus pages. This change adds a flag to disable getting surplus pages if there are insufficient HugeTLB pages. Signed-off-by: Ackerley Tng <ackerleytng@xxxxxxxxxx> Change-Id: Id79fdeaa236b4fed38fc3c20482b03fff729198f --- fs/hugetlbfs/inode.c | 2 +- include/linux/hugetlb.h | 2 +- mm/hugetlb.c | 77 +++++++++++++++++++++++++++++++---------- 3 files changed, 61 insertions(+), 20 deletions(-) diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index e4de5425838d..609a88950354 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -1424,7 +1424,7 @@ hugetlbfs_fill_super(struct super_block *sb, struct fs_context *fc) if (ctx->max_hpages != -1 || ctx->min_hpages != -1) { sbinfo->spool = hugepage_new_subpool(ctx->hstate, ctx->max_hpages, - ctx->min_hpages); + ctx->min_hpages, true); if (!sbinfo->spool) goto out_free; } diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index 8ba941d88956..c59264391c33 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -116,7 +116,7 @@ extern int hugetlb_max_hstate __read_mostly; for ((h) = hstates; (h) < &hstates[hugetlb_max_hstate]; (h)++) struct hugepage_subpool *hugepage_new_subpool(struct hstate *h, long max_hpages, - long min_hpages); + long min_hpages, bool use_surplus); void hugepage_put_subpool(struct hugepage_subpool *spool); void hugetlb_dup_vma_private(struct vm_area_struct *vma); diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 5b088fe002a2..d22c5a8fd441 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -115,6 +115,7 @@ static int num_fault_mutexes __ro_after_init; struct mutex *hugetlb_fault_mutex_table __ro_after_init; /* Forward declaration */ +static int __hugetlb_acct_memory(struct hstate *h, long delta, bool use_surplus); static int hugetlb_acct_memory(struct hstate *h, long delta); static void hugetlb_vma_lock_free(struct vm_area_struct *vma); static void hugetlb_vma_lock_alloc(struct vm_area_struct *vma); @@ -162,7 +163,7 @@ static inline void unlock_or_release_subpool(struct hugepage_subpool *spool, } struct hugepage_subpool *hugepage_new_subpool(struct hstate *h, long max_hpages, - long min_hpages) + long min_hpages, bool use_surplus) { struct hugepage_subpool *spool; @@ -176,7 +177,8 @@ struct hugepage_subpool *hugepage_new_subpool(struct hstate *h, long max_hpages, spool->hstate = h; spool->min_hpages = min_hpages; - if (min_hpages != -1 && hugetlb_acct_memory(h, min_hpages)) { + if (min_hpages != -1 && + __hugetlb_acct_memory(h, min_hpages, use_surplus)) { kfree(spool); return NULL; } @@ -2382,35 +2384,64 @@ static nodemask_t *policy_mbind_nodemask(gfp_t gfp) return NULL; } -/* - * Increase the hugetlb pool such that it can accommodate a reservation - * of size 'delta'. +/** + * hugetlb_hstate_reserve_pages() - Reserve @requested number of hugetlb pages + * from hstate @h. + * + * @h: the hstate to reserve from. + * @requested: number of hugetlb pages to reserve. + * + * If there are insufficient available hugetlb pages, no reservations are made. + * + * Return: the number of surplus pages required to meet the @requested number of + * hugetlb pages. */ -static int gather_surplus_pages(struct hstate *h, long delta) +static int hugetlb_hstate_reserve_pages(struct hstate *h, long requested) + __must_hold(&hugetlb_lock) +{ + long needed; + + needed = (h->resv_huge_pages + requested) - h->free_huge_pages; + if (needed <= 0) { + h->resv_huge_pages += requested; + return 0; + } + + return needed; +} + +/** + * gather_surplus_pages() - Increase the hugetlb pool such that it can + * accommodate a reservation of size @requested. + * + * @h: the hstate in concern. + * @requested: The requested number of hugetlb pages. + * @needed: The number of hugetlb pages the pool needs to be increased by, based + * on current number of reservations and free hugetlb pages. + * + * Return: 0 if successful or negative error otherwise. + */ +static int gather_surplus_pages(struct hstate *h, long requested, long needed) __must_hold(&hugetlb_lock) { LIST_HEAD(surplus_list); struct folio *folio, *tmp; int ret; long i; - long needed, allocated; + long allocated; bool alloc_ok = true; int node; nodemask_t *mbind_nodemask, alloc_nodemask; + if (needed == 0) + return 0; + mbind_nodemask = policy_mbind_nodemask(htlb_alloc_mask(h)); if (mbind_nodemask) nodes_and(alloc_nodemask, *mbind_nodemask, cpuset_current_mems_allowed); else alloc_nodemask = cpuset_current_mems_allowed; - lockdep_assert_held(&hugetlb_lock); - needed = (h->resv_huge_pages + delta) - h->free_huge_pages; - if (needed <= 0) { - h->resv_huge_pages += delta; - return 0; - } - allocated = 0; ret = -ENOMEM; @@ -2448,7 +2479,7 @@ static int gather_surplus_pages(struct hstate *h, long delta) * because either resv_huge_pages or free_huge_pages may have changed. */ spin_lock_irq(&hugetlb_lock); - needed = (h->resv_huge_pages + delta) - + needed = (h->resv_huge_pages + requested) - (h->free_huge_pages + allocated); if (needed > 0) { if (alloc_ok) @@ -2469,7 +2500,7 @@ static int gather_surplus_pages(struct hstate *h, long delta) * before they are reserved. */ needed += allocated; - h->resv_huge_pages += delta; + h->resv_huge_pages += requested; ret = 0; /* Free the needed pages to the hugetlb pool */ @@ -5284,7 +5315,7 @@ unsigned long hugetlb_total_pages(void) return nr_total_pages; } -static int hugetlb_acct_memory(struct hstate *h, long delta) +static int __hugetlb_acct_memory(struct hstate *h, long delta, bool use_surplus) { int ret = -ENOMEM; @@ -5316,7 +5347,12 @@ static int hugetlb_acct_memory(struct hstate *h, long delta) * above. */ if (delta > 0) { - if (gather_surplus_pages(h, delta) < 0) + long needed = hugetlb_hstate_reserve_pages(h, delta); + + if (!use_surplus && needed > 0) + goto out; + + if (gather_surplus_pages(h, delta, needed) < 0) goto out; if (delta > allowed_mems_nr(h)) { @@ -5334,6 +5370,11 @@ static int hugetlb_acct_memory(struct hstate *h, long delta) return ret; } +static int hugetlb_acct_memory(struct hstate *h, long delta) +{ + return __hugetlb_acct_memory(h, delta, true); +} + static void hugetlb_vm_op_open(struct vm_area_struct *vma) { struct resv_map *resv = vma_resv_map(vma); -- 2.49.0.1045.g170613ef41-goog