Working queue usage

Source: Internet
Author: User
Tags signal handler

 

I. Usage
Struct cpu_workqueue_struct {

Spinlock_t lock;

Long remove_sequence;/* Least-recently added (next to run )*/
Long insert_sequence;/* next to add */

Struct list_head worklist;
Wait_queue_head_t more_work;
Wait_queue_head_t work_done;

Struct workqueue_struct * WQ;
Struct task_struct * thread;

Int run_depth;/* detect run_workqueue () recursion depth */
}____ Cacheline_aligned;

/*
* The externally visible workqueue specified action is an array
* Per-CPU workqueues:
*/
Struct workqueue_struct {
Struct cpu_workqueue_struct * cpu_wq;
Const char * Name;
Struct list_head list;/* empty if single thread */
};

Working queue is easy to use.
1. First, create a work queue:
Struct workqueue_struct * keventd_wq;
Keventd_wq = create_workqueue ("events ");
2. Then insert the "job" you want to do in this queue ":
Declare_work (work, func, data)
Queue_work (keventd_wq, work );

Struct work_struct {
Unsigned long pending;
Struct list_head entry;
Void (* func) (void *);
Void * data;
Void * wq_data;
Struct timer_list timer;
};

There are two methods for initialization.
One is static:
# DEFINE _ work_initializer (n, F, d ){\
. Entry = {& (N). Entry, & (N). Entry },\
. Func = (F ),\
. Data = (D ),\
. Timer = timer_initializer (null, 0, 0 ),\
}

# Define declare_work (n, F, d )\
Struct work_struct n = _ work_initializer (n, F, d)

Another dynamic method:
/*
* Initialize all of a work-struct:
*/
# Define init_work (_ work, _ FUNC, _ DATA )\
Do {\
Init_list_head (& (_ WORK)-> entry );\
(_ WORK)-> pending = 0 ;\
Prepare_work (_ work), (_ FUNC), (_ DATA ));\
Init_timer (& (_ WORK)-> timer );\
} While (0)

Ii. Execution Process
Create_workqueue ()-> _ create_workqueue ()

Struct workqueue_struct * _ create_workqueue (const char * Name,
Int singlethread)
{
Int CPU, destroy = 0;
Struct workqueue_struct * WQ;
Struct task_struct * P;

WQ = kzarloc (sizeof (* WQ), gfp_kernel );
// Create a structure for each CPU
WQ-> cpu_wq = alloc_percpu (struct cpu_workqueue_struct );
...
WQ-> name = Name;
Mutex_lock (& workqueue_mutex );
If (singlethread ){
...
} Else {
List_add (& WQ-> list, & workqueues );
For_each_online_cpu (CPU ){
// Create a thread for each CPU
P = create_workqueue_thread (WQ, CPU );
If (p ){
Kthread_bind (p, CPU );
// Wake up this thread for execution
Wake_up_process (P );
} Else
Destroy = 1;
}
}
Mutex_unlock (& workqueue_mutex );
...
Return WQ;
}

Create_workqueue ()-> _ create_workqueue ()-> create_workqueue_thread ()

Static struct task_struct * create_workqueue_thread (struct workqueue_struct * WQ,
Int CPU)
{
Struct cpu_workqueue_struct * cwq = per_cpu_ptr (WQ-> cpu_wq, CPU );
Struct task_struct * P;

Spin_lock_init (& cwq-> lock );
Cwq-> WQ = WQ;
Cwq-> thread = NULL;
Cwq-> insert_sequence = 0;
Cwq-> remove_sequence = 0;
Init_list_head (& cwq-> worklist );
Init_waitqueue_head (& cwq-> more_work );
Init_waitqueue_head (& cwq-> work_done );

If (is_single_threaded (WQ ))
P = kthread_create (worker_thread, cwq, "% s", WQ-> name );
Else
// Create a thread. This thread uses cwq as the data to execute the worker_thread function.
P = kthread_create (worker_thread, cwq, "% S/% d", WQ-> name, CPU );
If (is_err (p ))
Return NULL;
Cwq-> thread = P ;//
Return P;
}

Create_workqueue ()-> _ create_workqueue ()-> create_workqueue_thread ()-> worker_thread ()

// This function waits for the arrival of work in an endless loop, which is usually in sleep state, waiting for the wake-up to execute the work
// When work arrives, queue_work () will wake up this thread
Static int worker_thread (void * _ cwq)
{
Struct cpu_workqueue_struct * cwq = _ cwq;
Declare_waitqueue (wait, current );
...

Current-> flags | = pf_nofreeze;

// Set the priority
Set_user_nice (current,-5 );
...
Set_current_state (task_interruptible );
While (! Kthread_should_stop ()){
// Add this thread to the sleep queue for waking after sleep
Add_wait_queue (& cwq-> more_work, & wait );

// If no "work" is executed, switch yourself out to sleep
If (list_empty (& cwq-> worklist ))
Schedule ();
Else // otherwise it may be awakened
_ Set_current_state (task_running );
Remove_wait_queue (& cwq-> more_work, & wait );

// The work queue is not empty.
If (! List_empty (& cwq-> worklist ))
Run_workqueue (cwq );
Set_current_state (task_interruptible );
}
_ Set_current_state (task_running );
Return 0;
}

