Work queues are, on the surface, similar to taskets; They allow kernel code to request a function to be called at some time in the future. However, there are several notable differences between these 2, including:
Tasklet the result of running in the context of software interrupts is that all Tasklet code must be atomic. Instead, work queue functions run in a particular kernel process context; As a result, they have more flexibility. In particular, work queue functions can sleep.
Tasklet are often run on the processor on which they were originally submitted. Work queues work the same way, by default.
The kernel code can request that the work queue function be deferred for a definite time interval.
The key difference between the two is that Tasklet executes very quickly, short periods, and in atomic states, while work queue functions may have high cycles but do not need to be atomic. Each mechanism has its suitable situation.
The Task Force column has a struct workqueue_struct type, defined in <linux/workqueue.h>. A Task force column must be explicitly created before use, using one of the following 2 functions:
struct workqueue_struct *create_workqueue (const char *name);
struct workqueue_struct *create_singlethread_workqueue (const char *name);
Each work queue has one or more dedicated processes ("Kernel threads") that run functions that are submitted to this queue. If you use Create_workqueue, you get a team column it has a dedicated thread on each processor in the system. In many cases, all of these threads are simply excessive behavior; If a single worker thread is sufficient, use create_singlethread_workqueue instead of creating a work queue
To submit a task to a Task Force column, you need to populate a work_struct structure. This can be done at compile time, as follows:
Declare_work (name, void (*function) (void *), void *data);
Here name is the name of the struct declared, function is called from the work queue, and data is a value passed to the function. If you need to build a work_struct structure at run time, use the following 2 macro definitions:
Init_work (struct work_struct *work, void (*function) (void *), void *data);
Prepare_work (struct work_struct *work, void (*function) (void *), void *data);
Init_work to do more comprehensive initialization of the structure of the work; You should use it the first time you build a structure. Prepare_work does almost the same job, but it does not initialize the pointer used to connect the WORK_STRUCT structure to the work queue. If there is any possibility that this structure is currently being submitted to a Task Force column, and you need to change the queue, use Prepare_work instead of init_work.
There are 2 functions to submit work to a Task Force column:
int queue_work (struct workqueue_struct *queue, struct work_struct);
int queue_delayed_work (struct workqueue_struct *queue, struct work_struct *work, unsigned long delay);
Each adds work to the given queue. If the queue_delay_work is used, however, the actual work does not proceed until at least delay jiffies has passed. The return value from these functions is 0 if the work is successfully joined to the queue; A non-0 result means that the WORK_STRUCT structure is already waiting in the queue and has not joined for the 2nd time.
At some point in the future, this work function will be invoked with the given data value. This function will run in the context of the worker thread, so it can sleep if needed-although you should know how this sleep may affect other tasks submitted to the same Task Force column. What this function cannot do, however, is to access user space. Because it runs in a kernel thread, there is absolutely no user space to access it.
If you need to cancel a pending task to include the port, you can call:
int cancel_delayed_work (struct work_struct *work);
The return value is 0 if this entry is canceled before it starts execution. The kernel guarantees that execution of a given portal is not initialized after calling Cancel_delay_work. If Cancel_delay_work returns 0, however, this entry may already be running on a different processor and may still be running after the call to Cancel_delayed_work. To be absolutely sure that the work function does not run anywhere after the Cancel_delayed_work returns 0, you must follow this call to invoke:
void Flush_workqueue (struct workqueue_struct *queue);
After the Flush_workqueue returns, no functions submitted before this call are run anywhere in the system.
When you run out of a task queue, you can remove it and use:
void destroy_workqueue (struct workqueue_struct);