Linux Media Controller development
 help / color / mirror / Atom feed
From: Philipp Stanner <phasta@kernel.org>
To: "Sumit Semwal" <sumit.semwal@linaro.org>,
	"Christian König" <christian.koenig@amd.com>,
	"Philipp Stanner" <phasta@kernel.org>,
	"Danilo Krummrich" <dakr@kernel.org>,
	"Alice Ryhl" <aliceryhl@google.com>,
	"Miguel Ojeda" <ojeda@kernel.org>,
	"Boqun Feng" <boqun@kernel.org>, "Gary Guo" <gary@garyguo.net>,
	"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
	"Benno Lossin" <lossin@kernel.org>,
	"Andreas Hindborg" <a.hindborg@kernel.org>,
	"Trevor Gross" <tmgross@umich.edu>,
	"Daniel Almeida" <daniel.almeida@collabora.com>,
	"Tamir Duberstein" <tamird@kernel.org>,
	"Alexandre Courbot" <acourbot@nvidia.com>,
	"Onur Özkan" <work@onurozkan.dev>,
	"David Airlie" <airlied@gmail.com>,
	"Simona Vetter" <simona@ffwll.ch>,
	"Tvrtko Ursulin" <tvrtko.ursulin@igalia.com>
Cc: linux-kernel@vger.kernel.org, linux-media@vger.kernel.org,
	dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org
Subject: [RFC PATCH v7 8/8] samples: rust: Add JobQueue Tester
Date: Tue,  3 Nov 2026 16:31:38 +0100	[thread overview]
Message-ID: <20261103153138.1087330-10-phasta@kernel.org> (raw)
In-Reply-To: <20261103153138.1087330-2-phasta@kernel.org>

From: Gary Guo <gary@garyguo.net>

Some test code for the JobQueue
---
 samples/rust/Kconfig                 |  11 ++
 samples/rust/Makefile                |   1 +
 samples/rust/rust_jobqueue_tester.rs | 242 +++++++++++++++++++++++++++
 3 files changed, 254 insertions(+)
 create mode 100644 samples/rust/rust_jobqueue_tester.rs

diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig
index 31d62533ef25..a5754fe03fd5 100644
--- a/samples/rust/Kconfig
+++ b/samples/rust/Kconfig
@@ -190,4 +190,15 @@ config SAMPLE_RUST_HOSTPROGS
 
 	  If unsure, say N.
 
+config SAMPLE_RUST_JOBQUEUE_TESTER
+	tristate "Jobqueue Tester"
+	select JOBQUEUE_TESTER
+	help
+	  This option builds the Rust Jobqueue Tester.
+
+	  To compile this as a module, choose M here:
+	  the module will be called rust_jobqueue_tester.
+
+	  If unsure, say N.
+
 endif # SAMPLES_RUST
diff --git a/samples/rust/Makefile b/samples/rust/Makefile
index b986b681cde5..69dd0b8d2597 100644
--- a/samples/rust/Makefile
+++ b/samples/rust/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_SAMPLE_RUST_DRIVER_AUXILIARY)	+= rust_driver_auxiliary.o
 obj-$(CONFIG_SAMPLE_RUST_DRIVER_SERDEV)		+= rust_driver_serdev.o
 obj-$(CONFIG_SAMPLE_RUST_CONFIGFS)		+= rust_configfs.o
 obj-$(CONFIG_SAMPLE_RUST_SOC)			+= rust_soc.o
+obj-$(CONFIG_SAMPLE_RUST_JOBQUEUE_TESTER)	+= rust_jobqueue_tester.o
 
 rust_print-y := rust_print_main.o rust_print_events.o
 
