Details of the workqueue interface
This article is part of the LWN Porting Drivers to 2.5 series. |
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.
Posted Oct 2, 2002 6:55 UTC (Wed)
by kspoon (guest, #223)
[Link] (1 responses)
A quick intro to the concept, a highlight of how to use it, and a couple of suggestions on where it might be useful. I'd love to see more articles like Even if I hadn't already bought a subscription, this would have sold me. You should really think about making a preview or something so non-subscribers can see what a bargain a subscription is.
Posted Oct 2, 2002 7:50 UTC (Wed)
by eskild (guest, #1556)
[Link]
Posted Feb 5, 2007 18:56 UTC (Mon)
by mrechberger (guest, #42730)
[Link]
That is perfect.More! More! :-)
this in all sections, but it is especially useful in the kernel section.
I second that! Very good quality kernel coverage, as always. For someone like me who don't have the time to follow the kernel mailing list, it strikes a perfect balance between being accessible yet "deep". Like kspoon wrote: More! More!
More! More! :-)
Since linux 2.6.20 workqueues changed a bit, so take care about itLinux 2.6.20