Thursday 28 November 2013

Tasklets Vs Work queues in the kernel

Tasklets Vs Work queues in the kernel

Tasklets and work queues used to implement deferrable functionality in the kernel. Using the Tasklets and Work queues, we can schedule a function to run at later point of time.

Deferrable functions:

It is a mechanism for supporting delayed execution of functions (not urgent functions), used in interrupt handlers.
Types of deferrable functions
1.       Tasklets
2.       Work queues
Mostly, we use these functions in Interrupt Handlers. Because some or majority of the interrupts are disabled in interrupt context. So, if the functions take more time for execution in this context then the latency to handle other hardware interrupts. Generally, we enter into interrupt context when a hardware interrupt is raised.
If we add Deferrable function in the interrupt handlers then this function will be executed in Kernel Context where the all hardware interrupts are in enable mode. So, we move function of code which can later be executed without any harm using deferrable function. And, hence we are minimizing the latency to handle hardware interrupts.
Generally the processing of work done in interrupt handler called as Top Half.
And, the processing of work done in kernel context called as Bottom Half.
Aim: Reduce work (execution time of handler code) in Top Half
We can achieve this using Tasklets method or Work queue.

Using Tasklets:

As already mentioned, the main job of the Tasklets is to schedule the work (function) to run later point of time so that we can decrease the amount of work done in interrupt handlers.

/* Declaring Tasklet */

void tasklet_function(unsigned long data);
DECLARE_TASKLET(tasklet_name, tasklet_function, tasklet_data);

/* Scheduling Tasklet */

tasklet_schedule(&tasklet_name);

Tasklets are represented by tasklet_struct structure.
struct tasklet_struct {
                struct tasklet_struct *next; /*pointing to next tasklet structure */
                unsigned long state; /* state = TASKLET_STATE_SCHED or TASKLET_STATE_RUN */
                atomic count; /* 0 = Enable; !0 = Disable */
unsigned long data;
void (*func) (unsigned long); /*func(data) */
}
Tasklets are defined using a macro DECLARE _TASKLET, which initializes the tasklet_struct using its arguments tasklet_name, tasklet_func and data.
Tasklets are enabled by default when defined using DECLARE_TASKLET macro, so that we can schedule it directly without using any other APIs.
But, if we declare tasklet using DECLARE_TASKLET_DISABLED macro then we should explicitly call tasklet_enable() API before scheduling this tasklet.

Tasklet APIs:

/* Defining Tasklet using macros */
DECLARE_TASKLET(name, func, data);
DECLARE_TASKLET_DISABLED(name, func, data);

void tasklet_init(struct tasklet_struct *, void (*func) (unsigned long), unsigned long data); /*initializes the tasklet structure with user provided info */


/* Enabling and Disabling a tasklet using enable() and disable() */
void tasklet_enable(struct tasklet_struct *); /* Enable normal priority scheduling */
void tasklet_disable(struct tasklet_struct *); /* returns after disabling the tasklet */
void tasklet_hi_enable(struct tasklet_struct *); /* Enabling High priority scheduling */
void tasklet_disable_nosync(struct tasklet_struct *); /* may returns before termination */


/* Schedule a tasklet*/
void tasklet_schedule (struct tasklet_struct *); /* Normal priority scheduling */
void tasklet_hi_schedule (struct tasklet_struct *); /* Higher priority scheduling */


CPU maintains the normal and high priority softirq vectors lists (normal priority vector list and high priority vector list) where these functions are queued.
If the function is higher priority function then it is enqueued in higher priority softirq vector list and similar case for normal priority functions.


The below two functions are used to kill a tasklet
void tasklet_kill(struct tasklet_struct *);
void tasklet_hi_kill(struct tasklet_struct *); /* Kill the tasklet and ensure they would never run */

Sample Program: tasklet.c

#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/interrupt.h>
char tasklet_data[] = "tasklet function was called";
void my_tasklet_function (unsigned long data) {
        printk("%s: in %s\n", (char*)data, __func__);
}
DECLARE_TASKLET(my_first_tasklet, my_tasklet_function, (unsigned long)&tasklet_data);
int init_module()
{
        /* schedule our my_tasklet_function */
        tasklet_schedule(&my_first_tasklet);
        return 0;
}
void cleanup_module(void)
{
        tasklet_kill(&my_first_tasklet);
        return;
}

Makefile:

obj-m += tasklet.o
KDIR = /usr/src/linux-headers-2.6.32-28-generic
all:
        $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
clean:
        rm -rf *.o *.ko *.mod.* *.symvers *.order

Execution:

sudo insmod tasklet.ko

Output: dmesg


[3302661.097630] tasklet function was called: in my_tasklet_function

Next: Will cover work queues also...

No comments:

Post a Comment

You might also like

Related Posts Plugin for WordPress, Blogger...