]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
futex: Add mutex around futex exit
authorThomas Gleixner <tglx@linutronix.de>
Wed, 6 Nov 2019 21:55:44 +0000 (22:55 +0100)
committerMarcelo Henrique Cerri <marcelo.cerri@canonical.com>
Fri, 17 Jan 2020 17:21:48 +0000 (14:21 -0300)
BugLink: https://bugs.launchpad.net/bugs/1855787
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: Kamal Mostafa <kamal@canonical.com>
Signed-off-by: Kleber Sacilotto de Souza <kleber.souza@canonical.com>
include/linux/futex.h
include/linux/sched.h
kernel/futex.c

index ff143f766b46703e60f72f2e5d085d88c1ee10a3..a4b6cba699bfbd1dd9686741ff116d3c8702a1e7 100644 (file)
@@ -68,6 +68,7 @@ static inline void futex_init_task(struct task_struct *tsk)
        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);
index 40e82018e27861f1449fbd3902aa8578bc1d55b7..d7d6ec85a419dbf96b29d95bf603de7918ff421b 100644 (file)
@@ -949,6 +949,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
index 1857bb50b6cb92036d9b9e9b5343181740aa3be4..959af4463c408121f00a4c05a01bfb2dc6a02370 100644 (file)
@@ -3721,11 +3721,22 @@ static void futex_cleanup(struct task_struct *tsk)
  */
 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.
         *
@@ -3749,6 +3760,11 @@ static void futex_cleanup_end(struct task_struct *tsk, int state)
         * 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)