Pages

2010-08-25

Linux: Sleeping in the Linux kernel

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

No comments:

Post a Comment