Contributors: 1
Author Tokens Token Proportion Commits Commit Proportion
Tvrtko A. Ursulin 475 100.00% 2 100.00%
Total 475 2


/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2025 Valve Corporation */

#ifndef _SCHED_TESTS_H_
#define _SCHED_TESTS_H_

#include <kunit/test.h>
#include <linux/atomic.h>
#include <linux/completion.h>
#include <linux/dma-fence.h>
#include <linux/hrtimer.h>
#include <linux/ktime.h>
#include <linux/list.h>
#include <linux/atomic.h>
#include <linux/mutex.h>
#include <linux/types.h>

#include <drm/gpu_scheduler.h>

/*
 * DOC: Mock DRM scheduler data structures
 *
 * drm_mock_* data structures are used to implement a mock "GPU".
 *
 * They subclass the core DRM scheduler objects and add their data on top, which
 * enables tracking the submitted jobs and simulating their execution with the
 * attributes as specified by the test case.
 */

/**
 * struct drm_mock_scheduler - implements a trivial mock GPU execution engine
 *
 * @base: DRM scheduler base class
 * @test: Backpointer to owning the kunit test case
 * @lock: Lock to protect the simulated @hw_timeline, @job_list and @done_list
 * @job_list: List of jobs submitted to the mock GPU
 * @done_list: List of jobs completed by the mock GPU
 * @hw_timeline: Simulated hardware timeline has a @context, @next_seqno and
 *		 @cur_seqno for implementing a struct dma_fence signaling the
 *		 simulated job completion.
 *
 * Trivial mock GPU execution engine tracks submitted jobs and enables
 * completing them strictly in submission order.
 */
struct drm_mock_scheduler {
	struct drm_gpu_scheduler base;

	struct kunit		*test;

	spinlock_t		lock;
	struct list_head	job_list;
	struct list_head	done_list;

	struct {
		u64		context;
		atomic_t	next_seqno;
		unsigned int	cur_seqno;
	} hw_timeline;
};

/**
 * struct drm_mock_sched_entity - implements a mock GPU sched entity
 *
 * @base: DRM scheduler entity base class
 * @test: Backpointer to owning the kunit test case
 *
 * Mock GPU sched entity is used by the test cases to submit jobs to the mock
 * scheduler.
 */
struct drm_mock_sched_entity {
	struct drm_sched_entity base;

	struct kunit		*test;
};

/**
 * struct drm_mock_sched_job - implements a mock GPU job
 *
 * @base: DRM sched job base class
 * @done: Completion signaling job completion.
 * @flags: Flags designating job state.
 * @link: List head element used by job tracking by the drm_mock_scheduler
 * @timer: Timer used for simulating job execution duration
 * @duration_us: Simulated job duration in micro seconds, or zero if in manual
 *		 timeline advance mode
 * @finish_at: Absolute time when the jobs with set duration will complete
 * @lock: Lock used for @hw_fence
 * @hw_fence: Fence returned to DRM scheduler as the hardware fence
 * @test: Backpointer to owning the kunit test case
 *
 * Mock GPU sched job is used by the test cases to submit jobs to the mock
 * scheduler.
 */
struct drm_mock_sched_job {
	struct drm_sched_job	base;

	struct completion	done;

#define DRM_MOCK_SCHED_JOB_DONE		0x1
#define DRM_MOCK_SCHED_JOB_TIMEDOUT	0x2
	unsigned long		flags;

	struct list_head	link;
	struct hrtimer		timer;

	unsigned int		duration_us;
	ktime_t			finish_at;

	spinlock_t		lock;
	struct dma_fence	hw_fence;

	struct kunit		*test;
};

static inline struct drm_mock_scheduler *
drm_sched_to_mock_sched(struct drm_gpu_scheduler *sched)
{
	return container_of(sched, struct drm_mock_scheduler, base);
};

static inline struct drm_mock_sched_entity *
drm_sched_entity_to_mock_entity(struct drm_sched_entity *sched_entity)
{
	return container_of(sched_entity, struct drm_mock_sched_entity, base);
};

static inline struct drm_mock_sched_job *
drm_sched_job_to_mock_job(struct drm_sched_job *sched_job)
{
	return container_of(sched_job, struct drm_mock_sched_job, base);
};

struct drm_mock_scheduler *drm_mock_sched_new(struct kunit *test,
					      long timeout);
void drm_mock_sched_fini(struct drm_mock_scheduler *sched);
unsigned int drm_mock_sched_advance(struct drm_mock_scheduler *sched,
				    unsigned int num);

struct drm_mock_sched_entity *
drm_mock_sched_entity_new(struct kunit *test,
			  enum drm_sched_priority priority,
			  struct drm_mock_scheduler *sched);
void drm_mock_sched_entity_free(struct drm_mock_sched_entity *entity);

struct drm_mock_sched_job *
drm_mock_sched_job_new(struct kunit *test,
		       struct drm_mock_sched_entity *entity);

/**
 * drm_mock_sched_job_submit - Arm and submit a job in one go
 *
 * @job: Job to arm and submit
 */
static inline void drm_mock_sched_job_submit(struct drm_mock_sched_job *job)
{
	drm_sched_job_arm(&job->base);
	drm_sched_entity_push_job(&job->base);
}

/**
 * drm_mock_sched_job_set_duration_us - Set a job duration
 *
 * @job: Job to set the duration for
 * @duration_us: Duration in micro seconds
 *
 * Jobs with duration set will be automatically completed by the mock scheduler
 * as the timeline progresses, unless a job without a set duration is
 * encountered in the timelime in which case calling drm_mock_sched_advance()
 * will be required to bump the timeline.
 */
static inline void
drm_mock_sched_job_set_duration_us(struct drm_mock_sched_job *job,
				   unsigned int duration_us)
{
	job->duration_us = duration_us;
}

/**
 * drm_mock_sched_job_is_finished - Check if a job is finished
 *
 * @job: Job to check
 *
 * Returns: true if finished
 */
static inline bool
drm_mock_sched_job_is_finished(struct drm_mock_sched_job *job)
{
	return job->flags & DRM_MOCK_SCHED_JOB_DONE;
}

/**
 * drm_mock_sched_job_wait_finished - Wait until a job is finished
 *
 * @job: Job to wait for
 * @timeout: Wait time in jiffies
 *
 * Returns: true if finished within the timeout provided, otherwise false
 */
static inline bool
drm_mock_sched_job_wait_finished(struct drm_mock_sched_job *job, long timeout)
{
	if (job->flags & DRM_MOCK_SCHED_JOB_DONE)
		return true;

	return wait_for_completion_timeout(&job->done, timeout) != 0;
}

/**
 * drm_mock_sched_job_wait_scheduled - Wait until a job is scheduled
 *
 * @job: Job to wait for
 * @timeout: Wait time in jiffies
 *
 * Returns: true if scheduled within the timeout provided, otherwise false
 */
static inline bool
drm_mock_sched_job_wait_scheduled(struct drm_mock_sched_job *job, long timeout)
{
	KUNIT_ASSERT_EQ(job->test, job->flags & DRM_MOCK_SCHED_JOB_DONE, 0);

	return dma_fence_wait_timeout(&job->base.s_fence->scheduled,
				      false,
				      timeout) != 0;
}

#endif