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; } |