Commit 32d618e9 authored by Karolina Stolarek's avatar Karolina Stolarek Committed by Arunpravin Paneer Selvam
Browse files

drm/ttm/tests: Add tests with mock resource managers



Add mock resource manager to test ttm_bo_validate() with non-system
placements. Update KConfig entry to enable DRM Buddy allocator, used
by the mock manager. Update move function to do more than just assign
a resource.

Signed-off-by: default avatarKarolina Stolarek <karolina.stolarek@intel.com>
Tested-by: default avatarSomalapuram, Amaranath <asomalap@amd.com>
Reviewed-by: default avatarThomas Hellström <thomas.hellstrom@linux.intel.com>
Signed-off-by: default avatarArunpravin Paneer Selvam <Arunpravin.PaneerSelvam@amd.com>
Link: https://patchwork.freedesktop.org/patch/msgid/664791dbb7cbada29e705d2fcaf176023f1b8649.1718192625.git.karolina.stolarek@intel.com
parent 8bd1ff5d
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -248,6 +248,7 @@ config DRM_TTM_KUNIT_TEST
        default n
        depends on DRM && KUNIT && MMU && (UML || COMPILE_TEST)
        select DRM_TTM
        select DRM_BUDDY
        select DRM_EXPORT_FOR_TESTS if m
        select DRM_KUNIT_TEST_HELPERS
        default KUNIT_ALL_TESTS
+1 −0
Original line number Diff line number Diff line
@@ -7,4 +7,5 @@ obj-$(CONFIG_DRM_TTM_KUNIT_TEST) += \
        ttm_tt_test.o \
        ttm_bo_test.o \
        ttm_bo_validate_test.o \
        ttm_mock_manager.o \
        ttm_kunit_helpers.o
+273 −0
Original line number Diff line number Diff line
@@ -8,12 +8,15 @@
#include <drm/ttm/ttm_tt.h>

#include "ttm_kunit_helpers.h"
#include "ttm_mock_manager.h"

#define BO_SIZE		SZ_4K
#define MANAGER_SIZE	SZ_1M

struct ttm_bo_validate_test_case {
	const char *description;
	enum ttm_bo_type bo_type;
	u32 mem_type;
	bool with_ttm;
};

@@ -102,6 +105,49 @@ static void ttm_bo_init_reserved_sys_man(struct kunit *test)
	ttm_bo_put(bo);
}

static void ttm_bo_init_reserved_mock_man(struct kunit *test)
{
	const struct ttm_bo_validate_test_case *params = test->param_value;
	enum ttm_bo_type bo_type = params->bo_type;
	struct ttm_test_devices *priv = test->priv;
	u32 size = ALIGN(BO_SIZE, PAGE_SIZE);
	struct ttm_operation_ctx ctx = { };
	struct ttm_placement *placement;
	u32 mem_type = TTM_PL_VRAM;
	struct ttm_buffer_object *bo;
	struct ttm_place *place;
	int err;

	ttm_mock_manager_init(priv->ttm_dev, mem_type, MANAGER_SIZE);

	bo = kunit_kzalloc(test, sizeof(*bo), GFP_KERNEL);
	KUNIT_ASSERT_NOT_NULL(test, bo);

	place = ttm_place_kunit_init(test, mem_type, 0);
	placement = ttm_placement_kunit_init(test, place, 1);

	drm_gem_private_object_init(priv->drm, &bo->base, size);

	err = ttm_bo_init_reserved(priv->ttm_dev, bo, bo_type, placement,
				   PAGE_SIZE, &ctx, NULL, NULL,
				   &dummy_ttm_bo_destroy);
	dma_resv_unlock(bo->base.resv);

	KUNIT_EXPECT_EQ(test, err, 0);
	KUNIT_EXPECT_EQ(test, kref_read(&bo->kref), 1);
	KUNIT_EXPECT_PTR_EQ(test, bo->bdev, priv->ttm_dev);
	KUNIT_EXPECT_EQ(test, bo->type, bo_type);
	KUNIT_EXPECT_EQ(test, ctx.bytes_moved, size);

	if (bo_type != ttm_bo_type_kernel)
		KUNIT_EXPECT_TRUE(test,
				  drm_mm_node_allocated(&bo->base.vma_node.vm_node));

	ttm_resource_free(bo, &bo->resource);
	ttm_bo_put(bo);
	ttm_mock_manager_fini(priv->ttm_dev, mem_type);
}

