]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
mm/zone_device: support large zone device private folios
authorBalbir Singh <balbirs@nvidia.com>
Wed, 1 Oct 2025 06:56:52 +0000 (16:56 +1000)
committerAndrew Morton <akpm@linux-foundation.org>
Wed, 22 Oct 2025 01:51:25 +0000 (18:51 -0700)
Patch series "mm: support device-private THP", v7.

This patch series introduces support for Transparent Huge Page (THP)
migration in zone device-private memory.  The implementation enables
efficient migration of large folios between system memory and
device-private memory

Background

Current zone device-private memory implementation only supports PAGE_SIZE
granularity, leading to:
- Increased TLB pressure
- Inefficient migration between CPU and device memory

This series extends the existing zone device-private infrastructure to
support THP, leading to:
- Reduced page table overhead
- Improved memory bandwidth utilization
- Seamless fallback to base pages when needed

In my local testing (using lib/test_hmm) and a throughput test, the series
shows a 350% improvement in data transfer throughput and a 80% improvement
in latency

These patches build on the earlier posts by Ralph Campbell [1]

Two new flags are added in vma_migration to select and mark compound
pages.  migrate_vma_setup(), migrate_vma_pages() and
migrate_vma_finalize() support migration of these pages when
MIGRATE_VMA_SELECT_COMPOUND is passed in as arguments.

The series also adds zone device awareness to (m)THP pages along with
fault handling of large zone device private pages.  page vma walk and the
rmap code is also zone device aware.  Support has also been added for
folios that might need to be split in the middle of migration (when the
src and dst do not agree on MIGRATE_PFN_COMPOUND), that occurs when src
side of the migration can migrate large pages, but the destination has not
been able to allocate large pages.  The code supported and used
folio_split() when migrating THP pages, this is used when
MIGRATE_VMA_SELECT_COMPOUND is not passed as an argument to
migrate_vma_setup().

The test infrastructure lib/test_hmm.c has been enhanced to support THP
migration.  A new ioctl to emulate failure of large page allocations has
been added to test the folio split code path.  hmm-tests.c has new test
cases for huge page migration and to test the folio split path.  A new
throughput test has been added as well.

The nouveau dmem code has been enhanced to use the new THP migration
capability.

mTHP support:

The patches hard code, HPAGE_PMD_NR in a few places, but the code has been
kept generic to support various order sizes.  With additional refactoring
of the code support of different order sizes should be possible.

The future plan is to post enhancements to support mTHP with a rough
design as follows:

1. Add the notion of allowable thp orders to the HMM based test driver
2. For non PMD based THP paths in migrate_device.c, check to see if
   a suitable order is found and supported by the driver
3. Iterate across orders to check the highest supported order for migration
4. Migrate and finalize

The mTHP patches can be built on top of this series, the key design
elements that need to be worked out are infrastructure and driver support
for multiple ordered pages and their migration.

