Pages

2010-08-27

Linux: Using Tasklet/Workqueues in Bottom Half Interrupt

In this post we will see how to use Tasklet and  Workqueue to deal with interrupts in the Linux Kernel.

First we declare our Tasklet using DECLARE_TASKLET(name, function, data):
void do_tasklet(unsigned long);
DECLARE_TASKLET(my_tasklet, do_tasklet, 0);
As you see name is the name of our Tasklet, function is the function that will called to treat the tasklet, data is what we bill passed to this function (unsigned long).

Now to schedule our Tasklet we do:
tasklet_schedule(&my_tasklet);

If we want to use Workqueue:
 #include <linux/workqueue.h>
static struct work_struct my_wq;
INIT_WORK(&my_wq, (void (*)(void *)) do_tasklet, NULL);
...
schedule_work(&my_wq);

2010-08-26

Linux: Interrupts in the Linux kernel

In this post, we will see how to deal with interrupts when writing device drivers for Linux.

First we should include the header file <linux/interrupt.h>

To deal with interrupts we use two functions:

 int request_irq(unsigned int irq, 
                         irqreturn_t (*handler)(int irq, void *dev_id), 
                         unsigned long flags, 
                         const char *dev_name,
                         void *dev_id);


void free_irq(unsigned int irq, void *dev_id);

The first function will return 0 if it succeed, or a negative value for fail.
  • irq: it's the IRQ number
  • handler: is the function that the kernel will call to deal with the requested IRQ
  • flags: SA_INTERRUPT for fast interrupt handler, SA_SHIRQ for shared interrupt handler, IRQF_DISABLED flag specifies that this interrupt handler has to be treated as a fast handler, so the kernel has to disable interrupts while invoking the handler, IRQF_TRIGGER_RISING announces a rising-edge interrupt, IRQF_TRIGGER_HIGH for level-sensitive interrupt
  • dev_name: this string will be visible form /proc/interrupts
  • dev_id: if the interrupt is not shared, it can be set to NULL 
Example: Interrupt handler for PC Keyboard (IRQ number 1)
int init_module()
{
    /*
     * Since the keyboard handler won't co-exist with another handler,
     * such as us, we have to disable it (free its IRQ) before we do
     * anything.  Since we don't know where it is, there's no way to
     * reinstate it later - so the computer will have to be rebooted
     * when we're done.
     */
    free_irq(1, NULL);

    /*
     * Request IRQ 1, the keyboard IRQ, to go to our irq_handler.
     * SA_SHIRQ means we're willing to have othe handlers on this IRQ.
     * SA_INTERRUPT can be used to make the handler into a fast interrupt.
     */
    return request_irq(1,             /* The number of the keyboard IRQ on PCs */
                       irq_handler,   /* our handler */
                       SA_SHIRQ,      

                       "keyboard_irq_handler",
                       (void *)(irq_handler));
}


void cleanup_module()
{
    /*
     * This is only here for completeness. It's totally irrelevant, since
     * we don't have a way to restore the normal keyboard interrupt so the
     * computer is completely useless and has to be rebooted.
     */
    free_irq(1, NULL);
}


/*
 * This function services keyboard interrupts. It reads the relevant
 * information from the keyboard.
 */
irqreturn_t irq_handler(int irq, void *dev_id)
{
    unsigned char scancode;
    unsigned char status;

    /*
     * Read keyboard status
     */
    status = inb(0x64);
    scancode = inb(0x60);
    printk(KERN_INFO "Scan Code %x %s.\n", 
                      (int)*((char *)scancode) & 0x7F,
                      *((char *)scancode) & 0x80 ? "Released" : "Pressed");
    return IRQ_HANDLED;
}


Now to enable/disable interrupts, we use respectively enable_irq(unsigned int irq_number) and disable_irq(unisigned int irq_number).

    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

    2010-08-24

    Linux: Complete BusyBox tutorial

    For a complete tutorial how to download, configure, make, and install BusyBox for Linux, please visit this Web page (IBM):

    http://www.ibm.com/developerworks/library/l-busybox/

    Linux: Extracting a .cpio archive

    To extract a .cpio archive we do
    1. cpio -idv < /tmp/initramfs.cpio

    Linux: List the contenent of a .cpio archive

    Now suppose that we want to verify the contenent of our /tmp/initramfs.cpio (See last post), so we do:
    1. cpio -idv < /tmp/initramfs.cpio

    Linux: Create .cpio archive for initramfs

    We suppose that our initramfs directory is under /tmp/initramfs
     To create the .cpio archive that will be loaded by the bootloader, we do:
    1. cd /tmp/initramfs
    2. find .  | cpio --quiet -c -o > /tmp/initramfs.cpio
    3. cat /tmp/initramfs.cpio | gzip > /tmp/initramfs.cpio.gz
    4. An voilĂ , /tmp/initramfs.cpio.gz can now be passed to the bootloader to be your root file system

    2010-08-18

    Linux: Calling user-land program from kernel-land in Linux

    #include <linux/module.h>
    #include <linux/init.h>
    #include <linux/kmod.h>
    
    MODULE_LICENSE( "GPL" );
    static int __init my_module_init( void )
    {
      return call_user_land_program();
    }
    
    
    static void __exit my_module_exit( void )
    {
      return;
    }
    
    module_init( my_module_init );
    module_exit( my_moduel_exit );
    static int call_user_land_program( void )
    {
      struct subprocess_info *sub_info;
      char *argv[] = { "/usr/bin/myprogram", "--help", NULL };
      static char *envp[] = {
            "HOME=/",
            "TERM=linux",
            "PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL };
      sub_info = call_usermodehelper_setup( argv[0], argv, envp, GFP_ATOMIC );
      if (sub_info == NULL) return -ENOMEM;
    return call_usermodehelper_exec( sub_info, UMH_WAIT_PROC ); // UMH_NO_WAIT, UMH_WAIT_EXEC
    }
    Second Solutions (Preferred solution):
     
    static int call_user_land_program( void )
    {
      
       char *argv[] = { "/usr/bin/myprogram", "--help", NULL }; 
    static char *envp[] = {
            "HOME=/",
            "TERM=linux",
            "PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL };
      return call_usermodehelper( argv[0], argv, envp, UMH_WAIT_PROC );
    }