A migrate.src[] entry with MIGRATE_PFN_VALID=0 && MIGRATE_PFN_COMPOUND=1
represents a compound (THP) source page, but migrate_pfn_to_page() returns
NULL for it. Both accounting loops in drm_pagemap_migrate_to_devmem()
derive the count from that NULL page and fall back to nr_pages = 1 instead
of HPAGE_PMD_NR, undercounting migrated_pages and risking a spurious
-EBUSY in the final migration race check. drm_pagemap_cpages() already
handles this case, so the open-coded sites disagree.
Factor the decoding out into drm_pagemap_src_pfn_nr_pages() and use it in
drm_pagemap_cpages() and both accounting loops of
drm_pagemap_migrate_to_devmem(), so a COMPOUND-only entry is consistently
counted as HPAGE_PMD_NR base pages.
Fixes: 192cb1f5cb16 ("drm/pagemap: Enable THP support for GPU memory migration")
Signed-off-by: Junhua Shen <[email protected]>
Reviewed-by: Francois Dugast <[email protected]>
---
v2:
- Reframe as a bug fix: a MIGRATE_PFN_VALID=0 && MIGRATE_PFN_COMPOUND=1
entry was undercounted as 1 base page instead of HPAGE_PMD_NR, risking a
spurious -EBUSY. Rewrite the commit message accordingly and add a Fixes
tag.
- Add Reviewed-by: Francois Dugast.
- No functional change to the diff since v1.
drivers/gpu/drm/drm_pagemap.c | 60 +++++++++++++++++++++++++----------
1 file changed, 43 insertions(+), 17 deletions(-)
diff --git a/drivers/gpu/drm/drm_pagemap.c b/drivers/gpu/drm/drm_pagemap.c
index 892b325fa99b..a31cc4f0af68 100644
--- a/drivers/gpu/drm/drm_pagemap.c
+++ b/drivers/gpu/drm/drm_pagemap.c
@@ -554,6 +554,37 @@ static int drm_pagemap_migrate_range(struct
drm_pagemap_devmem *devmem,
return ret;
}
+/**
+ * drm_pagemap_src_pfn_nr_pages() - Decode src entry and return base-page count
+ * @src_pfn: Source migrate entry
+ * @src_page: Optional decoded source page when MIGRATE_PFN_VALID is set
+ *
+ * Decode @src_pfn to compute how many base pages it represents: use folio
+ * page count for valid entries, HPAGE_PMD_NR for COMPOUND-only entries,
+ * otherwise 1.
+ *
+ * Return: Number of base pages represented by @src_pfn.
+ */
+static unsigned long drm_pagemap_src_pfn_nr_pages(unsigned long src_pfn,
+ struct page **src_page)
+{
+ struct page *page = NULL;
+ unsigned long nr_pages = 1;
+
+ if (src_pfn & MIGRATE_PFN_VALID) {
+ page = migrate_pfn_to_page(src_pfn);
+ if (page)
+ nr_pages = NR_PAGES(folio_order(page_folio(page)));
+ } else if (src_pfn & MIGRATE_PFN_COMPOUND) {
+ nr_pages = HPAGE_PMD_NR;
+ }
+
+ if (src_page)
+ *src_page = page;
+
+ return nr_pages;
+}
+
/**
* drm_pagemap_cpages() - Count collected pages
* @migrate_pfn: Array of migrate_pfn entries to account
@@ -570,20 +601,14 @@ static int drm_pagemap_cpages(unsigned long *migrate_pfn,
unsigned long npages)
unsigned long i, cpages = 0;
for (i = 0; i < npages;) {
- struct page *page = migrate_pfn_to_page(migrate_pfn[i]);
- struct folio *folio;
- unsigned int order = 0;
+ unsigned long src_pfn = migrate_pfn[i];
+ struct page *page;
+ unsigned long nr_pages = drm_pagemap_src_pfn_nr_pages(src_pfn,
&page);
- if (page) {
- folio = page_folio(page);
- order = folio_order(folio);
- cpages += NR_PAGES(order);
- } else if (migrate_pfn[i] & MIGRATE_PFN_COMPOUND) {
- order = HPAGE_PMD_ORDER;
- cpages += NR_PAGES(order);
- }
+ if (page || (src_pfn & MIGRATE_PFN_COMPOUND))
+ cpages += nr_pages;
- i += NR_PAGES(order);
+ i += nr_pages;
}
return cpages;
@@ -703,8 +728,9 @@ int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem
*devmem_allocation,
/* Count device-private pages to migrate */
for (i = 0; i < npages;) {
- struct page *src_page = migrate_pfn_to_page(migrate.src[i]);
- unsigned long nr_pages = src_page ?
NR_PAGES(folio_order(page_folio(src_page))) : 1;
+ unsigned long src_pfn = migrate.src[i];
+ struct page *src_page;
+ unsigned long nr_pages = drm_pagemap_src_pfn_nr_pages(src_pfn,
&src_page);
if (src_page && is_zone_device_page(src_page)) {
if (page_pgmap(src_page) == pagemap)
@@ -818,10 +844,10 @@ int drm_pagemap_migrate_to_devmem(struct
drm_pagemap_devmem *devmem_allocation,
migrate_vma_pages(&migrate);
for (i = 0; !err && i < npages;) {
- struct page *page = migrate_pfn_to_page(migrate.src[i]);
- unsigned long nr_pages = page ?
NR_PAGES(folio_order(page_folio(page))) : 1;
+ unsigned long src_pfn = migrate.src[i];
+ unsigned long nr_pages = drm_pagemap_src_pfn_nr_pages(src_pfn,
NULL);
- if (migrate.src[i] & MIGRATE_PFN_MIGRATE)
+ if (src_pfn & MIGRATE_PFN_MIGRATE)
migrated_pages += nr_pages;
i += nr_pages;
--
2.34.1