futex: Mark the begin of futex exit explicitly
authorThomas Gleixner <tglx@linutronix.de>
Wed, 6 Nov 2019 21:55:41 +0000 (22:55 +0100)
committerThomas Gleixner <tglx@linutronix.de>
Wed, 20 Nov 2019 08:40:09 +0000 (09:40 +0100)
Instead of relying on PF_EXITING use an explicit state for the futex exit
and set it in the futex exit function. This moves the smp barrier and the
lock/unlock serialization into the futex code.

As with the DEAD state this is restricted to the exit path as exec
continues to use the same task struct.

This allows to simplify that logic in a next step.

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.539409004@linutronix.de
include/linux/futex.h
kernel/exit.c
kernel/futex.c

index 6414cfaf88e0c25676e4b0b968e66edb52298155..9f2792427d64fa832a6f8378d0319555ec46fd5e 100644 (file)
@@ -52,6 +52,7 @@ union futex_key {
 #ifdef CONFIG_FUTEX
 enum {
        FUTEX_STATE_OK,
+       FUTEX_STATE_EXITING,
        FUTEX_STATE_DEAD,
 };
 
@@ -66,33 +67,7 @@ static inline void futex_init_task(struct task_struct *tsk)
        tsk->futex_state = FUTEX_STATE_OK;
 }
 
-/**
- * futex_exit_done - Sets the tasks futex state to FUTEX_STATE_DEAD
- * @tsk:       task to set the state on
- *
- * Set the futex exit state of the task lockless. The futex waiter code
- * observes that state when a task is exiting and loops until the task has
- * actually finished the futex cleanup. The worst case for this is that the
- * waiter runs through the wait loop until the state becomes visible.
- *
- * This has two callers:
- *
- * - futex_mm_release() after the futex exit cleanup has been done
- *
- * - do_exit() from the recursive fault handling path.
- *
- * In case of a recursive fault this is best effort. Either the futex exit
- * code has run already or not. If the OWNER_DIED bit has been set on the
- * futex then the waiter can take it over. If not, the problem is pushed
- * back to user space. If the futex exit code did not run yet, then an
- * already queued waiter might block forever, but there is nothing which
- * can be done about that.
- */
-static inline void futex_exit_done(struct task_struct *tsk)
-{
-       tsk->futex_state = FUTEX_STATE_DEAD;
-}
-
+void futex_exit_recursive(struct task_struct *tsk);
 void futex_exit_release(struct task_struct *tsk);
 void futex_exec_release(struct task_struct *tsk);
 
@@ -100,7 +75,7 @@ long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,
              u32 __user *uaddr2, u32 val2, u32 val3);
 #else
 static inline void futex_init_task(struct task_struct *tsk) { }
-static inline void futex_exit_done(struct task_struct *tsk) { }
+static inline void futex_exit_recursive(struct task_struct *tsk) { }
 static inline void futex_exit_release(struct task_struct *tsk) { }
 static inline void futex_exec_release(struct task_struct *tsk) { }
 static inline long do_futex(u32 __user *uaddr, int op, u32 val,
index f3b8fa1b894595f1e4eaeb68c83cfaf307ddc0e4..d351fd09e7395794c0197bfc3a6e840be1eed3b6 100644 (file)
@@ -746,23 +746,12 @@ void __noreturn do_exit(long code)
         */
        if (unlikely(tsk->flags & PF_EXITING)) {
                pr_alert("Fixing recursive fault but reboot is needed!\n");
-               futex_exit_done(tsk);
+               futex_exit_recursive(tsk);
                set_current_state(TASK_UNINTERRUPTIBLE);
                schedule();
        }
 
        exit_signals(tsk);  /* sets PF_EXITING */
-       /*
-        * Ensure that all new tsk->pi_lock acquisitions must observe
-        * PF_EXITING. Serializes against futex.c:attach_to_pi_owner().
-        */
-       smp_mb();
-       /*
-        * Ensure that we must observe the pi_state in exit_mm() ->
-        * mm_release() -> exit_pi_state_list().
-        */
-       raw_spin_lock_irq(&tsk->pi_lock);
-       raw_spin_unlock_irq(&tsk->pi_lock);
 
        if (unlikely(in_atomic())) {
                pr_info("note: %s[%d] exited with preempt_count %d\n",
index 426dd71e170db3a1135515de4205531fb2475e21..3488fb024a2042461525e053d559f9d53deb7279 100644 (file)
@@ -3679,10 +3679,45 @@ void futex_exec_release(struct task_struct *tsk)
                exit_pi_state_list(tsk);
 }
 
+/**
+ * futex_exit_recursive - Set the tasks futex state to FUTEX_STATE_DEAD
+ * @tsk:       task to set the state on
+ *
+ * Set the futex exit state of the task lockless. The futex waiter code
+ * observes that state when a task is exiting and loops until the task has
+ * actually finished the futex cleanup. The worst case for this is that the
+ * waiter runs through the wait loop until the state becomes visible.
+ *
+ * This is called from the recursive fault handling path in do_exit().
+ *
+ * This is best effort. Either the futex exit code has run already or
+ * not. If the OWNER_DIED bit has been set on the futex then the waiter can
+ * take it over. If not, the problem is pushed back to user space. If the
+ * futex exit code did not run yet, then an already queued waiter might
+ * block forever, but there is nothing which can be done about that.
+ */
+void futex_exit_recursive(struct task_struct *tsk)
+{
+       tsk->futex_state = FUTEX_STATE_DEAD;
+}
+
 void futex_exit_release(struct task_struct *tsk)
 {
+       tsk->futex_state = FUTEX_STATE_EXITING;
+       /*
+        * Ensure that all new tsk->pi_lock acquisitions must observe
+        * FUTEX_STATE_EXITING. Serializes against attach_to_pi_owner().
+        */
+       smp_mb();
+       /*
+        * Ensure that we must observe the pi_state in exit_pi_state_list().
+        */
+       raw_spin_lock_irq(&tsk->pi_lock);
+       raw_spin_unlock_irq(&tsk->pi_lock);
+
        futex_exec_release(tsk);
-       futex_exit_done(tsk);
+
+       tsk->futex_state = FUTEX_STATE_DEAD;
 }
 
 long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,