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