diff --git a/samples/rust/rust_jobqueue_tester.rs b/samples/rust/rust_jobqueue_tester.rs
new file mode 100644
index 000000000000..48dd1188f7db
--- /dev/null
+++ b/samples/rust/rust_jobqueue_tester.rs
@@ -0,0 +1,242 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Small example demonstrating how to use [`drm::JobQueue`].
+
+use kernel::dma_buf::{FenceContext, FenceContextOps, Fence, FenceStatus};
+use kernel::drm::{JobQueue, JobQueueOps, JobRunnable};
+use kernel::prelude::*;
+use kernel::str::CString;
+use kernel::task::might_sleep;
+use kernel::time::{delay::fsleep, Delta};
+
+module! {
+    type: RustJobqueueTester,
+    name: "rust_jobqueue_tester",
+    authors: ["Philipp Stanner"],
+    description: "Rust minimal sample",
+    license: "GPL",
+}
+
+// -- FenceContext data for external dependency fences -------------------------
+
+#[pin_data]
+struct DepFenceCtxData {}
+
+impl DepFenceCtxData {
+    fn new() -> impl PinInit<Self> {
+        pin_init!(Self {})
+    }
+}
+
+impl FenceContextOps for DepFenceCtxData {
+    type FenceDataType = ();
+}
+
+// -- JobQueue data and ops ----------------------------------------------------
+
+#[pin_data]
+struct JqData {}
+
+impl JqData {
+    fn new() -> impl PinInit<Self> {
+        pin_init!(Self {})
+    }
+}
+
+struct JobPayload {}
+
+impl Drop for JobPayload {
+    fn drop(&mut self) {
+        might_sleep();
+    }
+}
+
+impl JobQueueOps for JqData {
+    type JobDataType = JobPayload;
+
+    fn run_job<'a>(&self, job: JobRunnable<'a, JobPayload>) {
+        // Simulate GPU work completing after a short delay. In a real driver
+        // this would submit work to the hardware ring.
+        pr_info!("run_job: seqno {}\n", job.seqno);
+    }
+
+    fn kill(&self, _jq_nr: u64, _err: Error) {
+
+    }
+}
+
+fn is_signaled(fence: &Fence) -> bool {
+    match fence.status() {
+        FenceStatus::Unsignaled => false,
+        FenceStatus::Signaled(_) => true,
+    }
+}
+
+struct RustJobqueueTester {}
+
+impl kernel::Module for RustJobqueueTester {
+    fn init(_module: &'static ThisModule) -> Result<Self> {
+        pr_info!("Rust Jobqueue Tester (init)\n");
+        pr_info!("Am I built-in? {}\n", !cfg!(MODULE));
+
+        // A standalone FenceContext for creating external dependency fences.
+        let dep_fctx = KBox::pin_init(
+            FenceContext::new(
+                0,
+                c"rust-jq-tester",
+                c"dep-timeline",
+                DepFenceCtxData::new(),
+            ),
+            GFP_KERNEL,
+        )?;
+
+        let driver_name = CString::try_from_fmt(fmt!("rust-jobqueue-tester"))?;
+        let timeline_name = CString::try_from_fmt(fmt!("rust-jq-tester-hw-timeline"))?;
+        let jq = KBox::pin_init(
+            JobQueue::new(0, driver_name, timeline_name, 1_000_000, JqData::new()),
+            GFP_KERNEL,
+        )?;
+
+        pr_info!("Test 1: Test submitting two jobs without dependencies.\n");
+        let job1 = jq.new_job(1, JobPayload {})?;
+        let job2 = jq.new_job(1, JobPayload {})?;
+
+        let fence1 = job1.submit();
+        let fence2 = job2.submit();
+
+        // Complete both jobs by seqno so their fences get signaled.
+        jq.complete_jobs_up_to_seqno(fence2.seqno());
+
+        // Give the submit worker a chance to run.
+        fsleep(Delta::from_millis(100));
+
+        if !is_signaled(&fence1) || !is_signaled(&fence2) {
+            pr_info!("Test 1 failed.\n");
+            return Err(EAGAIN);
+        }
+        pr_info!("Test 1 succeeded.\n");
+
+        pr_info!("Test 2: Test submitting a job with already-fulfilled dependency.\n");
+        let mut job3 = jq.new_job(1, JobPayload {})?;
+        job3.add_dependency(&fence1)?;
+
+        let fence3 = job3.submit();
+        jq.complete_jobs_up_to_seqno(fence3.seqno());
+
+        // Give the submit worker a chance to run.
+        fsleep(Delta::from_millis(100));
+
+        if !is_signaled(&fence3) {
+            pr_info!("Test 2 failed.\n");
+            return Err(EAGAIN);
+        }
+        pr_info!("Test 2 succeeded.\n");
+
+        pr_info!("Test 3: Test that a job with unfulfilled dependency does not get run.\n");
+        let unsignaled_alloc = dep_fctx.new_fence_allocation(())?;
+        let unsignaled_fence = unsignaled_alloc.new_fence();
+
+        let mut job4 = jq.new_job(1, JobPayload {})?;
+        job4.add_dependency(unsignaled_fence.as_fence())?;
+
+        let blocked_job_fence = job4.submit();
+
+        // Give the submit worker a chance to run (it should NOT run job4).
+        fsleep(Delta::from_millis(100));
+
+        if is_signaled(&blocked_job_fence) {
+            pr_info!("Test 3 failed.\n");
+            return Err(EAGAIN);
+        }
+        pr_info!("Test 3 succeeded.\n");
+
+        pr_info!("Test 4: Test whether Test 3's blocked job can be unblocked.\n");
+        unsignaled_fence.signal(Ok(()));
+
+        // Give the submit worker a chance to run the now-unblocked job.
+        fsleep(Delta::from_millis(500));
+
+        // Complete it.
+        jq.complete_jobs_up_to_seqno(blocked_job_fence.seqno());
+        fsleep(Delta::from_millis(100));
+
+        if !is_signaled(&blocked_job_fence) {
+            pr_info!("Test 4 failed.\n");
+            return Err(EAGAIN);
+        }
+        pr_info!("Test 4 succeeded.\n");
+
+        pr_info!("Test 5: Submit unblocked jobs, then a blocked one, then unblocked ones.\n");
+        let job1 = jq.new_job(1, JobPayload {})?;
+        let job2 = jq.new_job(1, JobPayload {})?;
+        let mut job3 = jq.new_job(1, JobPayload {})?;
+        let job4 = jq.new_job(1, JobPayload {})?;
+        let job5 = jq.new_job(1, JobPayload {})?;
+
+        let unsignaled_alloc1 = dep_fctx.new_fence_allocation(())?;
+        let unsignaled_fence1 = unsignaled_alloc1.new_fence();
+        let unsignaled_alloc2 = dep_fctx.new_fence_allocation(())?;
+        let unsignaled_fence2 = unsignaled_alloc2.new_fence();
+        let unsignaled_alloc3 = dep_fctx.new_fence_allocation(())?;
+        let unsignaled_fence3 = unsignaled_alloc3.new_fence();
+
+        job3.add_dependency(unsignaled_fence1.as_fence())?;
+        job3.add_dependency(unsignaled_fence2.as_fence())?;
+        job3.add_dependency(unsignaled_fence3.as_fence())?;
+
+        let fence1 = job1.submit();
+        let fence2 = job2.submit();
+        let fence3 = job3.submit();
+
+        // Complete unblocked jobs 1 and 2.
+        jq.complete_jobs_up_to_seqno(fence2.seqno());
+        fsleep(Delta::from_millis(100));
+
+        if is_signaled(&fence3) || !is_signaled(&fence1) || !is_signaled(&fence2) {
+            pr_info!("Test 5 failed (phase 1).\n");
+            return Err(EAGAIN);
+        }
+
+        // Signal two of three dependencies — job3 should still be blocked.
+        unsignaled_fence1.signal(Ok(()));
+        unsignaled_fence3.signal(Ok(()));
+        fsleep(Delta::from_millis(200));
+
+        if is_signaled(&fence3) {
+            pr_info!("Test 5 failed (phase 2).\n");
+            return Err(EAGAIN);
+        }
+
+        // Signal the last dependency — job3 should now be runnable.
+        unsignaled_fence2.signal(Ok(()));
+        fsleep(Delta::from_millis(500));
+
+        // Complete job3 and the remaining jobs.
+        jq.complete_jobs_up_to_seqno(fence3.seqno());
+        fsleep(Delta::from_millis(100));
+
+        if !is_signaled(&fence3) {
+            pr_info!("Test 5 failed (phase 3).\n");
+            return Err(EAGAIN);
+        }
+
+        let fence4 = job4.submit();
+        let fence5 = job5.submit();
+        jq.complete_jobs_up_to_seqno(fence5.seqno());
+        fsleep(Delta::from_millis(100));
+
+        if !is_signaled(&fence4) || !is_signaled(&fence5) {
+            pr_info!("Test 5 failed (phase 4).\n");
+            return Err(EAGAIN);
+        }
+        pr_info!("Test 5 succeeded.\n");
+
+        Ok(RustJobqueueTester {})
+    }
+}
+
+impl Drop for RustJobqueueTester {
+    fn drop(&mut self) {
+        pr_info!("Rust Jobqueue Tester (exit)\n");
+    }
+}
-- 
2.56.0


  parent reply	other threads:[~2026-11-03 15:33 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-03 15:31 [RFC PATCH v7 0/8] rust: Add drm::JobQueue Philipp Stanner
2026-11-03 15:31 ` [RFC PATCH v7 1/8] rust: DmaFence: Implement Deref for FenceContext Philipp Stanner
2026-11-03 15:31 ` [RFC PATCH v7 2/8] rust: DmaFence: Add ID accessor for fctx Philipp Stanner
2026-11-03 15:31 ` [RFC PATCH v7 3/8] rust: DmaFence: Don't drop on signal Philipp Stanner
2026-11-03 15:31 ` [RFC PATCH v7 4/8] rust: DmaFence: Replace call_rcu() with synchronize_rcu() Philipp Stanner
2026-11-03 15:31 ` [RFC PATCH v7 5/8] rust: DmaFence: Replace is_signaled() with status() Philipp Stanner
2026-11-03 15:31 ` [RFC PATCH v7 6/8] rust: DmaFence: Tell results in signaling callbacks Philipp Stanner
2026-11-03 15:31 ` [RFC PATCH v7 7/8] rust: drm: Add JobQueue Philipp Stanner
2026-11-03 15:31 ` Philipp Stanner [this message]
2026-11-03 22:27   ` [RFC PATCH v7 8/8] samples: rust: Add JobQueue Tester Younes Akhouayri
2026-11-04  8:41     ` Philipp Stanner

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261103153138.1087330-10-phasta@kernel.org \
    --to=phasta@kernel.org \
    --cc=a.hindborg@kernel.org \
    --cc=acourbot@nvidia.com \
    --cc=airlied@gmail.com \
    --cc=aliceryhl@google.com \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun@kernel.org \
    --cc=christian.koenig@amd.com \
    --cc=dakr@kernel.org \
    --cc=daniel.almeida@collabora.com \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=gary@garyguo.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-media@vger.kernel.org \
    --cc=lossin@kernel.org \
    --cc=ojeda@kernel.org \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=simona@ffwll.ch \
    --cc=sumit.semwal@linaro.org \
    --cc=tamird@kernel.org \
    --cc=tmgross@umich.edu \
    --cc=tvrtko.ursulin@igalia.com \
    --cc=work@onurozkan.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox