Skip to content

Commit b542e38

Browse files
committed
eventfd: Make signal recursion protection a task bit
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 <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Tested-by: Daniel Bristot de Oliveira <[email protected]> Acked-by: Jason Wang <[email protected]> Cc: Al Viro <[email protected]> Link: https://lore.kernel.org/r/87wnp9idso.ffs@tglx
1 parent 366e7ad commit b542e38

File tree

4 files changed

+15
-14
lines changed

4 files changed

+15
-14
lines changed

fs/aio.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1695,7 +1695,7 @@ static int aio_poll_wake(struct wait_queue_entry *wait, unsigned mode, int sync,
16951695
list_del(&iocb->ki_list);
16961696
iocb->ki_res.res = mangle_poll(mask);
16971697
req->done = true;
1698-
if (iocb->ki_eventfd && eventfd_signal_count()) {
1698+
if (iocb->ki_eventfd && eventfd_signal_allowed()) {
16991699
iocb = NULL;
17001700
INIT_WORK(&req->work, aio_poll_put_work);
17011701
schedule_work(&req->work);

fs/eventfd.c

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@
2525
#include <linux/idr.h>
2626
#include <linux/uio.h>
2727

28-
DEFINE_PER_CPU(int, eventfd_wake_count);
29-
3028
static DEFINE_IDA(eventfd_ida);
3129

3230
struct eventfd_ctx {
@@ -67,21 +65,21 @@ __u64 eventfd_signal(struct eventfd_ctx *ctx, __u64 n)
6765
* Deadlock or stack overflow issues can happen if we recurse here
6866
* through waitqueue wakeup handlers. If the caller users potentially
6967
* nested waitqueues with custom wakeup handlers, then it should
70-
* check eventfd_signal_count() before calling this function. If
71-
* it returns true, the eventfd_signal() call should be deferred to a
68+
* check eventfd_signal_allowed() before calling this function. If
69+
* it returns false, the eventfd_signal() call should be deferred to a
7270
* safe context.
7371
*/
74-
if (WARN_ON_ONCE(this_cpu_read(eventfd_wake_count)))
72+
if (WARN_ON_ONCE(current->in_eventfd_signal))
7573
return 0;
7674

7775
spin_lock_irqsave(&ctx->wqh.lock, flags);
78-
this_cpu_inc(eventfd_wake_count);
76+
current->in_eventfd_signal = 1;
7977
if (ULLONG_MAX - ctx->count < n)
8078
n = ULLONG_MAX - ctx->count;
8179
ctx->count += n;
8280
if (waitqueue_active(&ctx->wqh))
8381
wake_up_locked_poll(&ctx->wqh, EPOLLIN);
84-
this_cpu_dec(eventfd_wake_count);
82+
current->in_eventfd_signal = 0;
8583
spin_unlock_irqrestore(&ctx->wqh.lock, flags);
8684

8785
return n;

include/linux/eventfd.h

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <linux/err.h>
1515
#include <linux/percpu-defs.h>
1616
#include <linux/percpu.h>
17+
#include <linux/sched.h>
1718

1819
/*
1920
* CAREFUL: Check include/uapi/asm-generic/fcntl.h when defining
@@ -43,11 +44,9 @@ int eventfd_ctx_remove_wait_queue(struct eventfd_ctx *ctx, wait_queue_entry_t *w
4344
__u64 *cnt);
4445
void eventfd_ctx_do_read(struct eventfd_ctx *ctx, __u64 *cnt);
4546

46-
DECLARE_PER_CPU(int, eventfd_wake_count);
47-
48-
static inline bool eventfd_signal_count(void)
47+
static inline bool eventfd_signal_allowed(void)
4948
{
50-
return this_cpu_read(eventfd_wake_count);
49+
return !current->in_eventfd_signal;
5150
}
5251

5352
#else /* CONFIG_EVENTFD */
@@ -78,9 +77,9 @@ static inline int eventfd_ctx_remove_wait_queue(struct eventfd_ctx *ctx,
7877
return -ENOSYS;
7978
}
8079

81-
static inline bool eventfd_signal_count(void)
80+
static inline bool eventfd_signal_allowed(void)
8281
{
83-
return false;
82+
return true;
8483
}
8584

8685
static inline void eventfd_ctx_do_read(struct eventfd_ctx *ctx, __u64 *cnt)

include/linux/sched.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -864,6 +864,10 @@ struct task_struct {
864864
/* Used by page_owner=on to detect recursion in page tracking. */
865865
unsigned in_page_owner:1;
866866
#endif
867+
#ifdef CONFIG_EVENTFD
868+
/* Recursion prevention for eventfd_signal() */
869+
unsigned in_eventfd_signal:1;
870+
#endif
867871

868872
unsigned long atomic_flags; /* Flags requiring atomic access. */
869873

0 commit comments

Comments
 (0)