static void ttm_bo_init_reserved_resv(struct kunit *test)
{
	enum ttm_bo_type bo_type = ttm_bo_type_device;
@@ -136,6 +182,51 @@ static void ttm_bo_init_reserved_resv(struct kunit *test)
	ttm_bo_put(bo);
}

static void ttm_bo_validate_basic(struct kunit *test)
{
	const struct ttm_bo_validate_test_case *params = test->param_value;
	u32 fst_mem = TTM_PL_SYSTEM, snd_mem = TTM_PL_VRAM;
	struct ttm_operation_ctx ctx_init = { }, ctx_val = { };
	struct ttm_placement *fst_placement, *snd_placement;
	struct ttm_test_devices *priv = test->priv;
	struct ttm_place *fst_place, *snd_place;
	u32 size = ALIGN(SZ_8K, PAGE_SIZE);
	struct ttm_buffer_object *bo;
	int err;

	ttm_mock_manager_init(priv->ttm_dev, snd_mem, MANAGER_SIZE);

	fst_place = ttm_place_kunit_init(test, fst_mem, 0);
	fst_placement = ttm_placement_kunit_init(test, fst_place, 1);

	bo = kunit_kzalloc(test, sizeof(*bo), GFP_KERNEL);
	KUNIT_ASSERT_NOT_NULL(test, bo);

	drm_gem_private_object_init(priv->drm, &bo->base, size);

	err = ttm_bo_init_reserved(priv->ttm_dev, bo, params->bo_type,
				   fst_placement, PAGE_SIZE, &ctx_init, NULL,
				   NULL, &dummy_ttm_bo_destroy);
	KUNIT_EXPECT_EQ(test, err, 0);

	snd_place = ttm_place_kunit_init(test, snd_mem, DRM_BUDDY_TOPDOWN_ALLOCATION);
	snd_placement = ttm_placement_kunit_init(test, snd_place, 1);

	err = ttm_bo_validate(bo, snd_placement, &ctx_val);
	dma_resv_unlock(bo->base.resv);

	KUNIT_EXPECT_EQ(test, err, 0);
	KUNIT_EXPECT_EQ(test, ctx_val.bytes_moved, bo->base.size);
	KUNIT_EXPECT_NOT_NULL(test, bo->ttm);
	KUNIT_EXPECT_TRUE(test, ttm_tt_is_populated(bo->ttm));
	KUNIT_EXPECT_EQ(test, bo->resource->mem_type, snd_mem);
	KUNIT_EXPECT_EQ(test, bo->resource->placement,
			DRM_BUDDY_TOPDOWN_ALLOCATION);

	ttm_bo_put(bo);
	ttm_mock_manager_fini(priv->ttm_dev, snd_mem);
}

static void ttm_bo_validate_invalid_placement(struct kunit *test)
{
	enum ttm_bo_type bo_type = ttm_bo_type_device;
@@ -162,6 +253,36 @@ static void ttm_bo_validate_invalid_placement(struct kunit *test)
	ttm_bo_put(bo);
}

static void ttm_bo_validate_failed_alloc(struct kunit *test)
{
	enum ttm_bo_type bo_type = ttm_bo_type_device;
	struct ttm_test_devices *priv = test->priv;
	u32 size = ALIGN(BO_SIZE, PAGE_SIZE);
	struct ttm_operation_ctx ctx = { };
	struct ttm_placement *placement;
	u32 mem_type = TTM_PL_VRAM;
	struct ttm_buffer_object *bo;
	struct ttm_place *place;
	int err;

	bo = ttm_bo_kunit_init(test, test->priv, size, NULL);
	bo->type = bo_type;

	ttm_bad_manager_init(priv->ttm_dev, mem_type, MANAGER_SIZE);

	place = ttm_place_kunit_init(test, mem_type, 0);
	placement = ttm_placement_kunit_init(test, place, 1);

	ttm_bo_reserve(bo, false, false, NULL);
	err = ttm_bo_validate(bo, placement, &ctx);
	dma_resv_unlock(bo->base.resv);

	KUNIT_EXPECT_EQ(test, err, -ENOMEM);

	ttm_bo_put(bo);
	ttm_bad_manager_fini(priv->ttm_dev, mem_type);
}

