linux/drivers/gpu/drm/ttm/tests/ttm_mock_manager.c

// 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 int ttm_busy_manager_alloc(struct ttm_resource_manager *man,
				  struct ttm_buffer_object *bo,
				  const struct ttm_place *place,
				  struct ttm_resource **res)
{
	return -EBUSY;
}

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
};

static const struct ttm_resource_manager_func ttm_bad_busy_manager_funcs = {
	.alloc = ttm_busy_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);

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

	ttm_bad_manager_init(bdev, mem_type, size);
	man = ttm_manager_type(bdev, mem_type);

	man->func = &ttm_bad_busy_manager_funcs;

	return 0;
}
EXPORT_SYMBOL_GPL(ttm_busy_manager_init);

void ttm_bad_manager_fini(struct ttm_device *bdev, uint32_t 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_DESCRIPTION("KUnit tests for ttm with mock resource managers");
MODULE_LICENSE("GPL and additional rights");