Some 2.6.12 API changes
[Posted March 8, 2005 by corbet]
The
workqueue interface allows kernel code
to request that a function be called at a later time, in process context.
It can thus be used to arrange for work which cannot be performed
immediately, perhaps because the current thread is running in an atomic
mode. It is also possible to queue delayed work requests which are
guaranteed not to run for a caller-requested delay period.
Sometimes the need arises to cancel tasks which have been queued to a
workqueue in a delayed mode. The function which performs this task is:
int cancel_delayed_work(struct work_struct *work);
This function attempts to intercept the given work before it runs
and remove it from the queue. If it is successful, it returns a nonzero
value. If, instead, cancel_delayed_work() returns zero, it means
that the delayed work request was fired off before the call; it might, in
fact, be running on another CPU when the cancel attempt is made. The
caller usually needs to know that the work function is not running, so the
standard procedure is to call flush_workqueue(), which waits until
all tasks currently in the queue are run. After flush_workqueue()
returns, the work function is guaranteed not to be running anywhere in the
system.
There is one remaining obnoxious detail, however: what if the work function
resubmits itself to the workqueue while it is running? In this case, that
function could run again when the rest of the kernel least expects it -
possibly after the module which contains that function has been removed
from the kernel. That is the sort of race condition which gives kernel
developers cold sweats. In general, this problem can be avoided by
creating a "do not resubmit yourself" flag which is set before calling
cancel_delayed_work(), but not all programmers make that effort.
In an attempt to make safe cancellation easier, Arjan van de Ven has added
a new function to the workqueue API:
void cancel_rearming_delayed_work(struct work_struct *work);
The implementation is straightforward; at its core, this function does the
following:
while (!cancel_delayed_work(work))
flush_workqueue(wq);
In other words, it simply keeps trying until it is able to catch the work
request when it is not executing, and, thus, cannot resubmit itself. This
approach works because it applies to delayed work - there has to be
some time when the work request is sitting in the timer queue waiting to
run. Sooner or later, the kernel is sure to catch it during that time and
keep it from running again.
The new function has been merged for 2.6.12.
Meanwhile, there are two functions which are used by drivers to send
messages to USB peripherals:
int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
void *data, int len, int *actual_length,
int timeout);
int usb_control_msg(struct usb_device *dev, unsigned int pipe,
__u8 request, __u8 requesttype,
__u16 value, __u16 index,
void *data, __u16 size, int timeout);
In 2.6.11 and prior kernels, the timeout value is expressed in
jiffies; for 2.6.12, the units of that parameter has been changed to
milliseconds. Dozens of patches were merged to bring in-tree drivers up to
the new version of the interface, but out-of-tree drivers will need to be
changed explicitly. The situation is complicated a bit by the fact that
the prototype of the function did not change, so the compiler will not
flag callers which have not been updated.
Finally, David Howells has changed the rwsem
implementation to use interrupt-disabling spinlocks. This change
should be transparent to most callers. Anybody who calls
down_read() or down_write() with interrupts already
disabled will be in for a surprise, however. There should be no such
callers, since those functions can sleep, but one never knows...
(
Log in to post comments)