task_work.c 1.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. #include <linux/spinlock.h>
  2. #include <linux/task_work.h>
  3. #include <linux/tracehook.h>
  4. int
  5. task_work_add(struct task_struct *task, struct task_work *twork, bool notify)
  6. {
  7. unsigned long flags;
  8. int err = -ESRCH;
  9. #ifndef TIF_NOTIFY_RESUME
  10. if (notify)
  11. return -ENOTSUPP;
  12. #endif
  13. /*
  14. * We must not insert the new work if the task has already passed
  15. * exit_task_work(). We rely on do_exit()->raw_spin_unlock_wait()
  16. * and check PF_EXITING under pi_lock.
  17. */
  18. raw_spin_lock_irqsave(&task->pi_lock, flags);
  19. if (likely(!(task->flags & PF_EXITING))) {
  20. struct task_work *last = task->task_works;
  21. struct task_work *first = last ? last->next : twork;
  22. twork->next = first;
  23. if (last)
  24. last->next = twork;
  25. task->task_works = twork;
  26. err = 0;
  27. }
  28. raw_spin_unlock_irqrestore(&task->pi_lock, flags);
  29. /* test_and_set_bit() implies mb(), see tracehook_notify_resume(). */
  30. if (likely(!err) && notify)
  31. set_notify_resume(task);
  32. return err;
  33. }
  34. struct task_work *
  35. task_work_cancel(struct task_struct *task, task_work_func_t func)
  36. {
  37. unsigned long flags;
  38. struct task_work *last, *res = NULL;
  39. raw_spin_lock_irqsave(&task->pi_lock, flags);
  40. last = task->task_works;
  41. if (last) {
  42. struct task_work *q = last, *p = q->next;
  43. while (1) {
  44. if (p->func == func) {
  45. q->next = p->next;
  46. if (p == last)
  47. task->task_works = q == p ? NULL : q;
  48. res = p;
  49. break;
  50. }
  51. if (p == last)
  52. break;
  53. q = p;
  54. p = q->next;
  55. }
  56. }
  57. raw_spin_unlock_irqrestore(&task->pi_lock, flags);
  58. return res;
  59. }
  60. void task_work_run(void)
  61. {
  62. struct task_struct *task = current;
  63. struct task_work *p, *q;
  64. raw_spin_lock_irq(&task->pi_lock);
  65. p = task->task_works;
  66. task->task_works = NULL;
  67. raw_spin_unlock_irq(&task->pi_lock);
  68. if (unlikely(!p))
  69. return;
  70. q = p->next; /* head */
  71. p->next = NULL; /* cut it */
  72. while (q) {
  73. p = q->next;
  74. q->func(q);
  75. q = p;
  76. }
  77. }