From: Muchun Song <muchun.song@linux.dev>
To: Lance Yang <lance.yang@linux.dev>
Cc: Muchun Song <songmuchun@bytedance.com>,
maddy@linux.ibm.com, rppt@kernel.org, akpm@linux-foundation.org,
david@kernel.org, mpe@ellerman.id.au, npiggin@gmail.com,
chleroy@kernel.org, ritesh.list@gmail.com, sshegde@linux.ibm.com,
ljs@kernel.org, liam@infradead.org, vbabka@kernel.org,
surenb@google.com, mhocko@suse.com, qi.zheng@linux.dev,
linuxppc-dev@lists.ozlabs.org, linux-kernel@vger.kernel.org,
linux-mm@kvack.org
Subject: Re: [PATCH v3 2/6] mm/sparse-vmemmap: support device DAX in common vmemmap path
Date: Wed, 30 Sep 2026 18:09:41 +0800 [thread overview]
Message-ID: <BAD58999-1EDD-4A37-ABA0-DB1BD8AB3453@linux.dev> (raw)
In-Reply-To: <20260930085329.17337-1-lance.yang@linux.dev>
> On Sep 30, 2026, at 16:53, Lance Yang <lance.yang@linux.dev> wrote:
>
>
> On Tue, Sep 29, 2026 at 01:32:27PM +0800, Muchun Song wrote:
>> The common vmemmap population path cannot yet handle optimized Device DAX
>> mappings on its own. It uses pfn_to_zone() to find the shared tail page,
>> but Device DAX populates its vmemmap at runtime before the ZONE_DEVICE span
>> is initialized.
>>
>> Teach the common path to use device_zone() for runtime optimized vmemmap
>> population while retaining pfn_to_zone() for early boot. This allows the
>> same path to support both early boot mappings and Device DAX.
>>
>> The backing PFN supplied by the Device DAX-specific population path is no
>> longer used, allowing the redundant lookup and population code to be
>> removed later.
>>
>> Signed-off-by: Muchun Song <songmuchun@bytedance.com>
>> Acked-by: Qi Zheng <qi.zheng@linux.dev>
>> ---
>> v3:
>> - Collect Acked-by from Qi Zheng
>>
>> v2:
>> - Expand comments around slab initialization to explain zone lookup and
>> page refcounting (suggested by Qi Zheng)
>> ---
>> mm/sparse-vmemmap.c | 64 +++++++++++++++++++++++++--------------------
>> 1 file changed, 35 insertions(+), 29 deletions(-)
>>
>> diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c
>> index 8219abc6c3e5..ee4c113ca938 100644
>> --- a/mm/sparse-vmemmap.c
>> +++ b/mm/sparse-vmemmap.c
>> @@ -237,18 +237,43 @@ static __meminit void *vmemmap_alloc_pte(unsigned long pfn, int node,
>> struct page *page;
>> const unsigned int order = pfn_to_section_compound_order(pfn);
>>
>> - /*
>> - * Device DAX still relies on vmemmap_populate_compound_pages() for
>> - * head/first-tail allocation and tail-page reuse.
>> - */
>> if (!vmemmap_optimizable_pfn(pfn))
>> return vmemmap_alloc_block_buf(PAGE_SIZE, node, altmap);
>>
>> - zone = pfn_to_zone(pfn, node);
>> + /*
>> + * Before slab is available, vmemmap optimization is used for early
>> + * system RAM, whose zone can be determined from the PFN.
>> + *
>> + * Once slab is available, only ZONE_DEVICE memory reaches this
>> + * optimized population path. Its zone span has not been initialized
>> + * while its vmemmap is being populated, so pfn_to_zone() cannot be
>> + * used. Obtain ZONE_DEVICE directly from the node instead.
>> + */
>> + zone = slab_is_available() ? device_zone(node) : pfn_to_zone(pfn, node);
>> page = vmemmap_shared_tail_page(order, zone);
>> if (!page)
>> return NULL;
>>
>> + /*
>> + * During early vmemmap population, the shared tail vmemmap backing
>> + * page is allocated from memblock before its struct page can safely
>> + * participate in page refcounting. Therefore, no reference can be
>> + * held for each shared PTE mapping, and the mappings must be unshared
>> + * before the vmemmap is depopulated.
>> + *
>> + * Once slab is available, the shared backing page is allocated from
>> + * the buddy allocator and can be refcounted. Hold one reference for
>> + * each shared PTE mapping. The architecture vmemmap teardown drops
>> + * the reference through __free_pages() when removing the mapping,
>> + * preventing the backing page from being freed while it is shared.
>> + *
>> + * The backing page may be shared by enough PTE mappings to exhaust
>> + * the positive range of its reference count. Stop populating the
>> + * vmemmap if another reference cannot be acquired.
>> + */
>> + if (slab_is_available() && !try_get_page(page))
>> + return NULL;
>
> BTW, shouldn't __add_pages() undo the earlier sections on a population
> failure? Say the first section is added successfully, but populating the
> next one fails, e.g. due to an allocation failure:
>
> void *memremap_pages(struct dev_pagemap *pgmap, int nid)
> {
> ...
> const int nr_range = pgmap->nr_range;
> int error, i;
> ...
> pgmap->nr_range = 0;
> error = 0;
> for (i = 0; i < nr_range; i++) {
> error = pagemap_range(pgmap, ¶ms, i, nid);
> if (error)
> break;
> pgmap->nr_range++;
> }
>
> if (i < nr_range) {
> memunmap_pages(pgmap);
> pgmap->nr_range = nr_range;
> return ERR_PTR(error);
> }
> ...
> }
>
> We still need to undo the sections already added in the failed range,
> though ... memunmap_pages() won't touch those, since it only removes
> completed ranges.
>
> If the range starts at a section boundary, we'd hit -EEXIST in
> fill_subsection_map() on retry while those subsection bits are still set.
>
> The old DAX path had this issue too. Could we roll back [start_pfn, pfn)
> in __add_pages() as a separate fix? Something like this:
>
> ---8<---
> diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
> index 796af1028ee2..16a0a2c885bc 100644
> --- a/mm/memory_hotplug.c
> +++ b/mm/memory_hotplug.c
> @@ -380,6 +380,7 @@ EXPORT_SYMBOL_GPL(pfn_to_online_page);
> int __add_pages(int nid, unsigned long pfn, unsigned long nr_pages,
> struct mhp_params *params)
> {
> + const unsigned long start_pfn = pfn;
> const unsigned long end_pfn = pfn + nr_pages;
> unsigned long cur_nr_pages;
> int err;
> @@ -417,6 +418,10 @@ int __add_pages(int nid, unsigned long pfn, unsigned long nr_pages,
> break;
> cond_resched();
> }
> +
> + /* Roll back the sections added before the failure. */
> + if (err && pfn != start_pfn)
> + __remove_pages(start_pfn, pfn - start_pfn, altmap, params->pgmap);
> vmemmap_populate_print_last();
> return err;
> }
> --
>
> Hope I haven't missed anything :)
Good catch. This is indeed a pre-existing issue, and the old DAX path was
affected as well.
Your proposed fix looks correct to me. I would slightly prefer keeping the
rollback close to the failure:
err = sparse_add_section(nid, pfn, cur_nr_pages, altmap,
params->pgmap);
if (err) {
__remove_pages(start_pfn, pfn - start_pfn, altmap,
params->pgmap);
break;
}
If the first section fails, this simply calls __remove_pages() with an
empty range, which is a harmless no-op.
Would you mind sending this as a separate bug fix? I will ACK it.
Thanks,
Muchun
next prev parent reply other threads:[~2026-09-30 10:10 UTC|newest]
Thread overview: 30+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-29 5:32 [PATCH v3 0/6] mm: Unify device DAX and HugeTLB vmemmap population paths Muchun Song
2026-09-29 5:32 ` [PATCH v3 1/6] mm/sparse-vmemmap: drop VMEMMAP_POPULATE_DAX Muchun Song
2026-09-30 4:24 ` Lance Yang
2026-09-29 5:32 ` [PATCH v3 2/6] mm/sparse-vmemmap: support device DAX in common vmemmap path Muchun Song
2026-09-30 8:53 ` Lance Yang
2026-09-30 10:09 ` Muchun Song [this message]
2026-09-30 11:11 ` Muchun Song
2026-09-30 13:38 ` Lance Yang
2026-09-30 10:02 ` Lance Yang
2026-09-29 5:32 ` [PATCH v3 3/6] mm/sparse-vmemmap: drop Device DAX-specific population path Muchun Song
2026-10-01 14:27 ` Lance Yang
2026-09-29 5:32 ` [PATCH v3 4/6] mm/sparse-vmemmap: remove the unused ptpfn argument Muchun Song
2026-10-01 14:52 ` Lance Yang
2026-09-29 5:32 ` [PATCH v3 5/6] mm/sparse-vmemmap: open-code vmemmap_populate_address() Muchun Song
2026-10-01 15:10 ` Lance Yang
2026-09-29 5:32 ` [PATCH v3 6/6] mm/mm_init: add zone mismatch warning during page init Muchun Song
2026-10-01 14:42 ` Mike Rapoport
2026-10-01 16:07 ` Lance Yang
2026-10-02 1:48 ` Muchun Song
2026-10-02 8:22 ` Mike Rapoport
2026-10-02 9:56 ` Muchun Song
2026-10-02 18:12 ` Mike Rapoport
2026-10-05 16:27 ` Muchun Song
2026-10-06 10:52 ` Mike Rapoport
2026-10-06 13:32 ` Muchun Song
2026-10-08 7:45 ` Mike Rapoport
2026-10-08 8:16 ` Muchun Song
2026-10-01 16:09 ` Lance Yang
2026-09-29 21:32 ` [PATCH v3 0/6] mm: Unify device DAX and HugeTLB vmemmap population paths Andrew Morton
2026-09-30 1: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=BAD58999-1EDD-4A37-ABA0-DB1BD8AB3453@linux.dev \
--to=muchun.song@linux.dev \
--cc=akpm@linux-foundation.org \
--cc=chleroy@kernel.org \
--cc=david@kernel.org \
--cc=lance.yang@linux.dev \
--cc=liam@infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=linuxppc-dev@lists.ozlabs.org \
--cc=ljs@kernel.org \
--cc=maddy@linux.ibm.com \
--cc=mhocko@suse.com \
--cc=mpe@ellerman.id.au \
--cc=npiggin@gmail.com \
--cc=qi.zheng@linux.dev \
--cc=ritesh.list@gmail.com \
--cc=rppt@kernel.org \
--cc=songmuchun@bytedance.com \
--cc=sshegde@linux.ibm.com \
--cc=surenb@google.com \
--cc=vbabka@kernel.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.