diff --git a/include/linux/vmemmap-optimization.h b/include/linux/vmemmap-optimization.h
index 065840f3c241..858311a71b0c 100644
--- a/include/linux/vmemmap-optimization.h
+++ b/include/linux/vmemmap-optimization.h
@@ -91,11 +91,13 @@ static inline struct page *vmemmap_shared_tail_page(unsigned int order,
}
#endif /* CONFIG_VMEMMAP_OPTIMIZATION */
-static inline bool vmemmap_optimizable_pfn(unsigned long pfn)
+static inline bool vmemmap_optimizable_pfn(unsigned long pfn, unsigned int order)
{
- const unsigned int order = pfn_to_section_compound_order(pfn);
const unsigned long nr_pages = 1UL << order;
+ if (!IS_ENABLED(CONFIG_VMEMMAP_OPTIMIZATION))
+ return false;
+
if (!is_power_of_2(sizeof(struct page)))
return false;
diff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c
index 5e84004db4b8..d0af251aa533 100644
--- a/mm/hugetlb_vmemmap.c
+++ b/mm/hugetlb_vmemmap.c
@@ -25,21 +25,17 @@
*
* @remap_pte: called for each lowest-level entry (PTE).
* @nr_walked: the number of walked pte.
- * @vmemmap_tail: the page to be installed as non-first in the vmemmap range
* @vmemmap_pages: list supplying replacement pages for the remap and
* receiving displaced backing pages for the caller to free.
* @flags: used to modify behavior in vmemmap page table walking
* operations.
*/
struct vmemmap_remap_walk {
- void (*remap_pte)(pte_t *pte, unsigned long addr,
+ int (*remap_pte)(pte_t *pte, unsigned long addr,
struct vmemmap_remap_walk *walk);
-
unsigned long nr_walked;
- struct page *vmemmap_tail;
struct list_head *vmemmap_pages;
-
/* Defer safely batchable TLB flushes to the caller. */
#define VMEMMAP_DEFER_TLB_FLUSH BIT(0)
unsigned long flags;@@ -140,12 +136,14 @@ static int vmemmap_pmd_entry(pmd_t *pmd, unsigned long addr,
static int vmemmap_pte_entry(pte_t *pte, unsigned long addr,
unsigned long next, struct mm_walk *walk)
{
+ int ret;
struct vmemmap_remap_walk *vmemmap_walk = walk->private;
- vmemmap_walk->remap_pte(pte, addr, vmemmap_walk);
- vmemmap_walk->nr_walked++;
+ ret = vmemmap_walk->remap_pte(pte, addr, vmemmap_walk);
+ if (!ret)
+ vmemmap_walk->nr_walked++;
- return 0;
+ return ret;
}
static const struct mm_walk_ops vmemmap_remap_ops = {@@ -213,53 +211,53 @@ static struct page *vmemmap_dup_page_from_list(const struct page *src,
return dst;
}
-static void vmemmap_optimize_pte(pte_t *pte, unsigned long addr,
- struct vmemmap_remap_walk *walk)
+static inline struct page *folio_vmemmap_shared_tail_page(const struct folio *folio)
{
- struct page *page = pte_page(ptep_get(pte));
- pte_t entry;
-
- /* Remapping the head page requires r/w */
- if (unlikely(walk->nr_walked == 0)) {
- struct page *head;
+ return vmemmap_shared_tail_page(folio_order(folio), folio_zone(folio));
+}
- VM_WARN_ON_ONCE(!PageHead((const struct page *)addr));
+static int vmemmap_optimize_pte(pte_t *pte, unsigned long addr,
+ struct vmemmap_remap_walk *walk)
+{
+ const struct page *page = (const struct page *)addr;
+ const struct folio *folio = page_folio(page);
+ struct page *src = pte_page(ptep_get(pte)), *dst;
- head = vmemmap_dup_page_from_list(page, walk->vmemmap_pages);
- entry = mk_pte(head, PAGE_KERNEL);
+ if (likely(vmemmap_optimizable_pfn(page_to_pfn(page), folio_order(folio)))) {
+ dst = folio_vmemmap_shared_tail_page(folio);
+ if (!dst)
+ return -ENOMEM;
} else {
- VM_WARN_ON_ONCE(!PageTail((const struct page *)addr));
-
- /*
- * Map the shared tail page writable. A lockless get that read
- * a non-zero refcount before the remap can still do its
- * cmpxchg after it. The refcount is zero, so the cmpxchg
- * fails and leaves the page unchanged.
- */
- entry = mk_pte(walk->vmemmap_tail, PAGE_KERNEL);
+ dst = vmemmap_dup_page_from_list(src, walk->vmemmap_pages);
}
- list_add(&page->lru, walk->vmemmap_pages);
- set_pte_at(&init_mm, addr, pte, entry);
+ list_add(&src->lru, walk->vmemmap_pages);
+ /*
+ * Map the shared tail page writable intentionally. A lockless get that
+ * read a non-zero refcount before the remap can still do its cmpxchg
+ * after it. The refcount is zero, so the cmpxchg fails and leaves the
+ * page unchanged.
+ */
+ set_pte_at(&init_mm, addr, pte, mk_pte(dst, PAGE_KERNEL));
+
+ return 0;
}
-static void vmemmap_restore_pte(pte_t *pte, unsigned long addr,
- struct vmemmap_remap_walk *walk)
+static int vmemmap_restore_pte(pte_t *pte, unsigned long addr,
+ struct vmemmap_remap_walk *walk)
{
+ const struct page *page = (const struct page *)addr;
+ const struct folio *folio = page_folio(page);
struct page *src = pte_page(ptep_get(pte)), *dst;
- /*
- * When rolling back vmemmap_remap_free(), keep the copied head page
- * mapping and restore only PTEs currently pointing at the shared tail
- * page.
- */
- if (walk->vmemmap_tail && walk->vmemmap_tail != src)
- return;
-
- VM_WARN_ON_ONCE(PageHead((const struct page *)addr));
+ if (unlikely(!vmemmap_optimizable_pfn(page_to_pfn(page), folio_order(folio))))
+ return 0;
+ VM_WARN_ON_ONCE(folio_vmemmap_shared_tail_page(folio) != src);
dst = vmemmap_dup_page_from_list(src, walk->vmemmap_pages);
set_pte_at(&init_mm, addr, pte, mk_pte(dst, PAGE_KERNEL));
+
+ return 0;
}
/**@@ -287,7 +285,6 @@ static int vmemmap_remap_split(unsigned long start, unsigned long end)
* to remap.
* @end: end address of the vmemmap virtual address range that we want to
* remap.
- * @vmemmap_tail: the page to be installed as non-first in the vmemmap range
* @vmemmap_pages: input/output list whose first entry supplies the new backing
* page for @start; replaced backing pages are added to the list for
* the caller to free.
@@ -296,14 +293,12 @@ static int vmemmap_remap_split(unsigned long start, unsigned long end)
* Return: %0 on success, negative error code otherwise.
*/
static int vmemmap_remap_free(unsigned long start, unsigned long end,
- struct page *vmemmap_tail,
struct list_head *vmemmap_pages,
unsigned long flags)
{
int ret;
struct vmemmap_remap_walk walk = {
.remap_pte = vmemmap_optimize_pte,
- .vmemmap_tail = vmemmap_tail,
.vmemmap_pages = vmemmap_pages,
.flags = flags,
};@@ -321,7 +316,6 @@ static int vmemmap_remap_free(unsigned long start, unsigned long end,
*/
walk = (struct vmemmap_remap_walk) {
.remap_pte = vmemmap_restore_pte,
- .vmemmap_tail = vmemmap_tail,
.vmemmap_pages = vmemmap_pages,
.flags = 0,
};@@ -496,7 +490,6 @@ static int __hugetlb_vmemmap_optimize_folio(const struct hstate *h,
unsigned long flags)
{
unsigned long vmemmap_start, vmemmap_end;
- struct page *vmemmap_tail;
int nid, ret = 0;
VM_WARN_ON_ONCE_FOLIO(!folio_test_hugetlb(folio), folio);@@ -506,9 +499,6 @@ static int __hugetlb_vmemmap_optimize_folio(const struct hstate *h,
return ret;
nid = folio_nid(folio);
- vmemmap_tail = vmemmap_shared_tail_page(h->order, folio_zone(folio));
- if (!vmemmap_tail)
- return -ENOMEM;
/*
* Very Subtle
@@ -537,7 +527,7 @@ static int __hugetlb_vmemmap_optimize_folio(const struct hstate *h,
* they can be freed by the caller.
*/
ret = vmemmap_remap_free(vmemmap_start, vmemmap_end,
- vmemmap_tail, vmemmap_pages, flags);
+ vmemmap_pages, flags);
out:
if (ret)
folio_clear_hugetlb_vmemmap_optimized(folio);
diff --git a/mm/mm_init.c b/mm/mm_init.c
index d99af80ec752..a9d848c92678 100644
--- a/mm/mm_init.c
+++ b/mm/mm_init.c
@@ -914,6 +914,8 @@ void __meminit memmap_init_range(unsigned long size, int nid, unsigned long zone
#endif
for (pfn = start_pfn; pfn < end_pfn; ) {
+ const unsigned int order = pfn_to_section_compound_order(pfn);
+
/*
* There can be holes in boot-time mem_map[]s handed to this
* function. They do not exist on hotplugged memory.@@ -929,9 +931,7 @@ void __meminit memmap_init_range(unsigned long size, int nid, unsigned long zone
* Vmemmap-optimizable PFNs are backed by shared tail struct pages,
* which have already been initialized during vmemmap population.
*/
- if (vmemmap_optimizable_pfn(pfn)) {
- const unsigned int order = pfn_to_section_compound_order(pfn);
-
+ if (vmemmap_optimizable_pfn(pfn, order)) {
pfn = min(ALIGN(pfn, 1UL << order), end_pfn);
continue;
}diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c
index 3e9d6617c4bb..b8b55e12f5ba 100644
--- a/mm/sparse-vmemmap.c
+++ b/mm/sparse-vmemmap.c
@@ -241,7 +241,7 @@ static __meminit void *vmemmap_alloc_pte(unsigned long pfn, int node,
struct page *page;
const unsigned int order = pfn_to_section_compound_order(pfn);
- if (!vmemmap_optimizable_pfn(pfn))
+ if (!vmemmap_optimizable_pfn(pfn, order))
return vmemmap_alloc_block_buf(PAGE_SIZE, node, altmap);
/*
@@ -286,6 +286,7 @@ static pte_t * __meminit vmemmap_pte_populate(pmd_t *pmd, unsigned long addr, in
{
pte_t *pte = pte_offset_kernel(pmd, addr);
unsigned long pfn = page_to_pfn((struct page *)addr);
+ const unsigned int order = pfn_to_section_compound_order(pfn);
if (pte_none(ptep_get(pte))) {
pte_t entry;@@ -296,10 +297,10 @@ static pte_t * __meminit vmemmap_pte_populate(pmd_t *pmd, unsigned long addr, in
entry = pfn_pte(PHYS_PFN(__pa(p)), PAGE_KERNEL);
/* Map the shared tail page as read-only to catch illegal writes. */
- if (vmemmap_optimizable_pfn(pfn))
+ if (vmemmap_optimizable_pfn(pfn, order))
entry = pte_wrprotect(entry);
set_pte_at(&init_mm, addr, pte, entry);
- } else if (WARN_ON_ONCE(vmemmap_optimizable_pfn(pfn)))
+ } else if (WARN_ON_ONCE(vmemmap_optimizable_pfn(pfn, order)))
return NULL;
return pte;
}
--
2.54.0