All of lore.kernel.org
 help / color / mirror / Atom feed
From: Maarten Lankhorst <dev@lankhorst.se>
To: intel-gfx@lists.freedesktop.org, intel-xe@lists.freedesktop.org
Cc: linux-rt-devel@lists.linux.dev, dri-devel@lists.freedesktop.org,
	Maarten Lankhorst <dev@lankhorst.se>
Subject: [i915-rt v6.1 1/1] drm/vblank_work: Add methods to schedule vblank_work in 2 stages
Date: Fri, 20 Feb 2026 13:24:25 +0100	[thread overview]
Message-ID: <20260220122425.97902-2-dev@lankhorst.se> (raw)
In-Reply-To: <20260220083657.28815-27-dev@lankhorst.se>

In case of vblank evasion in intel/display, it's necessary to
perform some work in advance, so the critical section will always run in
constant time on PREEMPT_RT.

By preparing all the work in advance, the part that needs to finish in
constant time only has to write a single variable instead. This allows
PREEMPT_RT to keep the interrupts disabled at the most critical part,
without completely reworking all locks to be raw spinlocks.

Signed-off-by: Maarten Lankhorst <dev@lankhorst.se>
---
I had a small bug where I set armed = true for drm_vblank_work_schedule_disabled(),
fortunately found by CI. Fix it to ensure work is delayed correctly.

 drivers/gpu/drm/drm_vblank_work.c | 106 ++++++++++++++++++++++--------
 include/drm/drm_vblank_work.h     |  12 ++++
 2 files changed, 92 insertions(+), 26 deletions(-)

diff --git a/drivers/gpu/drm/drm_vblank_work.c b/drivers/gpu/drm/drm_vblank_work.c
index 70f0199251ea0..6acb240b9c112 100644
--- a/drivers/gpu/drm/drm_vblank_work.c
+++ b/drivers/gpu/drm/drm_vblank_work.c
@@ -54,7 +54,8 @@ void drm_handle_vblank_works(struct drm_vblank_crtc *vblank)
 	assert_spin_locked(&vblank->dev->event_lock);
 
 	list_for_each_entry_safe(work, next, &vblank->pending_work, node) {
-		if (!drm_vblank_passed(count, work->count))
+		/* READ_ONCE pairs with WRITE_ONCE in drm_vblank_work_enable() */
+		if (!READ_ONCE(work->armed) || !drm_vblank_passed(count, work->count))
 			continue;
 
 		list_del_init(&work->node);
@@ -86,30 +87,8 @@ void drm_vblank_cancel_pending_works(struct drm_vblank_crtc *vblank)
 	wake_up_all(&vblank->work_wait_queue);
 }
 
