Commit c56ce956 authored by Thomas Hellström's avatar Thomas Hellström
Browse files

drm/i915 Implement LMEM backup and restore for suspend / resume



Just evict unpinned objects to system. For pinned LMEM objects,
make a backup system object and blit the contents to that.

Backup is performed in three steps,
1: Opportunistically evict evictable objects using the gpu blitter.
2: After gt idle, evict evictable objects using the gpu blitter. This will
be modified in an upcoming patch to backup pinned objects that are not used
by the blitter itself.
3: Backup remaining pinned objects using memcpy.

Also move uC suspend to after 2) to make sure we have a functional GuC
during 2) if using GuC submission.

v2:
- Major refactor to make sure gem_exec_suspend@hang-SX subtests work, and
  suspend / resume works with a slightly modified GuC submission enabling
  patch series.

v3:
- Fix a potential use-after-free (Matthew Auld)
- Use i915_gem_object_create_shmem() instead of
  i915_gem_object_create_region (Matthew Auld)
- Minor simplifications (Matthew Auld)
- Fix up kerneldoc for i195_ttm_restore_region().
- Final lmem_suspend() call moved to i915_gem_backup_suspend from
  i915_gem_suspend_late, since the latter gets called at driver unload
  and we don't unnecessarily want to run it at that time.

v4:
- Interface change of ttm- & lmem suspend / resume functions to use
  flags rather than bools. (Matthew Auld)
- Completely drop the i915_gem_backup_suspend change (Matthew Auld)

Signed-off-by: default avatarThomas Hellström <thomas.hellstrom@linux.intel.com>
Reviewed-by: default avatarMatthew Auld <matthew.auld@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20210922062527.865433-5-thomas.hellstrom@linux.intel.com
parent 81387fc4
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -154,6 +154,7 @@ gem-y += \
	gem/i915_gem_throttle.o \
	gem/i915_gem_tiling.o \
	gem/i915_gem_ttm.o \
	gem/i915_gem_ttm_pm.o \
	gem/i915_gem_userptr.o \
	gem/i915_gem_wait.o \
	gem/i915_gemfs.o
+1 −0
Original line number Diff line number Diff line
@@ -534,6 +534,7 @@ struct drm_i915_gem_object {
	struct {
		struct sg_table *cached_io_st;
		struct i915_gem_object_page_iter get_io_page;
		struct drm_i915_gem_object *backup;
		bool created:1;
	} ttm;

+87 −0
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@
 */

#include "gem/i915_gem_pm.h"
#include "gem/i915_gem_ttm_pm.h"
#include "gt/intel_gt.h"
#include "gt/intel_gt_pm.h"
#include "gt/intel_gt_requests.h"
@@ -39,6 +40,84 @@ void i915_gem_suspend(struct drm_i915_private *i915)
	i915_gem_drain_freed_objects(i915);
}

static int lmem_restore(struct drm_i915_private *i915, u32 flags)
{
	struct intel_memory_region *mr;
	int ret = 0, id;

	for_each_memory_region(mr, i915, id) {
		if (mr->type == INTEL_MEMORY_LOCAL) {
			ret = i915_ttm_restore_region(mr, flags);
			if (ret)
				break;
		}
	}

	return ret;
}

static int lmem_suspend(struct drm_i915_private *i915, u32 flags)
{
	struct intel_memory_region *mr;
	int ret = 0, id;

	for_each_memory_region(mr, i915, id) {
		if (mr->type == INTEL_MEMORY_LOCAL) {
			ret = i915_ttm_backup_region(mr, flags);
			if (ret)
				break;
		}
	}

	return ret;
}

static void lmem_recover(struct drm_i915_private *i915)
{
	struct intel_memory_region *mr;
	int id;

	for_each_memory_region(mr, i915, id)
		if (mr->type == INTEL_MEMORY_LOCAL)
			i915_ttm_recover_region(mr);
}

