Contributors: 5
Author Tokens Token Proportion Commits Commit Proportion
Matthew Brost 875 95.63% 3 30.00%
Matt Roper 26 2.84% 3 30.00%
Francois Dugast 7 0.77% 1 10.00%
Matthew Auld 4 0.44% 1 10.00%
Lucas De Marchi 3 0.33% 2 20.00%
Total 915 10


// 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"

/**
 * 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 ttm_device *bdev = &xe->ttm;
	struct xe_bo *bo;
	struct xe_tile *tile;
	struct list_head still_in_list;
	u32 mem_type;
	u8 id;
	int ret;

	if (!IS_DGFX(xe))
		return 0;

	/* User memory */
	for (mem_type = XE_PL_VRAM0; mem_type <= XE_PL_VRAM1; ++mem_type) {
		struct ttm_resource_manager *man =
			ttm_manager_type(bdev, mem_type);

		if (man) {
			ret = ttm_resource_manager_evict_all(bdev, man);
			if (ret)
				return ret;
		}
	}

	/* Pinned user memory in VRAM */
	INIT_LIST_HEAD(&still_in_list);
	spin_lock(&xe->pinned.lock);
	for (;;) {
		bo = list_first_entry_or_null(&xe->pinned.external_vram,
					      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);

		xe_bo_lock(bo, false);
		ret = xe_bo_evict_pinned(bo);
		xe_bo_unlock(bo);
		xe_bo_put(bo);
		if (ret) {
			spin_lock(&xe->pinned.lock);
			list_splice_tail(&still_in_list,
					 &xe->pinned.external_vram);
			spin_unlock(&xe->pinned.lock);
			return ret;
		}

		spin_lock(&xe->pinned.lock);
	}
	list_splice_tail(&still_in_list, &xe->pinned.external_vram);
	spin_unlock(&xe->pinned.lock);

	/*
	 * 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);

	spin_lock(&xe->pinned.lock);
	for (;;) {
		bo = list_first_entry_or_null(&xe->pinned.kernel_bo_present,
					      typeof(*bo), pinned_link);
		if (!bo)
			break;
		xe_bo_get(bo);
		list_move_tail(&bo->pinned_link, &xe->pinned.evicted);
		spin_unlock(&xe->pinned.lock);

		xe_bo_lock(bo, false);
		ret = xe_bo_evict_pinned(bo);
		xe_bo_unlock(bo);
		xe_bo_put(bo);
		if (ret)
			return ret;

		spin_lock(&xe->pinned.lock);
	}
	spin_unlock(&xe->pinned.lock);

	return 0;
}

/**
 * xe_bo_restore_kernel - restore 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_kernel(struct xe_device *xe)
{
	struct xe_bo *bo;
	int ret;

	if (!IS_DGFX(xe))
		return 0;

	spin_lock(&xe->pinned.lock);
	for (;;) {
		bo = list_first_entry_or_null(&xe->pinned.evicted,
					      typeof(*bo), pinned_link);
		if (!bo)
			break;
		xe_bo_get(bo);
		list_move_tail(&bo->pinned_link, &xe->pinned.kernel_bo_present);
		spin_unlock(&xe->pinned.lock);

		xe_bo_lock(bo, false);
		ret = xe_bo_restore_pinned(bo);
		xe_bo_unlock(bo);
		if (ret) {
			xe_bo_put(bo);
			return ret;
		}

		if (bo->flags & XE_BO_FLAG_GGTT) {
			struct xe_tile *tile = bo->tile;

			mutex_lock(&tile->mem.ggtt->lock);
			xe_ggtt_map_bo(tile->mem.ggtt, bo);
			mutex_unlock(&tile->mem.ggtt->lock);
		}

		/*
		 * We expect validate to trigger a move VRAM and our move code
		 * should setup the iosys map.
		 */
		xe_assert(xe, !iosys_map_is_null(&bo->vmap));
		xe_assert(xe, xe_bo_is_vram(bo));

		xe_bo_put(bo);

		spin_lock(&xe->pinned.lock);
	}
	spin_unlock(&xe->pinned.lock);

	return 0;
}

/**
 * xe_bo_restore_user - restore pinned user BOs to VRAM
 *
 * @xe: xe device
 *
 * Move pinned user BOs from temporary (typically system) memory to VRAM via
 * CPU. All moves done via TTM calls.
 *
 * This function should be called late, after GT init, on device resume.
 */
int xe_bo_restore_user(struct xe_device *xe)
{
	struct xe_bo *bo;
	struct xe_tile *tile;
	struct list_head still_in_list;
	u8 id;
	int ret;

	if (!IS_DGFX(xe))
		return 0;

	/* Pinned user memory in VRAM should be validated on resume */
	INIT_LIST_HEAD(&still_in_list);
	spin_lock(&xe->pinned.lock);
	for (;;) {
		bo = list_first_entry_or_null(&xe->pinned.external_vram,
					      typeof(*bo), pinned_link);
		if (!bo)
			break;
		list_move_tail(&bo->pinned_link, &still_in_list);
		xe_bo_get(bo);
		spin_unlock(&xe->pinned.lock);

		xe_bo_lock(bo, false);
		ret = xe_bo_restore_pinned(bo);
		xe_bo_unlock(bo);
		xe_bo_put(bo);
		if (ret) {
			spin_lock(&xe->pinned.lock);
			list_splice_tail(&still_in_list,
					 &xe->pinned.external_vram);
			spin_unlock(&xe->pinned.lock);
			return ret;
		}

		spin_lock(&xe->pinned.lock);
	}
	list_splice_tail(&still_in_list, &xe->pinned.external_vram);
	spin_unlock(&xe->pinned.lock);

	/* Wait for restore to complete */
	for_each_tile(tile, xe, id)
		xe_tile_migrate_wait(tile);

	return 0;
}