Top and bottom halves
Top half: section that executed within the ISR
Bottom half: section that executed after the interrupt
Both implemented as tasklets
each tasklet executed on one CPU after the interrupt is handled. The tasklets to be excited stored in a linked list.
Declare a tasklet:
DECLARE_TASKLET (module_tasklet, //Name
module_do_tasklet, //Function
tasklet_data); // tasklet argument
To schedule a tasklet (in the interrupt handler):
tasklet_schedule(&module_tasklet); // tasklets at the end of the linked list of tasklets
tasklet_hi_schedule(&module_tasklet); // start of the linked list of tasklets
tasklet example
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
MODULE_LICENSE("GPL");
char my_tasklet_data[]="my_tasklet_function was called";
/* Bottom Half Function */
void my_tasklet_function( unsigned long data )
{
printk( "%s\n", (char *)data );
return;
}
DECLARE_TASKLET( my_tasklet, my_tasklet_function,
(unsigned long) &my_tasklet_data );
int init_module( void )
{
/* Schedule the Bottom Half */
tasklet_schedule( &my_tasklet );
return 0;
}
void cleanup_module( void )
{
/* Stop the tasklet before we exit */
tasklet_kill( &my_tasklet );
return;
}
Deferring work
Work queues

another mechanism for deferring work
works on the same CPU
bigger latency than worklets
Work functions are in workqueue.h
Schedule work dynamically:
static inline bool schedule_work(struct work_struct *work)
static inline bool schedule_work_on(int cpu, struct work_struct *work)
static inline bool schedule_delayed_work(struct delayed_work *dwork,unsigned long delay)
static inline bool schedule_delayed_work_on(int cpu, struct delayed_work *dwork,unsigned long delay)
Schedule work statically with macros
INIT_WORK(_work, _func)
INIT_DELAYED_WORK(_work, _func)
Work queue example:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/workqueue.h>
#include <linux/slab.h>
MODULE_LICENSE("GPL");
static struct workqueue_struct *my_wq;
typedef struct {
struct work_struct my_work;
int x;
} my_work_t;
my_work_t *work, *work2;
static void my_wq_function( struct work_struct *work)
{
my_work_t *my_work = (my_work_t *)work;
printk( "my_work.x %d\n", my_work->x );
kfree( (void *)work );
return;
}
int init_module( void )
{
int ret;
my_wq = create_workqueue("my_queue");
if (my_wq) {
/* Queue some work (item 1) */
work = (my_work_t *)kmalloc(sizeof(my_work_t), GFP_KERNEL);
if (work) {
INIT_WORK( (struct work_struct *)work, my_wq_function );
work->x = 1;
ret = queue_work( my_wq, (struct work_struct *)work );
}
/* Queue some additional work (item 2) */
work2 = (my_work_t *)kmalloc(sizeof(my_work_t), GFP_KERNEL);
if (work2) {
INIT_WORK( (struct work_struct *)work2, my_wq_function );
work2->x = 2;
ret = queue_work( my_wq, (struct work_struct *)work2 );
}
}
return 0;
}
void cleanup_module( void )
{
flush_workqueue( my_wq );
destroy_workqueue( my_wq );
return;
}
Time in the kernel
Timers
Timer interrupts occur every 1/HZ of a second (= 1 jiffy)
HZ is configurable (in ‘Processor type and features’):
* 100,250(i368 default), 300 or 1000 (other architechtures)
* see kernel/Kconfig.hz
Global variable jiffies represents the number of tick since machine started. It increments with each timer interrupt
Read jiffies with get_jiffies_64 function
Convert to msec with jiffies_to_msecs or to microsecs with jiffies_to_usecs
Requires #include <linux/jiffies.h>
Timers
The kernel provides asynchronous timers
Run in atomic kernel
require #include <linux/timer.h>
Timers API
static: TIMER_INITIALIZER(_function,_expires,_data)
dynamic:
void init_timer(struct timer_list *timer)
void setup_timer( struct timer_list *timer,void (*function)(unsigned long),unsigned long data)
add new timer:
void add_timer (struct timer_list *timer)
remove timer:
void del_timer (struct timer_list *timer)
Timer simple program example
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/timer.h>
MODULE_LICENSE("GPL");
static struct timer_list my_timer;
void my_timer_callback( unsigned long data )
{
printk( "my_timer_callback called (%ld).\n", jiffies );
}
int init_module( void )
{
int ret;
printk("Timer module installing\n");
// my_timer.function, my_timer.data
setup_timer( &my_timer, my_timer_callback, 0 );
printk( "Starting timer to fire in 200ms (%ld)\n", jiffies );
ret = mod_timer( &my_timer, jiffies + msecs_to_jiffies(200) );
if (ret) printk("Error in mod_timer\n");
return 0;
}
void cleanup_module( void )
{
int ret;
ret = del_timer( &my_timer );
if (ret) printk("The timer is still in use...\n");
printk("Timer module uninstalling\n");
return;
}
High resolution timers
Allow timers in resolution of nano seconds
Depends on CONFIG_HIGH_RES_TIMERS
High resolution timers there two time bases (in oppose to jiffies on normal timers):
1. CLOCK_REALTIME: jiffies, same as normal timers
2. CLOCK_MONOTONIC: wide clock measuring the time in seconds and nanoseconds since system boot. Cannot be modified, so can be used for accurate time measurement.
High resolution timers example
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/hrtimer.h>
#include <linux/ktime.h>
MODULE_LICENSE("GPL");
#define MS_TO_NS(x) (x * 1E6L)
static struct hrtimer hr_timer;
enum hrtimer_restart my_hrtimer_callback( struct hrtimer *timer )
{
ktime_t now;
ktime_t delay = ktime_set(0, MS_TO_NS(200));
printk( "my_hrtimer_callback called (%ld).\n", jiffies );
now = ktime_get();
hrtimer_forward(&hr_timer, now, delay);
return HRTIMER_RESTART;
}
int init_module( void )
{
ktime_t ktime;
unsigned long delay_in_ms = 200L;
printk("HR Timer module installing\n");
ktime = ktime_set( 0, MS_TO_NS(delay_in_ms) );
hrtimer_init( &hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL );
hr_timer.function = &my_hrtimer_callback;
printk( "Starting timer to fire in %ldms (%ld)\n", delay_in_ms, jiffies );
hrtimer_start( &hr_timer, ktime, HRTIMER_MODE_REL );
return 0;
}
void cleanup_module( void )
{
int ret;
ret = hrtimer_cancel( &hr_timer );
if (ret) printk("The timer was still in use...\n");
printk("HR Timer module uninstalling\n");
return;
}