git: 097991f8ffd6 - stable/13 - linuxkpi: Mitigate a seqlock livelock

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Mon, 09 May 2022 12:32:20 UTC
The branch stable/13 has been updated by markj:

URL: https://cgit.FreeBSD.org/src/commit/?id=097991f8ffd639b38fce65d4acf7b4986bca8dcf

commit 097991f8ffd639b38fce65d4acf7b4986bca8dcf
Author:     Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2022-04-25 13:13:03 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2022-05-09 12:32:09 +0000

    linuxkpi: Mitigate a seqlock livelock
    
    Disable preemption in seqlock write sections when using the _irqsave
    variant.  This ensures that a writer can't be preempted and subsequently
    starved by a reader running in a callout handler on the same CPU.
    
    This fixes occasional display hangs seen when using the i915 driver.
    
    Tested by:      emaste, wulf
    Reviewed by:    wulf, hselasky
    Sponsored by:   The FreeBSD Foundation
    
    (cherry picked from commit efb8f0b8db8747243f7999b874403d5c86e93b74)
---
 sys/compat/linuxkpi/common/include/linux/seqlock.h | 32 +++++++++++++++++++---
 1 file changed, 28 insertions(+), 4 deletions(-)

diff --git a/sys/compat/linuxkpi/common/include/linux/seqlock.h b/sys/compat/linuxkpi/common/include/linux/seqlock.h
index 03d36d89de89..2f4892525e8a 100644
--- a/sys/compat/linuxkpi/common/include/linux/seqlock.h
+++ b/sys/compat/linuxkpi/common/include/linux/seqlock.h
@@ -30,6 +30,7 @@
 #define	_LINUXKPI_LINUX_SEQLOCK_H__
 
 #include <sys/param.h>
+#include <sys/systm.h>
 #include <sys/lock.h>
 #include <sys/mutex.h>
 #include <sys/seqc.h>
@@ -100,29 +101,52 @@ seqlock_init(struct seqlock *seqlock)
 }
 
 static inline void
-write_seqlock(struct seqlock *seqlock)
+lkpi_write_seqlock(struct seqlock *seqlock, const bool irqsave)
 {
 	mtx_lock(&seqlock->seql_lock);
+	if (irqsave)
+		critical_enter();
 	write_seqcount_begin(&seqlock->seql_count);
 }
 
 static inline void
-write_sequnlock(struct seqlock *seqlock)
+write_seqlock(struct seqlock *seqlock)
+{
+	lkpi_write_seqlock(seqlock, false);
+}
+
+static inline void
+lkpi_write_sequnlock(struct seqlock *seqlock, const bool irqsave)
 {
 	write_seqcount_end(&seqlock->seql_count);
+	if (irqsave)
+		critical_exit();
 	mtx_unlock(&seqlock->seql_lock);
 }
 
+static inline void
+write_sequnlock(struct seqlock *seqlock)
+{
+	lkpi_write_sequnlock(seqlock, false);
+}
+
+/*
+ * Disable preemption when the consumer wants to disable interrupts.  This
+ * ensures that the caller won't be starved if it is preempted by a
+ * higher-priority reader, but assumes that the caller won't perform any
+ * blocking operations while holding the write lock; probably a safe
+ * assumption.
+ */
 #define	write_seqlock_irqsave(seqlock, flags)	do {	\
 	(flags) = 0;					\
-	write_seqlock(seqlock);				\
+	lkpi_write_seqlock(seqlock, true);		\
 } while (0)
 
 static inline void
 write_sequnlock_irqrestore(struct seqlock *seqlock,
     unsigned long flags __unused)
 {
-	write_sequnlock(seqlock);
+	lkpi_write_sequnlock(seqlock, true);
 }
 
 static inline unsigned