git: efb8f0b8db87 - main - linuxkpi: Mitigate a seqlock livelock
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 25 Apr 2022 13:26:38 UTC
The branch main has been updated by markj:
URL: https://cgit.FreeBSD.org/src/commit/?id=efb8f0b8db8747243f7999b874403d5c86e93b74
commit efb8f0b8db8747243f7999b874403d5c86e93b74
Author: Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2022-04-25 13:13:03 +0000
Commit: Mark Johnston <markj@FreeBSD.org>
CommitDate: 2022-04-25 13:13:03 +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
MFC after: 2 weeks
Sponsored by: The FreeBSD Foundation
Differential Revision: https://reviews.freebsd.org/D35021
---
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 6f0618d7ac9d..6e81e7a0fa45 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