kernel timing issues an introduction to the use of kernel timers and work queues

27
Kernel timing issues An introduction to the use of kernel timers and work queues

Post on 20-Dec-2015

224 views

Category:

Documents


3 download

TRANSCRIPT

Page 1: Kernel timing issues An introduction to the use of kernel timers and work queues

Kernel timing issues

An introduction to the use of kernel timers and work queues

Page 2: Kernel timing issues An introduction to the use of kernel timers and work queues

Kernel timers

• Linux offers a facility that lets drivers put a process to sleep until a fixed amount of time has elapsed (as measured in jiffies)

• When the timer expires, a driver-defined action will be performed, which can ‘wake up’ the process that was put to sleep, or could perform some alternative action (for example, the kernel timer could re-start)

Page 3: Kernel timing issues An introduction to the use of kernel timers and work queues

jiffies

• unsigned long volatile jiffies;

• global kernel variable (used by scheduler)

• initialized to zero when system reboots

• gets incremented during a timer interrupt

• so it counts ‘clock-ticks’ since cpu restart

• ‘tick-frequency’ is a ‘configuration’ option

• On our machines: HZ=250 (in ‘.config’)

Page 4: Kernel timing issues An introduction to the use of kernel timers and work queues

jiffies overflow

• Won’t overflow for at least 16 months• Linux kernel got modified to ‘fix’ overflow• Now the declaration is in ‘linux/jiffies.h’:

unsigned long long jiffies_64; and a new instruction in ‘do_timer()’

(*(u64*)&jiffies_64)++;which compiles to assembly language as

add $1, jiffies+0adc $0, jiffies+4

Page 5: Kernel timing issues An introduction to the use of kernel timers and work queues

Kernel timer syntax

• Declare a timer: struct timer_list mytimer;• Initialize this timer: init_timer( &mytimer );

mytimer.func = mytimeraction;mytimer.data = (unsigned long)mydata;mytimer.expires = <number-of-jiffies>

• Install this timer: add_timer( &mytimer );• Modify this timer: mod_timer( &mytimer, <jifs> );• Delete this timer: del_timer( &mytimer );• Delete it safely: del_timer_sync( &mytimer);

Page 6: Kernel timing issues An introduction to the use of kernel timers and work queues

A kernel-timer caution

• A kernel timer’s timeout-action cannot do anything that could cause the current task to ‘sleep’ (such as copying data between user-space and kernel-space, or trying to allocate more kernel memory)

• However, to aid debugging, a timer CAN use ‘printk()’ within its timeout-routine

Page 7: Kernel timing issues An introduction to the use of kernel timers and work queues

‘trytimer.c’

