__schedule() // disable irqs <NMI> task_work_add(current, work, TWA_NMI_CURRENT); </NMI> // current = next; // enable irqs <IRQ> task_work_set_notify_irq() test_and_set_tsk_thread_flag(current, TIF_NOTIFY_RESUME); // wrong task! </IRQ> // original task skips task work on its next return to user (or exit!)
Unified · Split
| @@ -9,7 +9,12 @@ static struct callback_head work_exited; /* all we need is ->next == NULL */ · 8 unmodified lines | ||
| 9 | 9 | #ifdef CONFIG_IRQ_WORK |
| 10 | 10 | static void task_work_set_notify_irq(struct irq_work *entry) |
| 11 | 11 | { |
| 12 | − test_and_set_tsk_thread_flag(current, TIF_NOTIFY_RESUME); | |
| 12 | + /* | |
| 13 | + * no-op IPI | |
| 14 | + * | |
| 15 | + * TWA_NMI_CURRENT will already have set the TIF flag, all | |
| 16 | + * this interrupt does it tickle the return-to-user path. | |
| 17 | + */ | |
| 13 | 18 | } |
| 14 | 19 | static DEFINE_PER_CPU(struct irq_work, irq_work_NMI_resume) = |
| 15 | 20 | IRQ_WORK_INIT_HARD(task_work_set_notify_irq); |
| @@ -86,6 +91,7 @@ int task_work_add(struct task_struct *task, struct callback_head *work, · 70 unmodified lines | ||
| 86 | 91 | break; |
| 87 | 92 | #ifdef CONFIG_IRQ_WORK |
| 88 | 93 | case TWA_NMI_CURRENT: |
| 94 | + set_tsk_thread_flag(current, TIF_NOTIFY_RESUME); | |
| 89 | 95 | irq_work_queue(this_cpu_ptr(&irq_work_NMI_resume)); |
| 90 | 96 | break; |
| 91 | 97 | #endif |