HMM support for large folios was added in 10b9feee2d0d ("mm/hmm:
populate PFNs from PMD swap entry").

This patch (of 16)

Add routines to support allocation of large order zone device folios and
helper functions for zone device folios, to check if a folio is device
private and helpers for setting zone device data.

When large folios are used, the existing page_free() callback in pgmap is
called when the folio is freed, this is true for both PAGE_SIZE and higher
order pages.

Zone device private large folios do not support deferred split and scan
like normal THP folios.

Link: https://lkml.kernel.org/r/20251001065707.920170-1-balbirs@nvidia.com
Link: https://lkml.kernel.org/r/20251001065707.920170-2-balbirs@nvidia.com
Link: https://lore.kernel.org/linux-mm/20201106005147.20113-1-rcampbell@nvidia.com/
Signed-off-by: Balbir Singh <balbirs@nvidia.com>
Cc: David Hildenbrand <david@redhat.com>
Cc: Zi Yan <ziy@nvidia.com>
Cc: Joshua Hahn <joshua.hahnjy@gmail.com>
Cc: Rakie Kim <rakie.kim@sk.com>
Cc: Byungchul Park <byungchul@sk.com>
Cc: Gregory Price <gourry@gourry.net>
Cc: Ying Huang <ying.huang@linux.alibaba.com>
Cc: Alistair Popple <apopple@nvidia.com>
Cc: Oscar Salvador <osalvador@suse.de>
Cc: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
Cc: Baolin Wang <baolin.wang@linux.alibaba.com>
Cc: "Liam R. Howlett" <Liam.Howlett@oracle.com>
Cc: Nico Pache <npache@redhat.com>
Cc: Ryan Roberts <ryan.roberts@arm.com>
Cc: Dev Jain <dev.jain@arm.com>
Cc: Barry Song <baohua@kernel.org>
Cc: Lyude Paul <lyude@redhat.com>
Cc: Danilo Krummrich <dakr@kernel.org>
Cc: David Airlie <airlied@gmail.com>
Cc: Simona Vetter <simona@ffwll.ch>
Cc: Ralph Campbell <rcampbell@nvidia.com>
Cc: Mika Penttilä <mpenttil@redhat.com>
Cc: Matthew Brost <matthew.brost@intel.com>
Cc: Francois Dugast <francois.dugast@intel.com>
Cc: Madhavan Srinivasan <maddy@linux.ibm.com>
Cc: Christophe Leroy <christophe.leroy@csgroup.eu>
Cc: Felix Kuehling <Felix.Kuehling@amd.com>
Cc: Alex Deucher <alexander.deucher@amd.com>
Cc: "Christian König" <christian.koenig@amd.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
arch/powerpc/kvm/book3s_hv_uvmem.c
drivers/gpu/drm/amd/amdkfd/kfd_migrate.c
drivers/gpu/drm/drm_pagemap.c
drivers/gpu/drm/nouveau/nouveau_dmem.c
include/linux/memremap.h
lib/test_hmm.c
mm/memremap.c
mm/rmap.c

index 03f8c34fa0a2066918ec5b332a075c8ffd8a56f7..91f763410673f3164f41b6961793b99a11629cec 100644 (file)
@@ -723,7 +723,7 @@ static struct page *kvmppc_uvmem_get_page(unsigned long gpa, struct kvm *kvm)
 
        dpage = pfn_to_page(uvmem_pfn);
        dpage->zone_device_data = pvt;
-       zone_device_page_init(dpage);
+       zone_device_page_init(dpage, 0);
        return dpage;
 out_clear:
        spin_lock(&kvmppc_uvmem_bitmap_lock);
index 59a5a3fea65df8ea2cefdcda5c21e440e7336a09..f6198e66dc5af61b7f2cd0b2dfc7673c292c89a4 100644 (file)
@@ -218,7 +218,7 @@ svm_migrate_get_vram_page(struct svm_range *prange, unsigned long pfn)
        page = pfn_to_page(pfn);
        svm_range_bo_ref(prange->svm_bo);
        page->zone_device_data = prange->svm_bo;
-       zone_device_page_init(page);
+       zone_device_page_init(page, 0);
 }
 
 static void