-/**
- * drm_vblank_work_schedule - schedule a vblank work
- * @work: vblank work to schedule
- * @count: target vblank count
- * @nextonmiss: defer until the next vblank if target vblank was missed
- *
- * Schedule @work for execution once the crtc vblank count reaches @count.
- *
- * If the crtc vblank count has already reached @count and @nextonmiss is
- * %false the work starts to execute immediately.
- *
- * If the crtc vblank count has already reached @count and @nextonmiss is
- * %true the work is deferred until the next vblank (as if @count has been
- * specified as crtc vblank count + 1).
- *
- * If @work is already scheduled, this function will reschedule said work
- * using the new @count. This can be used for self-rearming work items.
- *
- * Returns:
- * %1 if @work was successfully (re)scheduled, %0 if it was either already
- * scheduled or cancelled, or a negative error code on failure.
- */
-int drm_vblank_work_schedule(struct drm_vblank_work *work,
-			     u64 count, bool nextonmiss)
+static int __drm_vblank_work_schedule(struct drm_vblank_work *work,
+				      u64 count, bool nextonmiss, bool armed)
 {
 	struct drm_vblank_crtc *vblank = work->vblank;
 	struct drm_device *dev = vblank->dev;
@@ -139,6 +118,7 @@ int drm_vblank_work_schedule(struct drm_vblank_work *work,
 		rescheduling = true;
 	}
 
+	work->armed = armed;
 	work->count = count;
 	cur_vbl = drm_vblank_count(dev, vblank->pipe);
 	passed = drm_vblank_passed(cur_vbl, count);
@@ -147,7 +127,7 @@ int drm_vblank_work_schedule(struct drm_vblank_work *work,
 			     "crtc %d vblank %llu already passed (current %llu)\n",
 			     vblank->pipe, count, cur_vbl);
 
-	if (!nextonmiss && passed) {
+	if (!nextonmiss && passed && armed) {
 		drm_vblank_put(dev, vblank->pipe);
 		ret = kthread_queue_work(vblank->worker, &work->base);
 
@@ -167,8 +147,82 @@ int drm_vblank_work_schedule(struct drm_vblank_work *work,
 		wake_up_all(&vblank->work_wait_queue);
 	return ret;
 }
+
+/**
+ * drm_vblank_work_schedule - schedule a vblank work
+ * @work: vblank work to schedule
+ * @count: target vblank count
+ * @nextonmiss: defer until the next vblank if target vblank was missed
+ *
+ * Schedule @work for execution once the crtc vblank count reaches @count.
+ *
+ * If the crtc vblank count has already reached @count and @nextonmiss is
+ * %false the work starts to execute immediately.
+ *
+ * If the crtc vblank count has already reached @count and @nextonmiss is
+ * %true the work is deferred until the next vblank (as if @count has been
+ * specified as crtc vblank count + 1).
+ *
+ * If @work is already scheduled, this function will reschedule said work
+ * using the new @count. This can be used for self-rearming work items.
+ *
+ * Returns:
+ * %1 if @work was successfully (re)scheduled, %0 if it was either already
+ * scheduled or cancelled, or a negative error code on failure.
+ */
+int drm_vblank_work_schedule(struct drm_vblank_work *work,
+			     u64 count, bool nextonmiss)
+{
+	return __drm_vblank_work_schedule(work, count, nextonmiss, true);
+}
 EXPORT_SYMBOL(drm_vblank_work_schedule);
 
+
+/**
+ * drm_vblank_work_schedule_disabled - schedule a vblank work, withoug enabling
+ * @work: vblank work to schedule
+ * @count: target vblank count
+ *
+ * Schedule @work for execution once the crtc vblank count reaches @count.
+ *
+ * The vblank work will not be scheduled until drm_vblank_work_enable() is called.
+ * If the crtc vblank count has already reached @count, the work will still
+ * not be scheduled until the first following vblank.
+ *
+ * If @work is already scheduled, this function will reschedule said work
+ * using the new @count. This can be used for self-rearming work items.
+ *
+ * Returns:
+ * %1 if @work was successfully (re)scheduled, %0 if it was either already
+ * scheduled or cancelled, or a negative error code on failure.
+ */
+int drm_vblank_work_schedule_disabled(struct drm_vblank_work *work, u64 count)
+{
+	return __drm_vblank_work_schedule(work, count, true, false);
+}
+EXPORT_SYMBOL(drm_vblank_work_schedule_disabled);
+
+/**
+ * drm_vblank_work_enable - enable vblank work
+ * @work: vblank work to enable
+ *
+ * This function is specifically only for when drm_vblank_work_schedule_disabled() is
+ * called. It allows for the work to be armed in any context, without any locks.
+ *
+ * The work will be signalled earliest at the @count argument, if it has been passed,
+ * it will signalled at the next vblank.
+ *
+ * This is particularly useful for PREEMPT_RT, where the spin_lock is converted
+ * into a sleeping rtmutex, and vblank evasion requires some work to be
+ * scheduled on completion with interrupts disabled.
+ */
+void drm_vblank_work_enable(struct drm_vblank_work *work)
+{
+	WARN_ON(work->armed);
+	WRITE_ONCE(work->armed, true);
+}
+EXPORT_SYMBOL(drm_vblank_work_enable);
+
 /**
  * drm_vblank_work_cancel_sync - cancel a vblank work and wait for it to
  * finish executing
diff --git a/include/drm/drm_vblank_work.h b/include/drm/drm_vblank_work.h
index e04d436b72973..e19351200da24 100644
--- a/include/drm/drm_vblank_work.h
+++ b/include/drm/drm_vblank_work.h
@@ -47,6 +47,14 @@ struct drm_vblank_work {
 	 */
 	int cancelling;
 
+	/**
+	 * @armed: If false, the work item has been added to the
+	 * drm_vblank_crtc.pending_work list, but will not yet be signalled.
+	 *
+	 * Call drm_vblank_work_enable() to fire on next vblank.
+	 */
+	bool armed;
+
 	/**
 	 * @node: The position of this work item in
 	 * &drm_vblank_crtc.pending_work.
@@ -64,6 +72,10 @@ struct drm_vblank_work {
 
 int drm_vblank_work_schedule(struct drm_vblank_work *work,
 			     u64 count, bool nextonmiss);
+
+int drm_vblank_work_schedule_disabled(struct drm_vblank_work *work, u64 count);
+void drm_vblank_work_enable(struct drm_vblank_work *work);
+
 void drm_vblank_work_init(struct drm_vblank_work *work, struct drm_crtc *crtc,
 			  void (*func)(struct kthread_work *work));
 bool drm_vblank_work_cancel_sync(struct drm_vblank_work *work);
-- 
2.51.0


  reply	other threads:[~2026-02-20 12:24 UTC|newest]

Thread overview: 49+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-02-20  8:36 [i915-rt v6 00/24] drm/i915/display: All patches to make PREEMPT_RT work on i915 + xe Maarten Lankhorst
2026-02-20  8:36 ` [i915-rt v6 01/24] drm/vblank_work: Add methods to schedule vblank_work in 2 stages Maarten Lankhorst
2026-02-20 12:24   ` Maarten Lankhorst [this message]
2026-02-20  8:37 ` [i915-rt v6 02/24] drm/vblank: Add a 2-stage version of drm_crtc_arm_vblank_event Maarten Lankhorst
2026-02-20  8:37 ` [i915-rt v6 03/24] drm/intel/display: Make intel_crtc_arm_vblank_event static Maarten Lankhorst
2026-02-20  8:37 ` [i915-rt v6 04/24] drm/intel/display: Convert vblank event handling to 2-stage arming Maarten Lankhorst
2026-02-20  8:37 ` [i915-rt v6 05/24] drm/i915/display: Move vblank put until after critical section Maarten Lankhorst
2026-02-20  8:37 ` [i915-rt v6 06/24] drm/i915/display: Remove locking from intel_vblank_evade " Maarten Lankhorst
2026-02-20  8:37 ` [i915-rt v6 07/24] drm/i915/display: Handle vlv dsi workaround in scanline_in_safe_range too Maarten Lankhorst
2026-02-20  8:37 ` [i915-rt v6 08/24] drm/i915: Use preempt_disable/enable_rt() where recommended Maarten Lankhorst
2026-02-20  8:37 ` [i915-rt v6 09/24] drm/i915/display: Make get_vblank_counter use intel_de_read_fw() Maarten Lankhorst
2026-02-20  8:37 ` [i915-rt v6 10/24] drm/i915/display: Do not take uncore lock in i915_get_vblank_counter Maarten Lankhorst
2026-02-20  8:37 ` [i915-rt v6 11/24] drm/i915/display: Make icl_dsi_frame_update use _fw too Maarten Lankhorst
2026-02-20  8:37 ` [i915-rt v6 12/24] drm/i915/display: Use intel_de_read/write_fw in colorops Maarten Lankhorst
2026-02-20  8:37 ` [i915-rt v6 13/24] drm/i915/display: Use intel_de_write_fw in intel_pipe_fastset Maarten Lankhorst
2026-02-25  9:25   ` Jani Nikula
2026-02-25 11:59     ` Maarten Lankhorst
2026-02-20  8:37 ` [i915-rt v6 14/24] drm/i915/display: Make set_pipeconf use the fw variants Maarten Lankhorst
2026-02-20  8:37 ` [i915-rt v6 15/24] drm/i915/display: Fix intel_lpe_audio_irq_handler for PREEMPT-RT Maarten Lankhorst
2026-02-20  8:37 ` [i915-rt v6 16/24] drm/i915/gt: Use spin_lock_irq() instead of local_irq_disable() + spin_lock() Maarten Lankhorst
2026-02-20  8:37 ` [i915-rt v6 17/24] drm/i915: Drop the irqs_disabled() check Maarten Lankhorst
2026-02-20  8:37 ` [i915-rt v6 18/24] drm/i915/guc: Consider also RCU depth in busy loop Maarten Lankhorst
2026-02-20  8:37 ` [i915-rt v6 19/24] drm/i915/gt: Fix selftests on PREEMPT_RT Maarten Lankhorst
2026-02-20  8:37 ` [i915-rt v6 20/24] drm/i915/gt: Set stop_timeout() correctly on PREEMPT-RT Maarten Lankhorst
2026-02-20  8:37 ` [i915-rt v6 21/24] drm/i915/display: Remove uncore lock from vlv_atomic_update_fifo Maarten Lankhorst
2026-02-20  8:37 ` [i915-rt v6 22/24] Revert "drm/i915: Depend on !PREEMPT_RT." Maarten Lankhorst
2026-02-20  8:37 ` [i915-rt v6 23/24] PREEMPT_RT injection Maarten Lankhorst
2026-02-20  8:37 ` [i915-rt v6 24/24] FOR-CI: bump MAX_STACK_TRACE_ENTRIES Maarten Lankhorst
2026-02-24 14:15   ` Sebastian Andrzej Siewior
2026-02-25 12:32     ` Maarten Lankhorst
2026-02-20  8:46 ` ✗ CI.checkpatch: warning for drm/i915/display: All patches to make PREEMPT_RT work on i915 + xe. (rev12) Patchwork
2026-02-20  8:48 ` ✓ CI.KUnit: success " Patchwork
2026-02-20  9:04 ` ✗ CI.checksparse: warning " Patchwork
2026-02-20  9:38 ` ✓ Xe.CI.BAT: success " Patchwork
2026-02-20  9:50 ` ✗ i915.CI.BAT: failure " Patchwork
2026-02-20 21:03 ` ✗ Xe.CI.FULL: " Patchwork
2026-02-24 16:27 ` [i915-rt v6 00/24] drm/i915/display: All patches to make PREEMPT_RT work on i915 + xe Sebastian Andrzej Siewior
2026-02-24 16:59   ` Sebastian Andrzej Siewior
2026-02-25  7:58     ` Sebastian Andrzej Siewior
2026-02-25 12:15       ` Maarten Lankhorst
2026-02-25 20:06       ` Maarten Lankhorst
2026-02-26 12:07         ` Sebastian Andrzej Siewior
2026-02-26 14:19           ` Sebastian Andrzej Siewior
2026-02-26 14:38             ` Sebastian Andrzej Siewior
2026-03-05 10:19               ` Maarten Lankhorst
2026-03-05 10:42               ` Maarten Lankhorst
2026-03-05 10:50                 ` Sebastian Andrzej Siewior
2026-03-05 11:11                   ` Maarten Lankhorst
2026-03-05 11:19                     ` Sebastian Andrzej Siewior

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=20260220122425.97902-2-dev@lankhorst.se \
    --to=dev@lankhorst.se \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=intel-gfx@lists.freedesktop.org \
    --cc=intel-xe@lists.freedesktop.org \
    --cc=linux-rt-devel@lists.linux.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.