Saturday 3 December 2016

Sleep Driver development

       Execution of a system call
       Sleeping
       Waking up
       Example

Blocking I/O
       In previous session we looked at how to implement the read and write driver methods.
       At that point, however, we skipped over one important issue: how does a driver respond if it cannot immediately satisfy the request?
       A call to read may come when no data is available, but more is expected in the future. Or a process could attempt to write, but your device is not ready to accept the data, because your output buffer is full. The calling process usually does not care about such issues; the programmer simply expects to call read or write and have the call return after the necessary work has been done.
       So, in such cases, your driver should (by default) block the process, putting it to sleep until the request can proceed.
       This section shows how to put a process to sleep and wake it up again later on.
       Lest look at a few concepts first.

Introduction to sleeping
       What does it mean for a process to “sleep”? When a process is put to sleep, it is marked as being in a special state and removed from the scheduler’s run queue. Until something comes along to change that state, the process will not be scheduled on any CPU and, therefore, will not run.


Rules
       A couple of rules that you must keep in mind to be able to code sleeps in a safe manner.
       The first of these rules is: never sleep when you are running in an atomic context.
       An Atomic context is simply a state where multiple steps must be performed without any sort of concurrent access.
       It means your driver cannot sleep while holding a spinlock, seqlock, or RCU lock.
       Another thing to remember with sleeping is that, when you wake up, you never know how long your process may have been out of the CPU or what may have changed in the mean time. You also do not usually know if another process may have been sleeping for the same event; that process may wake before you and grab whatever resource you were waiting for. The end result is that you can make no assumptions about the state of the system after you wake up, and you must check to ensure that the condition you were waiting for is, indeed, true.
       Another point is that your process cannot sleep unless it is assured that somebody else, somewhere, will wake it up.

How to sleep
       Linux kernel uses data structure called “wait queues” to accomplish process sleeping.
       A wait queue is a list of processes waiting for a specific event to occur.
       In Linux, a wait queue is managed by means of a “wait queue head,” a structure of type wait_queue_head_t, which is defined in <linux/wait.h>. A wait queue head can be defined and initialized
       Statically (useful to declare as global variable)
      DECLARE_WAIT_QUEUE_HEAD(name);
       Dynamically (useful to embed wait queue inside another data structure)
      wait_queue_head_t my_queue;
      init_waitqueue_head(&my_queue);
       A process sleeps with an expectation that some condition will become true in the future
       Several ways to make a kernel process sleep
       wait_event(queue, condition);
      Sleeps until the task is woken up and the given C expression is true.
      Caution: can't be interrupted (can't kill the user-space process!)
       int wait_event_interruptible(queue, condition);
      Can be interrupted by any signal. Returns -ERESTARTSYS if interrupted.
       int wait_event_timeout(queue, condition, timeout);
      Wait for limited time(expressed in jiffies); after that time period expires, the macro returns with the value of “0” regardless of how condition evaluates. Reurns non-zero if the condition was met.
       int wait_event_interruptible_timeout(queue, condition,
       timeout);
      Same as above, interruptible. Returns 0 if the timeout elapsed, -ERESTARTSYS if interrupted, positive value if the condition was met.
       “Condition” is arbitary boolean expression that is evaluated. Until condition is evaluated to a true value, the process continues to sleep.

Waking up
       Typically the processes are woken up by some other thread of execution (a different process, or an interrupt handler)
       wake_up(&queue);
      Wakes up all processes in the wait queue
       wake_up_interruptible(&queue);
      Wakes up all processes waiting in an interruptible sleep on the given queue

Example
       Process sleep in read() for data to become availabe
static ssize_t sample_char_read(struct file * file, char __user * buf,
                       size_t count, loff_t *ppos)
{
        wait_event_interruptible(queue, flag);
        printk("sample_char_read size(%ld)\n", count);
        return 0;
}
       Write() wakes up the sleeping process
static ssize_t sample_char_write(struct file *filp, const char *buf,
                 size_t size, loff_t *offp)
{
        flag = 1;
        wake_up_interruptible(&queue);
        printk("sample_char_write size(%ld)\n", size);
        return size;
}

Quick Reference
       #include <linux/wait.h>
       typedef struct { /* ... */ } wait_queue_head_t;
       void init_waitqueue_head(wait_queue_head_t *queue);
       DECLARE_WAIT_QUEUE_HEAD(queue);
      he defined type for Linux wait queues. A wait_queue_head_t must be explicitly initialized with either init_waitqueue_head at runtime or DECLARE_WAIT_ QUEUE_HEAD at compile time.
       void wait_event(wait_queue_head_t q, int condition);
       int wait_event_interruptible(wait_queue_head_t q, int condition);
       int wait_event_timeout(wait_queue_head_t q, int condition, int time);
       int  wait_event_interruptible_timeout(wait_queue_head_t q, int condition, int time);
      Cause the process to sleep on the given queue until the given condition evaluates to a true value.          
       void wake_up(struct wait_queue **q);
       void wake_up_interruptible(struct wait_queue **q);
      Wake processes that are sleeping on the queue q. The _interruptible form wakes only interruptible processes.
        #include <linux/sched.h>
       set_current_state(int state);
       Sets the execution state of the current process. TASK_RUNNING means it is ready to run, while the sleep states are TASK_INTERRUPTIBLE and  TASK_UNINTERRUPTIBLE.
       Void schedule (void)
      Selects a runnable process from the run queue. The chosen process can be current or a different one.


No comments:

Post a Comment