162306a36Sopenharmony_ci// SPDX-License-Identifier: MIT
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright © 2021 Intel Corporation
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include <drm/ttm/ttm_device.h>
662306a36Sopenharmony_ci#include <drm/ttm/ttm_range_manager.h>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "i915_drv.h"
962306a36Sopenharmony_ci#include "i915_scatterlist.h"
1062306a36Sopenharmony_ci#include "i915_ttm_buddy_manager.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include "intel_region_ttm.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "gem/i915_gem_region.h"
1562306a36Sopenharmony_ci#include "gem/i915_gem_ttm.h" /* For the funcs/ops export only */
1662306a36Sopenharmony_ci/**
1762306a36Sopenharmony_ci * DOC: TTM support structure
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * The code in this file deals with setting up memory managers for TTM
2062306a36Sopenharmony_ci * LMEM and MOCK regions and converting the output from
2162306a36Sopenharmony_ci * the managers to struct sg_table, Basically providing the mapping from
2262306a36Sopenharmony_ci * i915 GEM regions to TTM memory types and resource managers.
2362306a36Sopenharmony_ci */
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/**
2662306a36Sopenharmony_ci * intel_region_ttm_device_init - Initialize a TTM device
2762306a36Sopenharmony_ci * @dev_priv: Pointer to an i915 device private structure.
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci * Return: 0 on success, negative error code on failure.
3062306a36Sopenharmony_ci */
3162306a36Sopenharmony_ciint intel_region_ttm_device_init(struct drm_i915_private *dev_priv)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	struct drm_device *drm = &dev_priv->drm;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	return ttm_device_init(&dev_priv->bdev, i915_ttm_driver(),
3662306a36Sopenharmony_ci			       drm->dev, drm->anon_inode->i_mapping,
3762306a36Sopenharmony_ci			       drm->vma_offset_manager, false, false);
3862306a36Sopenharmony_ci}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci/**
4162306a36Sopenharmony_ci * intel_region_ttm_device_fini - Finalize a TTM device
4262306a36Sopenharmony_ci * @dev_priv: Pointer to an i915 device private structure.
4362306a36Sopenharmony_ci */
4462306a36Sopenharmony_civoid intel_region_ttm_device_fini(struct drm_i915_private *dev_priv)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	ttm_device_fini(&dev_priv->bdev);
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/*
5062306a36Sopenharmony_ci * Map the i915 memory regions to TTM memory types. We use the
5162306a36Sopenharmony_ci * driver-private types for now, reserving TTM_PL_VRAM for stolen
5262306a36Sopenharmony_ci * memory and TTM_PL_TT for GGTT use if decided to implement this.
5362306a36Sopenharmony_ci */
5462306a36Sopenharmony_ciint intel_region_to_ttm_type(const struct intel_memory_region *mem)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	int type;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	GEM_BUG_ON(mem->type != INTEL_MEMORY_LOCAL &&
5962306a36Sopenharmony_ci		   mem->type != INTEL_MEMORY_MOCK &&
6062306a36Sopenharmony_ci		   mem->type != INTEL_MEMORY_SYSTEM);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	if (mem->type == INTEL_MEMORY_SYSTEM)
6362306a36Sopenharmony_ci		return TTM_PL_SYSTEM;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	type = mem->instance + TTM_PL_PRIV;
6662306a36Sopenharmony_ci	GEM_BUG_ON(type >= TTM_NUM_MEM_TYPES);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	return type;
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci/**
7262306a36Sopenharmony_ci * intel_region_ttm_init - Initialize a memory region for TTM.
7362306a36Sopenharmony_ci * @mem: The region to initialize.
7462306a36Sopenharmony_ci *
7562306a36Sopenharmony_ci * This function initializes a suitable TTM resource manager for the
7662306a36Sopenharmony_ci * region, and if it's a LMEM region type, attaches it to the TTM
7762306a36Sopenharmony_ci * device. MOCK regions are NOT attached to the TTM device, since we don't
7862306a36Sopenharmony_ci * have one for the mock selftests.
7962306a36Sopenharmony_ci *
8062306a36Sopenharmony_ci * Return: 0 on success, negative error code on failure.
8162306a36Sopenharmony_ci */
8262306a36Sopenharmony_ciint intel_region_ttm_init(struct intel_memory_region *mem)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	struct ttm_device *bdev = &mem->i915->bdev;
8562306a36Sopenharmony_ci	int mem_type = intel_region_to_ttm_type(mem);
8662306a36Sopenharmony_ci	int ret;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	ret = i915_ttm_buddy_man_init(bdev, mem_type, false,
8962306a36Sopenharmony_ci				      resource_size(&mem->region),
9062306a36Sopenharmony_ci				      mem->io_size,
9162306a36Sopenharmony_ci				      mem->min_page_size, PAGE_SIZE);
9262306a36Sopenharmony_ci	if (ret)
9362306a36Sopenharmony_ci		return ret;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	mem->region_private = ttm_manager_type(bdev, mem_type);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	return 0;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci/**
10162306a36Sopenharmony_ci * intel_region_ttm_fini - Finalize a TTM region.
10262306a36Sopenharmony_ci * @mem: The memory region
10362306a36Sopenharmony_ci *
10462306a36Sopenharmony_ci * This functions takes down the TTM resource manager associated with the
10562306a36Sopenharmony_ci * memory region, and if it was registered with the TTM device,
10662306a36Sopenharmony_ci * removes that registration.
10762306a36Sopenharmony_ci */
10862306a36Sopenharmony_ciint intel_region_ttm_fini(struct intel_memory_region *mem)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	struct ttm_resource_manager *man = mem->region_private;
11162306a36Sopenharmony_ci	int ret = -EBUSY;
11262306a36Sopenharmony_ci	int count;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	/*
11562306a36Sopenharmony_ci	 * Put the region's move fences. This releases requests that
11662306a36Sopenharmony_ci	 * may hold on to contexts and vms that may hold on to buffer
11762306a36Sopenharmony_ci	 * objects placed in this region.
11862306a36Sopenharmony_ci	 */
11962306a36Sopenharmony_ci	if (man)
12062306a36Sopenharmony_ci		ttm_resource_manager_cleanup(man);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	/* Flush objects from region. */
12362306a36Sopenharmony_ci	for (count = 0; count < 10; ++count) {
12462306a36Sopenharmony_ci		i915_gem_flush_free_objects(mem->i915);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci		mutex_lock(&mem->objects.lock);
12762306a36Sopenharmony_ci		if (list_empty(&mem->objects.list))
12862306a36Sopenharmony_ci			ret = 0;
12962306a36Sopenharmony_ci		mutex_unlock(&mem->objects.lock);
13062306a36Sopenharmony_ci		if (!ret)
13162306a36Sopenharmony_ci			break;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci		msleep(20);
13462306a36Sopenharmony_ci		drain_workqueue(mem->i915->bdev.wq);
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	/* If we leaked objects, Don't free the region causing use after free */
13862306a36Sopenharmony_ci	if (ret || !man)
13962306a36Sopenharmony_ci		return ret;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	ret = i915_ttm_buddy_man_fini(&mem->i915->bdev,
14262306a36Sopenharmony_ci				      intel_region_to_ttm_type(mem));
14362306a36Sopenharmony_ci	GEM_WARN_ON(ret);
14462306a36Sopenharmony_ci	mem->region_private = NULL;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	return ret;
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci/**
15062306a36Sopenharmony_ci * intel_region_ttm_resource_to_rsgt -
15162306a36Sopenharmony_ci * Convert an opaque TTM resource manager resource to a refcounted sg_table.
15262306a36Sopenharmony_ci * @mem: The memory region.
15362306a36Sopenharmony_ci * @res: The resource manager resource obtained from the TTM resource manager.
15462306a36Sopenharmony_ci * @page_alignment: Required page alignment for each sg entry. Power of two.
15562306a36Sopenharmony_ci *
15662306a36Sopenharmony_ci * The gem backends typically use sg-tables for operations on the underlying
15762306a36Sopenharmony_ci * io_memory. So provide a way for the backends to translate the
15862306a36Sopenharmony_ci * nodes they are handed from TTM to sg-tables.
15962306a36Sopenharmony_ci *
16062306a36Sopenharmony_ci * Return: A malloced sg_table on success, an error pointer on failure.
16162306a36Sopenharmony_ci */
16262306a36Sopenharmony_cistruct i915_refct_sgt *
16362306a36Sopenharmony_ciintel_region_ttm_resource_to_rsgt(struct intel_memory_region *mem,
16462306a36Sopenharmony_ci				  struct ttm_resource *res,
16562306a36Sopenharmony_ci				  u32 page_alignment)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	if (mem->is_range_manager) {
16862306a36Sopenharmony_ci		struct ttm_range_mgr_node *range_node =
16962306a36Sopenharmony_ci			to_ttm_range_mgr_node(res);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci		return i915_rsgt_from_mm_node(&range_node->mm_nodes[0],
17262306a36Sopenharmony_ci					      mem->region.start,
17362306a36Sopenharmony_ci					      page_alignment);
17462306a36Sopenharmony_ci	} else {
17562306a36Sopenharmony_ci		return i915_rsgt_from_buddy_resource(res, mem->region.start,
17662306a36Sopenharmony_ci						     page_alignment);
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci#ifdef CONFIG_DRM_I915_SELFTEST
18162306a36Sopenharmony_ci/**
18262306a36Sopenharmony_ci * intel_region_ttm_resource_alloc - Allocate memory resources from a region
18362306a36Sopenharmony_ci * @mem: The memory region,
18462306a36Sopenharmony_ci * @offset: BO offset
18562306a36Sopenharmony_ci * @size: The requested size in bytes
18662306a36Sopenharmony_ci * @flags: Allocation flags
18762306a36Sopenharmony_ci *
18862306a36Sopenharmony_ci * This functionality is provided only for callers that need to allocate
18962306a36Sopenharmony_ci * memory from standalone TTM range managers, without the TTM eviction
19062306a36Sopenharmony_ci * functionality. Don't use if you are not completely sure that's the
19162306a36Sopenharmony_ci * case. The returned opaque node can be converted to an sg_table using
19262306a36Sopenharmony_ci * intel_region_ttm_resource_to_st(), and can be freed using
19362306a36Sopenharmony_ci * intel_region_ttm_resource_free().
19462306a36Sopenharmony_ci *
19562306a36Sopenharmony_ci * Return: A valid pointer on success, an error pointer on failure.
19662306a36Sopenharmony_ci */
19762306a36Sopenharmony_cistruct ttm_resource *
19862306a36Sopenharmony_ciintel_region_ttm_resource_alloc(struct intel_memory_region *mem,
19962306a36Sopenharmony_ci				resource_size_t offset,
20062306a36Sopenharmony_ci				resource_size_t size,
20162306a36Sopenharmony_ci				unsigned int flags)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	struct ttm_resource_manager *man = mem->region_private;
20462306a36Sopenharmony_ci	struct ttm_place place = {};
20562306a36Sopenharmony_ci	struct ttm_buffer_object mock_bo = {};
20662306a36Sopenharmony_ci	struct ttm_resource *res;
20762306a36Sopenharmony_ci	int ret;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	if (flags & I915_BO_ALLOC_CONTIGUOUS)
21062306a36Sopenharmony_ci		place.flags |= TTM_PL_FLAG_CONTIGUOUS;
21162306a36Sopenharmony_ci	if (offset != I915_BO_INVALID_OFFSET) {
21262306a36Sopenharmony_ci		if (WARN_ON(overflows_type(offset >> PAGE_SHIFT, place.fpfn))) {
21362306a36Sopenharmony_ci			ret = -E2BIG;
21462306a36Sopenharmony_ci			goto out;
21562306a36Sopenharmony_ci		}
21662306a36Sopenharmony_ci		place.fpfn = offset >> PAGE_SHIFT;
21762306a36Sopenharmony_ci		if (WARN_ON(overflows_type(place.fpfn + (size >> PAGE_SHIFT), place.lpfn))) {
21862306a36Sopenharmony_ci			ret = -E2BIG;
21962306a36Sopenharmony_ci			goto out;
22062306a36Sopenharmony_ci		}
22162306a36Sopenharmony_ci		place.lpfn = place.fpfn + (size >> PAGE_SHIFT);
22262306a36Sopenharmony_ci	} else if (mem->io_size && mem->io_size < mem->total) {
22362306a36Sopenharmony_ci		if (flags & I915_BO_ALLOC_GPU_ONLY) {
22462306a36Sopenharmony_ci			place.flags |= TTM_PL_FLAG_TOPDOWN;
22562306a36Sopenharmony_ci		} else {
22662306a36Sopenharmony_ci			place.fpfn = 0;
22762306a36Sopenharmony_ci			if (WARN_ON(overflows_type(mem->io_size >> PAGE_SHIFT, place.lpfn))) {
22862306a36Sopenharmony_ci				ret = -E2BIG;
22962306a36Sopenharmony_ci				goto out;
23062306a36Sopenharmony_ci			}
23162306a36Sopenharmony_ci			place.lpfn = mem->io_size >> PAGE_SHIFT;
23262306a36Sopenharmony_ci		}
23362306a36Sopenharmony_ci	}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	mock_bo.base.size = size;
23662306a36Sopenharmony_ci	mock_bo.bdev = &mem->i915->bdev;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	ret = man->func->alloc(man, &mock_bo, &place, &res);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ciout:
24162306a36Sopenharmony_ci	if (ret == -ENOSPC)
24262306a36Sopenharmony_ci		ret = -ENXIO;
24362306a36Sopenharmony_ci	if (!ret)
24462306a36Sopenharmony_ci		res->bo = NULL; /* Rather blow up, then some uaf */
24562306a36Sopenharmony_ci	return ret ? ERR_PTR(ret) : res;
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci#endif
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci/**
25162306a36Sopenharmony_ci * intel_region_ttm_resource_free - Free a resource allocated from a resource manager
25262306a36Sopenharmony_ci * @mem: The region the resource was allocated from.
25362306a36Sopenharmony_ci * @res: The opaque resource representing an allocation.
25462306a36Sopenharmony_ci */
25562306a36Sopenharmony_civoid intel_region_ttm_resource_free(struct intel_memory_region *mem,
25662306a36Sopenharmony_ci				    struct ttm_resource *res)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	struct ttm_resource_manager *man = mem->region_private;
25962306a36Sopenharmony_ci	struct ttm_buffer_object mock_bo = {};
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	mock_bo.base.size = res->size;
26262306a36Sopenharmony_ci	mock_bo.bdev = &mem->i915->bdev;
26362306a36Sopenharmony_ci	res->bo = &mock_bo;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	man->func->free(man, res);
26662306a36Sopenharmony_ci}
267