[<prev] [next>] [day] [month] [year] [list]
Message-ID: <20260120205236.322086-1-marpagan@redhat.com>
Date: Tue, 20 Jan 2026 21:52:34 +0100
From: Marco Pagani <marpagan@...hat.com>
To: Matthew Brost <matthew.brost@...el.com>,
Danilo Krummrich <dakr@...nel.org>,
Philipp Stanner <phasta@...nel.org>,
Christian König <ckoenig.leichtzumerken@...il.com>,
Maarten Lankhorst <maarten.lankhorst@...ux.intel.com>,
Maxime Ripard <mripard@...nel.org>,
Thomas Zimmermann <tzimmermann@...e.de>,
David Airlie <airlied@...il.com>,
Simona Vetter <simona@...ll.ch>
Cc: Marco Pagani <marpagan@...hat.com>,
dri-devel@...ts.freedesktop.org,
linux-kernel@...r.kernel.org
Subject: [RFC PATCH] drm/sched: Add new KUnit test suite for concurrent job submission
Add a new test suite to simulate concurrent job submissions from userspace.
The suite includes a basic test case where each worker submits a single
job, and a more advanced case involving the submission of multiple jobs.
Signed-off-by: Marco Pagani <marpagan@...hat.com>
---
drivers/gpu/drm/scheduler/tests/tests_basic.c | 175 ++++++++++++++++++
1 file changed, 175 insertions(+)
diff --git a/drivers/gpu/drm/scheduler/tests/tests_basic.c b/drivers/gpu/drm/scheduler/tests/tests_basic.c
index 82a41a456b0a..7c25bcbbe7c9 100644
--- a/drivers/gpu/drm/scheduler/tests/tests_basic.c
+++ b/drivers/gpu/drm/scheduler/tests/tests_basic.c
@@ -2,6 +2,7 @@
/* Copyright (c) 2025 Valve Corporation */
#include <linux/delay.h>
+#include <linux/completion.h>
#include "sched_tests.h"
@@ -235,6 +236,179 @@ static void drm_sched_basic_cancel(struct kunit *test)
KUNIT_ASSERT_EQ(test, job->hw_fence.error, -ECANCELED);
}
+struct sched_concurrent_test_context {
+ struct drm_mock_scheduler *sched;
+ struct workqueue_struct *sub_wq;
+ struct completion wait_go;
+};
+
+KUNIT_DEFINE_ACTION_WRAPPER(destroy_workqueue_wrap, destroy_workqueue,
+ struct workqueue_struct *);
+
+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 int drm_sched_concurrent_init(struct kunit *test)
+{
+ struct sched_concurrent_test_context *ctx;
+ int ret;
+
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+
+ 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, destroy_workqueue_wrap, ctx->sub_wq);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ test->priv = ctx;
+
+ return 0;
+}
+
+struct drm_sched_concurrent_params {
+ const char *description;
+ unsigned int job_base_us;
+ unsigned int num_jobs;
+ unsigned int num_subs;
+};
+
+static const struct drm_sched_concurrent_params drm_sched_concurrent_cases[] = {
+ {
+ .description = "Concurrently submit a single job in a single entity",
+ .job_base_us = 1000,
+ .num_jobs = 1,
+ .num_subs = 32,
+ },
+ {
+ .description = "Concurrently submit multiple jobs in a single entity",
+ .job_base_us = 1000,
+ .num_jobs = 10,
+ .num_subs = 64,
+ },
+};
+
+static void
+drm_sched_concurrent_desc(const struct drm_sched_concurrent_params *params, char *desc)
+{
+ strscpy(desc, params->description, KUNIT_PARAM_DESC_SIZE);
+}
+
+KUNIT_ARRAY_PARAM(drm_sched_concurrent, drm_sched_concurrent_cases, drm_sched_concurrent_desc);
+
+struct submitter_data {
+ struct work_struct work;
+ struct sched_concurrent_test_context *ctx;
+ struct drm_mock_sched_entity *entity;
+ struct drm_mock_sched_job **jobs;
+ struct kunit *test;
+ unsigned int id;
+ bool timedout;
+};
+
+static void drm_sched_submitter_worker(struct work_struct *work)
+{
+ const struct drm_sched_concurrent_params *params;
+ struct sched_concurrent_test_context *ctx;
+ struct submitter_data *sub_data;
+ unsigned int i, duration_us;
+ unsigned long timeout_jiffies;
+ bool done;
+
+ sub_data = container_of(work, struct submitter_data, work);
+ ctx = sub_data->ctx;
+ params = sub_data->test->param_value;
+
+ wait_for_completion(&ctx->wait_go);
+
+ for (i = 0; i < params->num_jobs; i++) {
+ duration_us = params->job_base_us + (sub_data->id * 10);
+ drm_mock_sched_job_set_duration_us(sub_data->jobs[i], duration_us);
+ drm_mock_sched_job_submit(sub_data->jobs[i]);
+ }
+
+ timeout_jiffies = usecs_to_jiffies(params->job_base_us * params->num_subs *
+ params->num_jobs * 10);
+ for (i = 0; i < params->num_jobs; i++) {
+ done = drm_mock_sched_job_wait_finished(sub_data->jobs[i],
+ timeout_jiffies);
+ if (!done)
+ sub_data->timedout = true;
+ }
+}
+
+static void drm_sched_concurrent_submit_test(struct kunit *test)
+{
+ struct sched_concurrent_test_context *ctx = test->priv;
+ const struct drm_sched_concurrent_params *params = test->param_value;
+ struct submitter_data *subs_data;
+ unsigned int i, j;
+ int ret;
+
+ subs_data = kunit_kcalloc(test, params->num_subs, sizeof(*subs_data),
+ GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, subs_data);
+
+ /*
+ * Pre-allocate entities and jobs in the main thread to avoid KUnit
+ * assertions in submitters threads
+ */
+ for (i = 0; i < params->num_subs; i++) {
+ subs_data[i].id = i;
+ subs_data[i].ctx = ctx;
+ subs_data[i].test = test;
+ subs_data[i].timedout = false;
+ subs_data[i].entity = drm_mock_sched_entity_new(test,
+ DRM_SCHED_PRIORITY_NORMAL,
+ ctx->sched);
+
+ ret = kunit_add_action_or_reset(test, drm_mock_sched_entity_free_wrap,
+ subs_data[i].entity);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ subs_data[i].jobs = kunit_kcalloc(test, params->num_jobs,
+ sizeof(*subs_data[i].jobs), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, subs_data[i].jobs);
+
+ for (j = 0; j < params->num_jobs; j++)
+ subs_data[i].jobs[j] = drm_mock_sched_job_new(test,
+ subs_data[i].entity);
+
+ INIT_WORK(&subs_data[i].work, drm_sched_submitter_worker);
+ queue_work(ctx->sub_wq, &subs_data[i].work);
+ }
+
+ complete_all(&ctx->wait_go);
+ flush_workqueue(ctx->sub_wq);
+
+ for (i = 0; i < params->num_subs; i++)
+ KUNIT_ASSERT_FALSE_MSG(test, subs_data[i].timedout,
+ "Job submitter worker %u timedout", i);
+}
+
+static struct kunit_case drm_sched_concurrent_tests[] = {
+ KUNIT_CASE_PARAM(drm_sched_concurrent_submit_test, drm_sched_concurrent_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,
+};
+
static struct kunit_case drm_sched_cancel_tests[] = {
KUNIT_CASE(drm_sched_basic_cancel),
{}
@@ -556,6 +730,7 @@ static struct kunit_suite drm_sched_credits = {
};
kunit_test_suites(&drm_sched_basic,
+ &drm_sched_concurrent,
&drm_sched_timeout,
&drm_sched_cancel,
&drm_sched_priority,
--
2.52.0
Powered by blists - more mailing lists