In this post, I will show you how to do sleeping inside your LKM (Linux Kernel Module) by exploring the
Wait Queue API.
To use this API in our code, we should include the header file
<linux/wait.h>.
First we declare and init our wait queue by calling a dedicated macro in our code:
DECLARE_WAIT_QUEUE_HEAD(name);
This is called a static initialization, we can also do a dynamic initialization by using the following code:
wait_queue_head_t my_queue;
init_waitqueue_head(&my_queue);
Now for sleeping our code, we use one of these functions:
wait_event(queue, condition)
wait_event_interruptible(queue, condition)
wait_event_timeout(queue, condition, timeout)
wait_event_interruptible_timeout(queue, condition, timeout)
So while
condition is false, the process continue to sleep.
If we use
wait_event, our process is put into an uninterruptible sleep. The preferred alternative is
wait_event_interruptible, which can be interrupted by signals. This version returns an integer value that we should check; a nonzero value means our sleep was inter-rupted by some sort of signal, and our driver should probably return
-ERESTARTSYS.
The final versions (
wait_event_timeout and
wait_event_interruptible_timeout) wait for a limited time; after that time period (expressed in
jiffies) expires, the macros return with a value of 0 regardless of how condition evaluates.
Now to wake up all process in a given queue we should call:
void wake_up(wait_queue_head_t *queue);
void wake_up_interruptible(wait_queue_head_t *queue);
The last function restricts itself to processes performing an interruptible sleep.
We now know enough to look at a simple example of sleeping and waking up. In the sample source, you can find a module called sleepy. It implements a device with simple behavior: any process that attempts to read from the device is put to sleep. Whenever a process writes to the device, all sleeping processes are awakened. This behavior is implemented with the following
read and
write methods:
static DECLARE_WAIT_QUEUE_HEAD(wq);
static int flag = 0;
ssize_t sleepy_read (struct file *filp, char __user *buf, size_t count, loff_t *pos)
{
printk(KERN_DEBUG "process %i (%s) going to sleep\n",
current->pid, current->comm);
wait_event_interruptible(wq, flag != 0);
flag = 0;
printk(KERN_DEBUG "awoken %i (%s)\n", current->pid, current->comm);
return 0; /* EOF */
}
ssize_t sleepy_write (struct file *filp, const char __user *buf, size_t count,loff_t *pos)
{
printk(KERN_DEBUG "process %i (%s) awakening the readers...\n",
current->pid, current->comm);
flag = 1;
wake_up_interruptible(&wq);
return count; /* succeed, to avoid retrial */
}
Source: Linux Device Drivers, 3rd Edition