From: Sebastian Andrzej Siewior <bigeasy@xxxxxxxxxxxxx> v4.14.292-rt138-rc2 stable review patch. If anyone has any objections, please let me know. ----------- This is an all in one commit backporting rcuwait: - update.c, rcuwait.h as of commit 58d4292bd037b ("rcu: Uninline multi-use function: finish_rcuwait()") - exit.c as of commit 9d9a6ebfea329 ("rcuwait: Let rcuwait_wake_up() return whether or not a task was awoken") Signed-off-by: Sebastian Andrzej Siewior <bigeasy@xxxxxxxxxxxxx> Message-Id: <20220819092446.980320-8-bigeasy@xxxxxxxxxxxxx> Signed-off-by: Luis Claudio R. Goncalves <lgoncalv@xxxxxxxxxx> --- include/linux/rcuwait.h | 63 +++++++++++++++++++++-------------- kernel/exit.c | 20 +++++------ kernel/locking/percpu-rwsem.c | 3 +- kernel/rcu/update.c | 8 +++++ 4 files changed, 58 insertions(+), 36 deletions(-) diff --git a/include/linux/rcuwait.h b/include/linux/rcuwait.h index 90bfa3279a01c..251962a8e9ddf 100644 --- a/include/linux/rcuwait.h +++ b/include/linux/rcuwait.h @@ -3,22 +3,19 @@ #define _LINUX_RCUWAIT_H_ #include <linux/rcupdate.h> +#include <linux/sched.h> +#include <linux/sched/signal.h> /* * rcuwait provides a way of blocking and waking up a single - * task in an rcu-safe manner; where it is forbidden to use - * after exit_notify(). task_struct is not properly rcu protected, - * unless dealing with rcu-aware lists, ie: find_task_by_*(). + * task in an rcu-safe manner. * - * Alternatively we have task_rcu_dereference(), but the return - * semantics have different implications which would break the - * wakeup side. The only time @task is non-nil is when a user is - * blocked (or checking if it needs to) on a condition, and reset - * as soon as we know that the condition has succeeded and are - * awoken. + * The only time @task is non-nil is when a user is blocked (or + * checking if it needs to) on a condition, and reset as soon as we + * know that the condition has succeeded and are awoken. */ struct rcuwait { - struct task_struct *task; + struct task_struct __rcu *task; }; #define __RCUWAIT_INITIALIZER(name) \ @@ -29,36 +26,52 @@ static inline void rcuwait_init(struct rcuwait *w) w->task = NULL; } -extern void rcuwait_wake_up(struct rcuwait *w); +/* + * Note: this provides no serialization and, just as with waitqueues, + * requires care to estimate as to whether or not the wait is active. + */ +static inline int rcuwait_active(struct rcuwait *w) +{ + return !!rcu_access_pointer(w->task); +} + +extern int rcuwait_wake_up(struct rcuwait *w); /* * The caller is responsible for locking around rcuwait_wait_event(), - * such that writes to @task are properly serialized. + * and [prepare_to/finish]_rcuwait() such that writes to @task are + * properly serialized. */ -#define rcuwait_wait_event(w, condition) \ + +static inline void prepare_to_rcuwait(struct rcuwait *w) +{ + rcu_assign_pointer(w->task, current); +} + +extern void finish_rcuwait(struct rcuwait *w); + +#define rcuwait_wait_event(w, condition, state) \ ({ \ - /* \ - * Complain if we are called after do_exit()/exit_notify(), \ - * as we cannot rely on the rcu critical region for the \ - * wakeup side. \ - */ \ - WARN_ON(current->exit_state); \ - \ - rcu_assign_pointer((w)->task, current); \ + int __ret = 0; \ + prepare_to_rcuwait(w); \ for (;;) { \ /* \ * Implicit barrier (A) pairs with (B) in \ * rcuwait_wake_up(). \ */ \ - set_current_state(TASK_UNINTERRUPTIBLE); \ + set_current_state(state); \ if (condition) \ break; \ \ + if (signal_pending_state(state, current)) { \ + __ret = -EINTR; \ + break; \ + } \ + \ schedule(); \ } \ - \ - WRITE_ONCE((w)->task, NULL); \ - __set_current_state(TASK_RUNNING); \ + finish_rcuwait(w); \ + __ret; \ }) #endif /* _LINUX_RCUWAIT_H_ */ diff --git a/kernel/exit.c b/kernel/exit.c index d0025aef76a0e..b1b0f0db3bf23 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -290,8 +290,9 @@ struct task_struct *task_rcu_dereference(struct task_struct **ptask) return task; } -void rcuwait_wake_up(struct rcuwait *w) +int rcuwait_wake_up(struct rcuwait *w) { + int ret = 0; struct task_struct *task; rcu_read_lock(); @@ -299,25 +300,24 @@ void rcuwait_wake_up(struct rcuwait *w) /* * Order condition vs @task, such that everything prior to the load * of @task is visible. This is the condition as to why the user called - * rcuwait_trywake() in the first place. Pairs with set_current_state() + * rcuwait_wake() in the first place. Pairs with set_current_state() * barrier (A) in rcuwait_wait_event(). * * WAIT WAKE - * [S] tsk = current [S] cond = true - * MB (A) MB (B) - * [L] cond [L] tsk + * [S] tsk = current [S] cond = true + * MB (A) MB (B) + * [L] cond [L] tsk */ smp_mb(); /* (B) */ - /* - * Avoid using task_rcu_dereference() magic as long as we are careful, - * see comment in rcuwait_wait_event() regarding ->exit_state. - */ task = rcu_dereference(w->task); if (task) - wake_up_process(task); + ret = wake_up_process(task); rcu_read_unlock(); + + return ret; } +EXPORT_SYMBOL_GPL(rcuwait_wake_up); /* * Determine if a process group is "orphaned", according to the POSIX diff --git a/kernel/locking/percpu-rwsem.c b/kernel/locking/percpu-rwsem.c index 883cf1b92d908..76baed79f5722 100644 --- a/kernel/locking/percpu-rwsem.c +++ b/kernel/locking/percpu-rwsem.c @@ -159,7 +159,8 @@ void percpu_down_write(struct percpu_rw_semaphore *sem) */ /* Wait for all now active readers to complete. */ - rcuwait_wait_event(&sem->writer, readers_active_check(sem)); + rcuwait_wait_event(&sem->writer, readers_active_check(sem), + TASK_UNINTERRUPTIBLE); } EXPORT_SYMBOL_GPL(percpu_down_write); diff --git a/kernel/rcu/update.c b/kernel/rcu/update.c index 307592810f6bc..e81ea7d3338e5 100644 --- a/kernel/rcu/update.c +++ b/kernel/rcu/update.c @@ -51,6 +51,7 @@ #include <linux/kthread.h> #include <linux/tick.h> #include <linux/rcupdate_wait.h> +#include <linux/rcuwait.h> #define CREATE_TRACE_POINTS @@ -420,6 +421,13 @@ void __wait_rcu_gp(bool checktiny, int n, call_rcu_func_t *crcu_array, } EXPORT_SYMBOL_GPL(__wait_rcu_gp); +void finish_rcuwait(struct rcuwait *w) +{ + rcu_assign_pointer(w->task, NULL); + __set_current_state(TASK_RUNNING); +} +EXPORT_SYMBOL_GPL(finish_rcuwait); + #ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD void init_rcu_head(struct rcu_head *head) { -- 2.37.3