static void ttm_bo_validate_pinned(struct kunit *test)
{
	enum ttm_bo_type bo_type = ttm_bo_type_device;
@@ -193,11 +314,163 @@ static void ttm_bo_validate_pinned(struct kunit *test)
	ttm_bo_put(bo);
}

static const struct ttm_bo_validate_test_case ttm_mem_type_cases[] = {
	{
		.description = "System manager",
		.mem_type = TTM_PL_SYSTEM,
	},
	{
		.description = "VRAM manager",
		.mem_type = TTM_PL_VRAM,
	},
};

KUNIT_ARRAY_PARAM(ttm_bo_validate_mem, ttm_mem_type_cases,
		  ttm_bo_validate_case_desc);

static void ttm_bo_validate_same_placement(struct kunit *test)
{
	const struct ttm_bo_validate_test_case *params = test->param_value;
	struct ttm_operation_ctx ctx_init = { }, ctx_val = { };
	struct ttm_test_devices *priv = test->priv;
	u32 size = ALIGN(BO_SIZE, PAGE_SIZE);
	struct ttm_placement *placement;
	struct ttm_buffer_object *bo;
	struct ttm_place *place;
	int err;

	place = ttm_place_kunit_init(test, params->mem_type, 0);
	placement = ttm_placement_kunit_init(test, place, 1);

	if (params->mem_type != TTM_PL_SYSTEM)
		ttm_mock_manager_init(priv->ttm_dev, params->mem_type, MANAGER_SIZE);

	bo = kunit_kzalloc(test, sizeof(*bo), GFP_KERNEL);
	KUNIT_ASSERT_NOT_NULL(test, bo);

	drm_gem_private_object_init(priv->drm, &bo->base, size);

	err = ttm_bo_init_reserved(priv->ttm_dev, bo, params->bo_type,
				   placement, PAGE_SIZE, &ctx_init, NULL,
				   NULL, &dummy_ttm_bo_destroy);
	KUNIT_EXPECT_EQ(test, err, 0);

	err = ttm_bo_validate(bo, placement, &ctx_val);
	dma_resv_unlock(bo->base.resv);

	KUNIT_EXPECT_EQ(test, err, 0);
	KUNIT_EXPECT_EQ(test, ctx_val.bytes_moved, 0);

	ttm_bo_put(bo);

	if (params->mem_type != TTM_PL_SYSTEM)
		ttm_mock_manager_fini(priv->ttm_dev, params->mem_type);
}

static void ttm_bo_validate_busy_placement(struct kunit *test)
{
	u32 fst_mem = TTM_PL_VRAM, snd_mem = TTM_PL_VRAM + 1;
	struct ttm_operation_ctx ctx_init = { }, ctx_val = { };
	struct ttm_placement *placement_init, *placement_val;
	enum ttm_bo_type bo_type = ttm_bo_type_device;
	struct ttm_test_devices *priv = test->priv;
	u32 size = ALIGN(BO_SIZE, PAGE_SIZE);
	struct ttm_place *init_place, places[2];
	struct ttm_resource_manager *man;
	struct ttm_buffer_object *bo;
	int err;

	ttm_bad_manager_init(priv->ttm_dev, fst_mem, MANAGER_SIZE);
	ttm_mock_manager_init(priv->ttm_dev, snd_mem, MANAGER_SIZE);

	init_place = ttm_place_kunit_init(test, TTM_PL_SYSTEM, 0);
	placement_init = ttm_placement_kunit_init(test, init_place, 1);

	bo = kunit_kzalloc(test, sizeof(*bo), GFP_KERNEL);
	KUNIT_ASSERT_NOT_NULL(test, bo);

	drm_gem_private_object_init(priv->drm, &bo->base, size);

	err = ttm_bo_init_reserved(priv->ttm_dev, bo, bo_type, placement_init,
				   PAGE_SIZE, &ctx_init, NULL, NULL,
				   &dummy_ttm_bo_destroy);
	KUNIT_EXPECT_EQ(test, err, 0);

	places[0] = (struct ttm_place){ .mem_type = fst_mem, .flags = TTM_PL_FLAG_DESIRED };
	places[1] = (struct ttm_place){ .mem_type = snd_mem, .flags = TTM_PL_FLAG_FALLBACK };
	placement_val = ttm_placement_kunit_init(test, places, 2);

	err = ttm_bo_validate(bo, placement_val, &ctx_val);
	dma_resv_unlock(bo->base.resv);

	man = ttm_manager_type(priv->ttm_dev, snd_mem);

	KUNIT_EXPECT_EQ(test, err, 0);
	KUNIT_EXPECT_EQ(test, ctx_val.bytes_moved, bo->base.size);
	KUNIT_EXPECT_EQ(test, bo->resource->mem_type, snd_mem);
	KUNIT_ASSERT_TRUE(test, list_is_singular(&man->lru[bo->priority]));

	ttm_bo_put(bo);
	ttm_bad_manager_fini(priv->ttm_dev, fst_mem);
	ttm_mock_manager_fini(priv->ttm_dev, snd_mem);
}

