Wait queue

2,634 views

Published on

outdated

0 Comments
3 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,634
On SlideShare
0
From Embeds
0
Number of Embeds
12
Actions
Shares
0
Downloads
54
Comments
0
Likes
3
Embeds 0
No embeds

No notes for slide

Wait queue

  1. 1. Wait Queue & Wait Queue Head task_struct task_struct task_struct task task task prev next prev prev next ... prev next next lock flag func flag func flag func wait_queue_head wait_queue wait_queue wait_queue autoremove_wake_function() default_wake_function() Other_callback_function 1
  2. 2. wait_queue_head_tstruct __wait_queue_head { prev spinlock_t lock; next struct list_head task_list; lock};typedef struct __wait_queue_head wait_queue_head_t; wait_queue_head#define DECLARE_WAIT_QUEUE_HEAD(name) wait_queue_head_t name = __WAIT_QUEUE_HEAD_INITIALIZER(name)#define __WAIT_QUEUE_HEAD_INITIALIZER(name) { .lock = SPIN_LOCK_UNLOCKED, .task_list = { &(name).task_list, &(name).task_list } } 2
  3. 3. wait_queue_tstruct __wait_queue { unsigned int flags;#define WQ_FLAG_EXCLUSIVE 0x01 task struct task_struct * task; wait_queue_func_t func; prev next struct list_head task_list; flag func}; wait_queuetypedef struct __wait_queue wait_queue_t;#define DECLARE_WAITQUEUE(name, tsk) wait_queue_t name = __WAITQUEUE_INITIALIZER(name, tsk)#define __WAITQUEUE_INITIALIZER(name, tsk) { .task = tsk, .func = default_wake_function, .task_list = { NULL, NULL } } 3
  4. 4. Wake Up Control Flow [include/linux/wait.h] [kernel/sched.c]wait_event() wake_up(x) __wait_event() __wake_up() assign a handler DEFINE_WAIT() __wake_up_common() prepare_to_wait() curr->func() __add_wait_queue() loop try_to_wake_up() if (condition)? no yes resched_task schedule() finish_wait autoremove_wake_function() 4
  5. 5. wait_event() & __wait_event()#define wait_event(wq, condition) do { if (condition) break; __wait_event(wq, condition); } while (0)#define __wait_event(wq, condition) do { DEFINE_WAIT(__wait); for (;;) { prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE); if (condition) break; schedule(); } finish_wait(&wq, &__wait); } while (0) 5
  6. 6. DEFINE_WAIT() &autoremove_wake_function()#define DEFINE_WAIT(name) wait_queue_t name = { .task = current, .func = autoremove_wake_function, .task_list = LIST_HEAD_INIT((name).task_list), }int autoremove_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key){ int ret = default_wake_function(wait, mode, sync, key); if (ret) list_del_init(&wait->task_list); return ret;} 6
  7. 7. prepare_to_wait()void fastcallprepare_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); /* * dont alter the task state if this is just going to * queue an async wait queue callback */ task if (is_sync_wait(wait)) set_current_state(state); prev next spin_unlock_irqrestore(&q->lock, flags);} flag func wait_queue Used to distinguish between sync and async io wait context 7
  8. 8. finish_wait()void fastcall finish_wait(wait_queue_head_t *q, wait_queue_t *wait){ unsigned long flags; __set_current_state(TASK_RUNNING);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); }} 8
  9. 9. wait_event*() [include/linux/wait.h]wait_event() wait_event_interruptible() __wait_event() __wait_event_interruptible() ... ...wait_event_timeout() wait_event_interruptible_timeout() __wait_event_timeout() __wait_event_interruptible_timeout() ... ...wait_event_exclusive() wait_event_interruptible_exclusive() __wait_event_exclusive() __wait_event_interruptible_exclusive() ... ... 9
  10. 10. __wait_event_interruptible()#define wait_event_interruptible(wq, condition) ({ int __ret = 0; if (!(condition)) __wait_event_interruptible(wq, condition, __ret); __ret; })#define __wait_event_interruptible(wq, condition, ret) do { DEFINE_WAIT(__wait); for (;;) { prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE); if (condition) break; if (!signal_pending(current)) { schedule(); continue; } ret = -ERESTARTSYS; break; } finish_wait(&wq, &__wait); } while (0) 10
  11. 11. __wait_event_timeout()#define wait_event_timeout(wq, condition, timeout) ({ long __ret = timeout; if (!(condition)) __wait_event_timeout(wq, condition, __ret); __ret; })#define __wait_event_timeout(wq, condition, ret) do { DEFINE_WAIT(__wait); for (;;) { prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE); if (condition) break; ret = schedule_timeout(ret); if (!ret) break; } finish_wait(&wq, &__wait); } while (0) 11
  12. 12. __wait_event_interruptible_exclusive()#define wait_event_interruptible_timeout(wq, condition, timeout) ({ long __ret = timeout; if (!(condition)) __wait_event_interruptible_timeout(wq, condition, __ret); __ret; })#define __wait_event_interruptible_exclusive(wq, condition, ret) do { DEFINE_WAIT(__wait); for (;;) { prepare_to_wait_exclusive(&wq, &__wait, TASK_INTERRUPTIBLE); if (condition) break; if (!signal_pending(current)) { schedule(); continue; } ret = -ERESTARTSYS; break; } finish_wait(&wq, &__wait); } while (0) 12
  13. 13. wake up*() [include/linux/wait.h] [kernel/sched.c]#define wake_up(x) __wake_up(x, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, 1, NULL)#define wake_up_nr(x, nr) __wake_up(x, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, nr, NULL)#define wake_up_all(x) __wake_up(x, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, 0, NULL)#define wake_up_interruptible(x) __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)#define wake_up_interruptible_nr(x, nr) __wake_up(x, TASK_INTERRUPTIBLE, nr, NULL)#define wake_up_interruptible_all(x) __wake_up(x, TASK_INTERRUPTIBLE, 0, NULL)#define wake_up_locked(x) __wake_up_locked((x), TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE)#define wake_up_interruptible_sync(x) __wake_up_sync((x),TASK_INTERRUPTIBLE, 1) 13
  14. 14. wake up*() [include/linux/wait.h] [kernel/sched.c]init_waitqueue_entry()wake_up(x) wake_up_nr(x, nr) wake_up_all(x) wake_up_interruptible_sync(x) wake_up_locked(x) __wake_up() __wake_up_locked() __wake_up_sync() __wake_up_common(q,…) assigndefault_wake_function() q->func() try_to_wake_up() 14
  15. 15. __wake_up()void fastcall __wake_up(wait_queue_head_t *q, unsigned int mode, int nr_exclusive, void *key){ unsigned long flags; spin_lock_irqsave(&q->lock, flags); __wake_up_common(q, mode, nr_exclusive, 0, key); spin_unlock_irqrestore(&q->lock, flags);} 15
  16. 16. __wake_up_common()static void __wake_up_common(wait_queue_head_t *q, unsigned int mode, int nr_exclusive, int sync, void *key){ struct list_head *tmp, *next; list_for_each_safe(tmp, next, &q->task_list) { wait_queue_t *curr; unsigned flags; curr = list_entry(tmp, wait_queue_t, task_list); flags = curr->flags; if (curr->func(curr, mode, sync, key) && (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive) break; }} 16
  17. 17. default_wake_function()int default_wake_function(wait_queue_t *curr, unsigned mode, int sync, void *key){ task_t *p = curr->task; return try_to_wake_up(p, mode, sync);} 17
  18. 18. try_to_wake_up() (1/2) SMP part omittedstatic int try_to_wake_up(task_t * p, unsigned int state, int sync){ int cpu, this_cpu, success = 0; unsigned long flags; long old_state; runqueue_t *rq; rq = task_rq_lock(p, &flags); old_state = p->state; if (!(old_state & state)) goto out; if (p->array) goto out_running; cpu = task_cpu(p); this_cpu = smp_processor_id(); 18
  19. 19. try_to_wake_up() (2/2) SMP part omitted if (old_state == TASK_UNINTERRUPTIBLE) { rq->nr_uninterruptible--; p->activated = -1; } The waker said “I will leave the cpu soon, so you don’t have to do trigger a preemption.” activate_task(p, rq, cpu == this_cpu); if (!sync || cpu != this_cpu) { if (TASK_PREEMPTS_CURR(p, rq)) Put it back into the runqueue resched_task(rq->curr); } success = 1; #define TASK_PREEMPTS_CURR(p, rq) out_running: ((p)->prio < (rq)->curr->prio) p->state = TASK_RUNNING;out: task_rq_unlock(rq, &flags); static inline void resched_task(task_t *p) return success; {} set_tsk_need_resched(p); } 19
  20. 20. list_headstruct list_head { struct list_head *next, *prev;};list_add(new, head)list_add_tail(new, head)list_del(entry)list_empty(head)list_entry(head,t,member)list_for_each(pos,head)... 20

×