From: Maarten Lankhorst <dev@lankhorst.se>
To: intel-xe@lists.freedesktop.org, intel-gfx@lists.freedesktop.org
Cc: dri-devel@lists.freedesktop.org, Maarten Lankhorst <dev@lankhorst.se>
Subject: [PATCH v7 01/26] drm/vblank_work: Add methods to schedule vblank_work in 2 stages
Date: Tue, 10 Mar 2026 12:56:43 +0100 [thread overview]
Message-ID: <20260310115709.2276203-2-dev@lankhorst.se> (raw)
In-Reply-To: <20260310115709.2276203-1-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>
---
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
next prev parent reply other threads:[~2026-03-10 11:57 UTC|newest]
Thread overview: 32+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-10 11:56 [PATCH v7 00/26] drm/i915/display: All patches to make PREEMPT_RT work on i915 + xe Maarten Lankhorst
2026-03-10 11:56 ` Maarten Lankhorst [this message]
2026-03-10 11:56 ` [PATCH v7 02/26] drm/vblank: Add a 2-stage version of drm_crtc_arm_vblank_event Maarten Lankhorst
2026-03-10 11:56 ` [PATCH v7 03/26] drm/intel/display: Make intel_crtc_arm_vblank_event static Maarten Lankhorst
2026-03-10 11:56 ` [PATCH v7 04/26] drm/intel/display: Convert vblank event handling to 2-stage arming Maarten Lankhorst
2026-03-10 11:56 ` [PATCH v7 05/26] drm/i915/display: Move vblank put until after critical section Maarten Lankhorst
2026-03-10 11:56 ` [PATCH v7 06/26] drm/i915/display: Remove locking from intel_vblank_evade " Maarten Lankhorst
2026-03-10 11:56 ` [PATCH v7 07/26] drm/i915/display: Handle vlv dsi workaround in scanline_in_safe_range too Maarten Lankhorst
2026-03-10 11:56 ` [PATCH v7 08/26] drm/i915: Use preempt_disable/enable_rt() where recommended Maarten Lankhorst
2026-03-10 11:56 ` [PATCH v7 09/26] drm/i915/display: Make get_vblank_counter use intel_de_read_fw() Maarten Lankhorst
2026-03-10 11:56 ` [PATCH v7 10/26] drm/i915/display: Do not take uncore lock in i915_get_vblank_counter Maarten Lankhorst
2026-03-10 11:56 ` [PATCH v7 11/26] drm/i915/display: Make icl_dsi_frame_update use _fw too Maarten Lankhorst
2026-03-10 11:56 ` [PATCH v7 12/26] drm/i915/display: Use intel_de_read/write_fw in colorops Maarten Lankhorst
2026-03-10 11:56 ` [PATCH v7 13/26] drm/i915/display: Use intel_de_write_fw in intel_pipe_fastset Maarten Lankhorst
2026-03-10 11:56 ` [PATCH v7 14/26] drm/i915/display: Make set_pipeconf use the fw variants Maarten Lankhorst
2026-03-10 11:56 ` [PATCH v7 15/26] drm/i915/display: Fix intel_lpe_audio_irq_handler for PREEMPT-RT Maarten Lankhorst
2026-03-10 11:56 ` [PATCH v7 16/26] drm/i915/gt: Use spin_lock_irq() instead of local_irq_disable() + spin_lock() Maarten Lankhorst
2026-03-10 11:56 ` [PATCH v7 17/26] drm/i915: Drop the irqs_disabled() check Maarten Lankhorst
2026-03-10 11:57 ` [PATCH v7 18/26] drm/i915/guc: Consider also RCU depth in busy loop Maarten Lankhorst
2026-03-10 11:57 ` [PATCH v7 19/26] drm/i915/gt: Fix selftests on PREEMPT_RT Maarten Lankhorst
2026-03-10 11:57 ` [PATCH v7 20/26] drm/i915/gt: Set stop_timeout() correctly on PREEMPT-RT Maarten Lankhorst
2026-03-10 11:57 ` [PATCH v7 21/26] drm/i915/display: Remove uncore lock from vlv_atomic_update_fifo Maarten Lankhorst
2026-03-10 11:57 ` [PATCH v7 22/26] drm/i915: Use sleeping selftests for igt_atomic on PREEMPT_RT Maarten Lankhorst
2026-03-10 11:57 ` [PATCH v7 23/26] Revert "drm/i915: Depend on !PREEMPT_RT." Maarten Lankhorst
2026-03-10 11:57 ` [PATCH v7 24/26] PREEMPT_RT injection Maarten Lankhorst
2026-03-10 11:57 ` [PATCH v7 25/26] FOR-CI: bump MAX_STACK_TRACE_ENTRIES Maarten Lankhorst
2026-03-10 11:57 ` [PATCH v7 26/26] drm/i915/gt: Add a spinlock to prevent starvation of irq_work Maarten Lankhorst
2026-03-10 17:04 ` Sebastian Andrzej Siewior
2026-03-10 18:14 ` Maarten Lankhorst
2026-03-10 17:22 ` Sebastian Andrzej Siewior
2026-03-10 19:03 ` Maarten Lankhorst
2026-03-11 3:21 ` Claude review: drm/i915/display: All patches to make PREEMPT_RT work on i915 + xe Claude Code Review Bot
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=20260310115709.2276203-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 \
/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