static void ttm_bo_validate_multihop(struct kunit *test)
{
	const struct ttm_bo_validate_test_case *params = test->param_value;
	struct ttm_operation_ctx ctx_init = { }, ctx_val = { };
	struct ttm_placement *placement_init, *placement_val;
	u32 fst_mem = TTM_PL_VRAM, tmp_mem = TTM_PL_TT, final_mem = TTM_PL_SYSTEM;
	struct ttm_test_devices *priv = test->priv;
	struct ttm_place *fst_place, *final_place;
	u32 size = ALIGN(BO_SIZE, PAGE_SIZE);
	struct ttm_buffer_object *bo;
	int err;

	ttm_mock_manager_init(priv->ttm_dev, fst_mem, MANAGER_SIZE);
	ttm_mock_manager_init(priv->ttm_dev, tmp_mem, MANAGER_SIZE);

	fst_place = ttm_place_kunit_init(test, fst_mem, 0);
	placement_init = ttm_placement_kunit_init(test, fst_place, 1);

	bo = kunit_kzalloc(test, sizeof(*bo), GFP_KERNEL);
	KUNIT_ASSERT_NOT_NULL(test, bo);

	drm_gem_private_object_init(priv->drm, &bo->base, size);

	err = ttm_bo_init_reserved(priv->ttm_dev, bo, params->bo_type,
				   placement_init, PAGE_SIZE, &ctx_init, NULL,
				   NULL, &dummy_ttm_bo_destroy);
	KUNIT_EXPECT_EQ(test, err, 0);

	final_place = ttm_place_kunit_init(test, final_mem, 0);
	placement_val = ttm_placement_kunit_init(test, final_place, 1);

	err = ttm_bo_validate(bo, placement_val, &ctx_val);
	dma_resv_unlock(bo->base.resv);

	KUNIT_EXPECT_EQ(test, err, 0);
	KUNIT_EXPECT_EQ(test, ctx_val.bytes_moved, size * 2);
	KUNIT_EXPECT_EQ(test, bo->resource->mem_type, final_mem);

	ttm_bo_put(bo);

	ttm_mock_manager_fini(priv->ttm_dev, fst_mem);
	ttm_mock_manager_fini(priv->ttm_dev, tmp_mem);
}

static struct kunit_case ttm_bo_validate_test_cases[] = {
	KUNIT_CASE_PARAM(ttm_bo_init_reserved_sys_man, ttm_bo_types_gen_params),
	KUNIT_CASE_PARAM(ttm_bo_init_reserved_mock_man, ttm_bo_types_gen_params),
	KUNIT_CASE(ttm_bo_init_reserved_resv),
	KUNIT_CASE_PARAM(ttm_bo_validate_basic, ttm_bo_types_gen_params),
	KUNIT_CASE(ttm_bo_validate_invalid_placement),
	KUNIT_CASE_PARAM(ttm_bo_validate_same_placement,
			 ttm_bo_validate_mem_gen_params),
	KUNIT_CASE(ttm_bo_validate_failed_alloc),
	KUNIT_CASE(ttm_bo_validate_pinned),
	KUNIT_CASE(ttm_bo_validate_busy_placement),
	KUNIT_CASE_PARAM(ttm_bo_validate_multihop, ttm_bo_types_gen_params),
	{}
};

