| From foo@baz Mon Feb 1 04:21:37 PM CET 2021 |
| From: Lee Jones <lee.jones@linaro.org> |
| Date: Mon, 1 Feb 2021 15:12:12 +0000 |
| Subject: futex: Add mutex around futex exit |
| To: stable@vger.kernel.org |
| Cc: Thomas Gleixner <tglx@linutronix.de>, Ingo Molnar <mingo@kernel.org>, Peter Zijlstra <peterz@infradead.org>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Lee Jones <lee.jones@linaro.org> |
| Message-ID: <20210201151214.2193508-11-lee.jones@linaro.org> |
| |
| From: Thomas Gleixner <tglx@linutronix.de> |
| |
| commit 3f186d974826847a07bc7964d79ec4eded475ad9 upstream. |
| |
| The mutex will be used in subsequent changes to replace the busy looping of |
| a waiter when the futex owner is currently executing the exit cleanup to |
| prevent a potential live lock. |
| |
| Signed-off-by: Thomas Gleixner <tglx@linutronix.de> |
| Reviewed-by: Ingo Molnar <mingo@kernel.org> |
| Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org> |
| Link: https://lkml.kernel.org/r/20191106224556.845798895@linutronix.de |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| Signed-off-by: Lee Jones <lee.jones@linaro.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| include/linux/futex.h | 1 + |
| include/linux/sched.h | 1 + |
| kernel/futex.c | 16 ++++++++++++++++ |
| 3 files changed, 18 insertions(+) |
| |
| --- a/include/linux/futex.h |
| +++ b/include/linux/futex.h |
| @@ -70,6 +70,7 @@ static inline void futex_init_task(struc |
| INIT_LIST_HEAD(&tsk->pi_state_list); |
| tsk->pi_state_cache = NULL; |
| tsk->futex_state = FUTEX_STATE_OK; |
| + mutex_init(&tsk->futex_exit_mutex); |
| } |
| |
| void futex_exit_recursive(struct task_struct *tsk); |
| --- a/include/linux/sched.h |
| +++ b/include/linux/sched.h |
| @@ -1704,6 +1704,7 @@ struct task_struct { |
| #endif |
| struct list_head pi_state_list; |
| struct futex_pi_state *pi_state_cache; |
| + struct mutex futex_exit_mutex; |
| unsigned int futex_state; |
| #endif |
| #ifdef CONFIG_PERF_EVENTS |
| --- a/kernel/futex.c |
| +++ b/kernel/futex.c |
| @@ -3271,12 +3271,23 @@ static void futex_cleanup(struct task_st |
| */ |
| void futex_exit_recursive(struct task_struct *tsk) |
| { |
| + /* If the state is FUTEX_STATE_EXITING then futex_exit_mutex is held */ |
| + if (tsk->futex_state == FUTEX_STATE_EXITING) |
| + mutex_unlock(&tsk->futex_exit_mutex); |
| tsk->futex_state = FUTEX_STATE_DEAD; |
| } |
| |
| static void futex_cleanup_begin(struct task_struct *tsk) |
| { |
| /* |
| + * Prevent various race issues against a concurrent incoming waiter |
| + * including live locks by forcing the waiter to block on |
| + * tsk->futex_exit_mutex when it observes FUTEX_STATE_EXITING in |
| + * attach_to_pi_owner(). |
| + */ |
| + mutex_lock(&tsk->futex_exit_mutex); |
| + |
| + /* |
| * Switch the state to FUTEX_STATE_EXITING under tsk->pi_lock. |
| * |
| * This ensures that all subsequent checks of tsk->futex_state in |
| @@ -3299,6 +3310,11 @@ static void futex_cleanup_end(struct tas |
| * take another loop until it becomes visible. |
| */ |
| tsk->futex_state = state; |
| + /* |
| + * Drop the exit protection. This unblocks waiters which observed |
| + * FUTEX_STATE_EXITING to reevaluate the state. |
| + */ |
| + mutex_unlock(&tsk->futex_exit_mutex); |
| } |
| |
| void futex_exec_release(struct task_struct *tsk) |