Skip to content

Commit 6e00350

Browse files
jpemartinsopsiff
authored andcommitted
hugetlb: batch TLB flushes when freeing vmemmap
mainline inclusion from mainline-v6.7-rc1 category: performance Now that a list of pages is deduplicated at once, the TLB flush can be batched for all vmemmap pages that got remapped. Expand the flags field value to pass whether to skip the TLB flush on remap of the PTE. The TLB flush is global as we don't have guarantees from caller that the set of folios is contiguous, or to add complexity in composing a list of kVAs to flush. Modified by Mike Kravetz to perform TLB flush on single folio if an error is encountered. Link: https://lkml.kernel.org/r/20231019023113.345257-8-mike.kravetz@oracle.com Signed-off-by: Joao Martins <joao.m.martins@oracle.com> Signed-off-by: Mike Kravetz <mike.kravetz@oracle.com> Reviewed-by: Muchun Song <songmuchun@bytedance.com> Cc: Anshuman Khandual <anshuman.khandual@arm.com> Cc: Barry Song <21cnbao@gmail.com> Cc: David Hildenbrand <david@redhat.com> Cc: David Rientjes <rientjes@google.com> Cc: James Houghton <jthoughton@google.com> Cc: Konrad Dybcio <konradybcio@kernel.org> Cc: Matthew Wilcox (Oracle) <willy@infradead.org> Cc: Miaohe Lin <linmiaohe@huawei.com> Cc: Michal Hocko <mhocko@suse.com> Cc: Naoya Horiguchi <naoya.horiguchi@linux.dev> Cc: Oscar Salvador <osalvador@suse.de> Cc: Sergey Senozhatsky <senozhatsky@chromium.org> Cc: Usama Arif <usama.arif@bytedance.com> Cc: Xiongchun Duan <duanxiongchun@bytedance.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> (cherry picked from commit f13b83f) Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
1 parent e974dd0 commit 6e00350

1 file changed

Lines changed: 38 additions & 11 deletions

File tree

mm/hugetlb_vmemmap.c

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ struct vmemmap_remap_walk {
4040

4141
/* Skip the TLB flush when we split the PMD */
4242
#define VMEMMAP_SPLIT_NO_TLB_FLUSH BIT(0)
43+
/* Skip the TLB flush when we remap the PTE */
44+
#define VMEMMAP_REMAP_NO_TLB_FLUSH BIT(1)
4345
unsigned long flags;
4446
};
4547

@@ -214,7 +216,7 @@ static int vmemmap_remap_range(unsigned long start, unsigned long end,
214216
return ret;
215217
} while (pgd++, addr = next, addr != end);
216218

217-
if (walk->remap_pte)
219+
if (walk->remap_pte && !(walk->flags & VMEMMAP_REMAP_NO_TLB_FLUSH))
218220
flush_tlb_kernel_range(start, end);
219221

