All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Gupta, Pankaj" <pankaj.gupta@amd.com>
To: Muchun Song <songmuchun@bytedance.com>,
	Andrew Morton <akpm@linux-foundation.org>,
	Dan Williams <djbw@kernel.org>,
	David Hildenbrand <david@kernel.org>
Cc: linux-mm@kvack.org, nvdimm@lists.linux.dev,
	linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org,
	linux-cxl@vger.kernel.org,
	Vishal Verma <vishal.l.verma@intel.com>,
	Dave Jiang <dave.jiang@intel.com>,
	Alison Schofield <alison.schofield@intel.com>,
	Mike Rapoport <rppt@kernel.org>,
	Oscar Salvador <osalvador@suse.de>, Ira Weiny <iweiny@kernel.org>,
	Jan Kara <jack@suse.cz>, Matthew Wilcox <willy@infradead.org>,
	Lorenzo Stoakes <ljs@kernel.org>,
	Vlastimil Babka <vbabka@kernel.org>,
	Michal Hocko <mhocko@suse.com>, Qi Zheng <qi.zheng@linux.dev>,
	muchun.song@linux.dev
Subject: Re: [PATCH 3/4] mm: add shared read-only vmemmap support for FS-DAX
Date: Mon, 21 Sep 2026 08:42:19 +0200	[thread overview]
Message-ID: <4bc1f4de-40ad-46f0-8d37-4c4e0cad0ad6@amd.com> (raw)
In-Reply-To: <20260903122128.12264-4-songmuchun@bytedance.com>


