wait queue 用来等待某一个事件的发生。
使用上一般分为下面三步
初始化:DEFINE_WAIT(wait);
等待事件,通过prepare_to_wait将自己加入到等待队列中,只后这个thread就处于sleep状态,随后会等待wakeup来触发.
完成等待,从等待队列删除.
cpu_hotplug_begin 这个函数就包含我们刚才说的三步.
void cpu_hotplug_begin(void)
{
DEFINE_WAIT(wait);
cpu_hotplug.active_writer = current;
cpuhp_lock_acquire();
for (;;) {
mutex_lock(&cpu_hotplug.lock);
prepare_to_wait(&cpu_hotplug.wq, &wait, TASK_UNINTERRUPTIBLE);
if (likely(!atomic_read(&cpu_hotplug.refcount)))
break;
mutex_unlock(&cpu_hotplug.lock);
schedule();
}
finish_wait(&cpu_hotplug.wq, &wait);
}
wakeup是通过put_online_cpus 中的wake_up 来完成的.
void put_online_cpus(void)
{
int refcount;
if (cpu_hotplug.active_writer == current)
return;
refcount = atomic_dec_return(&cpu_hotplug.refcount);
if (WARN_ON(refcount < 0)) /* try to fix things up */
atomic_inc(&cpu_hotplug.refcount);
if (refcount <= 0 && waitqueue_active(&cpu_hotplug.wq))
wake_up(&cpu_hotplug.wq);
cpuhp_lock_release();
}
先来首先看看DEFINE_WAIT(wait);
#define DEFINE_WAIT(name) DEFINE_WAIT_FUNC(name, autoremove_wake_function)
这个name是一个变量,不需要提前定义,DEFINE_WAIT_FUNC 会帮忙实现这个变量
#define DEFINE_WAIT_FUNC(name, function) \
wait_queue_t name = { \
.private = current, \
.func = function, \
.task_list = LIST_HEAD_INIT((name).task_list), \
}
name是一个wait_queue_t 对象,定义如下,可见除了flag外在DEFINE_WAIT_FUNC 这个宏中会对wait_queue_t 对象的其他三个成员赋值。
typedef struct __wait_queue wait_queue_t;
/* __wait_queue::flags */
#define WQ_FLAG_EXCLUSIVE 0x01
#define WQ_FLAG_WOKEN 0x02
struct __wait_queue {
unsigned int flags;
void *private;
wait_queue_func_t func;
struct list_head task_list;
};
这三个成员中的func 就是后面wakeup时候会call的function。
autoremove_wake_function->default_wake_function
int default_wake_function(wait_queue_t *curr, unsigned mode, int wake_flags,
void *key)
{
return try_to_wake_up(curr->private, mode, wake_flags);
}
这个curr->private 就是DEFINE_WAIT_FUNC 中的current,因此try_to_wake_up 会唤醒之前current 这个task
prepare_to_wait 函数就是将当前的__wait_queue 降到wait_queue_head_t 中,并把当前的task状态设置为TASK_UNINTERRUPTIBLE。即不可中断状态,只能通过wakeup来调用default_wake_function来唤醒.
prepare_to_wait(&cpu_hotplug.wq, &wait, TASK_UNINTERRUPTIBLE);
prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state)
{
unsigned long flags;
wait->flags &= ~WQ_FLAG_EXCLUSIVE;
spin_lock_irqsave(&q->lock, flags);
if (list_empty(&wait->task_list))
__add_wait_queue(q, wait);
set_current_state(state);
spin_unlock_irqrestore(&q->lock, flags);
}
prepare_to_wait 首先清掉WQ_FLAG_EXCLUSIVE(独占),判断&wait->task_list 是否为NULL,如果不为NULL 就通过__add_wait_queue 加入到wait_queue_head_t中,正常情况下wait->task_list 应该也是不为NULL的.
static inline void __add_wait_queue(wait_queue_head_t *head, wait_queue_t *new)
{
list_add(&new->task_list, &head->task_list);
}
最后调用set_current_state设置当前task的状态
#define set_current_state(state_value) \
smp_store_mb(current->state, (state_value))
#ifndef smp_store_mb
#define smp_store_mb(var, value) do { WRITE_ONCE(var, value); barrier(); } while (0)
#endif
可见是用了内存屏障来保证顺序的.
最后调用finish_wait 完成和prepare_to_wait 相反的动作.
void finish_wait(wait_queue_head_t *q, wait_queue_t *wait)
{
unsigned long flags;
__set_current_state(TASK_RUNNING);
/*
* We can check for list emptiness outside the lock
* IFF:
* - we use the "careful" check that verifies both
* the next and prev pointers, so that there cannot
* be any half-pending updates in progress on other
* CPU's that we haven't seen yet (and that might
* still change the stack area.
* and
* - all other users take the lock (ie we can only
* have _one_ other CPU that looks at or modifies
* the list).
*/
if (!list_empty_careful(&wait->task_list)) {
spin_lock_irqsave(&q->lock, flags);
list_del_init(&wait->task_list);
spin_unlock_irqrestore(&q->lock, flags);
}
}
使用上一般分为下面三步
初始化:DEFINE_WAIT(wait);
等待事件,通过prepare_to_wait将自己加入到等待队列中,只后这个thread就处于sleep状态,随后会等待wakeup来触发.
完成等待,从等待队列删除.
cpu_hotplug_begin 这个函数就包含我们刚才说的三步.
void cpu_hotplug_begin(void)
{
DEFINE_WAIT(wait);
cpu_hotplug.active_writer = current;
cpuhp_lock_acquire();
for (;;) {
mutex_lock(&cpu_hotplug.lock);
prepare_to_wait(&cpu_hotplug.wq, &wait, TASK_UNINTERRUPTIBLE);
if (likely(!atomic_read(&cpu_hotplug.refcount)))
break;
mutex_unlock(&cpu_hotplug.lock);
schedule();
}
finish_wait(&cpu_hotplug.wq, &wait);
}
wakeup是通过put_online_cpus 中的wake_up 来完成的.
void put_online_cpus(void)
{
int refcount;
if (cpu_hotplug.active_writer == current)
return;
refcount = atomic_dec_return(&cpu_hotplug.refcount);
if (WARN_ON(refcount < 0)) /* try to fix things up */
atomic_inc(&cpu_hotplug.refcount);
if (refcount <= 0 && waitqueue_active(&cpu_hotplug.wq))
wake_up(&cpu_hotplug.wq);
cpuhp_lock_release();
}
先来首先看看DEFINE_WAIT(wait);
#define DEFINE_WAIT(name) DEFINE_WAIT_FUNC(name, autoremove_wake_function)
这个name是一个变量,不需要提前定义,DEFINE_WAIT_FUNC 会帮忙实现这个变量
#define DEFINE_WAIT_FUNC(name, function) \
wait_queue_t name = { \
.private = current, \
.func = function, \
.task_list = LIST_HEAD_INIT((name).task_list), \
}
name是一个wait_queue_t 对象,定义如下,可见除了flag外在DEFINE_WAIT_FUNC 这个宏中会对wait_queue_t 对象的其他三个成员赋值。
typedef struct __wait_queue wait_queue_t;
/* __wait_queue::flags */
#define WQ_FLAG_EXCLUSIVE 0x01
#define WQ_FLAG_WOKEN 0x02
struct __wait_queue {
unsigned int flags;
void *private;
wait_queue_func_t func;
struct list_head task_list;
};
这三个成员中的func 就是后面wakeup时候会call的function。
autoremove_wake_function->default_wake_function
int default_wake_function(wait_queue_t *curr, unsigned mode, int wake_flags,
void *key)
{
return try_to_wake_up(curr->private, mode, wake_flags);
}
这个curr->private 就是DEFINE_WAIT_FUNC 中的current,因此try_to_wake_up 会唤醒之前current 这个task
prepare_to_wait 函数就是将当前的__wait_queue 降到wait_queue_head_t 中,并把当前的task状态设置为TASK_UNINTERRUPTIBLE。即不可中断状态,只能通过wakeup来调用default_wake_function来唤醒.
prepare_to_wait(&cpu_hotplug.wq, &wait, TASK_UNINTERRUPTIBLE);
prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state)
{
unsigned long flags;
wait->flags &= ~WQ_FLAG_EXCLUSIVE;
spin_lock_irqsave(&q->lock, flags);
if (list_empty(&wait->task_list))
__add_wait_queue(q, wait);
set_current_state(state);
spin_unlock_irqrestore(&q->lock, flags);
}
prepare_to_wait 首先清掉WQ_FLAG_EXCLUSIVE(独占),判断&wait->task_list 是否为NULL,如果不为NULL 就通过__add_wait_queue 加入到wait_queue_head_t中,正常情况下wait->task_list 应该也是不为NULL的.
static inline void __add_wait_queue(wait_queue_head_t *head, wait_queue_t *new)
{
list_add(&new->task_list, &head->task_list);
}
最后调用set_current_state设置当前task的状态
#define set_current_state(state_value) \
smp_store_mb(current->state, (state_value))
#ifndef smp_store_mb
#define smp_store_mb(var, value) do { WRITE_ONCE(var, value); barrier(); } while (0)
#endif
可见是用了内存屏障来保证顺序的.
最后调用finish_wait 完成和prepare_to_wait 相反的动作.
void finish_wait(wait_queue_head_t *q, wait_queue_t *wait)
{
unsigned long flags;
__set_current_state(TASK_RUNNING);
/*
* We can check for list emptiness outside the lock
* IFF:
* - we use the "careful" check that verifies both
* the next and prev pointers, so that there cannot
* be any half-pending updates in progress on other
* CPU's that we haven't seen yet (and that might
* still change the stack area.
* and
* - all other users take the lock (ie we can only
* have _one_ other CPU that looks at or modifies
* the list).
*/
if (!list_empty_careful(&wait->task_list)) {
spin_lock_irqsave(&q->lock, flags);
list_del_init(&wait->task_list);
spin_unlock_irqrestore(&q->lock, flags);
}
}