Re: [RFC PATCH 08/19] drm/xe/bo: Add a bo remove callback

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, 2025-03-12 at 22:04 +0100, Thomas Hellström wrote:
> On device unbind, migrate exported bos, including pagemap bos to
> system. This allows importers to take proper action without
> disruption. In particular, SVM clients on remote devices may
> continue as if nothing happened, and can chose a different
> placement.
> 
> The evict_flags() placement is chosen in such a way that bos that
> aren't exported are purged.
> 
> Signed-off-by: Thomas Hellström <thomas.hellstrom@xxxxxxxxxxxxxxx>

Hmm. It seems like this patch acidentally got merged with another.
I'll separate and resend in v2.

Thanks,
Thomas



> ---
>  drivers/gpu/drm/drm_pagemap.c        | 113 ++++++--
>  drivers/gpu/drm/xe/xe_bo.c           |  53 +++-
>  drivers/gpu/drm/xe/xe_bo.h           |   2 +
>  drivers/gpu/drm/xe/xe_device.c       |   5 +
>  drivers/gpu/drm/xe/xe_device_types.h |  28 +-
>  drivers/gpu/drm/xe/xe_svm.c          | 412 ++++++++++++++++++++++---
> --
>  drivers/gpu/drm/xe/xe_svm.h          |  49 ++++
>  drivers/gpu/drm/xe/xe_tile.c         |  20 +-
>  drivers/gpu/drm/xe/xe_tile.h         |  28 +-
>  drivers/gpu/drm/xe/xe_vm_types.h     |   1 +
>  include/drm/drm_pagemap.h            |  53 +++-
>  11 files changed, 645 insertions(+), 119 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_pagemap.c
> b/drivers/gpu/drm/drm_pagemap.c
> index d1efcd78a023..dcb26328f94b 100644
> --- a/drivers/gpu/drm/drm_pagemap.c
> +++ b/drivers/gpu/drm/drm_pagemap.c
> @@ -97,6 +97,7 @@ drm_pagemap_zdd_alloc(struct drm_pagemap *dpagemap)
>  	kref_init(&zdd->refcount);
>  	zdd->devmem_allocation = NULL;
>  	zdd->dpagemap = dpagemap;
> +	kref_get(&dpagemap->ref);
>  
>  	return zdd;
>  }
> @@ -126,6 +127,7 @@ static void drm_pagemap_zdd_destroy(struct kref
> *ref)
>  	struct drm_pagemap_zdd *zdd =
>  		container_of(ref, struct drm_pagemap_zdd, refcount);
>  	struct drm_pagemap_devmem *devmem = zdd->devmem_allocation;
> +	struct drm_pagemap *dpagemap = zdd->dpagemap;
>  
>  	if (devmem) {
>  		complete_all(&devmem->detached);
> @@ -133,6 +135,7 @@ static void drm_pagemap_zdd_destroy(struct kref
> *ref)
>  			devmem->ops->devmem_release(devmem);
>  	}
>  	kfree(zdd);
> +	drm_pagemap_put(dpagemap);
>  }
>  
>  /**
> @@ -484,42 +487,113 @@ static int
> drm_pagemap_migrate_populate_ram_pfn(struct vm_area_struct *vas,
>  	return -ENOMEM;
>  }
>  
> +/**
> + * struct drm_pagemap_dev_hold - Struct to aid in drm_device
> release.
> + * @work: work struct for async release.
> + * @drm: drm device to put.
> + *
> + * When a struct drm_pagemap is released, we also need to release
> the
> + * reference it holds on the drm device. However, typically that
> needs
> + * to be done separately from a workqueue that is not removed in the
> + * drm device destructor since that would cause a deadlock flushing
> + * that workqueue. Each time a struct drm_pagemap is initialized
> + * (or re-initialized if cached) therefore allocate a separate work
> + * item using this struct, from which we put the drm device and
> + * associated module.
> + */
> +struct drm_pagemap_dev_hold {
> +	struct work_struct work;
> +	struct drm_device *drm;
> +};
> +
>  static void drm_pagemap_release(struct kref *ref)
>  {
>  	struct drm_pagemap *dpagemap = container_of(ref,
> typeof(*dpagemap), ref);
> +	struct drm_pagemap_dev_hold *dev_hold = dpagemap->dev_hold;
>  
> -	kfree(dpagemap);
> +	dpagemap->ops->destroy(dpagemap);
> +	schedule_work(&dev_hold->work);
> +}
> +
> +static void drm_pagemap_dev_unhold_work(struct work_struct *work)
> +{
> +	struct drm_pagemap_dev_hold *dev_hold =
> +		container_of(work, typeof(*dev_hold), work);
> +	struct drm_device *drm = dev_hold->drm;
> +	struct module *module = drm->driver->fops->owner;
> +
> +	drm_dev_put(drm);
> +	module_put(module);
> +	kfree(dev_hold);
> +}
> +
> +static struct drm_pagemap_dev_hold *
> +drm_pagemap_dev_hold(struct drm_pagemap *dpagemap)
> +{
> +	struct drm_pagemap_dev_hold *dev_hold;
> +	struct drm_device *drm = dpagemap->drm;
> +
> +	dev_hold = kzalloc(sizeof(*dev_hold), GFP_KERNEL);
> +	if (!dev_hold)
> +		return ERR_PTR(-ENOMEM);
> +
> +	INIT_WORK(&dev_hold->work, drm_pagemap_dev_unhold_work);
> +	dev_hold->drm = drm;
> +	(void)try_module_get(drm->driver->fops->owner);
> +	drm_dev_get(drm);
> +
> +	return dev_hold;
>  }
>  
>  /**
> - * drm_pagemap_create() - Create a struct drm_pagemap.
> - * @dev: Pointer to a struct device providing the device-private
> memory.
> - * @pagemap: Pointer to a pre-setup struct dev_pagemap providing the
> struct pages.
> - * @ops: Pointer to the struct drm_pagemap_ops.
> + * drm_pagemap_reinit() - Reinitialize a drm_pagemap
> + * @dpagemap: The drm_pagemap to reinitialize
>   *
> - * Allocate and initialize a struct drm_pagemap.
> + * Reinitialize a drm_pagemap, for which drm_pagemap_release
> + * has already been called. This interface is intended for the
> + * situation where the driver caches a destroyed drm_pagemap.
>   *
> - * Return: A refcounted pointer to a struct drm_pagemap on success.
> - * Error pointer on error.
> + * Return: 0 on success, negative error code on failure.
>   */
> -struct drm_pagemap *
> -drm_pagemap_create(struct device *dev,
> -		   struct dev_pagemap *pagemap,
> -		   const struct drm_pagemap_ops *ops)
> +int drm_pagemap_reinit(struct drm_pagemap *dpagemap)
>  {
> -	struct drm_pagemap *dpagemap = kzalloc(sizeof(*dpagemap),
> GFP_KERNEL);
> +	dpagemap->dev_hold = drm_pagemap_dev_hold(dpagemap);
> +	if (IS_ERR(dpagemap->dev_hold))
> +		return PTR_ERR(dpagemap->dev_hold);
>  
> -	if (!dpagemap)
> -		return ERR_PTR(-ENOMEM);
> +	kref_init(&dpagemap->ref);
> +	return 0;
> +}
> +EXPORT_SYMBOL(drm_pagemap_reinit);
>  
> +/**
> + * drm_pagemap_init() - Initialize a pre-allocated drm_pagemap
> + * @dpagemap: The drm_pagemap to initialize.
> + * @pagemap: The associated dev_pagemap providing the device
> + * private pages.
> + * @drm: The drm device. The drm_pagemap holds a reference on the
> + * drm_device and the module owning the drm_device until
> + * drm_pagemap_release(). This facilitates drm_pagemap exporting.
> + * @ops: The drm_pagemap ops.
> + *
> + * Initialize and take an initial reference on a drm_pagemap.
> + * After successful return, use drm_pagemap_put() to destroy.
> + *
> + ** Return: 0 on success, negative error code on error.
> + */
> +int drm_pagemap_init(struct drm_pagemap *dpagemap,
> +		     struct dev_pagemap *pagemap,
> +		     struct drm_device *drm,
> +		     const struct drm_pagemap_ops *ops)
> +{
>  	kref_init(&dpagemap->ref);
> -	dpagemap->dev = dev;
>  	dpagemap->ops = ops;
>  	dpagemap->pagemap = pagemap;
> +	dpagemap->drm = drm;
>  
> -	return dpagemap;
> +	return drm_pagemap_reinit(dpagemap);
>  }
> -EXPORT_SYMBOL(drm_pagemap_create);
> +EXPORT_SYMBOL(drm_pagemap_init);
>  
>  /**
>   * drm_pagemap_put() - Put a struct drm_pagemap reference
> @@ -530,7 +604,8 @@ EXPORT_SYMBOL(drm_pagemap_create);
>   */
>  void drm_pagemap_put(struct drm_pagemap *dpagemap)
>  {
> -	kref_put(&dpagemap->ref, drm_pagemap_release);
> +	if (dpagemap)
> +		kref_put(&dpagemap->ref, drm_pagemap_release);
>  }
>  EXPORT_SYMBOL(drm_pagemap_put);
>  
> diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c
> index 64f9c936eea0..390f90fbd366 100644
> --- a/drivers/gpu/drm/xe/xe_bo.c
> +++ b/drivers/gpu/drm/xe/xe_bo.c
> @@ -55,6 +55,8 @@ static struct ttm_placement sys_placement = {
>  	.placement = &sys_placement_flags,
>  };
>  
> +static struct ttm_placement purge_placement;
> +
>  static const struct ttm_place tt_placement_flags[] = {
>  	{
>  		.fpfn = 0,
> @@ -281,6 +283,8 @@ int xe_bo_placement_for_flags(struct xe_device
> *xe, struct xe_bo *bo,
>  static void xe_evict_flags(struct ttm_buffer_object *tbo,
>  			   struct ttm_placement *placement)
>  {
> +	struct xe_device *xe = container_of(tbo->bdev, typeof(*xe),
> ttm);
> +	bool device_unplugged = drm_dev_is_unplugged(&xe->drm);
>  	struct xe_bo *bo;
>  
>  	if (!xe_bo_is_xe_bo(tbo)) {
> @@ -290,7 +294,7 @@ static void xe_evict_flags(struct
> ttm_buffer_object *tbo,
>  			return;
>  		}
>  
> -		*placement = sys_placement;
> +		*placement = device_unplugged ? purge_placement :
> sys_placement;
>  		return;
>  	}
>  
> @@ -300,6 +304,11 @@ static void xe_evict_flags(struct
> ttm_buffer_object *tbo,
>  		return;
>  	}
>  
> +	if (device_unplugged && !tbo->base.dma_buf) {
> +		*placement = purge_placement;
> +		return;
> +	}
> +
>  	/*
>  	 * For xe, sg bos that are evicted to system just triggers a
>  	 * rebind of the sg list upon subsequent validation to
> XE_PL_TT.
> @@ -657,11 +666,20 @@ static int xe_bo_move_dmabuf(struct
> ttm_buffer_object *ttm_bo,
>  	struct xe_ttm_tt *xe_tt = container_of(ttm_bo->ttm, struct
> xe_ttm_tt,
>  					       ttm);
>  	struct xe_device *xe = ttm_to_xe_device(ttm_bo->bdev);
> +	bool device_unplugged = drm_dev_is_unplugged(&xe->drm);
>  	struct sg_table *sg;
>  
>  	xe_assert(xe, attach);
>  	xe_assert(xe, ttm_bo->ttm);
>  
> +	if (device_unplugged && new_res->mem_type == XE_PL_SYSTEM &&
> +	    ttm_bo->sg) {
> +		dma_resv_wait_timeout(ttm_bo->base.resv,
> DMA_RESV_USAGE_BOOKKEEP,
> +				      false, MAX_SCHEDULE_TIMEOUT);
> +		dma_buf_unmap_attachment(attach, ttm_bo->sg,
> DMA_BIDIRECTIONAL);
> +		ttm_bo->sg = NULL;
> +	}
> +
>  	if (new_res->mem_type == XE_PL_SYSTEM)
>  		goto out;
>  
> @@ -2945,6 +2963,39 @@ void
> xe_bo_runtime_pm_release_mmap_offset(struct xe_bo *bo)
>  	list_del_init(&bo->vram_userfault_link);
>  }
>  
> +/**
> + * xe_bo_remove() - Handle bos when the pci_device is about to be
> removed
> + * @xe: The xe device.
> + *
> + * On pci_device removal we need to drop all dma mappings and move
> + * the data of exported bos out to system. This includes SVM bos and
> + * exported dma-buf bos. This is done by evicting all bos, but
> + * the evict placement in xe_evict_flags() is chosen such that all
> + * bos except those mentioned are purged, and thus their memory
> + * is released.
> + *
> + * Pinned bos are not handled, though. Ideally they should be
> released
> + * using devm_ actions.
> + */
> +void xe_bo_remove(struct xe_device *xe)
> +{
> +	unsigned int mem_type;
> +	int ret;
> +
> +	/*
> +	 * Move pagemap bos and exported dma-buf to system.
> +	 */
> +	for (mem_type = XE_PL_VRAM1; mem_type >= XE_PL_TT; --
> mem_type) {
> +		struct ttm_resource_manager *man =
> +			ttm_manager_type(&xe->ttm, mem_type);
> +
> +		if (man) {
> +			ret = ttm_resource_manager_evict_all(&xe-
> >ttm, man);
> +			drm_WARN_ON(&xe->drm, ret);
> +		}
> +	}
> +}
> +
>  #if IS_ENABLED(CONFIG_DRM_XE_KUNIT_TEST)
>  #include "tests/xe_bo.c"
>  #endif
> diff --git a/drivers/gpu/drm/xe/xe_bo.h b/drivers/gpu/drm/xe/xe_bo.h
> index bda3fdd408da..22b1c63f9311 100644
> --- a/drivers/gpu/drm/xe/xe_bo.h
> +++ b/drivers/gpu/drm/xe/xe_bo.h
> @@ -405,6 +405,8 @@ long xe_bo_shrink(struct ttm_operation_ctx *ctx,
> struct ttm_buffer_object *bo,
>  		  const struct xe_bo_shrink_flags flags,
>  		  unsigned long *scanned);
>  
> +void xe_bo_remove(struct xe_device *xe);
> +
>  #if IS_ENABLED(CONFIG_DRM_XE_KUNIT_TEST)
>  /**
>   * xe_bo_is_mem_type - Whether the bo currently resides in the given
> diff --git a/drivers/gpu/drm/xe/xe_device.c
> b/drivers/gpu/drm/xe/xe_device.c
> index b2f656b2a563..68de09db9ad5 100644
> --- a/drivers/gpu/drm/xe/xe_device.c
> +++ b/drivers/gpu/drm/xe/xe_device.c
> @@ -54,6 +54,7 @@
>  #include "xe_query.h"
>  #include "xe_shrinker.h"
>  #include "xe_sriov.h"
> +#include "xe_svm.h"
>  #include "xe_tile.h"
>  #include "xe_ttm_stolen_mgr.h"
>  #include "xe_ttm_sys_mgr.h"
> @@ -925,6 +926,10 @@ void xe_device_remove(struct xe_device *xe)
>  	xe_display_unregister(xe);
>  
>  	drm_dev_unplug(&xe->drm);
> +
> +	xe_bo_remove(xe);
> +
> +	xe_pagemaps_remove(xe);
>  }
>  
>  void xe_device_shutdown(struct xe_device *xe)
> diff --git a/drivers/gpu/drm/xe/xe_device_types.h
> b/drivers/gpu/drm/xe/xe_device_types.h
> index 40c6f88f5933..41ba05ae4cd5 100644
> --- a/drivers/gpu/drm/xe/xe_device_types.h
> +++ b/drivers/gpu/drm/xe/xe_device_types.h
> @@ -110,19 +110,21 @@ struct xe_vram_region {
>  	/** @ttm: VRAM TTM manager */
>  	struct xe_ttm_vram_mgr ttm;
>  #if IS_ENABLED(CONFIG_DRM_XE_PAGEMAP)
> -	/** @pagemap: Used to remap device memory as ZONE_DEVICE */
> -	struct dev_pagemap pagemap;
> -	/**
> -	 * @dpagemap: The struct drm_pagemap of the ZONE_DEVICE
> memory
> -	 * pages of this tile.
> -	 */
> -	struct drm_pagemap *dpagemap;
> -	/**
> -	 * @hpa_base: base host physical address
> -	 *
> -	 * This is generated when remap device memory as ZONE_DEVICE
> -	 */
> -	resource_size_t hpa_base;
> +	/** @pagemap_cache: Cached struct xe_pagemap for this memory
> region's memory. */
> +	struct xe_pagemap_cache {
> +		/** @pagemap_cache.pagemap_mutex: Protects
> @pagemap_cache.xpagemap. */
> +		struct mutex mutex;
> +		/** @pagemap_cache.xpagemap: Pointer to a struct
> xe_pagemap */
> +		struct xe_pagemap *xpagemap;
> +		/**
> +		 * @pagemap_cache.queued: Completed when 
> @pagemap_cache.xpagemap is queued
> +		 * for destruction.
> +		 * There's a short interval in between
> @pagemap_cache.xpagemap's refcount
> +		 * dropping to zero and when it's queued for
> destruction and
> +		 * the destruction job can be canceled.
> +		 */
> +		struct completion queued;
> +	} pagemap_cache;
>  #endif
>  };
>  
> diff --git a/drivers/gpu/drm/xe/xe_svm.c
> b/drivers/gpu/drm/xe/xe_svm.c
> index 37e1607052ed..c49bcfea5644 100644
> --- a/drivers/gpu/drm/xe/xe_svm.c
> +++ b/drivers/gpu/drm/xe/xe_svm.c
> @@ -4,6 +4,8 @@
>   */
>  
>  #include <drm/drm_drv.h>
> +#include <drm/drm_managed.h>
> +#include <drm/drm_pagemap.h>
>  
>  #include "xe_bo.h"
>  #include "xe_gt_tlb_invalidation.h"
> @@ -17,6 +19,8 @@
>  #include "xe_vm.h"
>  #include "xe_vm_types.h"
>  
> +static int xe_svm_get_pagemaps(struct xe_vm *vm);
> +
>  static bool xe_svm_range_in_vram(struct xe_svm_range *range)
>  {
>  	/* Not reliable without notifier lock */
> @@ -345,28 +349,35 @@ static void
> xe_svm_garbage_collector_work_func(struct work_struct *w)
>  
>  #if IS_ENABLED(CONFIG_DRM_XE_PAGEMAP)
>  
> -static struct xe_vram_region *page_to_vr(struct page *page)
> +static struct xe_vram_region *xe_pagemap_to_vr(struct xe_pagemap
> *xpagemap)
>  {
> -	return container_of(page->pgmap, struct xe_vram_region,
> pagemap);
> +	return xpagemap->vr;
>  }
>  
> -static struct xe_tile *vr_to_tile(struct xe_vram_region *vr)
> +static struct xe_pagemap *xe_page_to_pagemap(struct page *page)
>  {
> -	return container_of(vr, struct xe_tile, mem.vram);
> +	return container_of(page->pgmap, struct xe_pagemap,
> pagemap);
>  }
>  
> -static u64 xe_vram_region_page_to_dpa(struct xe_vram_region *vr,
> -				      struct page *page)
> +static struct xe_vram_region *xe_page_to_vr(struct page *page)
>  {
> -	u64 dpa;
> -	struct xe_tile *tile = vr_to_tile(vr);
> +	return xe_pagemap_to_vr(xe_page_to_pagemap(page));
> +}
> +
> +static u64 xe_page_to_dpa(struct page *page)
> +{
> +	struct xe_pagemap *xpagemap = xe_page_to_pagemap(page);
> +	struct xe_vram_region *vr = xe_pagemap_to_vr(xpagemap);
> +	struct xe_tile *tile = xe_vr_to_tile(vr);
> +	u64 hpa_base = xpagemap->hpa_base;
>  	u64 pfn = page_to_pfn(page);
>  	u64 offset;
> +	u64 dpa;
>  
>  	xe_tile_assert(tile, is_device_private_page(page));
> -	xe_tile_assert(tile, (pfn << PAGE_SHIFT) >= vr->hpa_base);
> +	xe_tile_assert(tile, (pfn << PAGE_SHIFT) >= hpa_base);
>  
> -	offset = (pfn << PAGE_SHIFT) - vr->hpa_base;
> +	offset = (pfn << PAGE_SHIFT) - hpa_base;
>  	dpa = vr->dpa_base + offset;
>  
>  	return dpa;
> @@ -413,10 +424,10 @@ static int xe_svm_copy(struct page **pages,
> dma_addr_t *dma_addr,
>  			continue;
>  
>  		if (!vr && spage) {
> -			vr = page_to_vr(spage);
> -			tile = vr_to_tile(vr);
> +			vr = xe_page_to_vr(spage);
> +			tile = xe_vr_to_tile(vr);
>  		}
> -		XE_WARN_ON(spage && page_to_vr(spage) != vr);
> +		XE_WARN_ON(spage && xe_page_to_vr(spage) != vr);
>  
>  		/*
>  		 * CPU page and device page valid, capture physical
> address on
> @@ -424,7 +435,7 @@ static int xe_svm_copy(struct page **pages,
> dma_addr_t *dma_addr,
>  		 * device pages.
>  		 */
>  		if (dma_addr[i] && spage) {
> -			__vram_addr = xe_vram_region_page_to_dpa(vr,
> spage);
> +			__vram_addr = xe_page_to_dpa(spage);
>  			if (vram_addr == XE_VRAM_ADDR_INVALID) {
>  				vram_addr = __vram_addr;
>  				pos = i;
> @@ -547,12 +558,12 @@ static void xe_svm_devmem_release(struct
> drm_pagemap_devmem *devmem_allocation)
>  
>  static u64 block_offset_to_pfn(struct xe_vram_region *vr, u64
> offset)
>  {
> -	return PHYS_PFN(offset + vr->hpa_base);
> +	return PHYS_PFN(offset + vr->pagemap_cache.xpagemap-
> >hpa_base);
>  }
>  
>  static struct drm_buddy *tile_to_buddy(struct xe_tile *tile)
>  {
> -	return &tile->mem.vram.ttm.mm;
> +	return &xe_tile_to_vr(tile)->ttm.mm;
>  }
>  
>  static int xe_svm_populate_devmem_pfn(struct drm_pagemap_devmem
> *devmem_allocation,
> @@ -566,7 +577,7 @@ static int xe_svm_populate_devmem_pfn(struct
> drm_pagemap_devmem *devmem_allocati
>  
>  	list_for_each_entry(block, blocks, link) {
>  		struct xe_vram_region *vr = block->private;
> -		struct xe_tile *tile = vr_to_tile(vr);
> +		struct xe_tile *tile = xe_vr_to_tile(vr);
>  		struct drm_buddy *buddy = tile_to_buddy(tile);
>  		u64 block_pfn = block_offset_to_pfn(vr,
> drm_buddy_block_offset(block));
>  		int i;
> @@ -585,6 +596,11 @@ static const struct drm_pagemap_devmem_ops
> dpagemap_devmem_ops = {
>  	.copy_to_ram = xe_svm_copy_to_ram,
>  };
>  
> +#else
> +static int xe_svm_get_pagemaps(struct xe_vm *vm)
> +{
> +	return 0;
> +}
>  #endif
>  
>  static const struct drm_gpusvm_ops gpusvm_ops = {
> @@ -599,6 +615,26 @@ static const unsigned long fault_chunk_sizes[] =
> {
>  	SZ_4K,
>  };
>  
> +static void xe_pagemap_put(struct xe_pagemap *xpagemap)
> +{
> +	drm_pagemap_put(&xpagemap->dpagemap);
> +}
> +
> +static void xe_svm_put_pagemaps(struct xe_vm *vm)
> +{
> +	struct xe_device *xe = vm->xe;
> +	struct xe_tile *tile;
> +	int id;
> +
> +	for_each_tile(tile, xe, id) {
> +		struct xe_pagemap *xpagemap = vm->svm.pagemaps[id];
> +
> +		if (xpagemap)
> +			xe_pagemap_put(xpagemap);
> +		vm->svm.pagemaps[id] = NULL;
> +	}
> +}
> +
>  /**
>   * xe_svm_init() - SVM initialize
>   * @vm: The VM.
> @@ -616,13 +652,19 @@ int xe_svm_init(struct xe_vm *vm)
>  	INIT_WORK(&vm->svm.garbage_collector.work,
>  		  xe_svm_garbage_collector_work_func);
>  
> +	err = xe_svm_get_pagemaps(vm);
> +	if (err)
> +		return err;
> +
>  	err = drm_gpusvm_init(&vm->svm.gpusvm, "Xe SVM", &vm->xe-
> >drm,
>  			      current->mm, xe_svm_devm_owner(vm-
> >xe), 0,
>  			      vm->size,
> xe_modparam.svm_notifier_size * SZ_1M,
>  			      &gpusvm_ops, fault_chunk_sizes,
>  			      ARRAY_SIZE(fault_chunk_sizes));
> -	if (err)
> +	if (err) {
> +		xe_svm_put_pagemaps(vm);
>  		return err;
> +	}
>  
>  	drm_gpusvm_driver_set_lock(&vm->svm.gpusvm, &vm->lock);
>  
> @@ -639,6 +681,7 @@ void xe_svm_close(struct xe_vm *vm)
>  {
>  	xe_assert(vm->xe, xe_vm_is_closed(vm));
>  	flush_work(&vm->svm.garbage_collector.work);
> +	xe_svm_put_pagemaps(vm);
>  }
>  
>  /**
> @@ -661,20 +704,16 @@ static bool xe_svm_range_is_valid(struct
> xe_svm_range *range,
>  }
>  
>  #if IS_ENABLED(CONFIG_DRM_XE_PAGEMAP)
> -static struct xe_vram_region *tile_to_vr(struct xe_tile *tile)
> -{
> -	return &tile->mem.vram;
> -}
>  
>  static int xe_drm_pagemap_populate_mm(struct drm_pagemap *dpagemap,
>  				      unsigned long start, unsigned
> long end,
>  				      struct mm_struct *mm)
>  {
> -	struct xe_tile *tile = container_of(dpagemap->pagemap,
> typeof(*tile),
> -					    mem.vram.pagemap);
> +	struct xe_pagemap *xpagemap = container_of(dpagemap,
> typeof(*xpagemap), dpagemap);
> +	struct xe_vram_region *vr = xe_pagemap_to_vr(xpagemap);
> +	struct xe_tile *tile = xe_vr_to_tile(vr);
>  	struct xe_device *xe = tile_to_xe(tile);
>  	struct device *dev = xe->drm.dev;
> -	struct xe_vram_region *vr = tile_to_vr(tile);
>  	struct drm_buddy_block *block;
>  	struct list_head *blocks;
>  	struct xe_bo *bo;
> @@ -700,7 +739,7 @@ static int xe_drm_pagemap_populate_mm(struct
> drm_pagemap *dpagemap,
>  
>  	drm_pagemap_devmem_init(&bo->devmem_allocation, dev, mm,
>  				&dpagemap_devmem_ops,
> -				tile->mem.vram.dpagemap,
> +				dpagemap,
>  				end - start);
>  
>  	blocks = &to_xe_ttm_vram_mgr_resource(bo->ttm.resource)-
> >blocks;
> @@ -896,12 +935,12 @@ xe_drm_pagemap_device_map(struct drm_pagemap
> *dpagemap,
>  			  unsigned int order,
>  			  enum dma_data_direction dir)
>  {
> -	struct device *pgmap_dev = dpagemap->dev;
> +	struct device *pgmap_dev = dpagemap->drm->dev;
>  	enum drm_interconnect_protocol prot;
>  	dma_addr_t addr;
>  
>  	if (pgmap_dev == dev) {
> -		addr = xe_vram_region_page_to_dpa(page_to_vr(page),
> page);
> +		addr = xe_page_to_dpa(page);
>  		prot = XE_INTERCONNECT_VRAM;
>  	} else {
>  		addr = DMA_MAPPING_ERROR;
> @@ -911,73 +950,306 @@ xe_drm_pagemap_device_map(struct drm_pagemap
> *dpagemap,
>  	return drm_pagemap_device_addr_encode(addr, prot, order,
> dir);
>  }
>  
> +static void xe_pagemap_fini(struct xe_pagemap *xpagemap)
> +{
> +	struct dev_pagemap *pagemap = &xpagemap->pagemap;
> +	struct device *dev = xpagemap->dpagemap.drm->dev;
> +
> +	WRITE_ONCE(xpagemap->unplugged, true);
> +	if (xpagemap->hpa_base) {
> +		devm_memunmap_pages(dev, pagemap);
> +		xpagemap->hpa_base = 0;
> +	}
> +
> +	if (pagemap->range.start) {
> +		devm_release_mem_region(dev, pagemap->range.start,
> +					pagemap->range.end -
> pagemap->range.start + 1);
> +		pagemap->range.start = 0;
> +	}
> +}
> +
> +static void xe_pagemap_destroy_work(struct work_struct *work)
> +{
> +	struct xe_pagemap *xpagemap = container_of(work,
> typeof(*xpagemap), destroy_work.work);
> +	struct xe_pagemap_cache *cache = xpagemap->cache;
> +
> +	mutex_lock(&cache->mutex);
> +	if (cache->xpagemap == xpagemap)
> +		cache->xpagemap = NULL;
> +	mutex_unlock(&cache->mutex);
> +
> +	xe_pagemap_fini(xpagemap);
> +	kfree(xpagemap);
> +}
> +
> +static void xe_pagemap_destroy(struct drm_pagemap *dpagemap)
> +{
> +	struct xe_pagemap *xpagemap = container_of(dpagemap,
> typeof(*xpagemap), dpagemap);
> +	struct xe_device *xe = to_xe_device(dpagemap->drm);
> +
> +	/* Keep the pagemap cached for 5s, unless the device is
> unplugged. */
> +	queue_delayed_work(xe->unordered_wq, &xpagemap-
> >destroy_work,
> +			   READ_ONCE(xpagemap->unplugged) ? 0 :
> secs_to_jiffies(5));
> +
> +	complete_all(&xpagemap->cache->queued);
> +}
> +
>  static const struct drm_pagemap_ops xe_drm_pagemap_ops = {
>  	.device_map = xe_drm_pagemap_device_map,
>  	.populate_mm = xe_drm_pagemap_populate_mm,
> +	.destroy = xe_pagemap_destroy,
>  };
>  
>  /**
> - * xe_devm_add: Remap and provide memmap backing for device memory
> - * @tile: tile that the memory region belongs to
> - * @vr: vram memory region to remap
> + * xe_pagemap_create() - Create a struct xe_pagemap object
> + * @xe: The xe device.
> + * @cache: Back-pointer to the struct xe_pagemap_cache.
> + * @vr: Back-pointer to the struct xe_vram_region.
>   *
> - * This remap device memory to host physical address space and
> create
> - * struct page to back device memory
> + * Allocate and initialize a struct xe_pagemap. On successful
> + * return, drm_pagemap_put() on the embedded struct drm_pagemap
> + * should be used to unreference.
>   *
> - * Return: 0 on success standard error code otherwise
> + * Return: Pointer to a struct xe_pagemap if successful. Error
> pointer
> + * on failure.
>   */
> -int xe_devm_add(struct xe_tile *tile, struct xe_vram_region *vr)
> +struct xe_pagemap *xe_pagemap_create(struct xe_device *xe, struct
> xe_pagemap_cache *cache,
> +				     struct xe_vram_region *vr)
>  {
> -	struct xe_device *xe = tile_to_xe(tile);
> -	struct device *dev = &to_pci_dev(xe->drm.dev)->dev;
> +	struct device *dev = xe->drm.dev;
> +	struct xe_pagemap *xpagemap;
> +	struct dev_pagemap *pagemap;
> +	struct drm_pagemap *dpagemap;
>  	struct resource *res;
>  	void *addr;
> -	int ret;
> +	int err;
> +
> +	xpagemap = kzalloc(sizeof(*xpagemap), GFP_KERNEL);
> +	if (!xpagemap)
> +		return ERR_PTR(-ENOMEM);
> +
> +	pagemap = &xpagemap->pagemap;
> +	dpagemap = &xpagemap->dpagemap;
> +	INIT_DELAYED_WORK(&xpagemap->destroy_work,
> xe_pagemap_destroy_work);
> +	xpagemap->cache = cache;
> +	xpagemap->vr = vr;
> +
> +	err = drm_pagemap_init(dpagemap, pagemap, &xe->drm,
> &xe_drm_pagemap_ops);
> +	if (err)
> +		goto out_no_dpagemap;
>  
>  	res = devm_request_free_mem_region(dev, &iomem_resource,
>  					   vr->usable_size);
>  	if (IS_ERR(res)) {
> -		ret = PTR_ERR(res);
> -		return ret;
> +		err = PTR_ERR(res);
> +		goto out_err;
>  	}
>  
> -	vr->dpagemap = drm_pagemap_create(dev, &vr->pagemap,
> -					  &xe_drm_pagemap_ops);
> -	if (IS_ERR(vr->dpagemap)) {
> -		drm_err(&xe->drm, "Failed to create drm_pagemap tile
> %d memory: %pe\n",
> -			tile->id, vr->dpagemap);
> -		ret = PTR_ERR(vr->dpagemap);
> -		goto out_no_dpagemap;
> -	}
> -
> -	vr->pagemap.type = MEMORY_DEVICE_PRIVATE;
> -	vr->pagemap.range.start = res->start;
> -	vr->pagemap.range.end = res->end;
> -	vr->pagemap.nr_range = 1;
> -	vr->pagemap.ops = drm_pagemap_pagemap_ops_get();
> -	vr->pagemap.owner = xe_svm_devm_owner(xe);
> -	addr = devm_memremap_pages(dev, &vr->pagemap);
> +	pagemap->type = MEMORY_DEVICE_PRIVATE;
> +	pagemap->range.start = res->start;
> +	pagemap->range.end = res->end;
> +	pagemap->nr_range = 1;
> +	pagemap->owner = xe_svm_devm_owner(xe);
> +	pagemap->ops = drm_pagemap_pagemap_ops_get();
> +	addr = devm_memremap_pages(dev, pagemap);
>  	if (IS_ERR(addr)) {
> -		ret = PTR_ERR(addr);
> -		drm_err(&xe->drm, "Failed to remap tile %d memory,
> errno %pe\n",
> -			tile->id, ERR_PTR(ret));
> -		goto out_failed_memremap;
> +		err = PTR_ERR(addr);
> +		goto out_err;
>  	}
> -	vr->hpa_base = res->start;
> +	xpagemap->hpa_base = res->start;
> +	return xpagemap;
>  
> -	drm_dbg(&xe->drm, "Added tile %d memory [%llx-%llx] to devm,
> remapped to %pr\n",
> -		tile->id, vr->io_start, vr->io_start + vr-
> >usable_size, res);
> -	return 0;
> +out_err:
> +	drm_pagemap_put(dpagemap);
> +	return ERR_PTR(err);
>  
> -out_failed_memremap:
> -	drm_pagemap_put(vr->dpagemap);
>  out_no_dpagemap:
> -	devm_release_mem_region(dev, res->start,
> resource_size(res));
> -	return ret;
> +	kfree(xpagemap);
> +	return ERR_PTR(err);
>  }
> -#else
> -int xe_devm_add(struct xe_tile *tile, struct xe_vram_region *vr)
> +
> +static struct xe_pagemap *
> +xe_pagemap_find_or_create(struct xe_device *xe, struct
> xe_pagemap_cache *cache,
> +			  struct xe_vram_region *vr);
> +
> +static int xe_svm_get_pagemaps(struct xe_vm *vm)
>  {
> +	struct xe_device *xe = vm->xe;
> +	struct xe_pagemap *xpagemap;
> +	struct xe_tile *tile;
> +	int id;
> +
> +	for_each_tile(tile, xe, id) {
> +		struct xe_vram_region *vr;
> +
> +		if (!((BIT(id) << 1) & xe->info.mem_region_mask))
> +			continue;
> +
> +		vr = xe_tile_to_vr(tile);
> +		xpagemap = xe_pagemap_find_or_create(xe, &vr-
> >pagemap_cache, vr);
> +		if (IS_ERR(xpagemap))
> +			break;
> +		vm->svm.pagemaps[id] = xpagemap;
> +	}
> +
> +	if (IS_ERR(xpagemap)) {
> +		xe_svm_put_pagemaps(vm);
> +		return PTR_ERR(xpagemap);
> +	}
> +
>  	return 0;
>  }
> +
> +/**
> + * xe_pagemaps_remove() - Device remove work for the xe pagemaps
> + * @xe: The xe device
> + *
> + * This function needs to be run as part of the device remove
> (unplug)
> + * sequence to ensure that divice-private pages allocated using the
> + * xe pagemaps are not used anymore and that the dev_pagemaps are
> + * unregistered.
> + *
> + * The function needs to be called *after* the call to
> drm_dev_unplug()
> + * to ensure any calls to drm_pagemap_populate_mm() will return -
> ENODEV.
> + *
> + * Note that the pagemaps' references to the drm device and hence
> the
> + * xe device will remain until the pagemaps are destroyed.
> + */
> +void xe_pagemaps_remove(struct xe_device *xe)
> +{
> +	unsigned int id, mem_type;
> +	struct xe_tile *tile;
> +	int ret;
> +
> +	/* Migrate all PTEs of this pagemap to system */
> +	for (mem_type = XE_PL_VRAM1; mem_type >= XE_PL_TT; --
> mem_type) {
> +		struct ttm_resource_manager *man =
> +			ttm_manager_type(&xe->ttm, mem_type);
> +
> +		if (man) {
> +			ret = ttm_resource_manager_evict_all(&xe-
> >ttm, man);
> +			drm_WARN_ON(&xe->drm, ret);
> +		}
> +	}
> +
> +	/* Remove the device pages themselves */
> +	for_each_tile(tile, xe, id) {
> +		struct xe_pagemap_cache *cache;
> +
> +		if (!((BIT(id) << 1) & xe->info.mem_region_mask))
> +			continue;
> +
> +		cache = &tile->mem.vram.pagemap_cache;
> +		mutex_lock(&cache->mutex);
> +		if (cache->xpagemap)
> +			xe_pagemap_fini(cache->xpagemap);
> +		/* Nobody can resurrect, since the device is
> unplugged. */
> +		mutex_unlock(&cache->mutex);
> +	}
> +}
> +
> +static void xe_pagemap_cache_fini(struct drm_device *drm, void *arg)
> +{
> +	struct xe_pagemap_cache *cache = arg;
> +	struct xe_pagemap *xpagemap;
> +
> +	wait_for_completion(&cache->queued);
> +	mutex_lock(&cache->mutex);
> +	xpagemap = cache->xpagemap;
> +	if (xpagemap && cancel_delayed_work(&xpagemap-
> >destroy_work)) {
> +		mutex_unlock(&cache->mutex);
> +		xe_pagemap_destroy_work(&xpagemap-
> >destroy_work.work);
> +		return;
> +	}
> +	mutex_unlock(&cache->mutex);
> +	flush_workqueue(to_xe_device(drm)->unordered_wq);
> +	mutex_destroy(&cache->mutex);
> +}
> +
> +/**
> + * xe_pagemap_cache_init() - Initialize a struct xe_pagemap_cache
> + * @drm: Pointer to the struct drm_device
> + * @cache: Pointer to a struct xe_pagemap_cache
> + *
> + * Initialize a struct xe_pagemap_cache and if successful, register
> a cleanup
> + * function to be run at xe/drm device destruction.
> + *
> + * Return: 0 on success, negative error code on error.
> + */
> +int xe_pagemap_cache_init(struct drm_device *drm, struct
> xe_pagemap_cache *cache)
> +{
> +	mutex_init(&cache->mutex);
> +	init_completion(&cache->queued);
> +	complete_all(&cache->queued);
> +	return drmm_add_action_or_reset(drm, xe_pagemap_cache_fini,
> cache);
> +}
> +
> +static struct xe_pagemap *xe_pagemap_get_unless_zero(struct
> xe_pagemap *xpagemap)
> +{
> +	return (xpagemap && drm_pagemap_get_unless_zero(&xpagemap-
> >dpagemap)) ? xpagemap : NULL;
> +}
> +
> +/**
> + * xe_pagemap_find_or_create() - Find or create a struct xe_pagemap
> + * @xe: The xe device.
> + * @cache: The struct xe_pagemap_cache.
> + * @vr: The VRAM region.
> + *
> + * Check if there is an already used xe_pagemap for this tile, and
> in that case,
> + * return it.
> + * If not, check if there is a cached xe_pagemap for this tile, and
> in that case,
> + * cancel its destruction, re-initialize it and return it.
> + * Finally if there is no cached or already used pagemap, create one
> and
> + * register it in the tile's pagemap cache.
> + *
> + * Note that this function is typically called from within an IOCTL,
> and waits are
> + * therefore carried out interruptible if possible.
> + *
> + * Return: A pointer to a struct xe_pagemap if successful, Error
> pointer on failure.
> + */
> +static struct xe_pagemap *
> +xe_pagemap_find_or_create(struct xe_device *xe, struct
> xe_pagemap_cache *cache,
> +			  struct xe_vram_region *vr)
> +{
> +	struct xe_pagemap *xpagemap;
> +	int err;
> +
> +	err = mutex_lock_interruptible(&cache->mutex);
> +	if (err)
> +		return ERR_PTR(err);
> +
> +	xpagemap = cache->xpagemap;
> +	if (xpagemap && !xe_pagemap_get_unless_zero(xpagemap)) {
> +		/* Wait for the destroy work to get queued before
> canceling it! */
> +		err = wait_for_completion_interruptible(&cache-
> >queued);
> +		if (err) {
> +			mutex_unlock(&cache->mutex);
> +			return ERR_PTR(err);
> +		}
> +
> +		if (cancel_delayed_work(&xpagemap->destroy_work)) {
> +			err = drm_pagemap_reinit(&xpagemap-
> >dpagemap);
> +			if (!err) {
> +				reinit_completion(&cache->queued);
> +				goto out_unlock;
> +			}
> +
> +			queue_delayed_work(xe->unordered_wq,
> &xpagemap->destroy_work, 0);
> +		}
> +
> +		cache->xpagemap = NULL;
> +		xpagemap = NULL;
> +	}
> +	if (!xpagemap) {
> +		xpagemap = xe_pagemap_create(xe, cache, vr);
> +		if (IS_ERR(xpagemap))
> +			goto out_unlock;
> +
> +		cache->xpagemap = xpagemap;
> +		reinit_completion(&cache->queued);
> +	}
> +out_unlock:
> +	mutex_unlock(&cache->mutex);
> +	return xpagemap;
> +}
>  #endif
> diff --git a/drivers/gpu/drm/xe/xe_svm.h
> b/drivers/gpu/drm/xe/xe_svm.h
> index c32b6d46ecf1..19469fd91666 100644
> --- a/drivers/gpu/drm/xe/xe_svm.h
> +++ b/drivers/gpu/drm/xe/xe_svm.h
> @@ -13,7 +13,11 @@
>  
>  #define XE_INTERCONNECT_VRAM DRM_INTERCONNECT_DRIVER
>  
> +struct drm_device;
> +struct drm_file;
> +
>  struct xe_bo;
> +struct xe_device;
>  struct xe_vram_region;
>  struct xe_tile;
>  struct xe_vm;
> @@ -45,6 +49,28 @@ struct xe_svm_range {
>  	u8 skip_migrate	:1;
>  };
>  
> +/**
> + * struct xe_pagemap - Manages xe device_private memory for SVM.
> + * @pagemap: The struct dev_pagemap providing the struct pages.
> + * @dpagemap: The drm_pagemap managing allocation and migration.
> + * @destroy_work: Handles asnynchronous destruction and caching.
> + * @hpa_base: The host physical address base for the managemd
> memory.
> + * @cache: Backpointer to the struct xe_pagemap_cache for the memory
> region.
> + * @vr: Backpointer to the xe_vram region.
> + * @unplugged: Advisory only information whether the device owning
> this
> + * pagemap has been unplugged. This field is typically used for
> caching
> + * time determination.
> + */
> +struct xe_pagemap {
> +	struct dev_pagemap pagemap;
> +	struct drm_pagemap dpagemap;
> +	struct delayed_work destroy_work;
> +	resource_size_t hpa_base;
> +	struct xe_pagemap_cache *cache;
> +	struct xe_vram_region *vr;
> +	bool unplugged;
> +};
> +
>  /**
>   * xe_svm_range_pages_valid() - SVM range pages valid
>   * @range: SVM range
> @@ -95,11 +121,16 @@ static inline bool
> xe_svm_range_has_dma_mapping(struct xe_svm_range *range)
>  #define xe_svm_notifier_unlock(vm__)	\
>  	drm_gpusvm_notifier_unlock(&(vm__)->svm.gpusvm)
>  
> +struct xe_pagemap *
> +xe_pagemap_create(struct xe_device *xe, struct xe_pagemap_cache
> *cache,
> +		  struct xe_vram_region *vr);
> +
>  #else
>  #include <linux/interval_tree.h>
>  
>  struct drm_pagemap_device_addr;
>  struct xe_bo;
> +struct xe_device;
>  struct xe_vm;
>  struct xe_vma;
>  struct xe_tile;
> @@ -178,5 +209,23 @@ static inline void xe_svm_notifier_lock(struct
> xe_vm *vm)
>  static inline void xe_svm_notifier_unlock(struct xe_vm *vm)
>  {
>  }
> +
>  #endif
> +
> +#if IS_ENABLED(CONFIG_DRM_XE_PAGEMAP)
> +
> +int xe_pagemap_cache_init(struct drm_device *drm, struct
> xe_pagemap_cache *cache);
> +
> +void xe_pagemaps_remove(struct xe_device *xe);
> +
> +#else
> +
> +#define xe_pagemap_cache_init(...) 0
> +
> +static inline void xe_pagemaps_remove(struct xe_device *xe)
> +{
> +}
> +
> +#endif
> +
>  #endif
> diff --git a/drivers/gpu/drm/xe/xe_tile.c
> b/drivers/gpu/drm/xe/xe_tile.c
> index 0771acbbf367..f5d9d56418ee 100644
> --- a/drivers/gpu/drm/xe/xe_tile.c
> +++ b/drivers/gpu/drm/xe/xe_tile.c
> @@ -161,7 +161,6 @@ static int tile_ttm_mgr_init(struct xe_tile
> *tile)
>   */
>  int xe_tile_init_noalloc(struct xe_tile *tile)
>  {
> -	struct xe_device *xe = tile_to_xe(tile);
>  	int err;
>  
>  	err = tile_ttm_mgr_init(tile);
> @@ -170,8 +169,9 @@ int xe_tile_init_noalloc(struct xe_tile *tile)
>  
>  	xe_wa_apply_tile_workarounds(tile);
>  
> -	if (xe->info.has_usm && IS_DGFX(xe))
> -		xe_devm_add(tile, &tile->mem.vram);
> +	err = xe_pagemap_cache_init(&tile_to_xe(tile)->drm, &tile-
> >mem.vram.pagemap_cache);
> +	if (err)
> +		return err;
>  
>  	return xe_tile_sysfs_init(tile);
>  }
> @@ -188,3 +188,17 @@ void xe_tile_migrate_wait(struct xe_tile *tile)
>  {
>  	xe_migrate_wait(tile->migrate);
>  }
> +
> +#if IS_ENABLED(CONFIG_DRM_XE_PAGEMAP)
> +/**
> + * xe_tile_local_pagemap() - Return a pointer to the tile's local
> drm_pagemap if any
> + * @tile: The tile.
> + *
> + * Return: A pointer to the tile's local drm_pagemap, or NULL if
> local pagemap
> + * support has been compiled out.
> + */
> +struct drm_pagemap *xe_tile_local_pagemap(struct xe_tile *tile)
> +{
> +	return &xe_tile_to_vr(tile)->pagemap_cache.xpagemap-
> >dpagemap;
> +}
> +#endif
> diff --git a/drivers/gpu/drm/xe/xe_tile.h
> b/drivers/gpu/drm/xe/xe_tile.h
> index 1d42b235c322..375b8323cda6 100644
> --- a/drivers/gpu/drm/xe/xe_tile.h
> +++ b/drivers/gpu/drm/xe/xe_tile.h
> @@ -8,6 +8,7 @@
>  
>  #include "xe_device_types.h"
>  
> +struct xe_pagemap;
>  struct xe_tile;
>  
>  int xe_tile_init_early(struct xe_tile *tile, struct xe_device *xe,
> u8 id);
> @@ -16,11 +17,32 @@ int xe_tile_init(struct xe_tile *tile);
>  
>  void xe_tile_migrate_wait(struct xe_tile *tile);
>  
> -#if IS_ENABLED(CONFIG_DRM_XE_PAGEMAP)
> -static inline struct drm_pagemap *xe_tile_local_pagemap(struct
> xe_tile *tile)
> +/**
> + * xe_vr_to_tile() - Return the struct xe_tile pointer from a
> + * struct xe_vram_region pointer.
> + * @vr: The xe_vram_region.
> + *
> + * Return: Pointer to the struct xe_tile embedding *@vr.
> + */
> +static inline struct xe_tile *xe_vr_to_tile(struct xe_vram_region
> *vr)
>  {
> -	return tile->mem.vram.dpagemap;
> +	return container_of(vr, struct xe_tile, mem.vram);
>  }
> +
> +/**
> + * xe_tile_to_vr() - Return the struct xe_vram_region pointer from a
> + * struct xe_tile pointer
> + * @tile: Pointer to the struct xe_tile.
> + *
> + * Return: Pointer to the struct xe_vram_region embedded in *@tile.
> + */
> +static inline struct xe_vram_region *xe_tile_to_vr(struct xe_tile
> *tile)
> +{
> +	return &tile->mem.vram;
> +}
> +
> +#if IS_ENABLED(CONFIG_DRM_XE_PAGEMAP)
> +struct drm_pagemap *xe_tile_local_pagemap(struct xe_tile *tile);
>  #else
>  static inline struct drm_pagemap *xe_tile_local_pagemap(struct
> xe_tile *tile)
>  {
> diff --git a/drivers/gpu/drm/xe/xe_vm_types.h
> b/drivers/gpu/drm/xe/xe_vm_types.h
> index 84fa41b9fa20..08baea03df00 100644
> --- a/drivers/gpu/drm/xe/xe_vm_types.h
> +++ b/drivers/gpu/drm/xe/xe_vm_types.h
> @@ -168,6 +168,7 @@ struct xe_vm {
>  			 */
>  			struct work_struct work;
>  		} garbage_collector;
> +		struct xe_pagemap
> *pagemaps[XE_MAX_TILES_PER_DEVICE];
>  	} svm;
>  
>  	struct xe_device *xe;
> diff --git a/include/drm/drm_pagemap.h b/include/drm/drm_pagemap.h
> index 49f2e0b6c699..9f758a46988a 100644
> --- a/include/drm/drm_pagemap.h
> +++ b/include/drm/drm_pagemap.h
> @@ -120,6 +120,21 @@ struct drm_pagemap_ops {
>  	int (*populate_mm)(struct drm_pagemap *dpagemap,
>  			   unsigned long start, unsigned long end,
>  			   struct mm_struct *mm);
> +
> +	/**
> +	 * @destroy: Uninitialize a struct drm_pagemap.
> +	 * @dpagemap: The struct drm_pagemap to uninitialize.
> +	 *
> +	 * Uninitialize the drm_pagemap, potentially retaining it in
> +	 * a cache for re-initialization. This callback may be
> called
> +	 * with page locks held and typicall needs to queue any
> +	 * destruction or caching work on a workqueue to avoid
> locking
> +	 * order inversions. Since the drm_pagemap code also may put
> +	 * the owning device immediately after this function is
> called,
> +	 * the drm_pagemap destruction needs to be waited for in
> +	 * the device destruction code.
> +	 */
> +	void (*destroy)(struct drm_pagemap *dpagemap);
>  };
>  
>  /**
> @@ -127,14 +142,16 @@ struct drm_pagemap_ops {
>   * used for device p2p handshaking.
>   * @ops: The struct drm_pagemap_ops.
>   * @ref: Reference count.
> - * @dev: The struct drevice owning the device-private memory.
> + * @drm: The struct drm device owning the device-private memory.
>   * @pagemap: Pointer to the underlying dev_pagemap.
> + * @dev_hold: Pointer to a struct dev_hold for device referencing.
>   */
>  struct drm_pagemap {
>  	const struct drm_pagemap_ops *ops;
>  	struct kref ref;
> -	struct device *dev;
> +	struct drm_device *drm;
>  	struct dev_pagemap *pagemap;
> +	struct drm_pagemap_dev_hold *dev_hold;
>  };
>  
>  struct drm_pagemap_devmem;
> @@ -199,26 +216,44 @@ struct drm_pagemap_devmem_ops {
>  			   unsigned long npages);
>  };
>  
> -struct drm_pagemap *drm_pagemap_create(struct device *dev,
> -				       struct dev_pagemap *pagemap,
> -				       const struct drm_pagemap_ops
> *ops);
> +int drm_pagemap_reinit(struct drm_pagemap *dpagemap);
> +
> +int drm_pagemap_init(struct drm_pagemap *dpagemap,
> +		     struct dev_pagemap *pagemap,
> +		     struct drm_device *drm,
> +		     const struct drm_pagemap_ops *ops);
>  
>  void drm_pagemap_put(struct drm_pagemap *dpagemap);
>  
>  /**
>   * drm_pagemap_get() - Obtain a reference on a struct drm_pagemap
> - * @dpagemap: Pointer to the struct drm_pagemap.
> + * @dpagemap: Pointer to the struct drm_pagemap, or NULL.
>   *
> - * Return: Pointer to the struct drm_pagemap.
> + * Return: Pointer to the struct drm_pagemap, or NULL.
>   */
>  static inline struct drm_pagemap *
>  drm_pagemap_get(struct drm_pagemap *dpagemap)
>  {
> -	kref_get(&dpagemap->ref);
> +	if (likely(dpagemap))
> +		kref_get(&dpagemap->ref);
>  
>  	return dpagemap;
>  }
>  
> +/**
> + * drm_pagemap_get_unless_zero() - Obtain a reference on a struct
> drm_pagemap
> + * unless the current reference count is zero.
> + * @dpagemap: Pointer to the drm_pagemap or NULL.
> + *
> + * Return: A pointer to @dpagemap if the reference count was
> successfully
> + * incremented. NULL if @dpagemap was NULL, or its refcount was 0.
> + */
> +static inline struct drm_pagemap *
> +drm_pagemap_get_unless_zero(struct drm_pagemap *dpagemap)
> +{
> +	return (dpagemap && kref_get_unless_zero(&dpagemap->ref)) ?
> dpagemap : NULL;
> +}
> +
>  /**
>   * struct drm_pagemap_devmem - Structure representing a GPU SVM
> device memory allocation
>   *
> @@ -257,6 +292,4 @@ void drm_pagemap_devmem_init(struct
> drm_pagemap_devmem *devmem_allocation,
>  int drm_pagemap_populate_mm(struct drm_pagemap *dpagemap,
>  			    unsigned long start, unsigned long end,
>  			    struct mm_struct *mm);
> -
>  #endif
> -





[Index of Archives]     [Linux DRI Users]     [Linux Intel Graphics]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux