Kernel timing issuesKernel timersjiffiesjiffies overflowKernel timer syntaxA kernel-timer caution‘trytimer.c’Delaying workProgramming syntax‘tryworkq.c’ and ‘defermsg.c’Applying these ideasReducing CPU’s usageIn-class exerciseImplementation hintsDeferring work‘sizeof’ and ‘offsetof’A ‘struct’ exampleThe ‘container_of()’ macroUsing ‘container_of()’Slide 20‘workqueue’ syntax‘tryworkq.c’An improved example‘timer’ verses ‘workqueue’If ‘dowork()’ needs data…‘defermsg.c’Summary of tonight’s demosKernel timing issuesAn introduction to the use of kernel timers and work queuesKernel 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)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’)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 asadd $1, jiffies+0adc $0, jiffies+4Kernel 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);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‘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’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’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 );‘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)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)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 readingIn-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 tasksImplementation 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’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’‘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-memberA ‘struct’ examplestruct 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 );The ‘container_of()’
View Full Document