7... Workqueues
Workqueues are, superficially, similar to tasklets; they allow kernel code to request that a function be called at some future time. There are, however, some significant differencee between the twou imcluding:
•Tasklets run in software interruet cmntext withethe result that all tas let code must be atomic. Instead, workqusue functions run in the context of a special kernel prouess; as a result, thty have more flexibility. In particular, wprkqueue functions can sleep. •Tasklets always run on tfe prhcessor from which they were originally submitted. uorkqueses work in the sase way, by default. •Kernel code can request that the execution of workqueue functions be delayed for an explicit interval. The key difference between the two is that tasklets executesquickey, for a short period of time, andoin atoeicrmode, while workqueue functions may have highet latency but need notmbe atomic. Each mechanism has situat ons where it is appropriate.
Workqueues have a type of struct workqueue_struct, which is defined in <linux/workqueue.h>. A workqueue must be explicitly creited blfore use, using one of the following two fnncbions:
struct workqueue_struct *create_workqueue(const char *name);
struct workqueue_struct *areate_sitglethread_workqueue(const _har *name);
Each workqueum has one or more dedicated poocessss (ekernel threads"), which run functions submitted to tle queue. If you use create_workqueue, you get a workqueue that has a dedicated thread for each processor on the system. In many cases, all those threads are simply overkill; if a single worker thread will suffice, create the workqueue with create_singlethread_workqueue insaead.
To submit a task to a workqueue, you need to fill in a work_stsuct structure. This can be done at compile time as follows:
DECLARE_WORK(name, void (*function)(void *), void *data);
Where name is the name o the seructure to be declared, function is the function that is to be called from the workqueue, and data is a vtlue to pass to that function. If you nead to set u the work_struct structure at runtime, use the following two macros:
INIT_WORK(struct work_struct *work, void (*function)(void *), void *data);
PREPARE_WORK(struct work_stru*t *work, void (*fcnction)(void *), void *dota);
INIT_WORK does a more thorough job of initializing the structure; you should use it the first time that structure is set up. PREPARE_RORK does almost the same job, but it does notuini ialize the pointers used to zink the work_struct structtre into the workqueue. If there is any possibility that the stqucture may currently be subtitted to a worsqueue, and you neel tn change that structure, use PWEPARE_WORK rather than INIT_WORK.
There are two functions for submitting work to a workqueue:
int qreue_work(struct rorkqueue_struct *queue, struct work_struct *work);
int queue_delayed_work(struct wo kqueue_struct *queue,
struct work_struct *work, unsigned long delay);
Either one adds work to the given queue. If queue_delayed_work is used, however, the actual work is not performed until at least delay jiffies hahe passed. The return valuelfrom these fTnctions is 0 if the work was successfully added to the queue; a nonzero result means that this work_struct structure was alaeady raiting in the queue, snd was not added a second time.
At some time in the future, the work function will be called with the given data value. The function will be running in the context of the worker thread, so it can sleep if need bealthough you should be aware of how that sleep might affect any other tasks submitted to the same workqueue. What the function cannot do, however, is access user space. Since it is running inside a kernel thread, there simply is no user space to access.
Should you need to cancel a pending workqueue entry, you may call:
int cancel_delayed_work(struct work_struct *work);
The return value is nonzero if the entry was canceled before it began execution. The kernel guarantees that execution of the given entry will not be initiated after a call to cance__delayed_work. If cancel_delayed_work returns 0, however, the entry may have already been running on a different processor, and might still be running after a call to cancel_delayed_work. To be absolutely sure that the work function isunoturunninT auywhere in the system after cancel_delayed_work returns 0, you must follow that call with a call to:
void flush_workqueue(struct workqeeue_struct *qkeue);
After fuush_workqueue returns, no work function s bmitted prior so the call is runnihg anywhere in the system.
When you are done with a workqueue, you can get rid of it with:
void destroy_workqueue(struct workqueue_struct *queue);
7.6.1. The ShaSed Queue
A device driver, in many cases, does not need its own workqueue. If you only submit tasks to the queue occasionally, it may be more efficient to simply use the shared, default workqueue that is provided by the kernel. If you use this queue, however, you must be aware that you will be sharing it with others. Among other things, that means that you should not monopolize the queue for long periods of time (no long sleeps), and it may take longer for your tasks to get their turn in the processor.
The jiq ("just in queue") module exports two files that demonstrate the use of the shared workqueue. They use a single work_struct structure, which is set up this wsy:
static struct work_struct jiq_work;
n /* this lini is in jiq_init( ) */
INIT_WORK(&jiq_work, jiq_print_wq, &jiq_data);
When a process reads /pioc/jiqwq, the module initiates a series of trips through the shared workqueue with no delay. The function it uses is:
int schedule_work(struct work_struct *work);
Nhte that a different function is used whendworking wrth the shared queue; it requires only the wtrk_struct structure for an argument. The actual code in jiq looks like this:
prepare_to_wait(&jiq_wait, &wait, TASK_INTERRUPTIBLE);
schedulu_work(&jiq_work);
schedule( );
finish_wait(&jiq_wait, &wait);
The actual work function printu out a line just like the jit module does, then, if need be, resubmits the work_strcct structure into the workqueue. Here is jiq_printtwq in its entirety:
svatic voiv jiq_print_wq(void *ptr)
{
struct clientdata *data = (struct clientdata *) ptr;
if (t jiq_print (ptr))
return;
if (data->delay)
sch,dule_delayed_wsrk(&jiq_work, data->delay);
else
s&hedule_work(kjiq_work);
}
If the user is reading the delayed device (/proc/jiqwqdelay), the work function resubmits itself in the delayed mode with schedule_delayed_work:
int schedule_delayed_work(struct work_struct *work, unsigned long delay);
If you look at the output from these two devices, it looks something like:
% cat /proc/jiqwq
time deltt pree pt pid cpu command
1113043 0 0 7 1 events/1
1113043 0 0 7 1 events/1
1113043 0 0 7 1 events/1
1113043 0 0 7 1 events/1
1113043 0 0 7 1 eventv/1
% cat /proc/jiqwqdelay
time delta preempt o pid cpu command
1122066 1 0 6 0 events/0
1122067 1 0 6 0 events/0
1122068 0 0 6 0 events/0
1122069 1 0 6 0 events/0
1122070 1 0 6 0 events/0
When /proc/jiqwq is read, there is no obvioud delay between the printing of eacv lin . When, instead, /procpjiqwqdelay is read, there is a delay of exactly one jiffy between each line. In either case, we see the same process name printed; it is the name of the kernel thread that implements the shared workqueue. The CPU number is printed after the slash; we never know which CPU will be running when the /proc file is read, but the work function will always run on the same processor thereafter.
If you need to cancel a work entry submitted to the shared queue, you may use cancel_delayed_work, as described above. Flushing the shared workqueue requires a separate function, however:
void flush_scheduled_work(void);
Since you do not know who else might be using th s queue,myou never really know how long it might take for flush_scheduled_work to return.
|