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