Skip to content

Commit faf1c00

Browse files
osalvadorvilardagatorvalds
authored andcommitted
x86/vmemmap: optimize for consecutive sections in partial populated PMDs
We can optimize in the case we are adding consecutive sections, so no memset(PAGE_UNUSED) is needed. In that case, let us keep track where the unused range of the previous memory range begins, so we can compare it with start of the range to be added. If they are equal, we know sections are added consecutively. For that purpose, let us introduce 'unused_pmd_start', which always holds the beginning of the unused memory range. In the case a section does not contiguously follow the previous one, we know we can memset [unused_pmd_start, PMD_BOUNDARY) with PAGE_UNUSE. This patch is based on a similar patch by David Hildenbrand: https://lore.kernel.org/linux-mm/[email protected]/ Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Oscar Salvador <[email protected]> Acked-by: Dave Hansen <[email protected]> Cc: Andy Lutomirski <[email protected]> Cc: Borislav Petkov <[email protected]> Cc: "H . Peter Anvin" <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: Michal Hocko <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Thomas Gleixner <[email protected]> Cc: Zi Yan <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 8d40091 commit faf1c00

File tree

1 file changed

+61
-6
lines changed

1 file changed

+61
-6
lines changed

arch/x86/mm/init_64.c

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -829,17 +829,42 @@ void __init paging_init(void)
829829
#ifdef CONFIG_SPARSEMEM_VMEMMAP
830830
#define PAGE_UNUSED 0xFD
831831

832+
/*
833+
* The unused vmemmap range, which was not yet memset(PAGE_UNUSED), ranges
834+
* from unused_pmd_start to next PMD_SIZE boundary.
835+
*/
836+
static unsigned long unused_pmd_start __meminitdata;
837+
838+
static void __meminit vmemmap_flush_unused_pmd(void)
839+
{
840+
if (!unused_pmd_start)
841+
return;
842+
/*
843+
* Clears (unused_pmd_start, PMD_END]
844+
*/
845+
memset((void *)unused_pmd_start, PAGE_UNUSED,
846+
ALIGN(unused_pmd_start, PMD_SIZE) - unused_pmd_start);
847+
unused_pmd_start = 0;
848+
}
849+
850+
#ifdef CONFIG_MEMORY_HOTPLUG
832851
/* Returns true if the PMD is completely unused and thus it can be freed */
833852
static bool __meminit vmemmap_pmd_is_unused(unsigned long addr, unsigned long end)
834853
{
835854
unsigned long start = ALIGN_DOWN(addr, PMD_SIZE);
836855

856+
/*
857+
* Flush the unused range cache to ensure that memchr_inv() will work
858+
* for the whole range.
859+
*/
860+
vmemmap_flush_unused_pmd();
837861
memset((void *)addr, PAGE_UNUSED, end - addr);
838862

839863
return !memchr_inv((void *)start, PAGE_UNUSED, PMD_SIZE);
840864
}
865+
#endif
841866

842-
static void __meminit vmemmap_use_sub_pmd(unsigned long start)
867+
static void __meminit __vmemmap_use_sub_pmd(unsigned long start)
843868
{
844869
/*
845870
* As we expect to add in the same granularity as we remove, it's
@@ -851,23 +876,53 @@ static void __meminit vmemmap_use_sub_pmd(unsigned long start)
851876
memset((void *)start, 0, sizeof(struct page));
852877
}
853878

879+
static void __meminit vmemmap_use_sub_pmd(unsigned long start, unsigned long end)
880+
{
881+
/*
882+
* We only optimize if the new used range directly follows the
883+
* previously unused range (esp., when populating consecutive sections).
884+
*/
885+
if (unused_pmd_start == start) {
886+
if (likely(IS_ALIGNED(end, PMD_SIZE)))
887+
unused_pmd_start = 0;
888+
else
889+
unused_pmd_start = end;
890+
return;
891+
}
892+
893+
/*
894+
* If the range does not contiguously follows previous one, make sure
895+
* to mark the unused range of the previous one so it can be removed.
896+
*/
897+
vmemmap_flush_unused_pmd();
898+
__vmemmap_use_sub_pmd(start);
899+
}
900+
901+
854902
static void __meminit vmemmap_use_new_sub_pmd(unsigned long start, unsigned long end)
855903
{
904+
vmemmap_flush_unused_pmd();
905+
856906
/*
857907
* Could be our memmap page is filled with PAGE_UNUSED already from a
858908
* previous remove. Make sure to reset it.
859909
*/
860-
vmemmap_use_sub_pmd(start);
910+
__vmemmap_use_sub_pmd(start);
861911

862912
/*
863913
* Mark with PAGE_UNUSED the unused parts of the new memmap range
864914
*/
865915
if (!IS_ALIGNED(start, PMD_SIZE))
866916
memset((void *)start, PAGE_UNUSED,
867-
start - ALIGN_DOWN(start, PMD_SIZE));
917+
start - ALIGN_DOWN(start, PMD_SIZE));
918+
919+
/*
920+
* We want to avoid memset(PAGE_UNUSED) when populating the vmemmap of
921+
* consecutive sections. Remember for the last added PMD where the
922+
* unused range begins.
923+
*/
868924
if (!IS_ALIGNED(end, PMD_SIZE))
869-
memset((void *)end, PAGE_UNUSED,
870-
ALIGN(end, PMD_SIZE) - end);
925+
unused_pmd_start = end;
871926
}
872927
#endif
873928

@@ -1538,7 +1593,7 @@ static int __meminit vmemmap_populate_hugepages(unsigned long start,
15381593
return -ENOMEM; /* no fallback */
15391594
} else if (pmd_large(*pmd)) {
15401595
vmemmap_verify((pte_t *)pmd, node, addr, next);
1541-
vmemmap_use_sub_pmd(addr);
1596+
vmemmap_use_sub_pmd(addr, next);
15421597
continue;
15431598
}
15441599
if (vmemmap_populate_basepages(addr, next, node, NULL))

0 commit comments

Comments
 (0)