int i915_gem_backup_suspend(struct drm_i915_private *i915)
{
	int ret;

	/* Opportunistically try to evict unpinned objects */
	ret = lmem_suspend(i915, I915_TTM_BACKUP_ALLOW_GPU);
	if (ret)
		goto out_recover;

	i915_gem_suspend(i915);

	/*
	 * More objects may have become unpinned as requests were
	 * retired. Now try to evict again. The gt may be wedged here
	 * in which case we automatically fall back to memcpy.
	 */
	ret = lmem_suspend(i915, I915_TTM_BACKUP_ALLOW_GPU);
	if (ret)
		goto out_recover;

	/*
	 * Remaining objects are backed up using memcpy once we've stopped
	 * using the migrate context.
	 */
	ret = lmem_suspend(i915, I915_TTM_BACKUP_PINNED);
	if (ret)
		goto out_recover;

	return 0;

out_recover:
	lmem_recover(i915);

	return ret;
}

void i915_gem_suspend_late(struct drm_i915_private *i915)
{
	struct drm_i915_gem_object *obj;
@@ -128,12 +207,20 @@ int i915_gem_freeze_late(struct drm_i915_private *i915)

void i915_gem_resume(struct drm_i915_private *i915)
{
	int ret;

	GEM_TRACE("%s\n", dev_name(i915->drm.dev));

	ret = lmem_restore(i915, 0);
	GEM_WARN_ON(ret);

	/*
	 * As we didn't flush the kernel context before suspend, we cannot
	 * guarantee that the context image is complete. So let's just reset
	 * it and start again.
	 */
	intel_gt_resume(&i915->gt);

	ret = lmem_restore(i915, I915_TTM_BACKUP_ALLOW_GPU);
	GEM_WARN_ON(ret);
}
+1 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ void i915_gem_idle_work_handler(struct work_struct *work);

void i915_gem_suspend(struct drm_i915_private *i915);
void i915_gem_suspend_late(struct drm_i915_private *i915);
int i915_gem_backup_suspend(struct drm_i915_private *i915);

int i915_gem_freeze(struct drm_i915_private *i915);
int i915_gem_freeze_late(struct drm_i915_private *i915);
+22 −8
Original line number Diff line number Diff line
@@ -10,18 +10,16 @@
#include "intel_memory_region.h"
#include "intel_region_ttm.h"

#include "gem/i915_gem_mman.h"
#include "gem/i915_gem_object.h"
#include "gem/i915_gem_region.h"
#include "gem/i915_gem_ttm.h"
#include "gem/i915_gem_mman.h"
#include "gem/i915_gem_ttm_pm.h"

#include "gt/intel_migrate.h"
#include "gt/intel_engine_pm.h"

#define I915_PL_LMEM0 TTM_PL_PRIV
#define I915_PL_SYSTEM TTM_PL_SYSTEM
#define I915_PL_STOLEN TTM_PL_VRAM
#define I915_PL_GGTT TTM_PL_TT
#include "gt/intel_engine_pm.h"
#include "gt/intel_gt.h"
#include "gt/intel_migrate.h"

#define I915_TTM_PRIO_PURGE     0
#define I915_TTM_PRIO_NO_PAGES  1
@@ -64,6 +62,20 @@ static struct ttm_placement i915_sys_placement = {
	.busy_placement = &sys_placement_flags,
};

/**
 * i915_ttm_sys_placement - Return the struct ttm_placement to be
 * used for an object in system memory.
 *
 * Rather than making the struct extern, use this
 * function.
 *
 * Return: A pointer to a static variable for sys placement.
 */
struct ttm_placement *i915_ttm_sys_placement(void)
{
	return &i915_sys_placement;
}

static int i915_ttm_err_to_gem(int err)
{
	/* Fastpath */
@@ -443,7 +455,7 @@ static int i915_ttm_accel_move(struct ttm_buffer_object *bo,
	enum i915_cache_level src_level, dst_level;
	int ret;

	if (!i915->gt.migrate.context)
	if (!i915->gt.migrate.context || intel_gt_is_wedged(&i915->gt))
		return -EINVAL;

	dst_level = i915_ttm_cache_level(i915, dst_mem, dst_ttm);
@@ -887,6 +899,8 @@ void i915_ttm_bo_destroy(struct ttm_buffer_object *bo)
{
	struct drm_i915_gem_object *obj = i915_ttm_to_gem(bo);

	i915_ttm_backup_free(obj);

	/* This releases all gem object bindings to the backend. */
	__i915_gem_free_object(obj);

Loading