mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
mm/sparse-vmemmap: switch device DAX to shared tail vmemmap pages
HugeTLB vmemmap optimization now uses per-zone shared tail vmemmap pages. Device DAX has not been switched to that mechanism yet. Switch device DAX to vmemmap_shared_tail_page() as well. This aligns DAX with HugeTLB by using the common per-zone shared tail vmemmap page. The optimization is enabled only for DEV-DAX through pgmap->vmemmap_shift, which supplies the compound page order recorded in section metadata before vmemmap population. Unlike FS-DAX, DEV-DAX does not modify tail struct pages, so sharing them is safe. Since the shared tail page can now back ZONE_DEVICE vmemmap mappings, initialize its entries with PG_reserved for device zones. Also skip poisoning vmemmap-optimizable sections while their struct pages may be shared. Each PTE mapping the shared device DAX tail page takes a page reference. A sufficiently large range could therefore cycle the reference count back to zero if population were allowed to continue after it became non-positive. Use try_get_page() so further mappings fail at that point. The section population error path tears down mappings created for the failed section, while the warning makes this currently impractical limit visible. [muchun.song@linux.dev: set PG_reserved before preparing the compound tail] Link: https://lore.kernel.org/20260930150748.1134516-1-songmuchun@bytedance.com Link: https://lore.kernel.org/20260930140627.57431-8-songmuchun@bytedance.com Signed-off-by: Muchun Song <muchun.song@linux.dev> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Acked-by: Qi Zheng <qi.zheng@linux.dev> Cc: David Hildenbrand <david@kernel.org> Cc: Oscar Salvador <osalvador@suse.de> Cc: Madhavan Srinivasan <maddy@linux.ibm.com> Cc: Michael Ellerman <mpe@ellerman.id.au> Cc: Jonathan Corbet <corbet@lwn.net> Cc: Lorenzo Stoakes <ljs@kernel.org> Cc: Mike Rapoport <rppt@kernel.org> Cc: Nicholas Piggin <npiggin@gmail.com> Cc: Christophe Leroy <chleroy@kernel.org> Cc: Ritesh Harjani (IBM) <ritesh.list@gmail.com> Cc: Shrikanth Hegde <sshegde@linux.ibm.com> Cc: Randy Dunlap <rdunlap@infradead.org> Cc: Lance Yang <lance.yang@linux.dev>
This commit is contained in:
parent
2ef77f4a7d
commit
ba9164f880
|
|
@ -2143,11 +2143,21 @@ static inline int online_device_section(const struct mem_section *section)
|
|||
|
||||
return section && ((section->section_mem_map & flags) == flags);
|
||||
}
|
||||
|
||||
static inline struct zone *device_zone(int nid)
|
||||
{
|
||||
return &NODE_DATA(nid)->node_zones[ZONE_DEVICE];
|
||||
}
|
||||
#else
|
||||
static inline int online_device_section(const struct mem_section *section)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline struct zone *device_zone(int nid)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline int online_section_nr(unsigned long nr)
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@
|
|||
#include "mm_init.h"
|
||||
#include "page_alloc.h"
|
||||
#include "shuffle.h"
|
||||
#include "sparse.h"
|
||||
|
||||
enum {
|
||||
MEMMAP_ON_MEMORY_DISABLE = 0,
|
||||
|
|
@ -554,8 +555,9 @@ void remove_pfn_range_from_zone(struct zone *zone,
|
|||
/* Select all remaining pages up to the next section boundary */
|
||||
cur_nr_pages =
|
||||
min(end_pfn - pfn, SECTION_ALIGN_UP(pfn + 1) - pfn);
|
||||
page_init_poison(pfn_to_page(pfn),
|
||||
sizeof(struct page) * cur_nr_pages);
|
||||
if (!section_vmemmap_optimizable(__pfn_to_section(pfn)))
|
||||
page_init_poison(pfn_to_page(pfn),
|
||||
sizeof(struct page) * cur_nr_pages);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -224,6 +224,8 @@ struct page __ref *vmemmap_shared_tail_page(unsigned int order, struct zone *zon
|
|||
atomic_set(&page->_mapcount, -1);
|
||||
set_page_node(page, zone_to_nid(zone));
|
||||
set_page_zone(page, zone_idx(zone));
|
||||
if (zone_is_zone_device(zone))
|
||||
__SetPageReserved(page);
|
||||
prep_compound_tail(page, NULL, order);
|
||||
}
|
||||
|
||||
|
|
@ -288,14 +290,18 @@ static pte_t * __meminit vmemmap_pte_populate(pmd_t *pmd, unsigned long addr, in
|
|||
/*
|
||||
* When a PTE/PMD entry is freed from the init_mm
|
||||
* there's a free_pages() call to this page allocated
|
||||
* above. Thus this get_page() is paired with the
|
||||
* above. Thus this try_get_page() is paired with the
|
||||
* put_page_testzero() on the freeing path.
|
||||
* This can only called by certain ZONE_DEVICE path,
|
||||
* and through vmemmap_populate_compound_pages() when
|
||||
* slab is available.
|
||||
*
|
||||
* Use try_get_page() to prevent the shared page refcount
|
||||
* from overflowing.
|
||||
*/
|
||||
if (flags & VMEMMAP_POPULATE_DAX)
|
||||
get_page(pfn_to_page(ptpfn));
|
||||
if ((flags & VMEMMAP_POPULATE_DAX) &&
|
||||
!try_get_page(pfn_to_page(ptpfn)))
|
||||
return NULL;
|
||||
}
|
||||
entry = pfn_pte(ptpfn, PAGE_KERNEL);
|
||||
set_pte_at(&init_mm, addr, pte, entry);
|
||||
|
|
@ -529,47 +535,27 @@ static bool __meminit reuse_compound_section(unsigned long start_pfn,
|
|||
return !IS_ALIGNED(offset, nr_pages) && nr_pages > PAGES_PER_SUBSECTION;
|
||||
}
|
||||
|
||||
static pte_t * __meminit compound_section_tail_page(unsigned long addr)
|
||||
{
|
||||
pte_t *pte;
|
||||
|
||||
addr -= PAGE_SIZE;
|
||||
|
||||
/*
|
||||
* Assuming sections are populated sequentially, the previous section's
|
||||
* page data can be reused.
|
||||
*/
|
||||
pte = pte_offset_kernel(pmd_off_k(addr), addr);
|
||||
if (!pte)
|
||||
return NULL;
|
||||
|
||||
return pte;
|
||||
}
|
||||
|
||||
static int __meminit vmemmap_populate_compound_pages(unsigned long start_pfn,
|
||||
unsigned long start,
|
||||
unsigned long end, int node,
|
||||
struct dev_pagemap *pgmap)
|
||||
{
|
||||
const unsigned long flags = VMEMMAP_POPULATE_DAX;
|
||||
const unsigned int order = pfn_to_section_compound_order(start_pfn);
|
||||
unsigned long size, addr;
|
||||
pte_t *pte;
|
||||
struct page *page;
|
||||
int rc;
|
||||
|
||||
if (reuse_compound_section(start_pfn, pgmap)) {
|
||||
pte = compound_section_tail_page(start);
|
||||
if (!pte)
|
||||
return -ENOMEM;
|
||||
page = vmemmap_shared_tail_page(order, device_zone(node));
|
||||
if (!page)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Reuse the page that was populated in the prior iteration
|
||||
* with just tail struct pages.
|
||||
*/
|
||||
if (reuse_compound_section(start_pfn, pgmap))
|
||||
return vmemmap_populate_range(start, end, node, NULL,
|
||||
pte_pfn(ptep_get(pte)), flags);
|
||||
}
|
||||
page_to_pfn(page), flags);
|
||||
|
||||
size = min(end - start, pgmap_vmemmap_nr(pgmap) * sizeof(struct page));
|
||||
size = min(end - start, (1UL << order) * sizeof(struct page));
|
||||
for (addr = start; addr < end; addr += size) {
|
||||
unsigned long next, last = addr + size;
|
||||
|
||||
|
|
@ -585,12 +571,12 @@ static int __meminit vmemmap_populate_compound_pages(unsigned long start_pfn,
|
|||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Reuse the previous page for the rest of tail pages
|
||||
* Reuse the shared page for the rest of tail pages
|
||||
* See layout diagram in Documentation/mm/vmemmap_dedup.rst
|
||||
*/
|
||||
next += PAGE_SIZE;
|
||||
rc = vmemmap_populate_range(next, last, node, NULL,
|
||||
pte_pfn(ptep_get(pte)), flags);
|
||||
page_to_pfn(page), flags);
|
||||
if (rc)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
|
@ -922,13 +908,16 @@ int __meminit sparse_add_section(int nid, unsigned long start_pfn,
|
|||
if (IS_ERR(memmap))
|
||||
return PTR_ERR(memmap);
|
||||
|
||||
/*
|
||||
* Poison uninitialized struct pages in order to catch invalid flags
|
||||
* combinations.
|
||||
*/
|
||||
page_init_poison(memmap, sizeof(struct page) * nr_pages);
|
||||
|
||||
ms = __nr_to_section(section_nr);
|
||||
/*
|
||||
* Poison uninitialized struct pages to catch invalid flag combinations.
|
||||
*
|
||||
* Tail struct pages in a vmemmap-optimized section are initialized and
|
||||
* shared during vmemmap population, so they must not be overwritten here.
|
||||
*/
|
||||
if (!section_vmemmap_optimizable(ms))
|
||||
page_init_poison(memmap, sizeof(struct page) * nr_pages);
|
||||
|
||||
__section_mark_present(ms, section_nr);
|
||||
|
||||
/* Align memmap to section boundary in the subsection case */
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user