Create_workqueue ()-> _ create_workqueue ()-> create_workqueue_thread ()
-> Worker_thread ()-> run_workqueue ()
// This function executes real work
Static void run_workqueue (struct cpu_workqueue_struct * cwq)
{
Unsigned long flags;

Spin_lock_irqsave (& cwq-> lock, flags );
...
// Perform all tasks in the queue in sequence
While (! List_empty (& cwq-> worklist )){
Struct work_struct * Work = list_entry (cwq-> worklist. Next,
Struct work_struct, entry );
Void (* f) (void *) = work-> func;
Void * Data = work-> data;

// Delete the task to be executed from the queue
List_del_init (cwq-> worklist. Next );
Spin_unlock_irqrestore (& cwq-> lock, flags );

Bug_on (work-> wq_data! = Cwq );
Clear_bit (0, & work-> pending );

// Execute "work"
F (data );

Spin_lock_irqsave (& cwq-> lock, flags );
Cwq-> remove_sequence ++;
Wake_up (& cwq-> work_done );//
}
Cwq-> run_depth --;
Spin_unlock_irqrestore (& cwq-> lock, flags );
}

Iii. detailed process of creating a working thread
Create_workqueue ()-> _ create_workqueue ()-> create_workqueue_thread ()
-> Kthread_create ()

Struct task_struct * kthread_create (INT (* threadfn) (void * data ),
Void * data,
Const char namefmt [],
...)
{
// Initialize the auxiliary structure used to create a thread
Struct kthread_create_info create;
Declare_work (work, keventd_create_kthread, & create );
Create. threadfn = threadfn;
Create. Data = data;
Init_completion (& create. Started );
Init_completion (& create. Done );

If (! Helper_wq) // first create a secondary work queue
Work. func (work. data );
Else {
// Note that "create a work queue" is a helper_wq work queue.
//. Therefore, the worker is added to the auxiliary work queue for execution.
Queue_work (helper_wq, & work );
Wait_for_completion (& create. Done );
}
...
Return create. result;
}

Create_workqueue ()-> _ create_workqueue ()-> create_workqueue_thread ()
-> Kthread_create ()-> keventd_create_kthread ()
// It will eventually call kernel_thread to create a thread for each working queue
// In this way, the created thread will execute kthread (as follows) as the data in create, while threadfn (data) in create in kthread ),
// Worker_thread (cwq) in create_workqueue_thread is the function to be executed in our work queue.

Static void keventd_create_kthread (void * _ create)
{
Struct kthread_create_info * Create = _ create;
Int PID;

/* We want our own signal handler (we take no signals by default ).*/
PID = kernel_thread (kthread, create, clone_fs | clone_files | sigchld );
If (PID <0 ){
Create-> result = err_ptr (PID );
} Else {
Wait_for_completion (& create-> started );
Read_lock (& tasklist_lock );
Create-> result = find_task_by_pid (PID );
Read_unlock (& tasklist_lock );
}
Complete (& create-> done );
}

Static int kthread (void * _ create)
{
Struct kthread_create_info * Create = _ create;
INT (* threadfn) (void * data );
Void * data;
...
Threadfn = create-> threadfn;
Data = create-> data;
...
If (! Kthread_should_stop ())
Ret = threadfn (data );
...
Return 0;
}

4. Insert "work"
/* Preempt must be disabled .*/
Static void _ queue_work (struct cpu_workqueue_struct * cwq,
Struct work_struct * Work)
{
Unsigned long flags;

Spin_lock_irqsave (& cwq-> lock, flags );
Work-> wq_data = cwq;
// Insert the current job to the work queue for execution
List_add_tail (& work-> entry, & cwq-> worklist );
Cwq-> insert_sequence ++;
Wake_up (& cwq-> more_work); // wake up the corresponding thread
Spin_unlock_irqrestore (& cwq-> lock, flags );
}

Int fastcall queue_work (struct workqueue_struct * WQ, struct work_struct * Work)
{
Int ret = 0, CPU = get_cpu ();

// If the current job is not inserted in the queue
If (! Test_and_set_bit (0, & work-> pending )){
...
_ Queue_work (per_cpu_ptr (WQ-> cpu_wq, CPU), work );
Ret = 1;
}
Put_cpu ();
Return ret;
}

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.