Re: [External] Re: [PATCH v14 4/8] mm: hugetlb: alloc the vmemmap pages associated with each HugeTLB page
From: Muchun Song <hidden>
Date: 2021-02-06 08:03:34
Also in:
linux-fsdevel, linux-mm, lkml
On Fri, Feb 5, 2021 at 7:54 PM Oscar Salvador [off-list ref] wrote:
On Thu, Feb 04, 2021 at 11:50:39AM +0800, Muchun Song wrote:quoted
When we free a HugeTLB page to the buddy allocator, we should allocate the vmemmap pages associated with it. But we may cannot allocate vmemmap pages when the system is under memory pressure, in this case, we just refuse to free the HugeTLB page instead of looping forever trying to allocate the pages. Signed-off-by: Muchun Song <redacted>[...]quoted
diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 4cfca27c6d32..5518283aa667 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c@@ -1397,16 +1397,26 @@ static void __free_huge_page(struct page *page) h->resv_huge_pages++; if (HPageTemporary(page)) { - list_del(&page->lru); ClearHPageTemporary(page); + + if (alloc_huge_page_vmemmap(h, page, GFP_ATOMIC)) { + h->surplus_huge_pages++; + h->surplus_huge_pages_node[nid]++; + goto enqueue; + } + list_del(&page->lru); update_and_free_page(h, page); } else if (h->surplus_huge_pages_node[nid]) { + if (alloc_huge_page_vmemmap(h, page, GFP_ATOMIC)) + goto enqueue; + /* remove the page from active list */ list_del(&page->lru); update_and_free_page(h, page); h->surplus_huge_pages--; h->surplus_huge_pages_node[nid]--; } else { +enqueue: arch_clear_hugepage_flags(page); enqueue_huge_page(h, page);Ok, we just keep them in the pool in case we fail to allocate.quoted
diff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c index ddd872ab6180..0bd6b8d7282d 100644 --- a/mm/hugetlb_vmemmap.c +++ b/mm/hugetlb_vmemmap.c@@ -169,6 +169,8 @@ * (last) level. So this type of HugeTLB page can be optimized only when its * size of the struct page structs is greater than 2 pages.[...]quoted
+int alloc_huge_page_vmemmap(struct hstate *h, struct page *head, gfp_t gfp_mask) +{ + int ret; + unsigned long vmemmap_addr = (unsigned long)head; + unsigned long vmemmap_end, vmemmap_reuse; + + if (!free_vmemmap_pages_per_hpage(h)) + return 0; + + vmemmap_addr += RESERVE_VMEMMAP_SIZE; + vmemmap_end = vmemmap_addr + free_vmemmap_pages_size_per_hpage(h); + vmemmap_reuse = vmemmap_addr - PAGE_SIZE; + + /* + * The pages which the vmemmap virtual address range [@vmemmap_addr, + * @vmemmap_end) are mapped to are freed to the buddy allocator, and + * the range is mapped to the page which @vmemmap_reuse is mapped to. + * When a HugeTLB page is freed to the buddy allocator, previously + * discarded vmemmap pages must be allocated and remapping. + */ + ret = vmemmap_remap_alloc(vmemmap_addr, vmemmap_end, vmemmap_reuse, + gfp_mask | __GFP_NOWARN | __GFP_THISNODE);Why don't you set all the GFP flags here?
Originally, I wanted to let the caller know the GFP flag which they used. But setting all the GFP flags here also makes sense to me. And we can remove the @gfp_mask parameter of the alloc_huge_page_vmemmap. It is simple.
vmemmap_remap_alloc(vmemmap_addr, vmemmap_end, vmemmap_reuse, GFP_ATOMIC|
__GFP_NOWARN | __GFP_THISNODE) ?I will use this.
quoted
diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c index 50c1dc00b686..277eb43aebd5 100644 --- a/mm/sparse-vmemmap.c +++ b/mm/sparse-vmemmap.c[...]quoted
+static int alloc_vmemmap_page_list(unsigned long start, unsigned long end, + gfp_t gfp_mask, struct list_head *list)I think it would make more sense for this function to get the nid and the nr_pages to allocate directly.
Just like alloc_pages(), right? If so, make sense to me.
quoted
+{ + unsigned long addr; + int nid = page_to_nid((const void *)start);Uh, that void is a bit ugly. page_to_nid(struct page *)start). Do not need the const either.
OK. Will do. Thanks.
quoted
+ struct page *page, *next; + + for (addr = start; addr < end; addr += PAGE_SIZE) { + page = alloc_pages_node(nid, gfp_mask, 0); + if (!page) + goto out; + list_add_tail(&page->lru, list); + }and replace this by while(--nr_pages) etc.
OK. Will do.
I did not really go in depth, but looks good to me, and much more simply overall.
Yeah. The series only has 8 patches now. It is simpler.
The only thing I am not sure about is the use of GFP_ATOMIC. It has been raised before than when we are close to OOM, the user might want to try to free up some memory by dissolving free_huge_pages, and so we might want to dip in the reserves. Given the fact that we are prepared to fail, and that we do not retry, I would rather use GFP_KERNEL than to have X pages atomically allocated and then realize we need to drop them on the ground because we cannot go further at some point. I think those reserves would be better off used by someone else in that situation. But this is just my thoughs, and given the fact that there seems to be a consensus of susing GFP_ATOMIC. -- Oscar Salvador SUSE L3