This article attempts to document the "workqueue" interface as implemented
in Ingo Molnar's
September 30 patch and
updated on October 1..
The real interface, as it appears in some future kernel, will certainly
be different. So do not depend too much on this document at this time.
A "workqueue" is a list of tasks to perform, along with a (per-CPU) kernel
thread to
execute those tasks. Since a kernel thread is used, all tasks are run in
process context and may safely sleep. Tasks running out of a
special-purpose workqueue can sleep indefinitely, since they will not
interfere with tasks in any other queue. Other activities often performed
in process context, such as access to user space, can not be performed out
of workqueues, of course - there is no user space to access.
Workqueues are created with create_workqueue:
workqueue_t *create_workqueue(const char *name);
The name of the queue is limited to ten characters; it is only used for
generating the "command" for the kernel thread (which can be seen in
ps or
top).
Tasks to be run out of a workqueue need to be packaged in a work_t
structure. (Yes, the workqueue code uses typedefs; when Rusty Russell
returns from his honeymoon we may see the associated "typedef removal"
patch). [Update: Linus has merged this patch into his BitKeeper
tree, but he edited out the typedefs first...] A work_t structure
may be declared and initialized at compile time as follows:
DECLARE_WORK(name, void (*function)(void *), void *data);
Here,
name is the name of the resulting
work_t structure,
function is the function to call to execute the work, and
data is a pointer to pass to that function.
To set up a work_t structure at run time, instead, use the following two
macros:
PREPARE_WORK(work_t *work, void (*function)(void *), void *data);
INIT_WORK(work_t *work, void (*function)(void *), void *data);
The difference between the two is that
INIT_WORK initializes the
linked list pointers within the
work_t structure, while
PREPARE_WORK changes only the function and data pointers.
INIT_WORK must be used at least once before queueing the
work_t structure, but should
not be used if the
work_t might already be in a workqueue.
Actually queueing a job to be executed is simple:
int queue_work(workqueue_t *queue, work_t *work);
int queue_delayed_work(workqueue_t *queue, work_t *work,
unsigned long delay);
The second form of the call ensures that a minimum delay (in jiffies)
passes before the work is actually executed.
The return value from both functions
is nonzero if the
work_t was actually added to
queue (otherwise, it may have already been there and will not be
added a second time).
Entries in workqueues are executed at some undefined time in the future,
when the associated worker thread is scheduled to run. If it is necessary
to wait until all workqueue entries have actually run, a simple call:
void flush_workqueue(workqueue_t *queue);
will suffice. This would be a good thing to do, for example, in a device
driver shutdown routine. Note that if the queue contains work with long
delays, or if something keeps refilling the queue, this call could take a
long time to complete.
Work queues can be destroyed with:
void destroy_workqueue(workqueue_t *queue);
This operation will flush the queue, then delete it.
Finally, for tasks that do not justify their own workqueue, a "default"
work queue (called "events") is defined. work_t
structures can be added to this queue with:
int schedule_work(work_t *work);
int schedule_delayed_work(work_t *work, unsigned long delay);
There is a
flush_scheduled_work() function which will wait for
everything on this queue to be executed.
One final note: schedule_work(), schedule_delayed_work()
and
flush_scheduled_work() are exported to any modules which wish to
use them. The other functions (for working with separate workqueues) are
exported to GPL-licensed modules only.
(
Log in to post comments)