index 22c44807e3fe97d78398497e6b0533f0fff43f53..46a8edb279dccc2bb7d6bd9e4b1e239f8850cd53 100644 (file)
@@ -196,7 +196,7 @@ static void drm_pagemap_get_devmem_page(struct page *page,
                                        struct drm_pagemap_zdd *zdd)
 {
        page->zone_device_data = drm_pagemap_zdd_get(zdd);
-       zone_device_page_init(page);
+       zone_device_page_init(page, 0);
 }
 
 /**
index ca4932a150e3875fba41af2d3832e07cc9f816c2..53cc1926b9da58ab8d19f425735fb5911271edd5 100644 (file)
@@ -318,7 +318,7 @@ nouveau_dmem_page_alloc_locked(struct nouveau_drm *drm)
                        return NULL;
        }
 
-       zone_device_page_init(page);
+       zone_device_page_init(page, 0);
        return page;
 }
 
index e5951ba12a2825ab7816a96de07bb7e982e80936..d2487a19cba2a420bc7a8a921a714547505b5672 100644 (file)
@@ -206,7 +206,7 @@ static inline bool is_fsdax_page(const struct page *page)
 }
 
 #ifdef CONFIG_ZONE_DEVICE
-void zone_device_page_init(struct page *page);
+void zone_device_page_init(struct page *page, unsigned int order);
 void *memremap_pages(struct dev_pagemap *pgmap, int nid);
 void memunmap_pages(struct dev_pagemap *pgmap);
 void *devm_memremap_pages(struct device *dev, struct dev_pagemap *pgmap);
@@ -215,6 +215,14 @@ struct dev_pagemap *get_dev_pagemap(unsigned long pfn);
 bool pgmap_pfn_valid(struct dev_pagemap *pgmap, unsigned long pfn);
 
 unsigned long memremap_compat_align(void);
+
+static inline void zone_device_folio_init(struct folio *folio, unsigned int order)
+{
+       zone_device_page_init(&folio->page, order);
+       if (order)
+               folio_set_large_rmappable(folio);
+}
+
 #else
 static inline void *devm_memremap_pages(struct device *dev,
                struct dev_pagemap *pgmap)
index 83e3d8208a54073e6349ed8e9533ccbc77618a87..24d82121cde8a13de188f8ae7658f0c0ea3b3d88 100644 (file)
@@ -627,7 +627,7 @@ static struct page *dmirror_devmem_alloc_page(struct dmirror_device *mdevice)
                        goto error;
        }
 
-       zone_device_page_init(dpage);
+       zone_device_page_init(dpage, 0);
        dpage->zone_device_data = rpage;
        return dpage;
 
index 46cb1b0b6f72238b1688a823b97ad9bd95903bea..e45dfb568710976f34bdbf35eeed71181efd3731 100644 (file)
@@ -416,20 +416,19 @@ EXPORT_SYMBOL_GPL(get_dev_pagemap);
 void free_zone_device_folio(struct folio *folio)
 {
        struct dev_pagemap *pgmap = folio->pgmap;
+       unsigned long nr = folio_nr_pages(folio);
+       int i;
 
        if (WARN_ON_ONCE(!pgmap))
                return;
 
        mem_cgroup_uncharge(folio);
 
-       /*
-        * Note: we don't expect anonymous compound pages yet. Once supported
-        * and we could PTE-map them similar to THP, we'd have to clear
-        * PG_anon_exclusive on all tail pages.
-        */
        if (folio_test_anon(folio)) {
-               VM_BUG_ON_FOLIO(folio_test_large(folio), folio);
-               __ClearPageAnonExclusive(folio_page(folio, 0));
+               for (i = 0; i < nr; i++)
+                       __ClearPageAnonExclusive(folio_page(folio, i));
+       } else {
+               VM_WARN_ON_ONCE(folio_test_large(folio));
        }
 
        /*
@@ -456,8 +455,8 @@ void free_zone_device_folio(struct folio *folio)
        case MEMORY_DEVICE_COHERENT:
                if (WARN_ON_ONCE(!pgmap->ops || !pgmap->ops->page_free))
                        break;
-               pgmap->ops->page_free(folio_page(folio, 0));
-               put_dev_pagemap(pgmap);
+               pgmap->ops->page_free(&folio->page);
+               percpu_ref_put_many(&folio->pgmap->ref, nr);
                break;
 
        case MEMORY_DEVICE_GENERIC:
@@ -480,14 +479,19 @@ void free_zone_device_folio(struct folio *folio)
        }
 }
 
-void zone_device_page_init(struct page *page)
+void zone_device_page_init(struct page *page, unsigned int order)
 {
+       VM_WARN_ON_ONCE(order > MAX_ORDER_NR_PAGES);
+
        /*
         * Drivers shouldn't be allocating pages after calling
         * memunmap_pages().
         */
-       WARN_ON_ONCE(!percpu_ref_tryget_live(&page_pgmap(page)->ref));
+       WARN_ON_ONCE(!percpu_ref_tryget_many(&page_pgmap(page)->ref, 1 << order));
        set_page_count(page, 1);
        lock_page(page);
+
+       if (order)
+               prep_compound_page(page, order);
 }
 EXPORT_SYMBOL_GPL(zone_device_page_init);
index ac4f783d6ec2f50c281cdc0221c1918ed4f1a17b..9bab13429975686fa9bbda5bae3fd63104c0cae1 100644 (file)
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -1757,9 +1757,13 @@ static __always_inline void __folio_remove_rmap(struct folio *folio,
         * the folio is unmapped and at least one page is still mapped.
         *
         * Check partially_mapped first to ensure it is a large folio.
+        *
+        * Device private folios do not support deferred splitting and
+        * shrinker based scanning of the folios to free.
         */
        if (partially_mapped && folio_test_anon(folio) &&
-           !folio_test_partially_mapped(folio))
+           !folio_test_partially_mapped(folio) &&
+           !folio_is_device_private(folio))
                deferred_split_folio(folio, true);
 
        __folio_mod_stat(folio, -nr, -nr_pmdmapped);