有关workqueue 数据结构是怎样组织的?
/************************************************************************/
首先是per-cpu变量:struct global_cwq:赋值为:实际上per-cpu变量是global变量,可以直接得到
INIT_LIST_HEAD(&gcwq->worklist);/*这里的worklist指的是什么?*/
gcwq->cpu = cpu;
根据输入参数global_cwq创建执行实体worker
struct worker *create_worker(struct global_cwq *gcwq, bool bind)
创建workqueue_struct
struct workqueue_struct *wq;
wq = kzalloc(sizeof(*wq) + namelen, GFP_KERNEL);
INIT_LIST_HEAD(&wq->list);
list_add(&wq->list, &workqueues);
然后根据该workqueue_struct创建per-cpu变量 struct cpu_workqueue_struct:
到这里已经创建了变量:global_cwq/ worker; workqueue_struct/cpu_workqueue_struct
还有一个常见的work_struct没有create那?
这些数据结构是怎样组织在一起的那?
从进程的执行体开始看:
create_worker -> kthread_create_on_node/kthread_create: worker_thread./**
* worker_thread - the worker thread function
* @__worker: self
*
* The gcwq worker thread function. There's a single dynamic pool of
* these per each cpu. These workers process all works regardless of
* their specific target workqueue. The only exception is works which
* belong to workqueues with a rescuer which will be explained in
* rescuer_thread().
*/
static int worker_thread(void *__worker)
{
struct worker *worker = __worker;
struct global_cwq *gcwq = worker->gcwq;
/* tell the scheduler that this is a workqueue worker
* scheduler会对PF_WQ_WORKER进行特殊处理?
*/
worker->task->flags |= PF_WQ_WORKER;
/*worker_leave_idle:
*idle worker减1,从worker的 idle list中删除一项?
**/
worker_leave_idle(worker);
/* no more worker necessary?
*need_more_worker-> !list_empty(&gcwq->worklist)
*这里的worklist是什么元素:work_struct?
*/
if (!need_more_worker(gcwq))
goto sleep;
/*
* When control reaches this point, we're guaranteed to have
* at least one idle worker or that someone else has already
* assumed the manager role.
*/
worker_clr_flags(worker, WORKER_PREP);
do {
/*从gcwq->worklist得到其所在的work_struct*/
struct work_struct *work =
list_first_entry(&gcwq->worklist,
struct work_struct, entry);
/*最终调到process_scheduled_works
*worker->scheduled所在元素也是 work_struct,从这里看这个thread有两层循环
*外层是gcwq->worklist,内层是worker->scheduled
*static void process_scheduled_works(struct worker *worker)
*{
* while (!list_empty(&worker->scheduled)) {
* struct work_struct *work = list_first_entry(&worker->scheduled,
* struct work_struct, entry);
* process_one_work(worker, work);
* }
*}
**/
if (likely(!(*work_data_bits(work) & WORK_STRUCT_LINKED))) {
/* optimization path, not strictly necessary */
process_one_work(worker, work);
if (unlikely(!list_empty(&worker->scheduled)))
process_scheduled_works(worker);
} else {
move_linked_works(work, &worker->scheduled, NULL);
process_scheduled_works(worker);
}
} while (keep_working(gcwq));
worker_enter_idle(worker);
__set_current_state(TASK_INTERRUPTIBLE);
schedule();
goto woke_up;
}
/**
* process_one_work - process single work
* @worker: self
* @work: work to process
*
* Process @work. This function contains all the logics necessary to
* process a single work including synchronization against and
* interaction with other workers on the same cpu, queueing and
* flushing. As long as context requirement is met, any worker can
* call this function to process a work.
*/
static void process_one_work(struct worker *worker, struct work_struct *work)
{
struct cpu_workqueue_struct *cwq = get_work_cwq(work);
struct global_cwq *gcwq = cwq->gcwq;
work_func_t f = work->func;
work_clear_pending(work);
/*这个函数还是很复杂的,但是最后总是用调用 work function*/
f(work);
}
从这里看关键是怎样赋值gcwq->worklist and worker->scheduled
/*****************************************************************************//**还是从queue_work开始查吧!
* queue_work - queue work on a workqueue
* @wq: workqueue to use
* @work: work to queue
*
* Returns 0 if @work was already on a queue, non-zero otherwise.
*
* We queue the work to the CPU on which it was submitted, but if the CPU dies
* it can be processed by another CPU.
*/
int queue_work(struct workqueue_struct *wq, struct work_struct *work)
{
int ret;
ret = queue_work_on(get_cpu(), wq, work);
put_cpu();
return ret;
}
crash> work_struct
struct work_struct {
atomic_long_t data;
struct list_head entry;
work_func_t func;
}
SIZE: 16
/**
* queue_work_on - queue work on specific cpu
* @cpu: CPU number to execute work on
* @wq: workqueue to use
* @work: work to queue
*
* Returns 0 if @work was already on a queue, non-zero otherwise.
*
* We queue the work to a specific CPU, the caller must ensure it
* can't go away.
*/
int
queue_work_on(int cpu, struct workqueue_struct *wq, struct work_struct *work)
{
int ret = 0;
if (!test_and_set_bit(WORK_STRUCT_PENDING_BIT, work_data_bits(work))) {
__queue_work(cpu, wq, work);
ret = 1;
}
return ret;
}
/* WQ_UNBOUND = 1 << 1, not bound to any cpu */
static void __queue_work(unsigned int cpu, struct workqueue_struct *wq,
struct work_struct *work)
{
struct global_cwq *gcwq;
struct cpu_workqueue_struct *cwq;
struct list_head *worklist;
unsigned int work_flags;
gcwq = get_gcwq(cpu);
/* determine gcwq to use */
/* gcwq determined, get cwq and queue */
cwq = get_cwq(gcwq->cpu, wq);
work_flags = work_color_to_flags(cwq->work_color);
if (likely(cwq->nr_active < cwq->max_active)) {
cwq->nr_active++;/*number of active works*/
worklist = gcwq_determine_ins_pos(gcwq, cwq);
/*&gcwq->worklist;*/
} else {
work_flags |= WORK_STRUCT_DELAYED;
worklist = &cwq->delayed_works;
}/*得到cpu_workqueue_struct, worklist 把work加入到worklist中*/
insert_work(cwq, work, worklist, work_flags);
}
static void insert_work(struct cpu_workqueue_struct *cwq,
struct work_struct *work, struct list_head *head,
unsigned int extra_flags)
{
struct global_cwq *gcwq = cwq->gcwq;
list_add_tail(&work->entry, head);
wake_up_worker(gcwq);
}
/**
* wake_up_worker - wake up an idle worker
* @gcwq: gcwq to wake worker for
*
* Wake up the first idle worker of @gcwq.
*
* CONTEXT:
* spin_lock_irq(gcwq->lock).
*/
static void wake_up_worker(struct global_cwq *gcwq)
{
struct worker *worker = first_worker(gcwq);
if (likely(worker))
wake_up_process(worker->task);
}
static struct worker *first_worker(struct global_cwq *gcwq)
{
if (unlikely(list_empty(&gcwq->idle_list)))
return NULL;
return list_first_entry(&gcwq->idle_list, struct worker, entry);
}
到此,有关workqueue心里亮敞了吗?
怎样得到当前没有处理的wok_struct
crash> global_cwq
PER-CPU DATA TYPE:
struct global_cwq global_cwq;
PER-CPU ADDRESSES:
[0]: c1118680
[1]: c1120680
crash> set radix 16
output radix: 16 (hex)
crash> struct global_cwq -o
struct global_cwq {
[0x0] spinlock_t lock;
[0x14] struct list_head worklist;
[0x1c] unsigned int cpu;
[0x20] unsigned int flags;
[0x24] int nr_workers;
[0x28] int nr_idle;
[0x2c] struct list_head idle_list;
[0x34] struct hlist_head busy_hash[64];
[0x134] struct timer_list idle_timer;
[0x150] struct timer_list mayday_timer;
[0x16c] struct ida worker_ida;
[0x194] struct task_struct *trustee;
[0x198] unsigned int trustee_state;
[0x19c] wait_queue_head_t trustee_wait;
[0x1b8] struct worker *first_idle;
}
SIZE: 0x1c0
crash> struct global_cwq c1118680
struct global_cwq {
lock = {
{
rlock = {
raw_lock = {
lock = 0
},
break_lock = 0,
magic = 3735899821,
owner_cpu = 4294967295,
owner = 0xffffffff
}
}
},
worklist = {
next = 0xc1118694,
prev = 0xc1118694
},
cpu = 0,
}
c1118680+14 = c1118694:
可知这里的worklist指向header, 所以是空。当前没有未被处理的work_struct.