Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923// SPDX-License-Identifier: GPL-2.0/* Copyright (c) 2025 Valve Corporation */
#include <linux/completion.h>#include <linux/delay.h>#include <linux/minmax.h>#include <linux/time.h>#include <linux/workqueue.h>
#include "sched_tests.h"
#define MOCK_TIMEOUT (HZ / 5)
/* * DRM scheduler basic tests should check the basic functional correctness of * the scheduler, including some very light smoke testing. More targeted tests, * for example focusing on testing specific bugs and other more complicated test * scenarios, should be implemented in separate source units. */
static int drm_sched_basic_init(struct kunit *test){ test->priv = drm_mock_sched_new(test, MAX_SCHEDULE_TIMEOUT);
return 0;}
static void drm_sched_basic_exit(struct kunit *test){ struct drm_mock_scheduler *sched = test->priv;
drm_mock_sched_fini(sched);}
static int drm_sched_timeout_init(struct kunit *test){ test->priv = drm_mock_sched_new(test, MOCK_TIMEOUT);
return 0;}
static void drm_sched_basic_submit(struct kunit *test){ struct drm_mock_scheduler *sched = test->priv; struct drm_mock_sched_entity *entity; struct drm_mock_sched_job *job; unsigned int i; bool done;
/* * Submit one job to the scheduler and verify that it gets scheduled * and completed only when the mock hw backend processes it. */
entity = drm_mock_sched_entity_new(test, DRM_SCHED_PRIORITY_NORMAL, sched); job = drm_mock_sched_job_new(test, entity);
drm_mock_sched_job_submit(job);
done = drm_mock_sched_job_wait_scheduled(job, HZ); KUNIT_ASSERT_TRUE(test, done);
done = drm_mock_sched_job_wait_finished(job, HZ / 2); KUNIT_ASSERT_FALSE(test, done);
i = drm_mock_sched_advance(sched, 1); KUNIT_ASSERT_EQ(test, i, 1);
done = drm_mock_sched_job_wait_finished(job, HZ); KUNIT_ASSERT_TRUE(test, done);
drm_mock_sched_entity_free(entity);}
struct drm_sched_basic_params { const char *description; unsigned int queue_depth; unsigned int num_entities; unsigned int job_us; bool dep_chain;};
static const struct drm_sched_basic_params drm_sched_basic_cases[] = { { .description = "A queue of jobs in a single entity", .queue_depth = 100, .job_us = 1000, .num_entities = 1, }, { .description = "A chain of dependent jobs in a single entity", .queue_depth = 100, .job_us = 1000, .num_entities = 1, .dep_chain = true, }, { .description = "Multiple independent job queues", .queue_depth = 100, .job_us = 1000, .num_entities = 4, }, { .description = "Multiple inter-dependent job queues", .queue_depth = 100, .job_us = 1000, .num_entities = 4, .dep_chain = true, },};
static voiddrm_sched_basic_desc(const struct drm_sched_basic_params *params, char *desc){ strscpy(desc, params->description, KUNIT_PARAM_DESC_SIZE);}
KUNIT_ARRAY_PARAM(drm_sched_basic, drm_sched_basic_cases, drm_sched_basic_desc);
static void drm_sched_basic_test(struct kunit *test){ const struct drm_sched_basic_params *params = test->param_value; struct drm_mock_scheduler *sched = test->priv; struct drm_mock_sched_job *job, *prev = NULL; struct drm_mock_sched_entity **entity; unsigned int i, cur_ent = 0; bool done;
entity = kunit_kcalloc(test, params->num_entities, sizeof(*entity), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, entity);
for (i = 0; i < params->num_entities; i++) entity[i] = drm_mock_sched_entity_new(test, DRM_SCHED_PRIORITY_NORMAL, sched);
for (i = 0; i < params->queue_depth; i++) { job = drm_mock_sched_job_new(test, entity[cur_ent++]); cur_ent %= params->num_entities; drm_mock_sched_job_set_duration_us(job, params->job_us); if (params->dep_chain && prev) drm_sched_job_add_dependency(&job->base, dma_fence_get(&prev->base.s_fence->finished)); drm_mock_sched_job_submit(job); prev = job; }
done = drm_mock_sched_job_wait_finished(job, HZ); KUNIT_ASSERT_TRUE(test, done);
for (i = 0; i < params->num_entities; i++) drm_mock_sched_entity_free(entity[i]);}
static void drm_sched_basic_entity_cleanup(struct kunit *test){ struct drm_mock_sched_job *job, *mid, *prev = NULL; struct drm_mock_scheduler *sched = test->priv; struct drm_mock_sched_entity *entity[4]; const unsigned int qd = 100; unsigned int i, cur_ent = 0; bool done;
/* * Submit a queue of jobs across different entities with an explicit * chain of dependencies between them and trigger entity cleanup while * the queue is still being processed. */
for (i = 0; i < ARRAY_SIZE(entity); i++) entity[i] = drm_mock_sched_entity_new(test, DRM_SCHED_PRIORITY_NORMAL, sched);
for (i = 0; i < qd; i++) { job = drm_mock_sched_job_new(test, entity[cur_ent++]); cur_ent %= ARRAY_SIZE(entity); drm_mock_sched_job_set_duration_us(job, 1000); if (prev) drm_sched_job_add_dependency(&job->base, dma_fence_get(&prev->base.s_fence->finished)); drm_mock_sched_job_submit(job); if (i == qd / 2) mid = job; prev = job; }
done = drm_mock_sched_job_wait_finished(mid, HZ); KUNIT_ASSERT_TRUE(test, done);
/* Exit with half of the queue still pending to be executed. */ for (i = 0; i < ARRAY_SIZE(entity); i++) drm_mock_sched_entity_free(entity[i]);}
static struct kunit_case drm_sched_basic_tests[] = { KUNIT_CASE(drm_sched_basic_submit), KUNIT_CASE_PARAM(drm_sched_basic_test, drm_sched_basic_gen_params), KUNIT_CASE(drm_sched_basic_entity_cleanup), {}};
static struct kunit_suite drm_sched_basic = { .name = "drm_sched_basic_tests", .init = drm_sched_basic_init, .exit = drm_sched_basic_exit, .test_cases = drm_sched_basic_tests,};
static void drm_sched_basic_cancel(struct kunit *test){ struct drm_mock_sched_entity *entity; struct drm_mock_scheduler *sched; struct drm_mock_sched_job *job; bool done;
/* * Check that drm_sched_fini() uses the cancel_job() callback to cancel * jobs that are still pending. */
sched = drm_mock_sched_new(test, MAX_SCHEDULE_TIMEOUT); entity = drm_mock_sched_entity_new(test, DRM_SCHED_PRIORITY_NORMAL, sched);
job = drm_mock_sched_job_new(test, entity);
drm_mock_sched_job_submit(job);
done = drm_mock_sched_job_wait_scheduled(job, HZ); KUNIT_ASSERT_TRUE(test, done);
drm_mock_sched_entity_free(entity); drm_mock_sched_fini(sched);
KUNIT_ASSERT_EQ(test, job->hw_fence.error, -ECANCELED);}
struct sched_concurrent_context { struct drm_mock_scheduler *sched; struct workqueue_struct *sub_wq; struct kunit *test; struct completion wait_go;};
KUNIT_DEFINE_ACTION_WRAPPER(drm_mock_sched_fini_wrap, drm_mock_sched_fini, struct drm_mock_scheduler *);
KUNIT_DEFINE_ACTION_WRAPPER(drm_mock_sched_entity_free_wrap, drm_mock_sched_entity_free, struct drm_mock_sched_entity *);
static void complete_destroy_workqueue(void *context){ struct sched_concurrent_context *ctx = context;
complete_all(&ctx->wait_go);
destroy_workqueue(ctx->sub_wq);}
static int drm_sched_concurrent_init(struct kunit *test){ struct sched_concurrent_context *ctx; int ret;
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, ctx);
init_completion(&ctx->wait_go);
ctx->sched = drm_mock_sched_new(test, MAX_SCHEDULE_TIMEOUT);
ret = kunit_add_action_or_reset(test, drm_mock_sched_fini_wrap, ctx->sched); KUNIT_ASSERT_EQ(test, ret, 0);
/* Use an unbounded workqueue to maximize job submission concurrency */ ctx->sub_wq = alloc_workqueue("drm-sched-submitters-wq", WQ_UNBOUND, WQ_UNBOUND_MAX_ACTIVE); KUNIT_ASSERT_NOT_NULL(test, ctx->sub_wq);
ret = kunit_add_action_or_reset(test, complete_destroy_workqueue, ctx); KUNIT_ASSERT_EQ(test, ret, 0);
ctx->test = test; test->priv = ctx;
return 0;}
struct drm_sched_parallel_params { const char *description; unsigned int num_jobs; unsigned int num_workers;};
static const struct drm_sched_parallel_params drm_sched_parallel_cases[] = { { .description = "Parallel submission of multiple jobs per worker", .num_jobs = 8, .num_workers = 16, },};
static voiddrm_sched_parallel_desc(const struct drm_sched_parallel_params *params, char *desc){ strscpy(desc, params->description, KUNIT_PARAM_DESC_SIZE);}
KUNIT_ARRAY_PARAM(drm_sched_parallel, drm_sched_parallel_cases, drm_sched_parallel_desc);
struct parallel_worker { struct work_struct work; struct sched_concurrent_context *ctx; struct drm_mock_sched_entity *entity; struct drm_mock_sched_job **jobs; unsigned int id;};
static void drm_sched_parallel_worker(struct work_struct *work){ const struct drm_sched_parallel_params *params; struct sched_concurrent_context *test_ctx; struct parallel_worker *worker; unsigned int i;
worker = container_of(work, struct parallel_worker, work); test_ctx = worker->ctx; params = test_ctx->test->param_value;
wait_for_completion(&test_ctx->wait_go);
kunit_info(test_ctx->test, "Parallel worker %u submitting %u jobs started\n", worker->id, params->num_jobs);
for (i = 0; i < params->num_jobs; i++) drm_mock_sched_job_submit(worker->jobs[i]);}
/* * Spawns workers that submit a sequence of jobs to the mock scheduler. * Once all jobs are submitted, the timeline is manually advanced. */static void drm_sched_parallel_submit_test(struct kunit *test){ struct sched_concurrent_context *ctx = test->priv; const struct drm_sched_parallel_params *params = test->param_value; struct parallel_worker *workers, *worker; struct drm_mock_sched_job *job; unsigned int i, j, completed_jobs, total_jobs; bool done; int ret;
KUNIT_ASSERT_GT(test, params->num_workers, 0); KUNIT_ASSERT_GT(test, params->num_jobs, 0);
workers = kunit_kcalloc(test, params->num_workers, sizeof(*workers), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, workers);
/* * Init workers only after all jobs and entities have been successfully * allocated. In this way, the cleanup logic for when an assertion fail * can be simplified. */ for (i = 0; i < params->num_workers; i++) { worker = &workers[i]; worker->id = i; worker->ctx = ctx; worker->entity = drm_mock_sched_entity_new(test, DRM_SCHED_PRIORITY_NORMAL, ctx->sched);
worker->jobs = kunit_kcalloc(test, params->num_jobs, sizeof(*worker->jobs), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, worker->jobs);
for (j = 0; j < params->num_jobs; j++) { job = drm_mock_sched_job_new(test, worker->entity); worker->jobs[j] = job; }
ret = kunit_add_action_or_reset(test, drm_mock_sched_entity_free_wrap, worker->entity); KUNIT_ASSERT_EQ(test, ret, 0); }
for (i = 0; i < params->num_workers; i++) { worker = &workers[i]; INIT_WORK(&worker->work, drm_sched_parallel_worker); queue_work(ctx->sub_wq, &worker->work); }
complete_all(&ctx->wait_go); flush_workqueue(ctx->sub_wq);
for (i = 0; i < params->num_workers; i++) { worker = &workers[i]; for (j = 0; j < params->num_jobs; j++) { job = worker->jobs[j]; done = drm_mock_sched_job_wait_scheduled(job, HZ); KUNIT_EXPECT_TRUE(test, done); } }
total_jobs = params->num_workers * params->num_jobs; completed_jobs = drm_mock_sched_advance(ctx->sched, total_jobs); KUNIT_EXPECT_EQ(test, completed_jobs, total_jobs);
for (i = 0; i < params->num_workers; i++) { worker = &workers[i]; for (j = 0; j < params->num_jobs; j++) { job = worker->jobs[j]; done = drm_mock_sched_job_wait_finished(job, HZ); KUNIT_EXPECT_TRUE(test, done); } }}
struct drm_sched_interleaved_params { const char *description; unsigned int test_duration_ms; unsigned int job_base_duration_us; unsigned int num_workers; unsigned int num_in_flight_jobs;};
static const struct drm_sched_interleaved_params drm_sched_interleaved_cases[] = { { .description = "Interleaved submission of multiple jobs per worker", .test_duration_ms = 1000, .job_base_duration_us = 100, .num_workers = 16, .num_in_flight_jobs = 8, },};
static voiddrm_sched_interleaved_desc(const struct drm_sched_interleaved_params *params, char *desc){ strscpy(desc, params->description, KUNIT_PARAM_DESC_SIZE);}
KUNIT_ARRAY_PARAM(drm_sched_interleaved, drm_sched_interleaved_cases, drm_sched_interleaved_desc);
struct interleaved_worker { struct work_struct work; struct sched_concurrent_context *ctx; struct drm_mock_sched_entity *entity; struct drm_mock_sched_job **jobs; unsigned int id; unsigned int job_count; unsigned int job_duration_us;};
static void drm_sched_interleaved_worker(struct work_struct *work){ struct sched_concurrent_context *test_ctx; const struct drm_sched_interleaved_params *params; struct interleaved_worker *worker; unsigned int i, j, max_in_flight_job; unsigned long timeout; bool done;
worker = container_of(work, struct interleaved_worker, work); test_ctx = worker->ctx; params = test_ctx->test->param_value;
wait_for_completion(&test_ctx->wait_go);
kunit_info(test_ctx->test, "Worker %u submitting %u jobs of %u us started\n", worker->id, worker->job_count, worker->job_duration_us);
timeout = msecs_to_jiffies(params->test_duration_ms * 2);
/* Fill the submission window */ max_in_flight_job = min(worker->job_count, params->num_in_flight_jobs); for (i = 0; i < max_in_flight_job; i++) drm_mock_sched_job_submit(worker->jobs[i]);
/* Keep the window full by submitting a new job at once until done */ for (i = 0; i < worker->job_count; i++) { done = drm_mock_sched_job_wait_finished(worker->jobs[i], timeout); if (!done) kunit_info(test_ctx->test, "Job %u of worker %u timed out\n", i, worker->id);
j = i + max_in_flight_job; if (j < worker->job_count) drm_mock_sched_job_submit(worker->jobs[j]); }}
/* * Spawns workers that submit a sequence of jobs to the mock scheduler. Job * durations are chosen as multiples of a base duration value specified as * a test parameter. Since the scheduler serializes jobs from all workers, * the total test duration budget is divided into equal shares among workers. * These shares are then used to compute the number of jobs that each worker * can submit. */static void drm_sched_interleaved_submit_test(struct kunit *test){ const struct drm_sched_interleaved_params *params = test->param_value; struct sched_concurrent_context *ctx = test->priv; struct interleaved_worker *workers, *worker; struct drm_mock_sched_job *job; unsigned int worker_share_us; unsigned int i, j; bool done; int ret;
KUNIT_ASSERT_GT(test, params->num_workers, 0); KUNIT_ASSERT_GT(test, params->job_base_duration_us, 0);
workers = kunit_kcalloc(test, params->num_workers, sizeof(*workers), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, workers);
/* Divide the available test time into equal shares among the workers */ worker_share_us = (params->test_duration_ms * USEC_PER_MSEC) / params->num_workers;
/* * Init workers only after all jobs and entities have been successfully * allocated. In this way, the cleanup logic for when an assertion fails * can be simplified. */ for (i = 0; i < params->num_workers; i++) { worker = &workers[i]; worker->id = i; worker->ctx = ctx;
worker->job_duration_us = params->job_base_duration_us * (i + 1); worker->job_count = worker_share_us / worker->job_duration_us; worker->job_count = max(1U, worker->job_count);
worker->entity = drm_mock_sched_entity_new(test, DRM_SCHED_PRIORITY_NORMAL, ctx->sched);
worker->jobs = kunit_kcalloc(test, worker->job_count, sizeof(*worker->jobs), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, worker->jobs);
for (j = 0; j < worker->job_count; j++) { job = drm_mock_sched_job_new(test, worker->entity); drm_mock_sched_job_set_duration_us(job, worker->job_duration_us);
worker->jobs[j] = job; }
ret = kunit_add_action_or_reset(test, drm_mock_sched_entity_free_wrap, worker->entity); KUNIT_ASSERT_EQ(test, ret, 0); }
for (i = 0; i < params->num_workers; i++) { worker = &workers[i]; INIT_WORK(&worker->work, drm_sched_interleaved_worker); queue_work(ctx->sub_wq, &worker->work); }
complete_all(&ctx->wait_go); flush_workqueue(ctx->sub_wq);
for (i = 0; i < params->num_workers; i++) { worker = &workers[i]; for (j = 0; j < worker->job_count; j++) { job = worker->jobs[j]; done = drm_mock_sched_job_is_finished(job); KUNIT_EXPECT_TRUE(test, done); } }}
static struct kunit_case drm_sched_concurrent_tests[] = { KUNIT_CASE_PARAM(drm_sched_parallel_submit_test, drm_sched_parallel_gen_params), KUNIT_CASE_PARAM(drm_sched_interleaved_submit_test, drm_sched_interleaved_gen_params), {}};
static struct kunit_suite drm_sched_concurrent = { .name = "drm_sched_concurrent_tests", .init = drm_sched_concurrent_init, .test_cases = drm_sched_concurrent_tests, .attr = { .speed = KUNIT_SPEED_SLOW, },};
static struct kunit_case drm_sched_cancel_tests[] = { KUNIT_CASE(drm_sched_basic_cancel), {}};
static struct kunit_suite drm_sched_cancel = { .name = "drm_sched_basic_cancel_tests", .init = drm_sched_basic_init, .exit = drm_sched_basic_exit, .test_cases = drm_sched_cancel_tests,};
static void drm_sched_basic_timeout(struct kunit *test){ struct drm_mock_scheduler *sched = test->priv; struct drm_mock_sched_entity *entity; struct drm_mock_sched_job *job; bool done;
/* * Submit a single job against a scheduler with the timeout configured * and verify that the timeout handling will run if the backend fails * to complete it in time. */
entity = drm_mock_sched_entity_new(test, DRM_SCHED_PRIORITY_NORMAL, sched); job = drm_mock_sched_job_new(test, entity);
drm_mock_sched_job_submit(job);
done = drm_mock_sched_job_wait_scheduled(job, HZ); KUNIT_ASSERT_TRUE(test, done);
done = drm_mock_sched_job_wait_finished(job, MOCK_TIMEOUT / 2); KUNIT_ASSERT_FALSE(test, done);
KUNIT_ASSERT_EQ(test, job->flags & DRM_MOCK_SCHED_JOB_TIMEDOUT, 0);
done = drm_mock_sched_job_wait_finished(job, MOCK_TIMEOUT); KUNIT_ASSERT_FALSE(test, done);
KUNIT_ASSERT_EQ(test, job->flags & DRM_MOCK_SCHED_JOB_TIMEDOUT, DRM_MOCK_SCHED_JOB_TIMEDOUT);
drm_mock_sched_entity_free(entity);}
static void drm_sched_skip_reset(struct kunit *test){ struct drm_mock_scheduler *sched = test->priv; struct drm_mock_sched_entity *entity; struct drm_mock_sched_job *job; unsigned int i; bool done;
/* * Submit a single job against a scheduler with the timeout configured * and verify that if the job is still running, the timeout handler * will skip the reset and allow the job to complete. */
entity = drm_mock_sched_entity_new(test, DRM_SCHED_PRIORITY_NORMAL, sched); job = drm_mock_sched_job_new(test, entity);
job->flags = DRM_MOCK_SCHED_JOB_DONT_RESET;
drm_mock_sched_job_submit(job);
done = drm_mock_sched_job_wait_scheduled(job, HZ); KUNIT_ASSERT_TRUE(test, done);
done = drm_mock_sched_job_wait_finished(job, 2 * MOCK_TIMEOUT); KUNIT_ASSERT_FALSE(test, done);
KUNIT_ASSERT_EQ(test, job->flags & DRM_MOCK_SCHED_JOB_RESET_SKIPPED, DRM_MOCK_SCHED_JOB_RESET_SKIPPED);
i = drm_mock_sched_advance(sched, 1); KUNIT_ASSERT_EQ(test, i, 1);
done = drm_mock_sched_job_wait_finished(job, HZ); KUNIT_ASSERT_TRUE(test, done);
drm_mock_sched_entity_free(entity);}
static struct kunit_case drm_sched_timeout_tests[] = { KUNIT_CASE(drm_sched_basic_timeout), KUNIT_CASE(drm_sched_skip_reset), {}};
static struct kunit_suite drm_sched_timeout = { .name = "drm_sched_basic_timeout_tests", .init = drm_sched_timeout_init, .exit = drm_sched_basic_exit, .test_cases = drm_sched_timeout_tests,};
static void drm_sched_priorities(struct kunit *test){ struct drm_mock_sched_entity *entity[DRM_SCHED_PRIORITY_COUNT]; struct drm_mock_scheduler *sched = test->priv; struct drm_mock_sched_job *job; const unsigned int qd = 100; unsigned int i, cur_ent = 0; enum drm_sched_priority p; bool done;
/* * Submit a bunch of jobs against entities configured with different * priorities. */
BUILD_BUG_ON(DRM_SCHED_PRIORITY_KERNEL > DRM_SCHED_PRIORITY_LOW); BUILD_BUG_ON(ARRAY_SIZE(entity) != DRM_SCHED_PRIORITY_COUNT);
for (p = DRM_SCHED_PRIORITY_KERNEL; p <= DRM_SCHED_PRIORITY_LOW; p++) entity[p] = drm_mock_sched_entity_new(test, p, sched);
for (i = 0; i < qd; i++) { job = drm_mock_sched_job_new(test, entity[cur_ent++]); cur_ent %= ARRAY_SIZE(entity); drm_mock_sched_job_set_duration_us(job, 1000); drm_mock_sched_job_submit(job); }
done = drm_mock_sched_job_wait_finished(job, HZ); KUNIT_ASSERT_TRUE(test, done);
for (i = 0; i < ARRAY_SIZE(entity); i++) drm_mock_sched_entity_free(entity[i]);}
static void drm_sched_change_priority(struct kunit *test){ struct drm_mock_sched_entity *entity[DRM_SCHED_PRIORITY_COUNT]; struct drm_mock_scheduler *sched = test->priv; struct drm_mock_sched_job *job; const unsigned int qd = 1000; unsigned int i, cur_ent = 0; enum drm_sched_priority p;
/* * Submit a bunch of jobs against entities configured with different * priorities and while waiting for them to complete, periodically keep * changing their priorities. * * We set up the queue-depth (qd) and job duration so the priority * changing loop has some time to interact with submissions to the * backend and job completions as they progress. */
for (p = DRM_SCHED_PRIORITY_KERNEL; p <= DRM_SCHED_PRIORITY_LOW; p++) entity[p] = drm_mock_sched_entity_new(test, p, sched);
for (i = 0; i < qd; i++) { job = drm_mock_sched_job_new(test, entity[cur_ent++]); cur_ent %= ARRAY_SIZE(entity); drm_mock_sched_job_set_duration_us(job, 1000); drm_mock_sched_job_submit(job); }
do { drm_sched_entity_set_priority(&entity[cur_ent]->base, (entity[cur_ent]->base.priority + 1) % DRM_SCHED_PRIORITY_COUNT); cur_ent++; cur_ent %= ARRAY_SIZE(entity); usleep_range(200, 500); } while (!drm_mock_sched_job_is_finished(job));
for (i = 0; i < ARRAY_SIZE(entity); i++) drm_mock_sched_entity_free(entity[i]);}
static struct kunit_case drm_sched_priority_tests[] = { KUNIT_CASE(drm_sched_priorities), KUNIT_CASE_SLOW(drm_sched_change_priority), {}};
static struct kunit_suite drm_sched_priority = { .name = "drm_sched_basic_priority_tests", .init = drm_sched_basic_init, .exit = drm_sched_basic_exit, .test_cases = drm_sched_priority_tests,};
static void drm_sched_test_modify_sched(struct kunit *test){ unsigned int i, cur_ent = 0, cur_sched = 0; struct drm_mock_sched_entity *entity[13]; struct drm_mock_scheduler *sched[3]; struct drm_mock_sched_job *job; const unsigned int qd = 1000;
/* * Submit a bunch of jobs against entities configured with different * schedulers and while waiting for them to complete, periodically keep * changing schedulers associated with each entity. * * We set up the queue-depth (qd) and job duration so the sched modify * loop has some time to interact with submissions to the backend and * job completions as they progress. * * For the number of schedulers and entities we use primes in order to * perturb the entity->sched assignments with less of a regular pattern. */
for (i = 0; i < ARRAY_SIZE(sched); i++) sched[i] = drm_mock_sched_new(test, MAX_SCHEDULE_TIMEOUT);
for (i = 0; i < ARRAY_SIZE(entity); i++) entity[i] = drm_mock_sched_entity_new(test, DRM_SCHED_PRIORITY_NORMAL, sched[i % ARRAY_SIZE(sched)]);
for (i = 0; i < qd; i++) { job = drm_mock_sched_job_new(test, entity[cur_ent++]); cur_ent %= ARRAY_SIZE(entity); drm_mock_sched_job_set_duration_us(job, 1000); drm_mock_sched_job_submit(job); }
do { struct drm_gpu_scheduler *modify;
usleep_range(200, 500); cur_ent++; cur_ent %= ARRAY_SIZE(entity); cur_sched++; cur_sched %= ARRAY_SIZE(sched); modify = &sched[cur_sched]->base; drm_sched_entity_modify_sched(&entity[cur_ent]->base, &modify, 1); } while (!drm_mock_sched_job_is_finished(job));
for (i = 0; i < ARRAY_SIZE(entity); i++) drm_mock_sched_entity_free(entity[i]);
for (i = 0; i < ARRAY_SIZE(sched); i++) drm_mock_sched_fini(sched[i]);}
static struct kunit_case drm_sched_modify_sched_tests[] = { KUNIT_CASE(drm_sched_test_modify_sched), {}};
static struct kunit_suite drm_sched_modify_sched = { .name = "drm_sched_basic_modify_sched_tests", .test_cases = drm_sched_modify_sched_tests,};
static void drm_sched_test_credits(struct kunit *test){ struct drm_mock_sched_entity *entity; struct drm_mock_scheduler *sched; struct drm_mock_sched_job *job[2]; bool done; int i;
/* * Check that the configured credit limit is respected. */
sched = drm_mock_sched_new(test, MAX_SCHEDULE_TIMEOUT); sched->base.credit_limit = 1;
entity = drm_mock_sched_entity_new(test, DRM_SCHED_PRIORITY_NORMAL, sched);
job[0] = drm_mock_sched_job_new(test, entity); job[1] = drm_mock_sched_job_new(test, entity);
drm_mock_sched_job_submit(job[0]); drm_mock_sched_job_submit(job[1]);
done = drm_mock_sched_job_wait_scheduled(job[0], HZ); KUNIT_ASSERT_TRUE(test, done);
done = drm_mock_sched_job_wait_scheduled(job[1], HZ); KUNIT_ASSERT_FALSE(test, done);
i = drm_mock_sched_advance(sched, 1); KUNIT_ASSERT_EQ(test, i, 1);
done = drm_mock_sched_job_wait_scheduled(job[1], HZ); KUNIT_ASSERT_TRUE(test, done);
i = drm_mock_sched_advance(sched, 1); KUNIT_ASSERT_EQ(test, i, 1);
done = drm_mock_sched_job_wait_finished(job[1], HZ); KUNIT_ASSERT_TRUE(test, done);
drm_mock_sched_entity_free(entity); drm_mock_sched_fini(sched);}
static struct kunit_case drm_sched_credits_tests[] = { KUNIT_CASE_SLOW(drm_sched_test_credits), {}};
static struct kunit_suite drm_sched_credits = { .name = "drm_sched_basic_credits_tests", .test_cases = drm_sched_credits_tests,};
kunit_test_suites(&drm_sched_basic, &drm_sched_concurrent, &drm_sched_timeout, &drm_sched_cancel, &drm_sched_priority, &drm_sched_modify_sched, &drm_sched_credits);