+25 −2
Original line number Diff line number Diff line
@@ -27,10 +27,33 @@ static int mock_move(struct ttm_buffer_object *bo, bool evict,
		     struct ttm_resource *new_mem,
		     struct ttm_place *hop)
{
	bo->resource = new_mem;
	struct ttm_resource *old_mem = bo->resource;

	if (!old_mem || (old_mem->mem_type == TTM_PL_SYSTEM && !bo->ttm)) {
		ttm_bo_move_null(bo, new_mem);
		return 0;
	}

	if (bo->resource->mem_type == TTM_PL_VRAM &&
	    new_mem->mem_type == TTM_PL_SYSTEM) {
		hop->mem_type = TTM_PL_TT;
		hop->flags = TTM_PL_FLAG_TEMPORARY;
		hop->fpfn = 0;
		hop->lpfn = 0;
		return -EMULTIHOP;
	}

	if ((old_mem->mem_type == TTM_PL_SYSTEM &&
	     new_mem->mem_type == TTM_PL_TT) ||
	    (old_mem->mem_type == TTM_PL_TT &&
	     new_mem->mem_type == TTM_PL_SYSTEM)) {
		ttm_bo_move_null(bo, new_mem);
		return 0;
	}

	return ttm_bo_move_memcpy(bo, ctx, new_mem);
}

struct ttm_device_funcs ttm_dev_funcs = {
	.ttm_tt_create = ttm_tt_simple_create,
	.ttm_tt_destroy = ttm_tt_simple_destroy,
+206 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0 AND MIT
/*
 * Copyright © 2023 Intel Corporation
 */
#include <drm/ttm/ttm_resource.h>
#include <drm/ttm/ttm_device.h>
#include <drm/ttm/ttm_placement.h>

#include "ttm_mock_manager.h"

static inline struct ttm_mock_manager *
to_mock_mgr(struct ttm_resource_manager *man)
{
	return container_of(man, struct ttm_mock_manager, man);
}

static inline struct ttm_mock_resource *
to_mock_mgr_resource(struct ttm_resource *res)
{
	return container_of(res, struct ttm_mock_resource, base);
}

static int ttm_mock_manager_alloc(struct ttm_resource_manager *man,
				  struct ttm_buffer_object *bo,
				  const struct ttm_place *place,
				  struct ttm_resource **res)
{
	struct ttm_mock_manager *manager = to_mock_mgr(man);
	struct ttm_mock_resource *mock_res;
	struct drm_buddy *mm = &manager->mm;
	u64 lpfn, fpfn, alloc_size;
	int err;

	mock_res = kzalloc(sizeof(*mock_res), GFP_KERNEL);

	if (!mock_res)
		return -ENOMEM;

	fpfn = 0;
	lpfn = man->size;

	ttm_resource_init(bo, place, &mock_res->base);
	INIT_LIST_HEAD(&mock_res->blocks);

	if (place->flags & TTM_PL_FLAG_TOPDOWN)
		mock_res->flags |= DRM_BUDDY_TOPDOWN_ALLOCATION;

	if (place->flags & TTM_PL_FLAG_CONTIGUOUS)
		mock_res->flags |= DRM_BUDDY_CONTIGUOUS_ALLOCATION;

	alloc_size = (uint64_t)mock_res->base.size;
	mutex_lock(&manager->lock);
	err = drm_buddy_alloc_blocks(mm, fpfn, lpfn, alloc_size,
				     manager->default_page_size,
				     &mock_res->blocks,
				     mock_res->flags);

	if (err)
		goto error_free_blocks;
	mutex_unlock(&manager->lock);

	*res = &mock_res->base;
	return 0;

error_free_blocks:
	drm_buddy_free_list(mm, &mock_res->blocks, 0);
	ttm_resource_fini(man, &mock_res->base);
	mutex_unlock(&manager->lock);

