linux/include/linux/rcuwait.h
<<
>>
Prefs
   1/* SPDX-License-Identifier: GPL-2.0 */
   2#ifndef _LINUX_RCUWAIT_H_
   3#define _LINUX_RCUWAIT_H_
   4
   5#include <linux/rcupdate.h>
   6
   7/*
   8 * rcuwait provides a way of blocking and waking up a single
   9 * task in an rcu-safe manner; where it is forbidden to use
  10 * after exit_notify(). task_struct is not properly rcu protected,
  11 * unless dealing with rcu-aware lists, ie: find_task_by_*().
  12 *
  13 * Alternatively we have task_rcu_dereference(), but the return
  14 * semantics have different implications which would break the
  15 * wakeup side. The only time @task is non-nil is when a user is
  16 * blocked (or checking if it needs to) on a condition, and reset
  17 * as soon as we know that the condition has succeeded and are
  18 * awoken.
  19 */
  20struct rcuwait {
  21        struct task_struct __rcu *task;
  22};
  23
  24#define __RCUWAIT_INITIALIZER(name)             \
  25        { .task = NULL, }
  26
  27static inline void rcuwait_init(struct rcuwait *w)
  28{
  29        w->task = NULL;
  30}
  31
  32extern void rcuwait_wake_up(struct rcuwait *w);
  33
  34/*
  35 * The caller is responsible for locking around rcuwait_wait_event(),
  36 * such that writes to @task are properly serialized.
  37 */
  38#define rcuwait_wait_event(w, condition)                                \
  39({                                                                      \
  40        /*                                                              \
  41         * Complain if we are called after do_exit()/exit_notify(),     \
  42         * as we cannot rely on the rcu critical region for the         \
  43         * wakeup side.                                                 \
  44         */                                                             \
  45        WARN_ON(current->exit_state);                                   \
  46                                                                        \
  47        rcu_assign_pointer((w)->task, current);                         \
  48        for (;;) {                                                      \
  49                /*                                                      \
  50                 * Implicit barrier (A) pairs with (B) in               \
  51                 * rcuwait_wake_up().                                   \
  52                 */                                                     \
  53                set_current_state(TASK_UNINTERRUPTIBLE);                \
  54                if (condition)                                          \
  55                        break;                                          \
  56                                                                        \
  57                schedule();                                             \
  58        }                                                               \
  59                                                                        \
  60        WRITE_ONCE((w)->task, NULL);                                    \
  61        __set_current_state(TASK_RUNNING);                              \
  62})
  63
  64#endif /* _LINUX_RCUWAIT_H_ */
  65