220222
return 0;
@@ -355,19 +357,21 @@ static int vmemmap_remap_split(unsigned long start, unsigned long end,
355357
* @reuse: reuse address.
356358
* @vmemmap_pages: list to deposit vmemmap pages to be freed. It is callers
357359
* responsibility to free pages.
360+
* @flags: modifications to vmemmap_remap_walk flags
358361
*
359362
* Return: %0 on success, negative error code otherwise.
360363
*/
361364
static int vmemmap_remap_free(unsigned long start, unsigned long end,
362365
unsigned long reuse,
363-
struct list_head *vmemmap_pages)
366+
struct list_head *vmemmap_pages,
367+
unsigned long flags)
364368
{
365369
int ret;
366370
struct vmemmap_remap_walk walk = {
367371
.remap_pte = vmemmap_remap_pte,
368372
.reuse_addr = reuse,
369373
.vmemmap_pages = vmemmap_pages,
370-
.flags = 0,
374+
.flags = flags,
371375
};
372376
int nid = page_to_nid((struct page *)reuse);
373377
gfp_t gfp_mask = GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN;
@@ -629,7 +633,8 @@ static bool vmemmap_should_optimize(const struct hstate *h, const struct page *h
629633

630634
static int __hugetlb_vmemmap_optimize(const struct hstate *h,
631635
struct page *head,
632-
struct list_head *vmemmap_pages)
636+
struct list_head *vmemmap_pages,
637+
unsigned long flags)
633638
{
634639
int ret = 0;
635640
unsigned long vmemmap_start = (unsigned long)head, vmemmap_end;
@@ -640,6 +645,18 @@ static int __hugetlb_vmemmap_optimize(const struct hstate *h,
640645
return ret;
641646

642647
static_branch_inc(&hugetlb_optimize_vmemmap_key);
648+
/*
649+
* Very Subtle
650+
* If VMEMMAP_REMAP_NO_TLB_FLUSH is set, TLB flushing is not performed
651+
* immediately after remapping. As a result, subsequent accesses
652+
* and modifications to struct pages associated with the hugetlb
653+
* page could be to the OLD struct pages. Set the vmemmap optimized
654+
* flag here so that it is copied to the new head page. This keeps
655+
* the old and new struct pages in sync.
656+
* If there is an error during optimization, we will immediately FLUSH
657+
* the TLB and clear the flag below.
658+
*/
659+
SetHPageVmemmapOptimized(head);
643660

644661
vmemmap_end = vmemmap_start + hugetlb_vmemmap_size(h);
645662
vmemmap_reuse = vmemmap_start;
@@ -651,11 +668,12 @@ static int __hugetlb_vmemmap_optimize(const struct hstate *h,
651668
* mapping the range to vmemmap_pages list so that they can be freed by
652669
* the caller.
653670
*/
654-
ret = vmemmap_remap_free(vmemmap_start, vmemmap_end, vmemmap_reuse, vmemmap_pages);
655-
if (ret)
671+
ret = vmemmap_remap_free(vmemmap_start, vmemmap_end, vmemmap_reuse,
672+
vmemmap_pages, flags);
673+
if (ret) {
656674
static_branch_dec(&hugetlb_optimize_vmemmap_key);
657-
else
658-
SetHPageVmemmapOptimized(head);
675+
ClearHPageVmemmapOptimized(head);
676+
}
659677

660678
return ret;
661679
}
@@ -674,7 +692,7 @@ void hugetlb_vmemmap_optimize(const struct hstate *h, struct page *head)
674692
{
675693
LIST_HEAD(vmemmap_pages);
676694

677-
__hugetlb_vmemmap_optimize(h, head, &vmemmap_pages);
695+
__hugetlb_vmemmap_optimize(h, head, &vmemmap_pages, 0);
678696
free_vmemmap_page_list(&vmemmap_pages);
679697
}
680698

@@ -719,19 +737,28 @@ void hugetlb_vmemmap_optimize_folios(struct hstate *h, struct list_head *folio_l
719737

720738
list_for_each_entry(folio, folio_list, lru) {
721739
int ret = __hugetlb_vmemmap_optimize(h, &folio->page,
722-
&vmemmap_pages);
740+
&vmemmap_pages,
741+
VMEMMAP_REMAP_NO_TLB_FLUSH);
723742

724743
/*
725744
* Pages to be freed may have been accumulated. If we
726745
* encounter an ENOMEM, free what we have and try again.
746+
* This can occur in the case that both spliting fails
747+
* halfway and head page allocation also failed. In this
748+
* case __hugetlb_vmemmap_optimize() would free memory
749+
* allowing more vmemmap remaps to occur.
727750
*/
728751
if (ret == -ENOMEM && !list_empty(&vmemmap_pages)) {
752+
flush_tlb_all();
729753
free_vmemmap_page_list(&vmemmap_pages);
730754
INIT_LIST_HEAD(&vmemmap_pages);
731-
__hugetlb_vmemmap_optimize(h, &folio->page, &vmemmap_pages);
755+
__hugetlb_vmemmap_optimize(h, &folio->page,
756+
&vmemmap_pages,
757+
VMEMMAP_REMAP_NO_TLB_FLUSH);
732758
}
733759
}
734760

761+
flush_tlb_all();
735762
free_vmemmap_page_list(&vmemmap_pages);
736763
}
737764

0 commit comments

Comments
 (0)