FAILED: patch "[PATCH] mm/hugetlb: fix avoid_reserve to allow taking folio from" failed to apply to 6.6-stable tree
Subsystems (3)
[email protected]
maintainerThe patch below does not apply to the 6.6-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.6.y
git checkout FETCH_HEAD
git cherry-pick -x 58db7c5fbe7daa42098d4965133a864f98ba90ba
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2025021020-copper-visibly-e6a9@gregkh' --subject-prefix 'PATCH 6.6.y' HEAD^..
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 58db7c5fbe7daa42098d4965133a864f98ba90ba Mon Sep 17 00:00:00 2001
From: Peter Xu <[email protected]>
Date: Tue, 7 Jan 2025 15:39:56 -0500
Subject: [PATCH] mm/hugetlb: fix avoid_reserve to allow taking folio from
subpool
Patch series "mm/hugetlb: Refactor hugetlb allocation resv accounting", v2.
This is a follow up on Ackerley's series here as replacement:
https://lore.kernel.org/r/[email protected]
The goal of this series is to cleanup hugetlb resv accounting, especially during folio allocation, to decouple a few things:
- Hugetlb folios v.s. Hugetlbfs: IOW, the hope is in the future hugetlb folios can be allocated completely without hugetlbfs.
- Decouple VMA v.s. hugetlb folio allocations: allocating a hugetlb folio should not always require a hugetlbfs VMA. For example, either it got allocated from the inode level (see hugetlbfs_fallocate() where it used a pesudo VMA for allocation), or it can be allocated by other kernel subsystems.
It paves way for other users to allocate hugetlb folios out of either system reservations, or subpools (instead of hugetlbfs, as a file system). For longer term, this prepares hugetlb as a separate concept versus hugetlbfs, so that hugetlb folios can be allocated by not only hugetlbfs and other things.
Tests I've done:
- I had a reproducer in patch 1 for the bug I found, this will start to work after patch 1 or the whole set applied.
- Hugetlb regression tests (on x86_64 2MBs), includes:
- All vmtests on hugetlbfs
- libhugetlbfs test suite (which may fail some tests, but no new failures will be introduced by this series, so all such failures happen before this series so shouldn't be relevant).
This patch (of 7):
Since commit 04f2cbe35699 ("hugetlb: guarantee that COW faults for a process that called mmap(MAP_PRIVATE) on hugetlbfs will succeed"), avoid_reserve was introduced for a special case of CoW on hugetlb private mappings, and only if the owner VMA is trying to allocate yet another hugetlb folio that is not reserved within the private vma reserved map.
Later on, in commit d85f69b0b533 ("mm/hugetlb: alloc_huge_page handle areas hole punched by fallocate"), alloc_huge_page() enforced to not consume any global reservation as long as avoid_reserve=true. This operation doesn't look correct, because even if it will enforce the allocation to not use global reservation at all, it will still try to take one reservation from the spool (if the subpool existed). Then since the spool reserved pages take from global reservation, it'll also take one reservation globally.
Logically it can cause global reservation to go wrong.
I wrote a reproducer below, trigger this special path, and every run of such program will cause global reservation count to increment by one, until it hits the number of free pages:
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mman.h>
#define MSIZE (2UL << 20)
int main(int argc, char *argv[])
{
const char *path;
int *buf;
int fd, ret;
pid_t child; if (argc < 2) {
printf("usage: %s <hugetlb_file>\n", argv[0]);
return -1;
}
path = argv[1];
fd = open(path, O_RDWR | O_CREAT, 0666);
if (fd < 0) {
perror("open failed");
return -1;
}
ret = fallocate(fd, 0, 0, MSIZE);
if (ret != 0) {
perror("fallocate");
return -1;
}
buf = mmap(NULL, MSIZE, PROT_READ|PROT_WRITE,
MAP_PRIVATE, fd, 0);
if (buf == MAP_FAILED) {
perror("mmap() failed");
return -1;
}
/* Allocate a page */
*buf = 1;
child = fork();
if (child == 0) {
/* child doesn't need to do anything */
exit(0);
}
/* Trigger CoW from owner */
*buf = 2;
munmap(buf, MSIZE);
close(fd);
unlink(path);
return 0;}
It can only reproduce with a sub-mount when there're reserved pages on the spool, like:
# sysctl vm.nr_hugepages=128 # mkdir ./hugetlb-pool # mount -t hugetlbfs -o min_size=8M,pagesize=2M none ./hugetlb-pool
Then run the reproducer on the mountpoint:
# ./reproducer ./hugetlb-pool/test
Fix it by taking the reservation from spool if available. In general, avoid_reserve is IMHO more about "avoid vma resv map", not spool's.
I copied stable, however I have no intention for backporting if it's not a clean cherry-pick, because private hugetlb mapping, and then fork() on top is too rare to hit.
mm/hugetlb.c +3 -19
@@ -1398,8 +1398,7 @@ static unsigned long available_huge_pages(struct hstate *h)static struct folio *dequeue_hugetlb_folio_vma (struct hstate *h,struct vm_area_struct *vma, -unsigned long address,int avoid_reserve, -long chg) +unsigned long address,long chg) {struct folio *folio =NULL ;struct mempolicy *mpol;
@@ -1415,10 +1414,6 @@ static struct folio *dequeue_hugetlb_folio_vma(struct hstate *h,if (!vma_has_reserves (vma, chg) && !available_huge_pages (h)) goto err; -/* If reserves cannot be used, ensure enough pages are in the pool */ -if (avoid_reserve && !available_huge_pages (h)) - goto err; - gfp_mask =htlb_alloc_mask (h); nid =huge_node (vma, address, gfp_mask, &mpol, &nodemask);
@@ -1434,7 +1429,7 @@ static struct folio *dequeue_hugetlb_folio_vma(struct hstate *h, folio =dequeue_hugetlb_folio_nodemask (h, gfp_mask, nid, nodemask); -if (folio && !avoid_reserve &&vma_has_reserves (vma, chg)) { +if (folio &&vma_has_reserves (vma, chg)) {folio_set_hugetlb_restore_reserve (folio); h->resv_huge_pages--; }
@@ -3051,17 +3046,6 @@ struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma, gbl_chg =hugepage_subpool_get_pages (spool,1 );if (gbl_chg <0 ) goto out_end_reservation; - -/* -* Even though there was no reservation in the region/reserve -* map, there could be reservations associated with the -* subpool that can be used. This would be indicated if the -* return value of hugepage_subpool_get_pages() is zero. -* However, if avoid_reserve is specified we still avoid even -* the subpool reservations. -*/ -if (avoid_reserve) - gbl_chg =1 ; }/* If this allocation is not consuming a reservation, charge it now.
@@ -3084,7 +3068,7 @@ struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma,* from the global free pool (global change). gbl_chg == 0 indicates * a reservation exists for the allocation. */ - folio =dequeue_hugetlb_folio_vma (h, vma, addr, avoid_reserve, gbl_chg); + folio =dequeue_hugetlb_folio_vma (h, vma, addr, gbl_chg);if (!folio) {spin_unlock_irq (&hugetlb_lock); folio =alloc_buddy_hugetlb_folio_with_mpol (h, vma, addr);
PATCH 1/1 mm/hugetlb: fix avoid_reserve to allow taking folio from subpool
From: Peter Xu <[email protected]>
commit 58db7c5fbe7daa42098d4965133a864f98ba90ba upstream.
Patch series "mm/hugetlb: Refactor hugetlb allocation resv accounting", v2.
This is a follow up on Ackerley's series here as replacement:
https://lore.kernel.org/r/[email protected]
The goal of this series is to cleanup hugetlb resv accounting, especially during folio allocation, to decouple a few things:
- Hugetlb folios v.s. Hugetlbfs: IOW, the hope is in the future hugetlb folios can be allocated completely without hugetlbfs.
- Decouple VMA v.s. hugetlb folio allocations: allocating a hugetlb folio should not always require a hugetlbfs VMA. For example, either it got allocated from the inode level (see hugetlbfs_fallocate() where it used a pesudo VMA for allocation), or it can be allocated by other kernel subsystems.
It paves way for other users to allocate hugetlb folios out of either system reservations, or subpools (instead of hugetlbfs, as a file system). For longer term, this prepares hugetlb as a separate concept versus hugetlbfs, so that hugetlb folios can be allocated by not only hugetlbfs and other things.
Tests I've done:
- I had a reproducer in patch 1 for the bug I found, this will start to work after patch 1 or the whole set applied.
- Hugetlb regression tests (on x86_64 2MBs), includes:
- All vmtests on hugetlbfs
- libhugetlbfs test suite (which may fail some tests, but no new failures will be introduced by this series, so all such failures happen before this series so shouldn't be relevant).
This patch (of 7):
Since commit 04f2cbe35699 ("hugetlb: guarantee that COW faults for a process that called mmap(MAP_PRIVATE) on hugetlbfs will succeed"), avoid_reserve was introduced for a special case of CoW on hugetlb private mappings, and only if the owner VMA is trying to allocate yet another hugetlb folio that is not reserved within the private vma reserved map.
Later on, in commit d85f69b0b533 ("mm/hugetlb: alloc_huge_page handle areas hole punched by fallocate"), alloc_huge_page() enforced to not consume any global reservation as long as avoid_reserve=true. This operation doesn't look correct, because even if it will enforce the allocation to not use global reservation at all, it will still try to take one reservation from the spool (if the subpool existed). Then since the spool reserved pages take from global reservation, it'll also take one reservation globally.
Logically it can cause global reservation to go wrong.
I wrote a reproducer below, trigger this special path, and every run of such program will cause global reservation count to increment by one, until it hits the number of free pages:
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mman.h>
#define MSIZE (2UL << 20)
int main(int argc, char *argv[])
{
const char *path;
int *buf;
int fd, ret;
pid_t child; if (argc < 2) {
printf("usage: %s <hugetlb_file>\n", argv[0]);
return -1;
}
path = argv[1];
fd = open(path, O_RDWR | O_CREAT, 0666);
if (fd < 0) {
perror("open failed");
return -1;
}
ret = fallocate(fd, 0, 0, MSIZE);
if (ret != 0) {
perror("fallocate");
return -1;
}
buf = mmap(NULL, MSIZE, PROT_READ|PROT_WRITE,
MAP_PRIVATE, fd, 0);
if (buf == MAP_FAILED) {
perror("mmap() failed");
return -1;
}
/* Allocate a page */
*buf = 1;
child = fork();
if (child == 0) {
/* child doesn't need to do anything */
exit(0);
}
/* Trigger CoW from owner */
*buf = 2;
munmap(buf, MSIZE);
close(fd);
unlink(path);
return 0;}
It can only reproduce with a sub-mount when there're reserved pages on the spool, like:
# sysctl vm.nr_hugepages=128 # mkdir ./hugetlb-pool # mount -t hugetlbfs -o min_size=8M,pagesize=2M none ./hugetlb-pool
Then run the reproducer on the mountpoint:
# ./reproducer ./hugetlb-pool/test
Fix it by taking the reservation from spool if available. In general, avoid_reserve is IMHO more about "avoid vma resv map", not spool's.
I copied stable, however I have no intention for backporting if it's not a clean cherry-pick, because private hugetlb mapping, and then fork() on top is too rare to hit.
[ Karl Mehltretter: Only the allocation-error context differs in this stable
tree. The backport retains this stable tree's direct -ENOSPC return instead of importing the later out_end_reservation path; the functional changes otherwise match upstream. ]
---
Tested against the current linux-6.6.y tip, 6.6.157:
- x86-64 QEMU, 2 MiB HugeTLB pages, at 1 and 4 vCPUs
- compared with the unpatched kernel, an extended version of the commit's fork/COW reproducer kept the child mapping alive during the parent write and changed HugePages_Rsvd after program cleanup from 5 to 4 and after unmount from 1 to 0
- parent/child data isolation passed and no kernel diagnostic was observed
mm/hugetlb.c | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-)
mm/hugetlb.c +3 -19
@@ -1398,8 +1398,7 @@ static unsigned long available_huge_pages(struct hstate *h)static struct folio *dequeue_hugetlb_folio_vma (struct hstate *h,struct vm_area_struct *vma, -unsigned long address,int avoid_reserve, -long chg) +unsigned long address,long chg) {struct folio *folio =NULL ;struct mempolicy *mpol;
@@ -1415,10 +1414,6 @@ static struct folio *dequeue_hugetlb_folio_vma(struct hstate *h,if (!vma_has_reserves (vma, chg) && !available_huge_pages (h)) goto err; -/* If reserves cannot be used, ensure enough pages are in the pool */ -if (avoid_reserve && !available_huge_pages (h)) - goto err; - gfp_mask =htlb_alloc_mask (h); nid =huge_node (vma, address, gfp_mask, &mpol, &nodemask);
@@ -1434,7 +1429,7 @@ static struct folio *dequeue_hugetlb_folio_vma(struct hstate *h, folio =dequeue_hugetlb_folio_nodemask (h, gfp_mask, nid, nodemask); -if (folio && !avoid_reserve &&vma_has_reserves (vma, chg)) { +if (folio &&vma_has_reserves (vma, chg)) {folio_set_hugetlb_restore_reserve (folio); h->resv_huge_pages--; }
@@ -3088,17 +3083,6 @@ struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma,vma_end_reservation (h, vma, addr);return ERR_PTR (-ENOSPC ); } - -/* -* Even though there was no reservation in the region/reserve -* map, there could be reservations associated with the -* subpool that can be used. This would be indicated if the -* return value of hugepage_subpool_get_pages() is zero. -* However, if avoid_reserve is specified we still avoid even -* the subpool reservations. -*/ -if (avoid_reserve) - gbl_chg =1 ; }/* If this allocation is not consuming a reservation, charge it now.
@@ -3121,7 +3105,7 @@ struct folio *alloc_hugetlb_folio(struct vm_area_struct *vma,* from the global free pool (global change). gbl_chg == 0 indicates * a reservation exists for the allocation. */ - folio =dequeue_hugetlb_folio_vma (h, vma, addr, avoid_reserve, gbl_chg); + folio =dequeue_hugetlb_folio_vma (h, vma, addr, gbl_chg);if (!folio) {spin_unlock_irq (&hugetlb_lock); folio =alloc_buddy_hugetlb_folio_with_mpol (h, vma, addr);
Signature · 1 line
2.53.0
Sasha Levin
maintainerRe: [PATCH 6.6.y] mm/hugetlb: fix avoid_reserve to allow taking folio from subpool
Quoted 3 lines
I copied stable, however I have no intention for backporting if it's not a clean cherry-pick, because private hugetlb mapping, and then
fork()on top is too rare to hit.
Peter, Oscar: given the above, I'd like an ack before taking these.
Karl sent this for 6.6, 6.1, 5.15 and 5.10. None of them is a clean pick, but the only differences are context lines and the older page/folio helper names, and the result matches what 6.12 has carried since 6.12.14. Are you OK with them going in? I'll queue all four if so.
Signature · 2 lines
Thanks, Sasha
Re: [PATCH 6.6.y] mm/hugetlb: fix avoid_reserve to allow taking folio from subpool
In reply to Karl MehltretterQuoted 7 lines · Karl Mehltretter
From: Peter Xu <[email protected]>
commit
58db7c5fbe7daa42098d4965133a864f98ba90baupstream.Patch series "mm/hugetlb: Refactor hugetlb allocation resv accounting", v2.
I can review this in more detail later. For now,
Quoted 4 lines
This is a follow up on Ackerley's series here as replacement:
This particular series linked was abandoned because an earlier version got merged instead.
88819619688c5 ("mm: hugetlb: consolidate interpretation of gbl_chg within alloc_hugetlb_folio()") was merged.
git log -n6 5737df3826dee4287f986dc2eaf7dc352068710d shows the 6 patches in that series.
If this patch replaces the linked series, the commit message should reference the upstream merged patch 88819619688c5 ("mm: hugetlb:
consolidate interpretation of gbl_chg within alloc_hugetlb_folio()") instead of the unmerged series.
This comment applies also for the other backports (and this one) for
+ 5.10.y
+ 5.15.y
+ 6.1.y
+ 6.6.y
Quoted 3 lines
[...snip...]
Sasha Levin
maintainerRe: [PATCH 6.6.y] mm/hugetlb: fix avoid_reserve to allow taking folio from subpool
In reply to Karl MehltretterQuoted 1 line
commit
58db7c5fbe7daa42098d4965133a864f98ba90baupstream.
Peter, Oscar: I will queue all four backports (6.6, 6.1, 5.15 and 5.10) on October 9 unless someone objects before then.
Signature · 2 lines
Thanks, Sasha
Sasha Levin
maintainerRe: [PATCH 6.6.y] mm/hugetlb: fix avoid_reserve to allow taking folio from subpool
In reply to Karl MehltretterQuoted 4 lines · Ackerley Tng
If this patch replaces the linked series, the commit message should reference the upstream merged patch
88819619688c5("mm: hugetlb:
consolidate interpretation ofgbl_chgwithinalloc_hugetlb_folio()") instead of the unmerged series.
The commit messages carry Peter Xu's upstream text verbatim, so the cover.1728684491 link stays. 88819619688c ("mm: hugetlb: consolidate interpretation of gbl_chg within alloc_hugetlb_folio()") only reached mainline in v7.3-rc1, well after 58db7c5fbe7d ("mm/hugetlb: fix avoid_reserve to allow taking folio from subpool"), where this text comes from.
Signature · 2 lines
Thanks, Sasha
Sasha Levin
maintainerRe: [PATCH 6.6.y] mm/hugetlb: fix avoid_reserve to allow taking folio from subpool
In reply to Karl MehltretterQuoted 2 lines
[ Karl Mehltretter: Only the allocation-error context differs in this stable
tree. The backport retains this stable tree's direct-ENOSPCreturn instead
No objections came in. Queued for 6.6, 6.1, 5.15 and 5.10, thanks.
Signature · 2 lines
Thanks, Sasha