Skip to content

Commit ec265e1

Browse files
author
Thomas Hellström
committed
drm/pagemap: Support source migration over interconnect
Support source interconnect migration by using the copy_to_ram() op of the source device private pages. Source interconnect migration is required to flush the L2 cache of the source device, which among other things is a requirement for correct global atomic operation. It also enables the source GPU to potentially decompress any compressed content which is not understood by peers, and finally for the PCIe case, it's expected that writes over PCIe will be faster than reads. The implementation can probably be improved by coalescing subregions with the same source. v5: - Update waiting for the pre_migrate_fence and comments around that, previously in another patch. (Himal). - Actually select device private pages to migrate when source_peer_migrates is true. Signed-off-by: Thomas Hellström <thomas.hellstrom@linux.intel.com> Reviewed-by: Matthew Brost <matthew.brost@intel.com> Acked-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> # For merging through drm-xe. Link: https://patch.msgid.link/20251219113320.183860-24-thomas.hellstrom@linux.intel.com
1 parent 75af93b commit ec265e1

1 file changed

Lines changed: 166 additions & 38 deletions

File tree

drivers/gpu/drm/drm_pagemap.c

Lines changed: 166 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -206,11 +206,11 @@ static void drm_pagemap_get_devmem_page(struct page *page,
206206

207207
/**
208208
* drm_pagemap_migrate_map_pages() - Map migration pages for GPU SVM migration
209-
* @dev: The device for which the pages are being mapped
210-
* @local_dpagemap: The drm_pagemap pointer of the local drm_pagemap.
211-
* @pagemap_addr: Array to store DMA information corresponding to mapped pages
212-
* @migrate_pfn: Array of migrate page frame numbers to map
213-
* @npages: Number of pages to map
209+
* @dev: The device performing the migration.
210+
* @local_dpagemap: The drm_pagemap local to the migrating device.
211+
* @pagemap_addr: Array to store DMA information corresponding to mapped pages.
212+
* @migrate_pfn: Array of page frame numbers of system pages or peer pages to map.
213+
* @npages: Number of system pages or peer pages to map.
214214
* @dir: Direction of data transfer (e.g., DMA_BIDIRECTIONAL)
215215
* @mdetails: Details governing the migration behaviour.
216216
*
@@ -229,8 +229,7 @@ static int drm_pagemap_migrate_map_pages(struct device *dev,
229229
enum dma_data_direction dir,
230230
const struct drm_pagemap_migrate_details *mdetails)
231231
{
232-
unsigned long i;
233-
unsigned long num_peer_pages = 0;
232+
unsigned long num_peer_pages = 0, num_local_pages = 0, i;
234233

235234
for (i = 0; i < npages;) {
236235
struct page *page = migrate_pfn_to_page(migrate_pfn[i]);
@@ -249,10 +248,15 @@ static int drm_pagemap_migrate_map_pages(struct device *dev,
249248
struct drm_pagemap *dpagemap = zdd->dpagemap;
250249
struct drm_pagemap_addr addr;
251250

252-
if (dpagemap == local_dpagemap && !mdetails->can_migrate_same_pagemap)
253-
goto next;
251+
if (dpagemap == local_dpagemap) {
252+
if (!mdetails->can_migrate_same_pagemap)
253+
goto next;
254+
255+
num_local_pages += NR_PAGES(order);
256+
} else {
257+
num_peer_pages += NR_PAGES(order);
258+
}
254259

255-
num_peer_pages += NR_PAGES(order);
256260
addr = dpagemap->ops->device_map(dpagemap, dev, page, order, dir);
257261
if (dma_mapping_error(dev, addr.addr))
258262
return -EFAULT;
@@ -276,6 +280,9 @@ static int drm_pagemap_migrate_map_pages(struct device *dev,
276280
if (num_peer_pages)
277281
drm_dbg(local_dpagemap->drm, "Migrating %lu peer pages over interconnect.\n",
278282
num_peer_pages);
283+
if (num_local_pages)
284+
drm_dbg(local_dpagemap->drm, "Migrating %lu local pages over interconnect.\n",
285+
num_local_pages);
279286

280287
return 0;
281288
}
@@ -328,6 +335,115 @@ npages_in_range(unsigned long start, unsigned long end)
328335
return (end - start) >> PAGE_SHIFT;
329336
}
330337

338+
static int
339+
drm_pagemap_migrate_remote_to_local(struct drm_pagemap_devmem *devmem,
340+
struct device *remote_device,
341+
struct drm_pagemap *remote_dpagemap,
342+
unsigned long local_pfns[],
343+
struct page *remote_pages[],
344+
struct drm_pagemap_addr pagemap_addr[],
345+
unsigned long npages,
346+
const struct drm_pagemap_devmem_ops *ops,
347+
const struct drm_pagemap_migrate_details *mdetails)
348+
349+
{
350+
int err = drm_pagemap_migrate_map_pages(remote_device, remote_dpagemap,
351+
pagemap_addr, local_pfns,
352+
npages, DMA_FROM_DEVICE, mdetails);
353+
354+
if (err)
355+
goto out;
356+
357+
err = ops->copy_to_ram(remote_pages, pagemap_addr, npages,
358+
devmem->pre_migrate_fence);
359+
out:
360+
drm_pagemap_migrate_unmap_pages(remote_device, pagemap_addr, local_pfns,
361+
npages, DMA_FROM_DEVICE);
362+
return err;
363+
}
364+
365+
static int
366+
drm_pagemap_migrate_sys_to_dev(struct drm_pagemap_devmem *devmem,
367+
unsigned long sys_pfns[],
368+
struct page *local_pages[],
369+
struct drm_pagemap_addr pagemap_addr[],
370+
unsigned long npages,
371+
const struct drm_pagemap_devmem_ops *ops,
372+
const struct drm_pagemap_migrate_details *mdetails)
373+
{
374+
int err = drm_pagemap_migrate_map_pages(devmem->dev, devmem->dpagemap,
375+
pagemap_addr, sys_pfns, npages,
376+
DMA_TO_DEVICE, mdetails);
377+
378+
if (err)
379+
goto out;
380+
381+
err = ops->copy_to_devmem(local_pages, pagemap_addr, npages,
382+
devmem->pre_migrate_fence);
383+
out:
384+
drm_pagemap_migrate_unmap_pages(devmem->dev, pagemap_addr, sys_pfns, npages,
385+
DMA_TO_DEVICE);
386+
return err;
387+
}
388+
389+
/**
390+
* struct migrate_range_loc - Cursor into the loop over migrate_pfns for migrating to
391+
* device.
392+
* @start: The current loop index.
393+
* @device: migrating device.
394+
* @dpagemap: Pointer to struct drm_pagemap used by the migrating device.
395+
* @ops: The copy ops to be used for the migrating device.
396+
*/
397+
struct migrate_range_loc {
398+
unsigned long start;
399+
struct device *device;
400+
struct drm_pagemap *dpagemap;
401+
const struct drm_pagemap_devmem_ops *ops;
402+
};
403+
404+
static int drm_pagemap_migrate_range(struct drm_pagemap_devmem *devmem,
405+
unsigned long src_pfns[],
406+
unsigned long dst_pfns[],
407+
struct page *pages[],
408+
struct drm_pagemap_addr pagemap_addr[],
409+
struct migrate_range_loc *last,
410+
const struct migrate_range_loc *cur,
411+
const struct drm_pagemap_migrate_details *mdetails)
412+
{
413+
int ret = 0;
414+
415+
if (cur->start == 0)
416+
goto out;
417+
418+
if (cur->start <= last->start)
419+
return 0;
420+
421+
if (cur->dpagemap == last->dpagemap && cur->ops == last->ops)
422+
return 0;
423+
424+
if (last->dpagemap)
425+
ret = drm_pagemap_migrate_remote_to_local(devmem,
426+
last->device,
427+
last->dpagemap,
428+
&dst_pfns[last->start],
429+
&pages[last->start],
430+
&pagemap_addr[last->start],
431+
cur->start - last->start,
432+
last->ops, mdetails);
433+
434+
else
435+
ret = drm_pagemap_migrate_sys_to_dev(devmem,
436+
&src_pfns[last->start],
437+
&pages[last->start],
438+
&pagemap_addr[last->start],
439+
cur->start - last->start,
440+
last->ops, mdetails);
441+
442+
out:
443+
*last = *cur;
444+
return ret;
445+
}
446+
331447
/**
332448
* drm_pagemap_migrate_to_devmem() - Migrate a struct mm_struct range to device memory
333449
* @devmem_allocation: The device memory allocation to migrate to.
@@ -365,10 +481,11 @@ int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem *devmem_allocation,
365481
.end = end,
366482
.pgmap_owner = pagemap->owner,
367483
.flags = MIGRATE_VMA_SELECT_SYSTEM | MIGRATE_VMA_SELECT_DEVICE_COHERENT |
368-
(mdetails->source_peer_migrates ? 0 : MIGRATE_VMA_SELECT_DEVICE_PRIVATE),
484+
MIGRATE_VMA_SELECT_DEVICE_PRIVATE,
369485
};
370486
unsigned long i, npages = npages_in_range(start, end);
371487
unsigned long own_pages = 0, migrated_pages = 0;
488+
struct migrate_range_loc cur, last = {.device = dpagemap->drm->dev, .ops = ops};
372489
struct vm_area_struct *vas;
373490
struct drm_pagemap_zdd *zdd = NULL;
374491
struct page **pages;
@@ -467,44 +584,55 @@ int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem *devmem_allocation,
467584
if (err)
468585
goto err_finalize;
469586

470-
err = drm_pagemap_migrate_map_pages(devmem_allocation->dev,
471-
devmem_allocation->dpagemap, pagemap_addr,
472-
migrate.src, npages, DMA_TO_DEVICE,
473-
mdetails);
474-
475-
if (err) {
476-
drm_pagemap_migrate_unmap_pages(devmem_allocation->dev, pagemap_addr,
477-
migrate.src, npages, DMA_TO_DEVICE);
478-
479-
goto err_finalize;
480-
}
481-
482587
own_pages = 0;
588+
483589
for (i = 0; i < npages; ++i) {
484590
struct page *page = pfn_to_page(migrate.dst[i]);
485591
struct page *src_page = migrate_pfn_to_page(migrate.src[i]);
486-
487-
if (unlikely(src_page && is_zone_device_page(src_page) &&
488-
page_pgmap(src_page) == pagemap &&
489-
!mdetails->can_migrate_same_pagemap)) {
490-
migrate.dst[i] = 0;
491-
pages[i] = NULL;
492-
own_pages++;
493-
continue;
592+
cur.start = i;
593+
594+
pages[i] = NULL;
595+
if (src_page && is_device_private_page(src_page)) {
596+
struct drm_pagemap_zdd *src_zdd = src_page->zone_device_data;
597+
598+
if (page_pgmap(src_page) == pagemap &&
599+
!mdetails->can_migrate_same_pagemap) {
600+
migrate.dst[i] = 0;
601+
own_pages++;
602+
continue;
603+
}
604+
if (mdetails->source_peer_migrates) {
605+
cur.dpagemap = src_zdd->dpagemap;
606+
cur.ops = src_zdd->devmem_allocation->ops;
607+
cur.device = cur.dpagemap->drm->dev;
608+
pages[i] = src_page;
609+
}
610+
}
611+
if (!pages[i]) {
612+
cur.dpagemap = NULL;
613+
cur.ops = ops;
614+
cur.device = dpagemap->drm->dev;
615+
pages[i] = page;
494616
}
495-
pages[i] = page;
496617
migrate.dst[i] = migrate_pfn(migrate.dst[i]);
497618
drm_pagemap_get_devmem_page(page, zdd);
498-
}
499-
drm_WARN_ON(dpagemap->drm, !!own_pages);
500619

501-
err = ops->copy_to_devmem(pages, pagemap_addr, npages,
502-
devmem_allocation->pre_migrate_fence);
503-
drm_pagemap_migrate_unmap_pages(devmem_allocation->dev, pagemap_addr,
504-
migrate.src, npages, DMA_TO_DEVICE);
620+
/* If we switched the migrating drm_pagemap, migrate previous pages now */
621+
err = drm_pagemap_migrate_range(devmem_allocation, migrate.src, migrate.dst,
622+
pages, pagemap_addr, &last, &cur,
623+
mdetails);
624+
if (err)
625+
goto err_finalize;
626+
}
627+
cur.start = npages;
628+
cur.ops = NULL; /* Force migration */
629+
err = drm_pagemap_migrate_range(devmem_allocation, migrate.src, migrate.dst,
630+
pages, pagemap_addr, &last, &cur, mdetails);
505631
if (err)
506632
goto err_finalize;
507633

634+
drm_WARN_ON(dpagemap->drm, !!own_pages);
635+
508636
dma_fence_put(devmem_allocation->pre_migrate_fence);
509637
devmem_allocation->pre_migrate_fence = NULL;
510638

0 commit comments

Comments
 (0)