mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 05:49:47 -04:00
drm/xe: have xe_svm_range embed one drm_gpusvm_pages
With drm_gpusvm_pages now self contained, make xe stop relying
on the drm_gpusvm_range pages and take responsibility for the page
lifecycle on the driver side.
Driver side (xe):
- Embed struct drm_gpusvm_pages in xe_svm_range and route all
xe accesses through it instead of range->base.pages.
- Initialise the embedded pages via drm_gpusvm_init_pages(), which
binds the owning &xe->drm up front, and take over the page
lifecycle: xe_svm_range_get_pages() calls drm_gpusvm_get_pages()
directly; the notifier event_end and xe_svm_range_free() paths
drive unmap/free on the embedded pages object.
- Convert the open-coded userptr pages init in xe_userptr_setup()
to the same drm_gpusvm_init_pages() helper.
- Switch xe_svm_range_pages_valid() to drm_gpusvm_pages_valid().
Framework side (drm_gpusvm):
- Add a small inline drm_gpusvm_init_pages() helper that records the
owning drm_device and initialises the per-pages state, giving
drivers a single hook to extend.
- Export drm_gpusvm_pages_valid() to let driver owned pages
can query mapping state without going through a range.
- Lifecycle change: drm_gpusvm_range_remove() no longer *triggers*
unmap/free of the embedded pages. The unmap/free logic itself stays
in the framework -- drm_gpusvm_free_pages() still performs the DMA
unmap (as an idempotent backstop) and frees the dma_addr array --
but the driver now owns *when* it runs, since the driver owns the
drm_gpusvm_pages object.
Side effect / contract: a driver that owns a drm_gpusvm_pages is now
responsible for its lifecycle: drm_gpusvm_init_pages() before first
use, and drm_gpusvm_free_pages() when the owner goes away. Xe does the
latter from its ops->range_free callback, which the framework invokes
once the range refcount drops to zero in drm_gpusvm_range_remove().
The timely DMA unmap for the IOMMU security model still happens in the
notifier invalidate path via drm_gpusvm_unmap_pages(); the unmap inside
drm_gpusvm_free_pages() is only a backstop for pages that were never
invalidated.
Suggested-by: Matthew Brost <matthew.brost@intel.com>
Reviewed-by: Matthew Brost <matthew.brost@intel.com>
Signed-off-by: Honglei Huang <honghuan@amd.com>
Signed-off-by: Matthew Brost <matthew.brost@intel.com>
Link: https://patch.msgid.link/20260630102127.392396-4-honghuan@amd.com
This commit is contained in:
committed by
Matthew Brost
parent
1d0c4c15c2
commit
24c339202c
@@ -1228,12 +1228,17 @@ EXPORT_SYMBOL_GPL(drm_gpusvm_free_pages);
|
||||
* This function removes the specified GPU SVM range and also removes the parent
|
||||
* GPU SVM notifier if no more ranges remain in the notifier. The caller must
|
||||
* hold a lock to protect range and notifier removal.
|
||||
*
|
||||
* This function does not unmap or free the drm_gpusvm_pages, the driver owns
|
||||
* that lifecycle. The caller must DMA unmap the range's pages before calling
|
||||
* this function, so a range is never removed from the MMU interval tree while
|
||||
* still DMA mapped. Typically the driver calls drm_gpusvm_unmap_pages() first.
|
||||
* And the range_free callback's drm_gpusvm_free_pages() is a final fallback safe
|
||||
* net.
|
||||
*/
|
||||
void drm_gpusvm_range_remove(struct drm_gpusvm *gpusvm,
|
||||
struct drm_gpusvm_range *range)
|
||||
{
|
||||
unsigned long npages = npages_in_range(drm_gpusvm_range_start(range),
|
||||
drm_gpusvm_range_end(range));
|
||||
struct drm_gpusvm_notifier *notifier;
|
||||
|
||||
drm_gpusvm_driver_lock_held(gpusvm);
|
||||
@@ -1245,8 +1250,6 @@ void drm_gpusvm_range_remove(struct drm_gpusvm *gpusvm,
|
||||
return;
|
||||
|
||||
drm_gpusvm_notifier_lock(gpusvm);
|
||||
__drm_gpusvm_unmap_pages(gpusvm, &range->pages, npages);
|
||||
__drm_gpusvm_free_pages(gpusvm, &range->pages);
|
||||
__drm_gpusvm_range_remove(notifier, range);
|
||||
drm_gpusvm_notifier_unlock(gpusvm);
|
||||
|
||||
@@ -1325,13 +1328,14 @@ EXPORT_SYMBOL_GPL(drm_gpusvm_range_put);
|
||||
*
|
||||
* Return: True if GPU SVM range has valid pages, False otherwise
|
||||
*/
|
||||
static bool drm_gpusvm_pages_valid(struct drm_gpusvm *gpusvm,
|
||||
struct drm_gpusvm_pages *svm_pages)
|
||||
bool drm_gpusvm_pages_valid(struct drm_gpusvm *gpusvm,
|
||||
struct drm_gpusvm_pages *svm_pages)
|
||||
{
|
||||
lockdep_assert_held(&gpusvm->notifier_lock);
|
||||
|
||||
return svm_pages->flags.has_devmem_pages || svm_pages->flags.has_dma_mapping;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_gpusvm_pages_valid);
|
||||
|
||||
/**
|
||||
* drm_gpusvm_range_pages_valid() - GPU SVM range pages valid
|
||||
|
||||
@@ -760,7 +760,7 @@ xe_pt_stage_bind(struct xe_tile *tile, struct xe_vma *vma,
|
||||
return -EAGAIN;
|
||||
}
|
||||
if (xe_svm_range_has_dma_mapping(range)) {
|
||||
xe_res_first_dma(range->base.pages.dma_addr, 0,
|
||||
xe_res_first_dma(range->pages.dma_addr, 0,
|
||||
xe_svm_range_size(range),
|
||||
&curs);
|
||||
xe_svm_range_debug(range, "BIND PREPARE - MIXED");
|
||||
|
||||
@@ -66,7 +66,7 @@ static bool xe_svm_range_in_vram(struct xe_svm_range *range)
|
||||
|
||||
struct drm_gpusvm_pages_flags flags = {
|
||||
/* Pairs with WRITE_ONCE in drm_gpusvm.c */
|
||||
.__flags = READ_ONCE(range->base.pages.flags.__flags),
|
||||
.__flags = READ_ONCE(range->pages.flags.__flags),
|
||||
};
|
||||
|
||||
return flags.has_devmem_pages;
|
||||
@@ -96,7 +96,7 @@ static struct xe_vm *range_to_vm(struct drm_gpusvm_range *r)
|
||||
(r__)->base.gpusvm, \
|
||||
xe_svm_range_in_vram((r__)) ? 1 : 0, \
|
||||
xe_svm_range_has_vram_binding((r__)) ? 1 : 0, \
|
||||
(r__)->base.pages.notifier_seq, \
|
||||
(r__)->pages.notifier_seq, \
|
||||
xe_svm_range_start((r__)), xe_svm_range_end((r__)), \
|
||||
xe_svm_range_size((r__)))
|
||||
|
||||
@@ -115,6 +115,7 @@ xe_svm_range_alloc(struct drm_gpusvm *gpusvm)
|
||||
return NULL;
|
||||
|
||||
INIT_LIST_HEAD(&range->garbage_collector_link);
|
||||
drm_gpusvm_init_pages(&range->pages, &gpusvm_to_vm(gpusvm)->xe->drm);
|
||||
xe_vm_get(gpusvm_to_vm(gpusvm));
|
||||
|
||||
return &range->base;
|
||||
@@ -122,8 +123,10 @@ xe_svm_range_alloc(struct drm_gpusvm *gpusvm)
|
||||
|
||||
static void xe_svm_range_free(struct drm_gpusvm_range *range)
|
||||
{
|
||||
drm_gpusvm_free_pages(range->gpusvm, &(to_xe_range(range)->pages),
|
||||
drm_gpusvm_range_size(range) >> PAGE_SHIFT);
|
||||
xe_vm_put(range_to_vm(range));
|
||||
kfree(range);
|
||||
kfree(to_xe_range(range));
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -134,7 +137,7 @@ xe_svm_garbage_collector_add_range(struct xe_vm *vm, struct xe_svm_range *range,
|
||||
|
||||
range_debug(range, "GARBAGE COLLECTOR ADD");
|
||||
|
||||
drm_gpusvm_range_set_unmapped(&range->base, &range->base.pages, 1,
|
||||
drm_gpusvm_range_set_unmapped(&range->base, &range->pages, 1,
|
||||
mmu_range);
|
||||
|
||||
spin_lock(&vm->svm.garbage_collector.lock);
|
||||
@@ -209,7 +212,8 @@ xe_svm_range_notifier_event_end(struct xe_vm *vm, struct drm_gpusvm_range *r,
|
||||
|
||||
xe_svm_assert_in_notifier(vm);
|
||||
|
||||
drm_gpusvm_range_unmap_pages(&vm->svm.gpusvm, r, &ctx);
|
||||
drm_gpusvm_unmap_pages(&vm->svm.gpusvm, &(to_xe_range(r)->pages),
|
||||
drm_gpusvm_range_size(r) >> PAGE_SHIFT, &ctx);
|
||||
if (!xe_vm_is_closed(vm) && mmu_range->event == MMU_NOTIFY_UNMAP)
|
||||
xe_svm_garbage_collector_add_range(vm, to_xe_range(r),
|
||||
mmu_range);
|
||||
@@ -300,6 +304,7 @@ static void xe_svm_invalidate(struct drm_gpusvm *gpusvm,
|
||||
static int __xe_svm_garbage_collector(struct xe_vm *vm,
|
||||
struct xe_svm_range *range)
|
||||
{
|
||||
struct drm_gpusvm_ctx ctx = { .in_notifier = false, };
|
||||
struct dma_fence *fence;
|
||||
|
||||
range_debug(range, "GARBAGE COLLECTOR");
|
||||
@@ -311,6 +316,10 @@ static int __xe_svm_garbage_collector(struct xe_vm *vm,
|
||||
return PTR_ERR(fence);
|
||||
dma_fence_put(fence);
|
||||
|
||||
drm_gpusvm_unmap_pages(&vm->svm.gpusvm, &range->pages,
|
||||
drm_gpusvm_range_size(&range->base) >> PAGE_SHIFT,
|
||||
&ctx);
|
||||
|
||||
drm_gpusvm_range_remove(&vm->svm.gpusvm, &range->base);
|
||||
|
||||
return 0;
|
||||
@@ -945,15 +954,28 @@ void xe_svm_close(struct xe_vm *vm)
|
||||
*/
|
||||
void xe_svm_fini(struct xe_vm *vm)
|
||||
{
|
||||
struct drm_gpusvm_notifier *notifier, *next;
|
||||
struct drm_gpusvm_ctx ctx = { .in_notifier = false, };
|
||||
|
||||
xe_assert(vm->xe, xe_vm_is_closed(vm));
|
||||
|
||||
drm_gpusvm_for_each_notifier_safe(notifier, next, &vm->svm.gpusvm, 0, LONG_MAX) {
|
||||
struct drm_gpusvm_range *range, *__next;
|
||||
|
||||
drm_gpusvm_for_each_range_safe(range, __next, notifier, 0, LONG_MAX)
|
||||
drm_gpusvm_unmap_pages(&vm->svm.gpusvm,
|
||||
&(to_xe_range(range)->pages),
|
||||
drm_gpusvm_range_size(range) >> PAGE_SHIFT,
|
||||
&ctx);
|
||||
}
|
||||
|
||||
drm_gpusvm_fini(&vm->svm.gpusvm);
|
||||
}
|
||||
|
||||
static bool xe_svm_range_has_pagemap_locked(const struct xe_svm_range *range,
|
||||
const struct drm_pagemap *dpagemap)
|
||||
{
|
||||
return range->base.pages.dpagemap == dpagemap;
|
||||
return range->pages.dpagemap == dpagemap;
|
||||
}
|
||||
|
||||
static bool xe_svm_range_has_pagemap(struct xe_svm_range *range,
|
||||
@@ -1018,7 +1040,7 @@ bool xe_svm_range_validate(struct xe_vm *vm,
|
||||
if (dpagemap)
|
||||
ret = ret && xe_svm_range_has_pagemap_locked(range, dpagemap);
|
||||
else
|
||||
ret = ret && !range->base.pages.dpagemap;
|
||||
ret = ret && !range->pages.dpagemap;
|
||||
|
||||
xe_svm_notifier_unlock(vm);
|
||||
|
||||
@@ -1513,7 +1535,11 @@ int xe_svm_range_get_pages(struct xe_vm *vm, struct xe_svm_range *range,
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
err = drm_gpusvm_range_get_pages(&vm->svm.gpusvm, &range->base, ctx);
|
||||
err = drm_gpusvm_get_pages(&vm->svm.gpusvm, &range->pages,
|
||||
vm->svm.gpusvm.mm,
|
||||
&range->base.notifier->notifier,
|
||||
drm_gpusvm_range_start(&range->base),
|
||||
drm_gpusvm_range_end(&range->base), ctx);
|
||||
if (err == -EOPNOTSUPP) {
|
||||
range_debug(range, "PAGE FAULT - EVICT PAGES");
|
||||
drm_gpusvm_range_evict(&vm->svm.gpusvm, &range->base);
|
||||
|
||||
@@ -31,6 +31,8 @@ struct xe_vram_region;
|
||||
struct xe_svm_range {
|
||||
/** @base: base drm_gpusvm_range */
|
||||
struct drm_gpusvm_range base;
|
||||
/** @pages: Page/DMA mapping state for this range (single drm_device). */
|
||||
struct drm_gpusvm_pages pages;
|
||||
/**
|
||||
* @garbage_collector_link: Link into VM's garbage collect SVM range
|
||||
* list. Protected by VM's garbage collect lock.
|
||||
@@ -74,7 +76,7 @@ struct xe_pagemap {
|
||||
*/
|
||||
static inline bool xe_svm_range_pages_valid(struct xe_svm_range *range)
|
||||
{
|
||||
return drm_gpusvm_range_pages_valid(range->base.gpusvm, &range->base);
|
||||
return drm_gpusvm_pages_valid(range->base.gpusvm, &range->pages);
|
||||
}
|
||||
|
||||
int xe_devm_add(struct xe_tile *tile, struct xe_vram_region *vr);
|
||||
@@ -132,7 +134,7 @@ void *xe_svm_private_page_owner(struct xe_vm *vm, bool force_smem);
|
||||
static inline bool xe_svm_range_has_dma_mapping(struct xe_svm_range *range)
|
||||
{
|
||||
lockdep_assert_held(&range->base.gpusvm->notifier_lock);
|
||||
return range->base.pages.flags.has_dma_mapping;
|
||||
return range->pages.flags.has_dma_mapping;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -210,10 +212,10 @@ struct xe_vram_region;
|
||||
struct xe_svm_range {
|
||||
struct {
|
||||
struct interval_tree_node itree;
|
||||
struct {
|
||||
const struct drm_pagemap_addr *dma_addr;
|
||||
} pages;
|
||||
} base;
|
||||
struct {
|
||||
const struct drm_pagemap_addr *dma_addr;
|
||||
} pages;
|
||||
u32 tile_present;
|
||||
u32 tile_invalidated;
|
||||
};
|
||||
|
||||
@@ -396,15 +396,14 @@ int xe_userptr_setup(struct xe_userptr_vma *uvma, unsigned long start,
|
||||
INIT_LIST_HEAD(&userptr->invalidate_link);
|
||||
INIT_LIST_HEAD(&userptr->repin_link);
|
||||
|
||||
drm_gpusvm_init_pages(&userptr->pages, &vm->xe->drm);
|
||||
|
||||
err = mmu_interval_notifier_insert(&userptr->notifier, current->mm,
|
||||
start, range,
|
||||
&vma_userptr_notifier_ops);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
userptr->pages.notifier_seq = LONG_MAX;
|
||||
userptr->pages.drm = &vm->xe->drm;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -310,6 +310,9 @@ void drm_gpusvm_range_put(struct drm_gpusvm_range *range);
|
||||
bool drm_gpusvm_range_pages_valid(struct drm_gpusvm *gpusvm,
|
||||
struct drm_gpusvm_range *range);
|
||||
|
||||
bool drm_gpusvm_pages_valid(struct drm_gpusvm *gpusvm,
|
||||
struct drm_gpusvm_pages *svm_pages);
|
||||
|
||||
int drm_gpusvm_range_get_pages(struct drm_gpusvm *gpusvm,
|
||||
struct drm_gpusvm_range *range,
|
||||
const struct drm_gpusvm_ctx *ctx);
|
||||
@@ -350,6 +353,23 @@ void drm_gpusvm_free_pages(struct drm_gpusvm *gpusvm,
|
||||
struct drm_gpusvm_pages *svm_pages,
|
||||
unsigned long npages);
|
||||
|
||||
/**
|
||||
* drm_gpusvm_init_pages() - Initialize a freshly allocated drm_gpusvm_pages
|
||||
* @svm_pages: Pointer to the drm_gpusvm_pages to initialize.
|
||||
* @drm: The DRM device that will own DMA mappings for this pages object.
|
||||
*
|
||||
* Drivers that embed one or more drm_gpusvm_pages in their own range
|
||||
* structure must call this once on each pages instance after allocation,
|
||||
* before the first drm_gpusvm_get_pages() / unmap / free.
|
||||
*/
|
||||
static inline void drm_gpusvm_init_pages(struct drm_gpusvm_pages *svm_pages,
|
||||
struct drm_device *drm)
|
||||
{
|
||||
memset(svm_pages, 0, sizeof(*svm_pages));
|
||||
svm_pages->drm = drm;
|
||||
svm_pages->notifier_seq = LONG_MAX;
|
||||
}
|
||||
|
||||
/**
|
||||
* enum drm_gpusvm_scan_result - Scan result from the drm_gpusvm_scan_mm() function.
|
||||
* @DRM_GPUSVM_SCAN_UNPOPULATED: At least one page was not present or inaccessible.
|
||||
|
||||
Reference in New Issue
Block a user