> FS-DAX registers persistent-memory ranges as ZONE_DEVICE memory, and the
> kernel normally allocates and initializes vmemmap storage for every
> advertised PFN up front. Sparse pmem images and workloads that only use the
> DAX direct-access path may never need writable per-PFN state for most of
> that range, but still pay the memory and initialization cost.
>
> Add an opt-in dev_pagemap mode that populates FS-DAX vmemmap PTEs from a
> shared read-only metadata page. The shared page is initialized with the
> common ZONE_DEVICE and dev_pagemap state, so every PFN still has a valid
> struct page representation while private metadata allocation is deferred.
>
> This relies on sizeof(struct page) being a power of two, so each vmemmap
> page contains a naturally aligned and repeatable set of struct page slots.
> It also requires architecture support for runtime vmemmap remapping,
> because shared mappings must be replaced with private writable pages before
> a PFN can enter userspace mappings.
>
> The initial implementation is deliberately limited to a single
> memory-block-aligned range. That is not a fundamental requirement, but keeps
> the registration and teardown paths simple; support for multiple ranges or
> less strict alignment can be added later.
>
> Provide vmemmap_materialize_page() to replace shared mappings in the
> requested metadata range with private writable copies. A later patch will
> call it from the FS-DAX fault path.
>
> No caller enables the mode yet.
>
> Signed-off-by: Muchun Song <songmuchun@bytedance.com>
> ---
>   include/linux/memremap.h | 11 ++++++-
>   mm/memremap.c            | 38 ++++++++++++++++++++++--
>   mm/mm_init.c             | 11 +++++++
>   mm/sparse-vmemmap.c      | 64 +++++++++++++++++++++++++++++++++++++---
>   4 files changed, 116 insertions(+), 8 deletions(-)
>
> diff --git a/include/linux/memremap.h b/include/linux/memremap.h
> index e3c2ccf872a8..21c9b6aeef67 100644
> --- a/include/linux/memremap.h
> +++ b/include/linux/memremap.h
> @@ -9,6 +9,7 @@
>   
>   struct resource;
>   struct device;
> +struct page;
>   
>   /**
>    * struct vmem_altmap - pre-allocated storage for vmemmap_populate
> @@ -108,7 +109,8 @@ struct dev_pagemap_ops {
>   	void (*folio_split)(struct folio *head, struct folio *tail);
>   };
>   
> -#define PGMAP_ALTMAP_VALID	(1 << 0)
> +#define PGMAP_ALTMAP_VALID				BIT(0)
> +#define PGMAP_VMEMMAP_OPTIMIZATION			BIT(1)

I liked the overall idea.

Minor naming suggestion I have:

'PGMAP_VMEMMAP_OPTIMIZATION' feel too generic to me.

Maybe something on the lines to reflect the actual optimization:

|PGMAP_SHARED_VMEMMAP or PGMAP_VMEMMAP_ON_DEMAND or some_other_name?|

|Thanks,|

|Pankaj|

>   
>   /**
>    * struct dev_pagemap - metadata for ZONE_DEVICE mappings
> @@ -122,6 +124,7 @@ struct dev_pagemap_ops {
>    *	A zero value (default) uses base pages as the vmemmap metadata
>    *	representation. A bigger value will set up compound struct pages
>    *	of the requested order value.
> + * @vmemmap_shared_page: shared read-only vmemmap page for optimized FS-DAX
>    * @ops: method table
>    * @owner: an opaque pointer identifying the entity that manages this
>    *	instance.  Used by various helpers to make sure that no
> @@ -137,6 +140,7 @@ struct dev_pagemap {
>   	enum memory_type type;
>   	unsigned int flags;
>   	unsigned long vmemmap_shift;
> +	struct page *vmemmap_shared_page;
>   	const struct dev_pagemap_ops *ops;
>   	void *owner;
>   	int nr_range;
> @@ -232,6 +236,7 @@ void *devm_memremap_pages(struct device *dev, struct dev_pagemap *pgmap);
>   void devm_memunmap_pages(struct device *dev, struct dev_pagemap *pgmap);
>   struct dev_pagemap *get_dev_pagemap(unsigned long pfn);
>   bool pgmap_pfn_valid(struct dev_pagemap *pgmap, unsigned long pfn);
> +int vmemmap_materialize_page(struct page *page, unsigned int order);
>   
>   unsigned long memremap_compat_align(void);
>   
> @@ -307,4 +312,8 @@ static inline void put_dev_pagemap(struct dev_pagemap *pgmap)
>   		percpu_ref_put(&pgmap->ref);
>   }
>   
> +static inline bool pgmap_vmemmap_optimizable(struct dev_pagemap *pgmap)
> +{
> +	return pgmap && pgmap->vmemmap_shared_page != NULL;
> +}
>   #endif /* _LINUX_MEMREMAP_H_ */
> diff --git a/mm/memremap.c b/mm/memremap.c
> index accba23aef28..a53d09b84eaa 100644
> --- a/mm/memremap.c
> +++ b/mm/memremap.c
> @@ -3,6 +3,7 @@
>   #include <linux/device.h>
>   #include <linux/io.h>
>   #include <linux/kasan.h>
> +#include <linux/memory.h>
>   #include <linux/memory_hotplug.h>
>   #include <linux/memremap.h>
>   #include <linux/swap.h>
> @@ -83,6 +84,30 @@ static unsigned long pfn_len(struct dev_pagemap *pgmap, unsigned long range_id)
>   		pfn_first(pgmap, range_id)) >> pgmap->vmemmap_shift;
>   }
>   
> +static int pgmap_vmemmap_shared_page_alloc(struct dev_pagemap *pgmap, int nid)
> +{
> +	const struct range *range = &pgmap->range;
> +
> +	if (!is_power_of_2(sizeof(struct page)) ||
> +	    !IS_ENABLED(CONFIG_ARCH_SUPPORTS_VMEMMAP_REMAP) ||
> +	    !(pgmap->flags & PGMAP_VMEMMAP_OPTIMIZATION))
> +		return 0;
> +
> +	if (pgmap->nr_range != 1 ||
> +	    !IS_ALIGNED(range->start | range_len(range), MIN_MEMORY_BLOCK_SIZE))
> +		return 0;
> +
> +	pgmap->vmemmap_shared_page = alloc_pages_node(nid, GFP_KERNEL, 0);
> +
> +	return pgmap->vmemmap_shared_page ? 0 : -ENOMEM;
> +}
> +
> +static inline void pgmap_vmemmap_shared_page_free(struct dev_pagemap *pgmap)
> +{
> +	if (pgmap->vmemmap_shared_page)
> +		put_page(pgmap->vmemmap_shared_page);
> +}
> +
>   static void pageunmap_range(struct dev_pagemap *pgmap, int range_id)
>   {
>   	struct range *range = &pgmap->ranges[range_id];
> @@ -93,8 +118,9 @@ static void pageunmap_range(struct dev_pagemap *pgmap, int range_id)
>   
>   	/* pages are dead and unused, undo the arch mapping */
>   	mem_hotplug_begin();
> -	remove_pfn_range_from_zone(page_zone(first_page), PHYS_PFN(range->start),
> -				   PHYS_PFN(range_len(range)));
> +	if (!pgmap_vmemmap_optimizable(pgmap))
> +		remove_pfn_range_from_zone(page_zone(first_page), PHYS_PFN(range->start),
> +					   PHYS_PFN(range_len(range)));
>   	if (pgmap->type == MEMORY_DEVICE_PRIVATE) {
>   		__remove_pages(PHYS_PFN(range->start),
>   			       PHYS_PFN(range_len(range)), NULL, pgmap);
> @@ -123,6 +149,7 @@ void memunmap_pages(struct dev_pagemap *pgmap)
>   
>   	for (i = 0; i < pgmap->nr_range; i++)
>   		pageunmap_range(pgmap, i);
> +	pgmap_vmemmap_shared_page_free(pgmap);
>   	percpu_ref_exit(&pgmap->ref);
>   
>   	WARN_ONCE(pgmap->altmap.alloc, "failed to free all reserved pages\n");
> @@ -310,6 +337,9 @@ void *memremap_pages(struct dev_pagemap *pgmap, int nid)
>   		break;
>   	case MEMORY_DEVICE_FS_DAX:
>   		params.pgprot = pgprot_decrypted(params.pgprot);
> +		error = pgmap_vmemmap_shared_page_alloc(pgmap, nid);
> +		if (error)
> +			return ERR_PTR(error);
>   		break;
>   	case MEMORY_DEVICE_GENERIC:
>   		break;
> @@ -324,8 +354,10 @@ void *memremap_pages(struct dev_pagemap *pgmap, int nid)
>   	init_completion(&pgmap->done);
>   	error = percpu_ref_init(&pgmap->ref, dev_pagemap_percpu_release, 0,
>   				GFP_KERNEL);
> -	if (error)
> +	if (error) {
> +		pgmap_vmemmap_shared_page_free(pgmap);
>   		return ERR_PTR(error);
> +	}
>   
>   	/*
>   	 * Clear the pgmap nr_range as it will be incremented for each
> diff --git a/mm/mm_init.c b/mm/mm_init.c
> index 2ed17cc707ed..7dd03b8a8d28 100644
> --- a/mm/mm_init.c
> +++ b/mm/mm_init.c
> @@ -33,6 +33,7 @@
>   #include <linux/vmstat.h>
>   #include <linux/kexec_handover.h>
>   #include <linux/hugetlb.h>
> +#include <linux/memremap.h>
>   #include "internal.h"
>   #include "mm_init.h"
>   #include "page_alloc.h"
> @@ -1133,6 +1134,15 @@ void __ref memmap_init_zone_device(struct zone *zone,
>   	if (!nr_pages)
>   		return;
>   
> +	if (pgmap_vmemmap_optimizable(pgmap)) {
> +		struct page *page = page_address(pgmap->vmemmap_shared_page);
> +
> +		for (int i = 0; i < PAGE_SIZE / sizeof(struct page); i++)
> +			__init_zone_device_page(page + i, start_pfn + i,
> +						ZONE_DEVICE, nid, pgmap);
> +		goto pageblock_init;
> +	}
> +
>   	/*
>   	 * Seed the reusable head-page template from the first real struct
>   	 * page. The normal page-init and refcount helpers must operate on
> @@ -1164,6 +1174,7 @@ void __ref memmap_init_zone_device(struct zone *zone,
>   				     compound_nr_pages(pfn, altmap, pgmap));
>   	}
>   
> +pageblock_init:
>   	pageblock_migratetype_init_range(start_pfn, nr_pages, MIGRATE_MOVABLE,
>   					 /* isolate */ false, /* atomic */ false);
>   
> diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c
> index e62e6aa07f12..4dc7f020ed10 100644
> --- a/mm/sparse-vmemmap.c
> +++ b/mm/sparse-vmemmap.c
> @@ -37,6 +37,8 @@
>    */
>   /* Get a ref on the head page struct page, for ZONE_DEVICE compound pages */
>   #define VMEMMAP_POPULATE_PAGEREF	0x0001
> +/* Read-only shared vmemmap mappings for FS-DAX base pages */
> +#define VMEMMAP_POPULATE_FSDAX_SHARED	0x0002
>   
>   #include "internal.h"
>   #include "mm_init.h"
> @@ -262,10 +264,11 @@ static pte_t * __meminit vmemmap_pte_populate(pmd_t *pmd, unsigned long addr, in
>   			 * and through vmemmap_populate_compound_pages() when
>   			 * slab is available.
>   			 */
> -			if (flags & VMEMMAP_POPULATE_PAGEREF)
> +			if (flags & (VMEMMAP_POPULATE_PAGEREF | VMEMMAP_POPULATE_FSDAX_SHARED))
>   				get_page(pfn_to_page(ptpfn));
>   		}
> -		entry = pfn_pte(ptpfn, PAGE_KERNEL);
> +		entry = pfn_pte(ptpfn, flags & VMEMMAP_POPULATE_FSDAX_SHARED ?
> +				PAGE_KERNEL_RO : PAGE_KERNEL);
>   		set_pte_at(&init_mm, addr, pte, entry);
>   	} else if (WARN_ON_ONCE(vmemmap_optimizable_pfn(pfn)))
>   		return NULL;
> @@ -379,6 +382,54 @@ int __meminit vmemmap_populate_basepages(unsigned long start, unsigned long end,
>   	return vmemmap_populate_range(start, end, node, altmap, -1, 0);
>   }
>   
> +#ifdef CONFIG_ZONE_DEVICE
> +static int __vmemmap_materialize_page(struct page *page)
> +{
> +	unsigned long addr = PAGE_ALIGN_DOWN((unsigned long)page);
> +	struct dev_pagemap *pgmap = page_pgmap(page);
> +	struct page *candidate, *template = pgmap->vmemmap_shared_page;
> +	pte_t *pte = virt_to_kpte(addr);
> +
> +	if (pte_page(ptep_get(pte)) != template)
> +		return 0;
> +
> +	candidate = alloc_pages_node(page_to_nid(page), GFP_KERNEL, 0);
> +	if (!candidate)
> +		return -ENOMEM;
> +	copy_page(page_address(candidate), page_address(template));
> +
> +	spin_lock(&init_mm.page_table_lock);
> +	if (pte_page(ptep_get(pte)) != template) {
> +		__free_page(candidate);
> +		goto out;
> +	}
> +	/* Make the copied struct page contents visible before the PTE update. */
> +	smp_wmb();
> +	set_pte_at(&init_mm, addr, pte, mk_pte(candidate, PAGE_KERNEL));
> +	flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
> +	put_page(template);
> +out:
> +	spin_unlock(&init_mm.page_table_lock);
> +
> +	return 0;
> +}
> +
> +int vmemmap_materialize_page(struct page *page, unsigned int order)
> +{
> +	struct dev_pagemap *pgmap = page_pgmap(page);
> +	unsigned long end = (unsigned long)(page + (1UL << order));
> +
> +	if (!pgmap_vmemmap_optimizable(pgmap))
> +		return 0;
> +
> +	for (unsigned long addr = (unsigned long)page; addr < end; addr += PAGE_SIZE)
> +		if (__vmemmap_materialize_page((struct page *)addr))
> +			return -ENOMEM;
> +
> +	return 0;
> +}
> +#endif /* CONFIG_ZONE_DEVICE */
> +
>   /*
>    * Write protect the mirrored tail page structs for HVO. This will be
>    * called from the hugetlb code when gathering and initializing the
> @@ -581,7 +632,11 @@ struct page * __meminit __populate_section_memmap(unsigned long pfn,
>   		!IS_ALIGNED(nr_pages, PAGES_PER_SUBSECTION)))
>   		return NULL;
>   
> -	if (vmemmap_can_optimize(altmap, pgmap))
> +	if (pgmap_vmemmap_optimizable(pgmap))
> +		r = vmemmap_populate_range(start, end, nid, NULL,
> +					   page_to_pfn(pgmap->vmemmap_shared_page),
> +					   VMEMMAP_POPULATE_FSDAX_SHARED);
> +	else if (vmemmap_can_optimize(altmap, pgmap))
>   		r = vmemmap_populate_compound_pages(pfn, start, end, nid, pgmap);
>   	else
>   		r = vmemmap_populate(start, end, nid, altmap);
> @@ -887,7 +942,8 @@ int __meminit sparse_add_section(int nid, unsigned long start_pfn,
>   	 * Poison uninitialized struct pages in order to catch invalid flags
>   	 * combinations.
>   	 */
> -	page_init_poison(memmap, sizeof(struct page) * nr_pages);
> +	if (!pgmap_vmemmap_optimizable(pgmap))
> +		page_init_poison(memmap, sizeof(struct page) * nr_pages);
>   
>   	ms = __nr_to_section(section_nr);
>   	__section_mark_present(ms, section_nr);

  reply	other threads:[~2026-09-21  6:42 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-03 12:21 [PATCH 0/4] mm: Reduce struct page overhead for FS-DAX pmem Muchun Song
2026-09-03 12:21 ` [PATCH 1/4] mm: generalize vmemmap remap architecture support Muchun Song
2026-09-21 12:25   ` Oscar Salvador (SUSE)
2026-09-03 12:21 ` [PATCH 2/4] nvdimm/pmem: avoid HWPoison flag updates for clean pages Muchun Song
2026-09-03 12:41   ` sashiko-bot
2026-09-21  7:33   ` Gupta, Pankaj
2026-09-21 12:53   ` Oscar Salvador (SUSE)
2026-09-22  2:33     ` Muchun Song
2026-09-03 12:21 ` [PATCH 3/4] mm: add shared read-only vmemmap support for FS-DAX Muchun Song
2026-09-21  6:42   ` Gupta, Pankaj [this message]
2026-09-21  9:33     ` Muchun Song
2026-09-21 11:05       ` Gupta, Pankaj
2026-09-21 13:09   ` Oscar Salvador (SUSE)
2026-09-22  3:17     ` Muchun Song
2026-09-03 12:21 ` [PATCH 4/4] fsdax: materialize pmem vmemmap metadata on faults Muchun Song
2026-09-03 12:53   ` sashiko-bot
2026-09-05  3:26     ` Muchun Song

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4bc1f4de-40ad-46f0-8d37-4c4e0cad0ad6@amd.com \
    --to=pankaj.gupta@amd.com \
    --cc=akpm@linux-foundation.org \
    --cc=alison.schofield@intel.com \
    --cc=dave.jiang@intel.com \
    --cc=david@kernel.org \
    --cc=djbw@kernel.org \
    --cc=iweiny@kernel.org \
    --cc=jack@suse.cz \
    --cc=linux-cxl@vger.kernel.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=ljs@kernel.org \
    --cc=mhocko@suse.com \
    --cc=muchun.song@linux.dev \
    --cc=nvdimm@lists.linux.dev \
    --cc=osalvador@suse.de \
    --cc=qi.zheng@linux.dev \
    --cc=rppt@kernel.org \
    --cc=songmuchun@bytedance.com \
    --cc=vbabka@kernel.org \
    --cc=vishal.l.verma@intel.com \
    --cc=willy@infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.