Title: Kernel timing issues
1Kernel timing issues
- An introduction to the use of kernel timers and
work queues
2Kernel 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)
3jiffies
- 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 HZ250 (in .config)
4jiffies overflow
- Wont 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, jiffies0
- adc 0, jiffies4
5Kernel 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 ltnumber-of-jiffiesgt
- Install this timer add_timer( mytimer )
- Modify this timer mod_timer( mytimer, ltjifsgt
) - Delete this timer del_timer( mytimer )
- Delete it safely del_timer_sync( mytimer)
6A kernel-timer caution
- A kernel timers 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
7trytimer.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
8Delaying 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
9Programming syntax
- Declare struct workqueue_struct myqueue
- struct work_struct thework
-
- Define void dowork( void data ) / actions
/ - Initialize myqueue create_singlethread_workqu
eue( mywork ) - INIT_WORK( thework, dowork, ltdata-pointergt )
- Schedule queue_dalayed_work( myqueue,
thework, ltdelaygt ) - Cleanup if ( !cancel_delayed_work( thework ) )
- flush_workqueue( myqueue )
- destroy_workqueue( myqueue )
10tryworkq.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)
11Applying 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)
12Reducing CPUs 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
13In-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 timers action-function should set ready
back to TRUE, then wake up any sleeping tasks
14Implementation hints
- You need a wait-queue (so your drivers reader
tasks can sleep on it) - You need a timer action-function
- You need to organize your timer-functions
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
15Deferring 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
16sizeof 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
17A 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 )
18The 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
structures field-member, then it returns the
structures address
19Using 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( wd xd yd zd \n,
p-gtw, p-gtx, p-gty, p-gtz )
20- include ltlinux/workqueue.hgt
- void dowork( struct work_struct data )
- DECLARE_DELAYED_WORK( mywork, dowork )
- struct workqueue_struct myqueue
- myqueue create_singlethread_workqueue( mywork
)
21workqueue syntax
include ltlinux/workqueue.hgt struct
workqueue_struct myqueue // pointer to your
workqueue void dowork( struct work_struct data
) // your functions prototype DECLARE_DELAYED_WO
RK( mywork, dowork ) int init_module( void
) myqueue create_singlethread_workqueue(
mywork ) if ( !queue_delayed_work( myqueue,
mywork, HZ5 ) ) return EBUSY return 0 //
SUCCESS void cleanup_module( void
) destroy_workqueue( myqueue )
22tryworkq.c
In this example the delayed work consists of
simply printing a message to the kernels
logfile -- you can view by typing the dmesg
command
void dowork( struct work_struct data
) printk( \n\n I am doing the delayed work
right now \n )
Notice that the action function in this example
ignores its data argument
23An 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
waitqueues action functions
24timer verses workqueue
- Any kernel-timers 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 workqueues 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
25If 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
objects member-fields myqueue
create_singlethread_workqueue( mywork
) INIT_DELAYED_WORK( mywork, dowork
) my_data.tty current-gtsignal-gttty // then
your action-function can access members of your
my_data object like this void dowork( struct
work_struct data ) struct mydata dp
container_of( my_data.msg, struct mydata, msg
) struct tty_struct tty dp-gttty tty-gtdriver
-gtwrite( tty, dp-gtmsg, strlen( dp-gtmsg ) )
26defermsg.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 kernels workqueues) - Our course-website has a link to an online
article by author Jonathan Corbet giving details
of ongoing kernel changes in 2.6
27Summary of tonights 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 its read() method