• We have posted an example that shows how a Linux kernel timer can be used to perform a periodic action (such as using ‘printk()’ to issue a message every time the time expires, then restart the timer

• Notice that our demo is not able to issue messages directly to the console – its timer-function executes without a ‘tty’

Page 8: Kernel timing issues An introduction to the use of kernel timers and work queues

Delaying work

• If a device-driver needs to perform actions that require using process resources (like a tty), or that may possibly ‘sleep’, then it can defer that work – using a ‘workqueue’

Page 9: Kernel timing issues An introduction to the use of kernel timers and work queues

Programming syntax• Declare: struct workqueue_struct *myqueue;

struct work_struct thework;

• Define: void dowork( void *data ) { /* actions */ };

• Initialize: myqueue = create_singlethread_workqueue( “mywork” );INIT_WORK( &thework, dowork, <data-pointer> );

• Schedule: queue_dalayed_work( myqueue, &thework, <delay> );

• Cleanup: if ( !cancel_delayed_work( &thework ) )flush_workqueue( myqueue );

destroy_workqueue( myqueue );

Page 10: Kernel timing issues An introduction to the use of kernel timers and work queues

‘tryworkq.c’ and ‘defermsg.c’

• We have posted demo-modules that show the use of workqueues to perform actions later, either as soon as a ‘process context’ is available, or after a prescribed time

• Further details on the options for using an existing kernel workqueue or a workqueue of your own creation may be found in our textbook (Chapter 7 of LDD3)

Page 11: Kernel timing issues An introduction to the use of kernel timers and work queues

Applying these ideas

• To demonstrate a programming situation in which using kernel timers is valuable, we created the ‘foo.c’ device-driver, plus an application that uses it (‘watchfoo.cpp’)

• You can compile and install the module, then execute the application: $ watchfoo

• But you will notice there are two ‘problems’ (excess cpu usage and loop-termination)

Page 12: Kernel timing issues An introduction to the use of kernel timers and work queues

Reducing CPU’s usage

• The ‘watchfoo’ program rereads ‘/dev/foo’ constantly (numerous times per second), much faster than the human eye can see

• If you run the ‘top’ utility, you will see that a high percentage of the available CPU time is being consumed by ‘watchfoo’

• You can add a kernel timer to the ‘foo.c’ driver to curtail this excessive reading

Page 13: Kernel timing issues An introduction to the use of kernel timers and work queues

In-class exercise

• Modify ‘foo.c’ (call it ‘timedfoo.c’) as follows• Create an integer flag-variable (‘ready’) as a

global object in your module• When your ‘read()’ function gets called, it should

sleep until ‘ready’ equals TRUE; it should set ‘ready’ equal to FALSE when it awakens, but should set a timer to expire after 1/10 seconds

• Your timer’s action-function should set ‘ready’ back to TRUE, then wake up any sleeping tasks

Page 14: Kernel timing issues An introduction to the use of kernel timers and work queues

Implementation hints

• You need a wait-queue (so your driver’s ‘reader’ tasks can sleep on it)

• You need a timer action-function• You need to organize your timer-function’s

data-items into a single structure (because the timer-function has only one argument)

• Your timer-function must do two things:– Change ‘ready’ to TRUE– Wake up any ‘sleepers’

Page 15: Kernel timing issues An introduction to the use of kernel timers and work queues

Deferring work

• Linux supports a ‘workqueue’ mechanism which allows the kernel to defer specified work until some later point in time

• This mechanism has been ‘reworked’ in a major way since our texts were published

• So any device-driver has to be modified if it made any use of a kernel ‘workqueue’

• Changes require grasp of some ‘macros’

Page 16: Kernel timing issues An introduction to the use of kernel timers and work queues

‘sizeof’ and ‘offsetof’

• Our GNU compilers permit use of these C/C++ operators on object types

• The ‘sizeof’ operator returns the number of bytes of memory the compiler allocated for storing the specified object

• The ‘offsetof’ operator returns the number of bytes in a structure which precede the specified structure-member

Page 17: Kernel timing issues An introduction to the use of kernel timers and work queues

A ‘struct’ example

struct mystruct {char w;short x;long y;long long z;} my_instance;

You can use the ‘sizeof’ operator to find out how much memory gets allocated to any ‘struct mystruct’ object, like this:

int nbytes = sizeof( my_instance );

You can use the ‘offsetof’’ operator to find out where within a given structure a particular field occurs, like this:

int offset_z = offsetof( struct mystruct, z );

Page 18: Kernel timing issues An introduction to the use of kernel timers and work queues

The ‘container_of()’ macro

• Recent versions of the Linux kernel have introduced a further operator on ‘structs’container_of( ptr, type, member );

• When given a pointer to a field within a structure-object, and the type-name for that that structure-object, and the field-name for that structure’s field-member, then it returns the structure’s address

Page 19: Kernel timing issues An introduction to the use of kernel timers and work queues

Using ‘container_of()’

struct mystruct {char w;short x;long y;long long z;} my_instance = { 1, 2, 3, 4 };

If you have a pointer to one of the fields in some instance of a this kind of ‘struct’ object, then you could use the ‘container_of()’ macro to get a pointer to that ‘struct’ object itself, like this: long *ptr = &my_instance.y; struct mystruct *p = container_of( ptr, struct mystruct, y );

This would be useful if you now wanted to access other members:

printk( “w=%d x=%d y=%d z=%d \n”, p->w, p->x, p->y, p->z );

Page 20: Kernel timing issues An introduction to the use of kernel timers and work queues

• #include <linux/workqueue.h>

• void dowork( struct work_struct *data );

• DECLARE_DELAYED_WORK( mywork, dowork );

• struct workqueue_struct *myqueue;

• myqueue = create_singlethread_workqueue( “mywork” );

Page 21: Kernel timing issues An introduction to the use of kernel timers and work queues

‘workqueue’ syntax#include <linux/workqueue.h>

struct workqueue_struct *myqueue; // pointer to your workqueuevoid dowork( struct work_struct *data ); // your function’s prototypeDECLARE_DELAYED_WORK( mywork, dowork );

int init_module( void ){

myqueue = create_singlethread_workqueue( “mywork” );if ( !queue_delayed_work( myqueue, &mywork, HZ*5 ) )

return –EBUSY;return 0; // SUCCESS

}void cleanup_module( void ){

destroy_workqueue( myqueue ); }

Page 22: Kernel timing issues An introduction to the use of kernel timers and work queues

‘tryworkq.c’

void dowork( struct work_struct *data ){

printk( “\n\n I am doing the delayed work right now \n” );}

In this example the delayed work consists of simply printing a message to the kernel’s logfile -- you can view by typing the ‘dmesg’ command

Notice that the ‘action’ function in this example ignores its ‘data’ argument

Page 23: Kernel timing issues An introduction to the use of kernel timers and work queues

An improved example

• Our ‘announce.c’ module shows how an LKM could display its messages within a window on the Linux graphical desktop

• It uses the ‘tty_struct’ object which exists in the process-descriptor for the ‘insmod’ task which you launch to install the LKM

• We shall see how this same idea can be used in a waitqueue’s ‘action’ functions

Page 24: Kernel timing issues An introduction to the use of kernel timers and work queues

‘timer’ verses ‘workqueue’

• Any kernel-timer’s action-function will be executed in ‘atomic’ context – just like an interrupt service routine: it cannot ‘sleep’, and it cannot access any user-space data

• But any workqueue’s action-function will be executed by a kernel-thread – and thus it possesses a ‘process’ context, so it can be ‘scheduled’ and ‘sleep’ if necessary – though it, too, cannot access user-space

Page 25: Kernel timing issues An introduction to the use of kernel timers and work queues

If ‘dowork()’ needs data…// data items needed by your ‘dowork’ function are packaged in a ‘struct’ struct mydata {

char *msg;struct tty_struct *tty;} my_data = { “\nHello\n”, NULL };

