summaryrefslogtreecommitdiffstats
path: root/debian/patches-rt/0310-eventfd-Make-signal-recursion-protection-a-task-bit.patch
diff options
context:
space:
mode:
Diffstat (limited to 'debian/patches-rt/0310-eventfd-Make-signal-recursion-protection-a-task-bit.patch')
-rw-r--r--debian/patches-rt/0310-eventfd-Make-signal-recursion-protection-a-task-bit.patch150
1 files changed, 150 insertions, 0 deletions
diff --git a/debian/patches-rt/0310-eventfd-Make-signal-recursion-protection-a-task-bit.patch b/debian/patches-rt/0310-eventfd-Make-signal-recursion-protection-a-task-bit.patch
new file mode 100644
index 000000000..719316f76
--- /dev/null
+++ b/debian/patches-rt/0310-eventfd-Make-signal-recursion-protection-a-task-bit.patch
@@ -0,0 +1,150 @@
+From 17c41196e72418f94cf308a8b23fe478612a1610 Mon Sep 17 00:00:00 2001
+From: Thomas Gleixner <tglx@linutronix.de>
+Date: Fri, 17 Dec 2021 11:32:09 +0100
+Subject: [PATCH 310/323] eventfd: Make signal recursion protection a task bit
+Origin: https://www.kernel.org/pub/linux/kernel/projects/rt/5.10/older/patches-5.10.204-rt100.tar.xz
+
+Upstream commit b542e383d8c005f06a131e2b40d5889b812f19c6
+
+The recursion protection for eventfd_signal() is based on a per CPU
+variable and relies on the !RT semantics of spin_lock_irqsave() for
+protecting this per CPU variable. On RT kernels spin_lock_irqsave() neither
+disables preemption nor interrupts which allows the spin lock held section
+to be preempted. If the preempting task invokes eventfd_signal() as well,
+then the recursion warning triggers.
+
+Paolo suggested to protect the per CPU variable with a local lock, but
+that's heavyweight and actually not necessary. The goal of this protection
+is to prevent the task stack from overflowing, which can be achieved with a
+per task recursion protection as well.
+
+Replace the per CPU variable with a per task bit similar to other recursion
+protection bits like task_struct::in_page_owner. This works on both !RT and
+RT kernels and removes as a side effect the extra per CPU storage.
+
+No functional change for !RT kernels.
+
+Reported-by: Daniel Bristot de Oliveira <bristot@redhat.com>
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Tested-by: Daniel Bristot de Oliveira <bristot@redhat.com>
+Acked-by: Jason Wang <jasowang@redhat.com>
+Cc: Al Viro <viro@zeniv.linux.org.uk>
+Link: https://lore.kernel.org/r/87wnp9idso.ffs@tglx
+Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Signed-off-by: Luis Claudio R. Goncalves <lgoncalv@redhat.com>
+---
+ fs/aio.c | 2 +-
+ fs/eventfd.c | 12 +++++-------
+ include/linux/eventfd.h | 11 +++++------
+ include/linux/sched.h | 4 ++++
+ 4 files changed, 15 insertions(+), 14 deletions(-)
+
+diff --git a/fs/aio.c b/fs/aio.c
+index c90e045a37bc..e1181dc37be1 100644
+--- a/fs/aio.c
++++ b/fs/aio.c
+@@ -1765,7 +1765,7 @@ static int aio_poll_wake(struct wait_queue_entry *wait, unsigned mode, int sync,
+ list_del_init(&req->wait.entry);
+ list_del(&iocb->ki_list);
+ iocb->ki_res.res = mangle_poll(mask);
+- if (iocb->ki_eventfd && eventfd_signal_count()) {
++ if (iocb->ki_eventfd && eventfd_signal_allowed()) {
+ iocb = NULL;
+ INIT_WORK(&req->work, aio_poll_put_work);
+ schedule_work(&req->work);
+diff --git a/fs/eventfd.c b/fs/eventfd.c
+index 3673eb8de035..ce9ca07d6c00 100644
+--- a/fs/eventfd.c
++++ b/fs/eventfd.c
+@@ -25,8 +25,6 @@
+ #include <linux/idr.h>
+ #include <linux/uio.h>
+
+-DEFINE_PER_CPU(int, eventfd_wake_count);
+-
+ static DEFINE_IDA(eventfd_ida);
+
+ struct eventfd_ctx {
+@@ -53,21 +51,21 @@ __u64 eventfd_signal_mask(struct eventfd_ctx *ctx, __u64 n, unsigned mask)
+ * Deadlock or stack overflow issues can happen if we recurse here
+ * through waitqueue wakeup handlers. If the caller users potentially
+ * nested waitqueues with custom wakeup handlers, then it should
+- * check eventfd_signal_count() before calling this function. If
+- * it returns true, the eventfd_signal() call should be deferred to a
++ * check eventfd_signal_allowed() before calling this function. If
++ * it returns false, the eventfd_signal() call should be deferred to a
+ * safe context.
+ */
+- if (WARN_ON_ONCE(this_cpu_read(eventfd_wake_count)))
++ if (WARN_ON_ONCE(current->in_eventfd_signal))
+ return 0;
+
+ spin_lock_irqsave(&ctx->wqh.lock, flags);
+- this_cpu_inc(eventfd_wake_count);
++ current->in_eventfd_signal = 1;
+ if (ULLONG_MAX - ctx->count < n)
+ n = ULLONG_MAX - ctx->count;
+ ctx->count += n;
+ if (waitqueue_active(&ctx->wqh))
+ wake_up_locked_poll(&ctx->wqh, EPOLLIN | mask);
+- this_cpu_dec(eventfd_wake_count);
++ current->in_eventfd_signal = 0;
+ spin_unlock_irqrestore(&ctx->wqh.lock, flags);
+
+ return n;
+diff --git a/include/linux/eventfd.h b/include/linux/eventfd.h
+index c1bd4883e2fa..842d223dfe17 100644
+--- a/include/linux/eventfd.h
++++ b/include/linux/eventfd.h
+@@ -14,6 +14,7 @@
+ #include <linux/err.h>
+ #include <linux/percpu-defs.h>
+ #include <linux/percpu.h>
++#include <linux/sched.h>
+
+ /*
+ * CAREFUL: Check include/uapi/asm-generic/fcntl.h when defining
+@@ -44,11 +45,9 @@ int eventfd_ctx_remove_wait_queue(struct eventfd_ctx *ctx, wait_queue_entry_t *w
+ __u64 *cnt);
+ void eventfd_ctx_do_read(struct eventfd_ctx *ctx, __u64 *cnt);
+
+-DECLARE_PER_CPU(int, eventfd_wake_count);
+-
+-static inline bool eventfd_signal_count(void)
++static inline bool eventfd_signal_allowed(void)
+ {
+- return this_cpu_read(eventfd_wake_count);
++ return !current->in_eventfd_signal;
+ }
+
+ #else /* CONFIG_EVENTFD */
+@@ -85,9 +84,9 @@ static inline int eventfd_ctx_remove_wait_queue(struct eventfd_ctx *ctx,
+ return -ENOSYS;
+ }
+
+-static inline bool eventfd_signal_count(void)
++static inline bool eventfd_signal_allowed(void)
+ {
+- return false;
++ return true;
+ }
+
+ static inline void eventfd_ctx_do_read(struct eventfd_ctx *ctx, __u64 *cnt)
+diff --git a/include/linux/sched.h b/include/linux/sched.h
+index a73528e8235d..13d4957189bb 100644
+--- a/include/linux/sched.h
++++ b/include/linux/sched.h
+@@ -848,6 +848,10 @@ struct task_struct {
+ /* Stalled due to lack of memory */
+ unsigned in_memstall:1;
+ #endif
++#ifdef CONFIG_EVENTFD
++ /* Recursion prevention for eventfd_signal() */
++ unsigned in_eventfd_signal:1;
++#endif
+
+ unsigned long atomic_flags; /* Flags requiring atomic access. */
+
+--
+2.43.0
+