chapter 6. flow of time what about various timing-related topics? 1. understanding kernel timing 2. knowing the current time 3. delaying operation for a specified amount of time 4. scheduling async functinos to happen after a time lapse Time Intervals in the Kernel HZ timer interrupts per second. defined in HZ is often 100. maybe faster. don't count on a specific value. [root@zymurgy proc]# cat /proc/interrupts CPU0 0: 52087011 XT-PIC timer ... per clock tick, jiffies++ at boot, jiffies=0 processor-specific registers platform dependent resources may be useful for timing. e.g., Intel has some special counter registers. incremented on clock cycle basis useful for small-scale timing probably best to not zero it. somebody else may be using it. just read it and store it in a variable ... start/stop (twice) x86 TSC timestamp counter introduced with Pentium 64-bit register that counts CPU clock cycles #include for machine specific register rdtsc(low, high); - read 64 value into 2 32 bit variables. rdtscl(low); - read only lower 32 bit part #define rdtsc(low,high) \ __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high)) #define rdtscl(low) \ __asm__ __volatile__("rdtsc" : "=a" (low) : : "edx") see code example p. 183 ... measure execution of instrution linux has timer independent way to do this: #include cycles_t get_cycles(void); 32 bits on pentium. Knowing the current time you are not likely to need to know wall-clock time ... %date Wed Oct 15 14:32:45 PDT 2003 however jiffies may be of use or seconds do_gettimeofday() fills out a structure based on current time, with secs/microseconds. or call get_fast_time() which accesses global xtime which has time in seconds/microseconds. also ORA module called "jit" for just in time. creates /proc/currentime which provides do_gettimeofday/get_fast_time/jiffies Delaying execution a driver may have to wait a bit for a status register to come ready ... however these times are subsecond and on the order of milliseconds or less question: is delay > than a clock tick? if not, need timed loop. long braindead delays unsinged long j = jiffies + jit_delay * HZ; whlie (jiffies < j) ; /* spin whee */ in general don't do this as you are wasting time. If you really need to wait seconds, use a watchdog timer, or block the process. while (jiffies < j) schedule(); is not much better, as this process is still running. if you are crashing in some wierd way, and you have printks ... and they don't seem to match the crash. Sometimes it can pay off to insert delays like the above (in a function) so that your prints will get logged. better ways: 1. block sleep_on_timeout() interruptible_sleep_on_timeout() - wait for a jiffies timeout before reawakening 2. also possible to do your own sleep/wakeup if you have an event that will wake you up short delays kernel functions: udelay (unsigned long usecs) - microsec delay mdelay (unsigned long msecs) - millisec delay these are timed loops in an arch. independent manner and are much better than for (i = 0; i < 1000000; i++) ; which is CPU speed dependent. these are busy waits. Task Queues a driver may need to block a process for some period of time and wake it up without an interrupt 3 ways to do this: 1. task queues 2. tasklets 3. kernel timers The 1st two will reappear in chapter 9 and are useful with interrupt handlers. kernel timers are dealt with later in the chapter. Typical situation: no interrupts but we still want a blocking read. you must poll the device to see when it is ready. waking the polling process at periodic intervals may be inefficient. another problem: might need to give directions to a simple hw device like a motor. process may tell us something but driver needs to handle it in reality. we can register a task for later execution. a task queue: lodge a task here for later operation. you can have your own task queues or use task queues elsewhere in the kernel. so we have 1. preexisting task queues, 2. your own task queues, and 3. tasklets the nature of task queues task q: list of tasks: each task is represented by a function pointer and an argument. ptr argument can be used to pass a ptr to a structure or NULL see book p. 190 bh is bottom-half handler (bottom half of intr handler in linux sense) routine - a function data - a ptr to data next and sync should be cleared once set, "task" is owned by kernel and should be left alone operations that can be run: 1. DECLARE_TASK_QUEUE(name) init to empty state 2. int queue_task(struct tq_struct *task, task_queue *list); queues a task on a named list 3. run_task_queue(task_queue *list) if kernel list, you don't need to call it. if your own list, you do need to call it. How Task Queues Are Run when run_task_queue called, all entries are executed do not assume anything about order in the task queue. each task should run independently. task queues are run when the kernel has nothing else to do. not run when process that setup task queue is executing. run ASYNCHRONOUSLY. in a sense, like interrupt handler. you may assume this is like interrupt mode. constraints: 1. no access to user space is allowed. there is no particular process context. 2. current pointer is not valid 3. no sleeping or scheduling may be done. can't call kmalloc(..., GPF_KERNEL). task may requeue itself in the same queue. e.g., you might reschedule something that moves stepper motors an increment at a time, until it reaches a desired position. predefined task queues queues available include: 1. scheduler queue. runs in process context. currently used keventd and is accessed via schedule_task() function. 2. tq_timer. interrupt time. basically "kernel watchdogs". driven by clock ticks. 3. tq_immediate. run "as soon as possible". return from system call, or when scheduler is run, whichever come sfirst. interrupt time. how the examples work jiq module has examples. creates /proc files that can be read using dd or other tools. process reads a jiq file ... and is put to sleep until buffer is full. DECLARE_WAIT_QUEUE_HEAD(jiq_wait); code for filling a buffer in successive passes is not of interest ... however see p. 193, jiq_task which queues a task the scheduler queue. tasks in this queue are not in interrupt mode, therefore can do more things. E.g., they can sleep. must call schedule_task() to do the work. keventd - special process whose job is running tasks from the scheduler queue keventd provides process context thus in point of fact, keventd provides a general mechanism for kernel threads the timer queue tq_timer is name of queue and is directly available. these tasks are in interrupt mode. queue will run at next clocktick. the intermediate queue run by linux bottom-half mechanism. interrupt mode. only run when bottom-half is "marked" (flags set) queue task and then do mark_bb(IMMEDIATE_BH); this is fastest queue in system with least latency running your own task queues can always declare your own. must use run_task_queue() to run it. no one else will. tasklets new with 2.4, we have "tasklets". now preferred way to do bottom-half tasks. they are a way of deferring work. they always run in interrupt time. on SMP systems, they have good characteristics, including generality not found with older BH code. they now do BH work. DECLARE_TASKLET(name, function, data) DECLARE_TASKLET_DISABLED(name, function, data); although declared, not ready to execute. to schedule: tasklet_schedule(&tasklet); guaranteed to be run once. kernel makes sure it only runs one one CPU, but other tasklets can run on other CPUs other functions: tasklet_disable() - can't run tasklet_enable() - can run tasklet_kill() - if tasklet rescheds itself, you can wipe it out anyway permanently. may not be called in interrupt time. Kernel Timers similar to tasks BUT you can tell exactly when your function will be executed. function is only executed once (but it can reschedule itself). See , p. 201 doubly-linked list timeout is in jiffies. functions: init_timer(struct timer_list *timer); add_timer(struct timer_lilst *timer); mod_timer(struct timer_list *timer, unsigned long expires); del_timer(struct timer_list *timer); delete timer before it expires del_timer_sync - guarantees that before it returns, timer function is NOT running. used to avoid race conditions and should probably always be used. caller should take pains to make sure function will not add itself again (variable ...). this is clock driven ... race conditions are inherent. shared structures should be protected by atomic types (see chapter 10) or by spinlocks