// your module-initialization function sets up your ‘struct delayed_work’ object// and it can also finish initializing your ‘my_data’ object’s member-fields

myqueue = create_singlethread_workqueue( “mywork” );INIT_DELAYED_WORK( &mywork, dowork );my_data.tty = current->signal->tty;

// then your action-function can access members of your ‘my_data’ object like thisvoid dowork( struct work_struct *data ){

struct mydata *dp = container_of( &my_data.msg, struct mydata, msg );struct tty_struct *tty = dp->tty;tty->driver->write( tty, dp->msg, strlen( dp->msg ) );

}

Page 26: Kernel timing issues An introduction to the use of kernel timers and work queues

‘defermsg.c’

• This LKM will display a message within a desktop window after a 10-second delay

• It illustrates a use of the ‘container_of()’ macro (as is needed by the reworked API for the Linux kernel’s workqueues)

• Our course-website has a link to an online article by author Jonathan Corbet giving details of ongoing kernel changes in 2.6

Page 27: Kernel timing issues An introduction to the use of kernel timers and work queues

Summary of tonight’s demos

• ‘foo.c’ and ‘watchfoo.cpp’• ‘announce.c’• ‘trytimer.c’• ‘trymacro.c’• ‘tryworkq.c’• ‘defermsg.c’

• EXERCISE: Modify the ‘foo.c’ device-driver to use a kernel timer in it’s ‘read()’ method