	return err;
}

static void ttm_mock_manager_free(struct ttm_resource_manager *man,
				  struct ttm_resource *res)
{
	struct ttm_mock_manager *manager = to_mock_mgr(man);
	struct ttm_mock_resource *mock_res = to_mock_mgr_resource(res);
	struct drm_buddy *mm = &manager->mm;

	mutex_lock(&manager->lock);
	drm_buddy_free_list(mm, &mock_res->blocks, 0);
	mutex_unlock(&manager->lock);

	ttm_resource_fini(man, res);
	kfree(mock_res);
}

static const struct ttm_resource_manager_func ttm_mock_manager_funcs = {
	.alloc = ttm_mock_manager_alloc,
	.free = ttm_mock_manager_free,
};

int ttm_mock_manager_init(struct ttm_device *bdev, u32 mem_type, u32 size)
{
	struct ttm_mock_manager *manager;
	struct ttm_resource_manager *base;
	int err;

	manager = kzalloc(sizeof(*manager), GFP_KERNEL);
	if (!manager)
		return -ENOMEM;

	mutex_init(&manager->lock);

	err = drm_buddy_init(&manager->mm, size, PAGE_SIZE);

	if (err) {
		kfree(manager);
		return err;
	}

	manager->default_page_size = PAGE_SIZE;
	base = &manager->man;
	base->func = &ttm_mock_manager_funcs;
	base->use_tt = true;

	ttm_resource_manager_init(base, bdev, size);
	ttm_set_driver_manager(bdev, mem_type, base);
	ttm_resource_manager_set_used(base, true);

	return 0;
}
EXPORT_SYMBOL_GPL(ttm_mock_manager_init);

void ttm_mock_manager_fini(struct ttm_device *bdev, u32 mem_type)
{
	struct ttm_resource_manager *man;
	struct ttm_mock_manager *mock_man;
	int err;

	man = ttm_manager_type(bdev, mem_type);
	mock_man = to_mock_mgr(man);

	err = ttm_resource_manager_evict_all(bdev, man);
	if (err)
		return;

	ttm_resource_manager_set_used(man, false);

	mutex_lock(&mock_man->lock);
	drm_buddy_fini(&mock_man->mm);
	mutex_unlock(&mock_man->lock);

	ttm_set_driver_manager(bdev, mem_type, NULL);
}
EXPORT_SYMBOL_GPL(ttm_mock_manager_fini);

static int ttm_bad_manager_alloc(struct ttm_resource_manager *man,
				 struct ttm_buffer_object *bo,
				 const struct ttm_place *place,
				 struct ttm_resource **res)
{
	return -ENOSPC;
}

static void ttm_bad_manager_free(struct ttm_resource_manager *man,
				 struct ttm_resource *res)
{
}

static bool ttm_bad_manager_compatible(struct ttm_resource_manager *man,
				       struct ttm_resource *res,
				       const struct ttm_place *place,
				       size_t size)
{
	return true;
}

static const struct ttm_resource_manager_func ttm_bad_manager_funcs = {
	.alloc = ttm_bad_manager_alloc,
	.free = ttm_bad_manager_free,
	.compatible = ttm_bad_manager_compatible
};

int ttm_bad_manager_init(struct ttm_device *bdev, u32 mem_type, u32 size)
{
	struct ttm_resource_manager *man;

	man = kzalloc(sizeof(*man), GFP_KERNEL);
	if (!man)
		return -ENOMEM;

	man->func = &ttm_bad_manager_funcs;

	ttm_resource_manager_init(man, bdev, size);
	ttm_set_driver_manager(bdev, mem_type, man);
	ttm_resource_manager_set_used(man, true);

	return 0;
}
EXPORT_SYMBOL_GPL(ttm_bad_manager_init);

void ttm_bad_manager_fini(struct ttm_device *bdev, u32 mem_type)
{
	struct ttm_resource_manager *man;

	man = ttm_manager_type(bdev, mem_type);

	ttm_resource_manager_set_used(man, false);
	ttm_set_driver_manager(bdev, mem_type, NULL);

	kfree(man);
}
EXPORT_SYMBOL_GPL(ttm_bad_manager_fini);

MODULE_LICENSE("GPL and additional rights");
Loading