mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-08-31 23:12:52 +00:00

This is the first step to hide the details of struct xe_ggtt. Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> Reviewed-by: Matthew Brost <matthew.brost@intel.com> Reviewed-by: Lucas De Marchi <lucas.demarchi@intel.com> Link: https://lore.kernel.org/r/20250505121924.921544-2-dev@lankhorst.se Signed-off-by: Maarten Lankhorst <dev@lankhorst.se>
352 lines
9.0 KiB
C
352 lines
9.0 KiB
C
// SPDX-License-Identifier: MIT
|
|
/*
|
|
* Copyright © 2022 Intel Corporation
|
|
*/
|
|
|
|
#include "xe_bo_evict.h"
|
|
|
|
#include "xe_bo.h"
|
|
#include "xe_device.h"
|
|
#include "xe_ggtt.h"
|
|
#include "xe_tile.h"
|
|
|
|
typedef int (*xe_pinned_fn)(struct xe_bo *bo);
|
|
|
|
static int xe_bo_apply_to_pinned(struct xe_device *xe,
|
|
struct list_head *pinned_list,
|
|
struct list_head *new_list,
|
|
const xe_pinned_fn pinned_fn)
|
|
{
|
|
LIST_HEAD(still_in_list);
|
|
struct xe_bo *bo;
|
|
int ret = 0;
|
|
|
|
spin_lock(&xe->pinned.lock);
|
|
while (!ret) {
|
|
bo = list_first_entry_or_null(pinned_list, typeof(*bo),
|
|
pinned_link);
|
|
if (!bo)
|
|
break;
|
|
xe_bo_get(bo);
|
|
list_move_tail(&bo->pinned_link, &still_in_list);
|
|
spin_unlock(&xe->pinned.lock);
|
|
|
|
ret = pinned_fn(bo);
|
|
if (ret && pinned_list != new_list) {
|
|
spin_lock(&xe->pinned.lock);
|
|
/*
|
|
* We might no longer be pinned, since PM notifier can
|
|
* call this. If the pinned link is now empty, keep it
|
|
* that way.
|
|
*/
|
|
if (!list_empty(&bo->pinned_link))
|
|
list_move(&bo->pinned_link, pinned_list);
|
|
spin_unlock(&xe->pinned.lock);
|
|
}
|
|
xe_bo_put(bo);
|
|
spin_lock(&xe->pinned.lock);
|
|
}
|
|
list_splice_tail(&still_in_list, new_list);
|
|
spin_unlock(&xe->pinned.lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* xe_bo_notifier_prepare_all_pinned() - Pre-allocate the backing pages for all
|
|
* pinned VRAM objects which need to be saved.
|
|
* @xe: xe device
|
|
*
|
|
* Should be called from PM notifier when preparing for s3/s4.
|
|
*
|
|
* Return: 0 on success, negative error code on error.
|
|
*/
|
|
int xe_bo_notifier_prepare_all_pinned(struct xe_device *xe)
|
|
{
|
|
int ret;
|
|
|
|
ret = xe_bo_apply_to_pinned(xe, &xe->pinned.early.kernel_bo_present,
|
|
&xe->pinned.early.kernel_bo_present,
|
|
xe_bo_notifier_prepare_pinned);
|
|
if (!ret)
|
|
ret = xe_bo_apply_to_pinned(xe, &xe->pinned.late.kernel_bo_present,
|
|
&xe->pinned.late.kernel_bo_present,
|
|
xe_bo_notifier_prepare_pinned);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* xe_bo_notifier_unprepare_all_pinned() - Remove the backing pages for all
|
|
* pinned VRAM objects which have been restored.
|
|
* @xe: xe device
|
|
*
|
|
* Should be called from PM notifier after exiting s3/s4 (either on success or
|
|
* failure).
|
|
*/
|
|
void xe_bo_notifier_unprepare_all_pinned(struct xe_device *xe)
|
|
{
|
|
(void)xe_bo_apply_to_pinned(xe, &xe->pinned.early.kernel_bo_present,
|
|
&xe->pinned.early.kernel_bo_present,
|
|
xe_bo_notifier_unprepare_pinned);
|
|
|
|
(void)xe_bo_apply_to_pinned(xe, &xe->pinned.late.kernel_bo_present,
|
|
&xe->pinned.late.kernel_bo_present,
|
|
xe_bo_notifier_unprepare_pinned);
|
|
}
|
|
|
|
/**
|
|
* xe_bo_evict_all_user - evict all non-pinned user BOs from VRAM
|
|
* @xe: xe device
|
|
*
|
|
* Evict non-pinned user BOs (via GPU).
|
|
*
|
|
* Evict == move VRAM BOs to temporary (typically system) memory.
|
|
*/
|
|
int xe_bo_evict_all_user(struct xe_device *xe)
|
|
{
|
|
struct ttm_device *bdev = &xe->ttm;
|
|
u32 mem_type;
|
|
int ret;
|
|
|
|
/* User memory */
|
|
for (mem_type = XE_PL_TT; mem_type <= XE_PL_VRAM1; ++mem_type) {
|
|
struct ttm_resource_manager *man =
|
|
ttm_manager_type(bdev, mem_type);
|
|
|
|
/*
|
|
* On igpu platforms with flat CCS we need to ensure we save and restore any CCS
|
|
* state since this state lives inside graphics stolen memory which doesn't survive
|
|
* hibernation.
|
|
*
|
|
* This can be further improved by only evicting objects that we know have actually
|
|
* used a compression enabled PAT index.
|
|
*/
|
|
if (mem_type == XE_PL_TT && (IS_DGFX(xe) || !xe_device_has_flat_ccs(xe)))
|
|
continue;
|
|
|
|
if (man) {
|
|
ret = ttm_resource_manager_evict_all(bdev, man);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* xe_bo_evict_all - evict all BOs from VRAM
|
|
* @xe: xe device
|
|
*
|
|
* Evict non-pinned user BOs first (via GPU), evict pinned external BOs next
|
|
* (via GPU), wait for evictions, and finally evict pinned kernel BOs via CPU.
|
|
* All eviction magic done via TTM calls.
|
|
*
|
|
* Evict == move VRAM BOs to temporary (typically system) memory.
|
|
*
|
|
* This function should be called before the device goes into a suspend state
|
|
* where the VRAM loses power.
|
|
*/
|
|
int xe_bo_evict_all(struct xe_device *xe)
|
|
{
|
|
struct xe_tile *tile;
|
|
u8 id;
|
|
int ret;
|
|
|
|
ret = xe_bo_evict_all_user(xe);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = xe_bo_apply_to_pinned(xe, &xe->pinned.late.kernel_bo_present,
|
|
&xe->pinned.late.evicted, xe_bo_evict_pinned);
|
|
|
|
if (!ret)
|
|
ret = xe_bo_apply_to_pinned(xe, &xe->pinned.late.kernel_bo_present,
|
|
&xe->pinned.late.evicted, xe_bo_evict_pinned);
|
|
|
|
/*
|
|
* Wait for all user BO to be evicted as those evictions depend on the
|
|
* memory moved below.
|
|
*/
|
|
for_each_tile(tile, xe, id)
|
|
xe_tile_migrate_wait(tile);
|
|
|
|
if (ret)
|
|
return ret;
|
|
|
|
return xe_bo_apply_to_pinned(xe, &xe->pinned.early.kernel_bo_present,
|
|
&xe->pinned.early.evicted,
|
|
xe_bo_evict_pinned);
|
|
}
|
|
|
|
static int xe_bo_restore_and_map_ggtt(struct xe_bo *bo)
|
|
{
|
|
struct xe_device *xe = xe_bo_device(bo);
|
|
int ret;
|
|
|
|
ret = xe_bo_restore_pinned(bo);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (bo->flags & XE_BO_FLAG_GGTT) {
|
|
struct xe_tile *tile;
|
|
u8 id;
|
|
|
|
for_each_tile(tile, xe_bo_device(bo), id) {
|
|
if (tile != bo->tile && !(bo->flags & XE_BO_FLAG_GGTTx(tile)))
|
|
continue;
|
|
|
|
xe_ggtt_map_bo_unlocked(tile->mem.ggtt, bo);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* We expect validate to trigger a move VRAM and our move code
|
|
* should setup the iosys map.
|
|
*/
|
|
xe_assert(xe, !(bo->flags & XE_BO_FLAG_PINNED_LATE_RESTORE) ||
|
|
!iosys_map_is_null(&bo->vmap));
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* xe_bo_restore_early - restore early phase kernel BOs to VRAM
|
|
*
|
|
* @xe: xe device
|
|
*
|
|
* Move kernel BOs from temporary (typically system) memory to VRAM via CPU. All
|
|
* moves done via TTM calls.
|
|
*
|
|
* This function should be called early, before trying to init the GT, on device
|
|
* resume.
|
|
*/
|
|
int xe_bo_restore_early(struct xe_device *xe)
|
|
{
|
|
return xe_bo_apply_to_pinned(xe, &xe->pinned.early.evicted,
|
|
&xe->pinned.early.kernel_bo_present,
|
|
xe_bo_restore_and_map_ggtt);
|
|
}
|
|
|
|
/**
|
|
* xe_bo_restore_late - restore pinned late phase BOs
|
|
*
|
|
* @xe: xe device
|
|
*
|
|
* Move pinned user and kernel BOs which can use blitter from temporary
|
|
* (typically system) memory to VRAM. All moves done via TTM calls.
|
|
*
|
|
* This function should be called late, after GT init, on device resume.
|
|
*/
|
|
int xe_bo_restore_late(struct xe_device *xe)
|
|
{
|
|
struct xe_tile *tile;
|
|
int ret, id;
|
|
|
|
ret = xe_bo_apply_to_pinned(xe, &xe->pinned.late.evicted,
|
|
&xe->pinned.late.kernel_bo_present,
|
|
xe_bo_restore_and_map_ggtt);
|
|
|
|
for_each_tile(tile, xe, id)
|
|
xe_tile_migrate_wait(tile);
|
|
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (!IS_DGFX(xe))
|
|
return 0;
|
|
|
|
/* Pinned user memory in VRAM should be validated on resume */
|
|
ret = xe_bo_apply_to_pinned(xe, &xe->pinned.late.external,
|
|
&xe->pinned.late.external,
|
|
xe_bo_restore_pinned);
|
|
|
|
/* Wait for restore to complete */
|
|
for_each_tile(tile, xe, id)
|
|
xe_tile_migrate_wait(tile);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void xe_bo_pci_dev_remove_pinned(struct xe_device *xe)
|
|
{
|
|
struct xe_tile *tile;
|
|
unsigned int id;
|
|
|
|
(void)xe_bo_apply_to_pinned(xe, &xe->pinned.late.external,
|
|
&xe->pinned.late.external,
|
|
xe_bo_dma_unmap_pinned);
|
|
for_each_tile(tile, xe, id)
|
|
xe_tile_migrate_wait(tile);
|
|
}
|
|
|
|
/**
|
|
* xe_bo_pci_dev_remove_all() - 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.
|
|
*
|
|
* For pinned bos, we're unmapping dma.
|
|
*/
|
|
void xe_bo_pci_dev_remove_all(struct xe_device *xe)
|
|
{
|
|
unsigned int mem_type;
|
|
|
|
/*
|
|
* Move pagemap bos and exported dma-buf to system, and
|
|
* purge everything else.
|
|
*/
|
|
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) {
|
|
int ret = ttm_resource_manager_evict_all(&xe->ttm, man);
|
|
|
|
drm_WARN_ON(&xe->drm, ret);
|
|
}
|
|
}
|
|
|
|
xe_bo_pci_dev_remove_pinned(xe);
|
|
}
|
|
|
|
static void xe_bo_pinned_fini(void *arg)
|
|
{
|
|
struct xe_device *xe = arg;
|
|
|
|
(void)xe_bo_apply_to_pinned(xe, &xe->pinned.late.kernel_bo_present,
|
|
&xe->pinned.late.kernel_bo_present,
|
|
xe_bo_dma_unmap_pinned);
|
|
(void)xe_bo_apply_to_pinned(xe, &xe->pinned.early.kernel_bo_present,
|
|
&xe->pinned.early.kernel_bo_present,
|
|
xe_bo_dma_unmap_pinned);
|
|
}
|
|
|
|
/**
|
|
* xe_bo_pinned_init() - Initialize pinned bo tracking
|
|
* @xe: The xe device.
|
|
*
|
|
* Initializes the lists and locks required for pinned bo
|
|
* tracking and registers a callback to dma-unmap
|
|
* any remaining pinned bos on pci device removal.
|
|
*
|
|
* Return: %0 on success, negative error code on error.
|
|
*/
|
|
int xe_bo_pinned_init(struct xe_device *xe)
|
|
{
|
|
spin_lock_init(&xe->pinned.lock);
|
|
INIT_LIST_HEAD(&xe->pinned.early.kernel_bo_present);
|
|
INIT_LIST_HEAD(&xe->pinned.early.evicted);
|
|
INIT_LIST_HEAD(&xe->pinned.late.kernel_bo_present);
|
|
INIT_LIST_HEAD(&xe->pinned.late.evicted);
|
|
INIT_LIST_HEAD(&xe->pinned.late.external);
|
|
|
|
return devm_add_action_or_reset(xe->drm.dev, xe_bo_pinned_fini, xe);
|
|
}
|