]> git.baikalelectronics.ru Git - kernel.git/commitdiff
task_work: teach task_work_add() to do signal_wake_up()
authorOleg Nesterov <oleg@redhat.com>
Tue, 30 Jun 2020 15:32:54 +0000 (17:32 +0200)
committerJens Axboe <axboe@kernel.dk>
Tue, 30 Jun 2020 18:18:08 +0000 (12:18 -0600)
So that the target task will exit the wait_event_interruptible-like
loop and call task_work_run() asap.

The patch turns "bool notify" into 0,TWA_RESUME,TWA_SIGNAL enum, the
new TWA_SIGNAL flag implies signal_wake_up().  However, it needs to
avoid the race with recalc_sigpending(), so the patch also adds the
new JOBCTL_TASK_WORK bit included in JOBCTL_PENDING_MASK.

TODO: once this patch is merged we need to change all current users
of task_work_add(notify = true) to use TWA_RESUME.

Cc: stable@vger.kernel.org # v5.7
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
include/linux/sched/jobctl.h
include/linux/task_work.h
kernel/signal.c
kernel/task_work.c

index fa067de9f1a94843f7402f2fd258d8b6339b59f0..d2b4204ba4d34984b344ae4b302de77bc9603c74 100644 (file)
@@ -19,6 +19,7 @@ struct task_struct;
 #define JOBCTL_TRAPPING_BIT    21      /* switching to TRACED */
 #define JOBCTL_LISTENING_BIT   22      /* ptracer is listening for events */
 #define JOBCTL_TRAP_FREEZE_BIT 23      /* trap for cgroup freezer */
+#define JOBCTL_TASK_WORK_BIT   24      /* set by TWA_SIGNAL */
 
 #define JOBCTL_STOP_DEQUEUED   (1UL << JOBCTL_STOP_DEQUEUED_BIT)
 #define JOBCTL_STOP_PENDING    (1UL << JOBCTL_STOP_PENDING_BIT)
@@ -28,9 +29,10 @@ struct task_struct;
 #define JOBCTL_TRAPPING                (1UL << JOBCTL_TRAPPING_BIT)
 #define JOBCTL_LISTENING       (1UL << JOBCTL_LISTENING_BIT)
 #define JOBCTL_TRAP_FREEZE     (1UL << JOBCTL_TRAP_FREEZE_BIT)
+#define JOBCTL_TASK_WORK       (1UL << JOBCTL_TASK_WORK_BIT)
 
 #define JOBCTL_TRAP_MASK       (JOBCTL_TRAP_STOP | JOBCTL_TRAP_NOTIFY)
-#define JOBCTL_PENDING_MASK    (JOBCTL_STOP_PENDING | JOBCTL_TRAP_MASK)
+#define JOBCTL_PENDING_MASK    (JOBCTL_STOP_PENDING | JOBCTL_TRAP_MASK | JOBCTL_TASK_WORK)
 
 extern bool task_set_jobctl_pending(struct task_struct *task, unsigned long mask);
 extern void task_clear_jobctl_trapping(struct task_struct *task);
index bd9a6a91c097e2c8c0806590353e44ee2a1b9d23..0fb93aafa4785e981aa92ef7e805495e28bb778c 100644 (file)
@@ -13,7 +13,10 @@ init_task_work(struct callback_head *twork, task_work_func_t func)
        twork->func = func;
 }
 
-int task_work_add(struct task_struct *task, struct callback_head *twork, bool);
+#define TWA_RESUME     1
+#define TWA_SIGNAL     2
+int task_work_add(struct task_struct *task, struct callback_head *twork, int);
+
 struct callback_head *task_work_cancel(struct task_struct *, task_work_func_t);
 void task_work_run(void);
 
index 5ca48cc5da760d2a6ad6ef2497cd92be13c4f9f7..ee22ec78fd6d5c136429f5a2f67ca19d40307ba1 100644 (file)
@@ -2529,9 +2529,6 @@ bool get_signal(struct ksignal *ksig)
        struct signal_struct *signal = current->signal;
        int signr;
 
-       if (unlikely(current->task_works))
-               task_work_run();
-
        if (unlikely(uprobe_deny_signal()))
                return false;
 
@@ -2544,6 +2541,13 @@ bool get_signal(struct ksignal *ksig)
 
 relock:
        spin_lock_irq(&sighand->siglock);
+       current->jobctl &= ~JOBCTL_TASK_WORK;
+       if (unlikely(current->task_works)) {
+               spin_unlock_irq(&sighand->siglock);
+               task_work_run();
+               goto relock;
+       }
+
        /*
         * Every stopped thread goes here after wakeup. Check to see if
         * we should notify the parent, prepare_signal(SIGCONT) encodes
index 825f28259a19a285399171df9007ab01c19b42b1..5c0848ca1287df065303b3abbc3b4e3cb5fffa81 100644 (file)
@@ -25,9 +25,10 @@ static struct callback_head work_exited; /* all we need is ->next == NULL */
  * 0 if succeeds or -ESRCH.
  */
 int
-task_work_add(struct task_struct *task, struct callback_head *work, bool notify)
+task_work_add(struct task_struct *task, struct callback_head *work, int notify)
 {
        struct callback_head *head;
+       unsigned long flags;
 
        do {
                head = READ_ONCE(task->task_works);
@@ -36,8 +37,19 @@ task_work_add(struct task_struct *task, struct callback_head *work, bool notify)
                work->next = head;
        } while (cmpxchg(&task->task_works, head, work) != head);
 
-       if (notify)
+       switch (notify) {
+       case TWA_RESUME:
                set_notify_resume(task);
+               break;
+       case TWA_SIGNAL:
+               if (lock_task_sighand(task, &flags)) {
+                       task->jobctl |= JOBCTL_TASK_WORK;
+                       signal_wake_up(task, 0);
+                       unlock_task_sighand(task, &flags);
+               }
+               break;
+       }
+
        return 0;
 }