diff -u -r -N -b --exclude-from=diff_exclude linux-ref/Documentation/utime.txt linux-work/Documentation/utime.txt --- linux-ref/Documentation/utime.txt Tue Dec 23 14:36:42 1997 +++ linux-work/Documentation/utime.txt Mon Apr 20 14:08:20 1998 @@ -0,0 +1,182 @@ +UTIME: On-demand Microsecond Resolution Timers +---------------------------------------------- +$ProductId: UTIME/linux/Documentation:utime.txt 1.17 $ + +0: Copyright and Credits +------------------------ + +Copyright (C) 1997 by the University of Kansas Center for Research, +Inc. This software was developed by the Information and +Telecommunication Technology Center (ITTC) at the University of +Kansas. Partial funding for this project was provided by Sprint. This +software may be used and distributed according to the terms of the GNU +Public License, incorporated herein by reference. Neither ITTC nor +Sprint accept any liability whatsoever for this product. + +This project was developed under the direction of Dr. Douglas Niehaus. + +Authors: Balaji S., Raghavan Menon + Furquan Ansari, Jason Keimig, Apurva Sheth + +Please send bug-reports/suggestions/comments to utime@ittc.ukans.edu + +Further details about this project can be obtained at + http://hegel.ittc.ukans.edu/projects/utime/ + or in the file Documentation/utime.txt + + +1: Introduction +--------------- + +Linux, as well as most other operating systems maintain a sense of +time using a periodic interrupt from a timer chip, which is known as +the "heartbeat"; of the system. The heartbeat of the Linux +kernel is 10 ms for the i386 and 1ms for the DEC Alpha. In our +efforts to impart real time characteristics to the Linux kernel, such +a coarse grained timing mechanism was found to be insufficient. + +Since we have concentrated on implementing UTIME for the i386, the +rest of this document uses terms that are specific to the i386. + +One of the ways to increase the temporal granularity of Linux would +be to program the timer chip of the PC to interrupt the kernel at +higher frequencies. This is not an acceptable solution as the overhead +increase due to this is tremendous. For example, if we program the +timer chip to interrupt the CPU at 40 micro-sec, the interrupt +processing cost is so high there is no time left for any other +computation. So basically we need to program the timer chip to +generate interrupts only when there is some scheduled work that needs +to be accomplished. This is what we have achieved. + +II: Usage: +---------- + +To use the UTIME system, patch the kernel with the supplied patch +(Use patch -p1 -s < patch_file_name). +Then configure the kernel and enable CONFIG_UTIME. This option is +located in the "Kernel Hacking" submenu. Once you configure the kernel +and build it then when you boot up this kernel, it will try to calibrate +itself. The calibrated speed can be observed by doing "cat /proc/cpuinfo" +For example, for a 200 MHz Pentium "cat /proc/cpuinfo" should give you: + +processor : 0 +cpu : 586 +model : Pentium 75+ +vendor_id : GenuineIntel +stepping : 12 +fdiv_bug : no +hlt_bug : no +sep_bug : no +fpu : yes +fpu_exception : yes +cpuid : yes +wp : yes +flags : fpu vme de pse tsc msr mce cx8 +bogomips : 79.67 +UTIME cps : 200000000 + +To add timers with microsecond resolution, set the timer->usec and the +timer->expires field appropriately and use "add_timer" to add the timer. + +You can test the UTIME subsystem by enabling CONFIG_UTIMER_TEST in the +kernel configuration. Enabling this, would add random timers to the timeout +queue and measure the time difference between when the timer was supposed +to expire and when it actually did. A histogram of this is outputted to the +console using syslog (The printks use KERN_DEBUG as the level, so the messages +should be in /var/run/debug) + +III. Implementation and Current Status: +-------------------------------------- + +We have implemented the UTIME system for the i386. All the +architecture dependent code is in the files include/asm-i386/mutime.h +and include/asm-i386/mutime-M586.h and +include/asm-i386/mutime-M386.h. The latter two files contain inline +routines specific to the Pentium and above processors and the 386/486 +processors. To port the UTIME system to other architectures, you would +have to provide the routines and the macros in the above asm include +file. + +We have moved the timers part in sched.c into a new file kernel/timers.c +This has been done to reduce the clutter in sched.c + +To achieve microsecond resolution timing, we have added a field to the +timer_list structure (see include/linux/timer.h). This field (usec) +indicates the microsecond within the current jiffy that the timer is +to timeout. + +To maintain compatability with the rest of the kernel, we have +introduced the notion of a software clock. This clock maintains the +"jiffies" granularity and calls the "do_timer" routine every jiffy, so +that the rest of the system which depends on this would run +properly. In addition to the "jiffies" variable which tracks the +number of 10ms ticks that have passed since startup, we have added a +"jiffies_u" variable which tracks the micro-seconds within a 10ms +tick. In the pentium and above processors we use the Pentium TSC to +maintain this increased resolution. In non-Pentium machines we use the +timer chip itself to get this increased resolution, though this is not +very accurate. + +We have introduced two modes of operation. + Periodic Mode: + -------------- + In this mode the timer chip can be programmed + to interrupt the CPU periodically using any resolution. This is + generally useful when you want to increase the temporal + resolution of the whole system by a certain amount. + + Oneshot Mode: + ------------ + In this mode the timer chip is programmed to + interrupt the CPU only when there are events in the timer + queue. If there are no events in the timer queue, the timer chip + is programmed to interrupt the cpu at 10ms boundaries. + +You can switch the mode that the kernel is operating in, by calling +change_timer_mode (see kernel/utime.c) Currently, as soon as the +cpu is calibrated, the kernel is put in oneshot mode. + +IV: Files Modified: +------------------ + New Files: + include/linux/mutime.h + include/asm/mutime.h + include/asm/mutime-M386.h + include/asm/mutime-M586.h + kernel/utime.c + kernel/hist.c + kernel/timers.c + + Modified Files: + include/linux/timer.h + include/linux/sched.h + include/linux/poll.h + fs/select.c + kernel/sched.c + arch/i386/kernel/time.c + arch/i386/kernel/setup.c + init/main.c + kernel/Makefile + +V: Known Bugs: +-------------- + +1) The machines lose time. This is about a few seconds in a day. + This loss seems to happen only in the oneshot mode in the non-pentiums. + +2) For the non-pentium machines, gettimeofday can run backwards.... + This is due to the fact that within one jiffy the timer chip + reading may be non-monotonic (ie if more than one interrupt is + programmed per jiffy) + Anyone knowledgeable about this?? + NOTE: Correction for this has already been made for the Pentium + and above case + +3) UTIME does not seem to work well with "X" running. With the new fast + timers code in the 2.1.xx kernels, when you run X with UTIME + the kernel freezes. Therefore, when CONFIG_UTIME is enabled, + we now use the old timers code (ie a doubly linked list of timers) + +Please send bug-reports/suggestions/comments to utime@ittc.ukans.edu + + diff -u -r -N -b --exclude-from=diff_exclude linux-ref/arch/i386/config.in linux-work/arch/i386/config.in --- linux-ref/arch/i386/config.in Sun Dec 21 19:27:18 1997 +++ linux-work/arch/i386/config.in Mon Apr 20 14:08:20 1998 @@ -142,6 +142,15 @@ if [ "$CONFIG_PROFILE" = "y" ]; then int ' Profile shift count' CONFIG_PROFILE_SHIFT 2 fi + +bool 'Microsecond resolution timer support' CONFIG_UTIME +if [ "$CONFIG_UTIME" = "y" ]; then + bool 'Microsecond resolution timer testing' CONFIG_UTIMER_TEST + if [ "$CONFIG_UTIMER_TEST" = "y" ]; then + int ' UTIMER_TEST bucket size' CONFIG_UTIMER_BSIZE 1 + fi +fi + bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ endmenu diff -u -r -N -b --exclude-from=diff_exclude linux-ref/arch/i386/kernel/setup.c linux-work/arch/i386/kernel/setup.c --- linux-ref/arch/i386/kernel/setup.c Sun Dec 21 19:27:18 1997 +++ linux-work/arch/i386/kernel/setup.c Mon Apr 20 14:08:20 1998 @@ -10,6 +10,10 @@ /* * This file handles the architecture-dependent parts of initialization */ +/* Modified for UTIME: On-demand Microsecond Resolution Timers + * See Documentation/utime.txt for further details + * $ProductId: UTIME/linux/arch/i386/kernel:setup.c 1.17 $ + */ #include #include @@ -32,6 +36,11 @@ #ifdef CONFIG_BLK_DEV_RAM #include #endif +#ifdef CONFIG_UTIME +#define _INCLUDED_FROM_SETUP_C_ +#include +#undef _INCLUDED_FROM_SETUP_C_ +#endif #include #include #include @@ -497,5 +506,9 @@ (c->loops_per_sec+2500)/500000, ((c->loops_per_sec+2500)/5000) % 100); } +#ifdef CONFIG_UTIME + p += sprintf(p,"UTIME cps\t: %lu ticks per second\n\n", + cycles_per_sec); +#endif return p - buffer; } diff -u -r -N -b --exclude-from=diff_exclude linux-ref/arch/i386/kernel/time.c linux-work/arch/i386/kernel/time.c --- linux-ref/arch/i386/kernel/time.c Sun Dec 21 19:27:18 1997 +++ linux-work/arch/i386/kernel/time.c Mon Apr 20 14:08:20 1998 @@ -13,6 +13,10 @@ * 1996-05-03 Ingo Molnar * fixed time warps in do_[slow|fast]_gettimeoffset() */ +/* Modified for UTIME: On-demand Microsecond Resolution Timers + * See Documentation/utime.txt for further details + * $ProductId: UTIME/linux/arch/i386/kernel:time.c 1.17 $ + */ #include #include #include @@ -33,6 +37,9 @@ #include #include #include +#ifdef CONFIG_UTIME +#include +#endif /* * for x86_do_profile() @@ -173,6 +180,7 @@ { int count; +#ifndef CONFIG_UTIME static int count_p = LATCH; /* for the first call after boot */ static unsigned long jiffies_p = 0; @@ -244,10 +252,21 @@ count = ((LATCH-1) - count) * TICK_SIZE; count = (count + LATCH/2) / LATCH; +#else /* CONFIG_UTIME */ + count=jiffies_u; +#endif return count; } +#ifdef CONFIG_UTIME +static unsigned long do_utime_gettimeoffset(void) +{ + arch_update_jiffies (0); + return jiffies_u; +} +#endif + /* * this is only used if we have fast gettimeoffset: */ @@ -374,9 +393,27 @@ * timer_interrupt() needs to keep up the real-time clock, * as well as call the "do_timer()" routine every clocktick */ +#ifdef CONFIG_UTIME +extern int jiffies_intr; +#endif + static inline void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) { +#ifdef CONFIG_UTIME +#if defined(CONFIG_M386)||defined(CONFIG_M486)||defined(CONFIG_APM) + /* if it is not a pentium or above, we need to call + * get_cpuctr so that the monotonic clock + * new_global_time is updated + */ + get_cpuctr_from_timer_interrupt(); +#endif +#endif do_timer(regs); +#ifdef CONFIG_UTIME + if (!jiffies_intr) + return; + jiffies_intr--; +#endif /* * In the SMP case we use the local APIC timer interrupt to do the * profiling, except when we simulate SMP mode on a uniprocessor @@ -428,6 +465,28 @@ } #ifndef CONFIG_APM /* cycle counter may be unreliable */ + +/* This functions is called from do_timer (the original one) + * orig_do_timer gets called only once every 10 ms, hence we note the + * last_timer_cc once every 10 ms only. + * + * When CONFIG_UTIME is set this function gets called from + * orig_do_timer. + * + * When CONFIG_UTIME is not set this function gets called from + * pentium_timer_interrupt + */ +#if defined(CONFIG_M586)||defined(CONFIG_M686) +inline void set_last_timer_cc(void) +{ + /* read the pentium timer chip */ + __asm__("rdtsc" + :"=a" (last_timer_cc.low), + "=d" (last_timer_cc.high)); +} + +#endif + /* * This is the same as the above, except we _also_ save the current * cycle counter value at the time of the timer interrupt, so that @@ -435,10 +494,11 @@ */ static void pentium_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - /* read Pentium cycle counter */ - __asm__("rdtsc" - :"=a" (last_timer_cc.low), - "=d" (last_timer_cc.high)); +#ifndef CONFIG_UTIME +#if defined(CONFIG_M586)||defined(CONFIG_M686) + set_last_timer_cc(); +#endif +#endif timer_interrupt(irq, NULL, regs); } #endif @@ -527,8 +587,19 @@ /* Don't use them if a suspend/resume could corrupt the timer value. This problem needs more debugging. */ + /* If we configure the kernel to be for a 386/486 but the + * kernel is run on a Pentium or above, setting do_gettimeoffset + * to do_fast_gettimeoffset would lead to a kernel crash. + * This is bcos in the pentium_timer_interrupt routine + * set_last_timer_cc would end being a null statement + */ +#if !defined(CONFIG_M386) && !defined(CONFIG_M486) if (boot_cpu_data.x86_capability & 16) { +#ifndef CONFIG_UTIME do_gettimeoffset = do_fast_gettimeoffset; +#else + do_gettimeoffset = do_utime_gettimeoffset; +#endif do_get_fast_time = do_x86_get_fast_time; if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD && @@ -549,6 +620,7 @@ "=d" (init_timer_cc.high)); irq0.handler = pentium_timer_interrupt; } +#endif #endif setup_x86_irq(0, &irq0); } diff -u -r -N -b --exclude-from=diff_exclude linux-ref/drivers/char/sysrq.c linux-work/drivers/char/sysrq.c --- linux-ref/drivers/char/sysrq.c Sun Dec 21 19:27:17 1997 +++ linux-work/drivers/char/sysrq.c Mon Apr 20 14:08:20 1998 @@ -26,6 +26,10 @@ #include #endif +#ifdef CONFIG_UTIME +#include +#endif + extern void wakeup_bdflush(int); extern void reset_vc(unsigned int); extern int console_loglevel; @@ -62,6 +66,12 @@ console_loglevel = 7; printk(KERN_INFO "SysRq: "); switch (key) { +#ifdef CONFIG_UTIME + case 'q': /* Change the timer mode to periodic */ /* Q */ + change_timer_mode(timer_chip_periodic, 10000); + printk("Timer mode switched to periodic"); + break; +#endif case 'r': /* R -- Reset raw mode */ if (kbd) { kbd->kbdmode = VC_XLATE; diff -u -r -N -b --exclude-from=diff_exclude linux-ref/fs/select.c linux-work/fs/select.c --- linux-ref/fs/select.c Fri Dec 19 17:52:10 1997 +++ linux-work/fs/select.c Mon Apr 20 14:08:21 1998 @@ -9,6 +9,10 @@ * flag set in its personality we do *not* modify the given timeout * parameter to reflect time remaining. */ +/* Modified for UTIME: On-demand Microsecond Resolution Timers + * See Documentation/utime.txt for further details + * $ProductId: UTIME/linux/fs:select.c 1.17 $ + */ #include #include @@ -29,6 +33,13 @@ #include #include +#include /* for CONFIG_UTIME */ +#ifdef CONFIG_UTIME +#define _INCLUDED_FROM_SETUP_C_ +#include +#undef _INCLUDE_FROM_SETUP_C_ +#endif + #define ROUND_UP(x,y) (((x)+(y)-1)/(y)) #define DEFAULT_POLLMASK (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM) @@ -122,7 +133,12 @@ #define POLLOUT_SET (POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR) #define POLLEX_SET (POLLPRI) +#ifndef CONFIG_UTIME int do_select(int n, fd_set_buffer *fds, unsigned long timeout) +#else +int do_select(int n, fd_set_buffer *fds, unsigned long timeout, + unsigned long timeout_u) +#endif { poll_table wait_table, *wait; int retval; @@ -132,7 +148,12 @@ wait = NULL; current->timeout = timeout; +#ifdef CONFIG_UTIME + current->timeout_u = timeout_u; + if (timeout || timeout_u) { +#else if (timeout) { +#endif struct poll_table_entry *entry = (struct poll_table_entry *) __get_free_page(GFP_KERNEL); if (!entry) { @@ -185,14 +206,23 @@ } } wait = NULL; +#ifdef CONFIG_UTIME + if (retval || (!current->timeout && !current->timeout_u) + || signal_pending(current)) +#else if (retval || !current->timeout || signal_pending(current)) +#endif break; schedule(); } current->state = TASK_RUNNING; out: +#ifdef CONFIG_UTIME + if (timeout || timeout_u) { +#else if (timeout) { +#endif free_wait(&wait_table); free_page((unsigned long) wait_table.entry); } @@ -214,6 +244,9 @@ { fd_set_buffer *fds; unsigned long timeout; +#ifdef CONFIG_UTIME + unsigned long timeout_u=0; +#endif int ret; timeout = ~0UL; @@ -225,10 +258,17 @@ || (ret = __get_user(usec, &tvp->tv_usec))) goto out_nofds; +#ifndef CONFIG_UTIME timeout = ROUND_UP(usec, 1000000/HZ); timeout += sec * (unsigned long) HZ; if (timeout) timeout += jiffies + 1; +#else + timeout_u = usec + jiffies_u; + timeout = timeout_u/USEC_PER_JIFFIES+jiffies; + timeout += sec * (unsigned long) HZ; + timeout_u = timeout_u%USEC_PER_JIFFIES; +#endif } ret = -ENOMEM; @@ -248,8 +288,11 @@ zero_fd_set(n, fds->res_out); zero_fd_set(n, fds->res_ex); +#ifdef CONFIG_UTIME + ret = do_select(n, fds, timeout, timeout_u); +#else ret = do_select(n, fds, timeout); - +#endif if (tvp && !(current->personality & STICKY_TIMEOUTS)) { unsigned long timeout = current->timeout - jiffies - 1; time_t sec = 0, usec = 0; @@ -262,7 +305,9 @@ put_user(usec, &tvp->tv_usec); } current->timeout = 0; - +#ifdef CONFIG_UTIME + current->timeout_u = 0; +#endif if (ret < 0) goto out; if (!ret) { @@ -314,7 +359,12 @@ } wait = NULL; +#ifdef CONFIG_UTIME + if (count || (!current->timeout && !current->timeout_u) + || signal_pending(current)) +#else if (count || !current->timeout || signal_pending(current)) +#endif break; schedule(); } diff -u -r -N -b --exclude-from=diff_exclude linux-ref/include/asm-i386/mutime-M386.h linux-work/include/asm-i386/mutime-M386.h --- linux-ref/include/asm-i386/mutime-M386.h Tue Dec 23 14:36:41 1997 +++ linux-work/include/asm-i386/mutime-M386.h Mon Apr 20 14:08:19 1998 @@ -0,0 +1,207 @@ +/* + * UTIME: On-demand Microsecond Resolution Timers + * ---------------------------------------------- + * $ProductId: UTIME/linux/include/asm-i386:mutime-M386.h 1.17 $ + * + * File: include/asm-i386/mutime-M386.h + * Copyright (C) 1997 by the University of Kansas Center for Research, + * Inc. This software was developed by the Information and + * Telecommunication Technology Center (ITTC) at the University of + * Kansas. Partial funding for this project was provided by Sprint. This + * software may be used and distributed according to the terms of the GNU + * Public License, incorporated herein by reference. Neither ITTC nor + * Sprint accept any liability whatsoever for this product. + * + * This project was developed under the direction of Dr. Douglas Niehaus. + * + * Authors: Balaji S., Raghavan Menon + * Furquan Ansari, Jason Keimig, Apurva Sheth + * + * Thanx to Michael Barabanov for helping me with the non-pentium code. + * + * Please send bug-reports/suggestions/comments to utime@ittc.ukans.edu + * + * Further details about this project can be obtained at + * http://hegel.ittc.ukans.edu/projects/utime/ + * or in the file Documentation/utime.txt + */ +/* This is in case its not a pentuim or a ppro. + * we dont have access to the cycle counters + */ +#ifndef _ASM_MUTIME_M386_H +#define _ASM_MUTIME_M386_H + +#ifdef __KERNEL__ + +#include /* for u16s */ +#include + +extern volatile unsigned long long new_global_time; +extern u16 base_c0; +extern spinlock_t timer_chip_lock; + +#define READ_CNT0(var) { var = inb(0x40);var |= (inb(0x40)<<8);} +#define READ_CNT1(var) { var = inb(0x41); } +#define LATCH_CNT0() { outb(0xd2,0x43); } +#define LATCH_CNT0_AND_CNT1() { outb(0xd6,0x43); } + +extern inline void get_cpuctr_from_timer_interrupt(void) +{ + register u16 c0; + unsigned long flags; + + spin_lock_irqsave(&timer_chip_lock, flags); + LATCH_CNT0(); + READ_CNT0(c0); + if (kernel_timer_mode == timer_chip_oneshot) { +#ifdef __UTIME_DEBUG__ + if (c0 < 65000) { + printk(KERN_DEBUG "after interrupt -> c0 = %d.\n", c0); + dprintk_address(); + } +#endif + new_global_time += (base_c0 + 0xffff - c0); + } else { +#ifdef __UTIME_DEBUG__ + if (c0 < TO_LATCH(latch_reload) - 100) { + printk(KERN_DEBUG "after interrupt -> c0 = %d.\n", c0); + dprintk_address(); + } +#endif + new_global_time += base_c0 + + (TO_LATCH((unsigned long) latch_reload) - c0); + } + base_c0 = c0; + spin_unlock_irqrestore(&timer_chip_lock, flags); + return; +} + +extern inline unsigned long long get_cpuctr(void) +{ + register u16 c0; + unsigned long flags; + + spin_lock_irqsave(&timer_chip_lock, flags); + LATCH_CNT0(); + READ_CNT0(c0); +#ifdef __UTIME_DEBUG__ + if (base_c0 < c0) { + printk(KERN_DEBUG "base_c0 (%d) < c0 (%d).\n", base_c0, c0); + dprintk_address(); + } +#endif + new_global_time += (u16) (base_c0 - c0); + +#ifdef __UTIME_DEBUG__ + if ((u16) (base_c0 - c0) > cycles_per_jiffies) { + printk(KERN_DEBUG "base_c0 (%d) - c0 (%d) = %d.\n", + base_c0, c0, (u16) (base_c0 - c0)); + dprintk_address(); + } +#endif + base_c0 = c0; + spin_unlock_irqrestore(&timer_chip_lock, flags); + return new_global_time; +} + +/* This routine would keep track of the lost jiffies_u + * for architectures that have decimal places in cycles_per_usec + * ie for the 386/486 cycles_per_usec = 1.1932 + */ +extern inline int arch_cycles_to_usec(long update) +{ + return ((update * USEC_PER_JIFFIES) / cycles_per_jiffies); +} + +extern int volatile jiffies_u; +extern volatile unsigned long lost_ticks; +extern int jiffies_intr; + +extern inline void arch_update_jiffies (unsigned long update) +{ + unsigned long jumped = 0; + + jiffies_u += ((update * USEC_PER_JIFFIES) / cycles_per_jiffies); + remainder_of_usec += ((update * USEC_PER_JIFFIES) % + cycles_per_jiffies); + + remainder_of_usec += update; + if (remainder_of_usec >= cycles_per_jiffies) { + remainder_of_usec -= cycles_per_jiffies; + jiffies_u++; + } + + /* The following while loop is used because sometimes + * jiffies_u may increase to more than one jiffy... + * This happens very rarely. Since the update is rarely more + * than one jiffy, use a loop instead of division. + */ + while (jiffies_u >= USEC_PER_JIFFIES) { + jumped++; + (*(unsigned long *) &jiffies_u) -= USEC_PER_JIFFIES; + } + (*(unsigned long *) &jiffies) += jumped; + lost_ticks += jumped; + jiffies_intr += jumped; +} + +#ifdef _INCLUDED_FROM_UTIME_C_ + +#define set_last_timer_cc() (void)(1) + +/* This returns the correct cycles_per_sec from a calibrated one + */ +#define arch_utime_init(x) (1193200) + +extern inline void reload_timer_chip(unsigned int new_latch_value) +{ + unsigned long flags; + register u16 c1, c1new; + /* convert to usecs + */ + spin_lock_irqsave(&timer_chip_lock, flags); + /* we need to get this last value of the timer chip + */ + LATCH_CNT0_AND_CNT1(); + READ_CNT0(c1new); + READ_CNT1(c1); +#ifdef __UTIME_DEBUG__ + if (base_c0 < c1new) { + printk(KERN_DEBUG "base_c0 (%d) < c1new (%d).\n", + base_c0, c1new); + dprintk_address(); + } +#endif + new_global_time += (base_c0 - c1new); + + new_latch_value = TO_LATCH(new_latch_value); + outb_p(new_latch_value & 0xff, 0x40); /* LSB */ + outb(new_latch_value >> 8, 0x40); /* MSB */ + udelay(4); + + LATCH_CNT0_AND_CNT1(); + READ_CNT0(base_c0); + READ_CNT1(c1new); + /* this is assuming that the counter one is latched on with + * 18 as the value + * Most BIOSes do this i guess.... + */ +#ifdef __UTIME_DEBUG__ + if ((c1 + 18) < c1new) { + printk(KERN_DEBUG "c1+18 (%d) < c1new (%d).\n", c1 + 18, c1new); + dprintk_address(); + } + if (base_c0 > new_latch_value) { + printk(KERN_DEBUG "base_c0 (%d) > new_latch_value (%d).\n", + base_c0, new_latch_value); + dprintk_address(); + } +#endif + new_global_time += ((c1 < c1new) ? ((c1 + 18) - c1new) : (c1 - c1new)); + spin_unlock_irqrestore(&timer_chip_lock, flags); + return; +} +#endif /* _INCLUDED_FROM_UTIME_C_ */ +#endif /* __KERNEL__ */ +#endif /* _ASM_MUTIME_M386_H */ + diff -u -r -N -b --exclude-from=diff_exclude linux-ref/include/asm-i386/mutime-M586.h linux-work/include/asm-i386/mutime-M586.h --- linux-ref/include/asm-i386/mutime-M586.h Tue Dec 23 14:36:41 1997 +++ linux-work/include/asm-i386/mutime-M586.h Mon Apr 20 16:18:10 1998 @@ -0,0 +1,114 @@ +/* + * UTIME: On-demand Microsecond Resolution Timers + * ---------------------------------------------- + * $ProductId: UTIME/linux/include/asm-i386:mutime-M586.h 1.17 $ + * + * File: include/asm-i386/mutime-M586.h + * Copyright (C) 1997 by the University of Kansas Center for Research, + * Inc. This software was developed by the Information and + * Telecommunication Technology Center (ITTC) at the University of + * Kansas. Partial funding for this project was provided by Sprint. This + * software may be used and distributed according to the terms of the GNU + * Public License, incorporated herein by reference. Neither ITTC nor + * Sprint accept any liability whatsoever for this product. + * + * This project was developed under the direction of Dr. Douglas Niehaus. + * + * Authors: Balaji S., Raghavan Menon + * Furquan Ansari, Jason Keimig, Apurva Sheth + * + * Please send bug-reports/suggestions/comments to utime@ittc.ukans.edu + * + * Further details about this project can be obtained at + * http://hegel.ittc.ukans.edu/projects/utime/ + * or in the file Documentation/utime.txt + */ +#ifndef _ASM_MUTIME_M586_H +#define _ASM_MUTIME_M586_H + +#ifdef __KERNEL__ + +/* NOTE: When trying to port this to other architectures define + * this to be (void)(1) (ie. #define set_last_timer_cc() (void)(1)) + * otherwise sched.c would give an undefined reference + */ +extern void set_last_timer_cc(void); + +/* These are specific to the pentium counters + */ +extern inline unsigned long long get_cpuctr(void) +{ + unsigned long long value; + /* read the pentium cycle counter */ + __asm__(".byte 0x0f,0x31" + : "=a"(((unsigned long *) &value)[0]), + "=d"(((unsigned long *) &value)[1])); + + return value; +} + +#define arch_utime_init(x) (x) +extern int volatile jiffies_u; +extern volatile unsigned long lost_ticks; +extern int jiffies_intr; +extern unsigned long long base_cpuctr; +extern unsigned long base_jiffies; + +extern inline int arch_cycles_to_usec(unsigned long update) +{ + return (update / cycles_per_usec); +} + +extern inline void arch_update_jiffies (unsigned long update) +{ + unsigned long new_jiffies, new_jiffies_u; + unsigned long seconds, remainder, excess, diff; + unsigned long jumped; + + __asm__("rdtsc\n\t" + "subl "SYMBOL_NAME_STR(base_cpuctr)",%0\n\t" + "sbbl "SYMBOL_NAME_STR(base_cpuctr)"+4,%1\n\t" + "divl %2" + :"=a" (seconds), "=d" (remainder) + :"m" (cycles_per_sec) + :"eax", "edx"); + + excess = (remainder / cycles_per_jiffies); + new_jiffies = base_jiffies + (seconds * HZ) + excess; + /* + * The calculation for new_jiffies_u is ugly because we need + * to maintain as much accuracy as possible. Since diff is + * always between 0 and cycles_per_jiffies - 1, we can + * multiply it by HZ without overflowing. Then, we can use + * the increased precision of cycles_per_jiffies (instead of + * the more straightforward division by cycles_per_usec). + */ + diff = remainder - (excess * cycles_per_jiffies); + new_jiffies_u = (diff * HZ) / (cycles_per_jiffies / + (USEC_PER_JIFFIES / HZ)); + + jumped = new_jiffies - jiffies; + if (jumped) { + lost_ticks += jumped; + jiffies_intr += jumped; + jiffies = new_jiffies; + } + jiffies_u = new_jiffies_u; +} + +#ifdef _INCLUDED_FROM_UTIME_C_ +#include + +extern inline void reload_timer_chip(unsigned int new_latch_value) +{ + /* convert to usecs + */ + new_latch_value = TO_LATCH(new_latch_value); + outb_p(new_latch_value & 0xff, 0x40); /* LSB */ + outb(new_latch_value >> 8, 0x40); /* MSB */ + + return; +} +#endif /* _INCLUDED_FROM_UTIME_C_ */ +#endif /* __KERNEL__ */ +#endif /* _ASM_MUTIME-M586_H */ diff -u -r -N -b --exclude-from=diff_exclude linux-ref/include/asm-i386/mutime.h linux-work/include/asm-i386/mutime.h --- linux-ref/include/asm-i386/mutime.h Tue Dec 23 14:36:41 1997 +++ linux-work/include/asm-i386/mutime.h Mon Apr 20 14:08:19 1998 @@ -0,0 +1,112 @@ +/* + * UTIME: On-demand Microsecond Resolution Timers + * ---------------------------------------------- + * $ProductId: UTIME/linux/include/asm-i386:mutime.h 1.17 $ + * + * File: include/asm-i386/mutime.h + * Copyright (C) 1997 by the University of Kansas Center for Research, + * Inc. This software was developed by the Information and + * Telecommunication Technology Center (ITTC) at the University of + * Kansas. Partial funding for this project was provided by Sprint. This + * software may be used and distributed according to the terms of the GNU + * Public License, incorporated herein by reference. Neither ITTC nor + * Sprint accept any liability whatsoever for this product. + * + * This project was developed under the direction of Dr. Douglas Niehaus. + * + * Authors: Balaji S., Raghavan Menon + * Furquan Ansari, Jason Keimig, Apurva Sheth + * + * Please send bug-reports/suggestions/comments to utime@ittc.ukans.edu + * + * Further details about this project can be obtained at + * http://hegel.ittc.ukans.edu/projects/utime/ + * or in the file Documentation/utime.txt + */ +#ifndef _I386_MUTIME_H +#ifdef __KERNEL__ + +#include /* for CONFIG_APM etc... */ + +#ifdef __UTIME_DEBUG__ +#define dprintk() printk(KERN_DEBUG "Error: %d (%lu), [%d:%s,%s,%d].\n", \ + from,this_value,get_timer_mode(), \ + __FUNCTION__,__FILE__,__LINE__) +#define dprintk_address() printk(KERN_DEBUG \ + "Error in [%s:%s:%d] called from 0x%p.\n", \ + __FILE__, __FUNCTION__, __LINE__, \ + __builtin_return_address(0)) +#else +#define dprintk() (void)(1) +#define dprintk_address() (void)(1) +#endif + +#define TO_LATCH(x) (((x)*LATCH)/USEC_PER_JIFFIES) + +/* no of usecs less than which events cannot be scheduled + */ +#define TIMER_DELTA 30 + +extern enum timer_chip_mode kernel_timer_mode; +extern spinlock_t utime_lock; +extern unsigned long next_intr; + +/* Architecture dependent values for UTIME. + * This is mainly the number of cycles in a second. + * It is calibrated during run time (run time calibration is + * done only of if command line "cps=" is not given) + */ +extern unsigned long cycles_per_sec; +extern unsigned long cycles_per_usec; +extern unsigned long cycles_per_jiffies; +extern unsigned long remainder_of_usec; +extern unsigned long long volatile last_update; +extern int jiffies_intr; +extern unsigned int volatile latch_reload; + +/* Now go ahead and include the 586/386 specific files + * These asm files have extern inline functions to do a lot of + * stuff + */ +#if defined(CONFIG_M386)||defined(CONFIG_M486)||defined(CONFIG_APM) +#ifndef _INCLUDED_FROM_SETUP_C_ +#include +#endif +#else +#include +#endif + +#ifdef _INCLUDED_FROM_UTIME_C_ +/* NOTE: Never call this routine from anywhere else other than + * update_jiffies_u() + */ +extern inline unsigned int read_timer_chip(void) +{ + unsigned int next_intr; + + outb_p(0xd2, 0x43); /* latch the count */ + next_intr = inb_p(0x40); /* read the latched count */ + next_intr |= inb(0x40) << 8; + next_intr = (next_intr * USEC_PER_JIFFIES) / LATCH; + return next_intr; +} + +extern inline void put_timer_in_oneshot_mode(void) +{ + if (cycles_per_sec) { + outb_p(0x38, 0x43); /* binary, mode 4, LSB/MSB, ch 0 */ + } else { + printk(KERN_CRIT "utime: Cant put kernel in oneshot mode, without calibrating cycles per sec.\n"); + } + udelay(4); +} + +extern inline void put_timer_in_periodic_mode(void) +{ + outb_p(0x34, 0x43); /* binary, mode 2, LSB/MSB, ch 0 */ + udelay(4); +} + +#endif /* __KERNEL__ */ +#endif /* _INCLUDED_FROM_UTIME_C_ */ +#endif /* _I386_MUTIME_H */ diff -u -r -N -b --exclude-from=diff_exclude linux-ref/include/linux/hist.h linux-work/include/linux/hist.h --- linux-ref/include/linux/hist.h Tue Dec 23 14:36:40 1997 +++ linux-work/include/linux/hist.h Mon Apr 20 14:08:19 1998 @@ -0,0 +1,68 @@ +/* + * UTIME: On-demand Microsecond Resolution Timers + * ---------------------------------------------- + * $ProductId: UTIME/linux/include/linux:hist.h 1.17 $ + * + * File: include/linux/hist.h + * Copyright (C) 1997 by the University of Kansas Center for Research, + * Inc. This software was developed by the Information and + * Telecommunication Technology Center (ITTC) at the University of + * Kansas. Partial funding for this project was provided by Sprint. This + * software may be used and distributed according to the terms of the GNU + * Public License, incorporated herein by reference. Neither ITTC nor + * Sprint accept any liability whatsoever for this product. + * + * This project was developed under the direction of Dr. Douglas Niehaus. + * + * Authors: Balaji S., Raghavan Menon + * Furquan Ansari, Jason Keimig, Apurva Sheth + * + * Please send bug-reports/suggestions/comments to utime@ittc.ukans.edu + * + * Further details about this project can be obtained at + * http://hegel.ittc.ukans.edu/projects/utime/ + * or in the file Documentation/utime.txt + */ +/* + * all types start with a capital letter, variables and functions with small letter + */ + +#ifndef _HIST_H_ +#define _HIST_H_ + +#define MAX_BUCKETS 600 + +typedef unsigned long HistValue; + +typedef struct _HistogramBucketStruct { /* histogram bucket struct */ + int entries; /* entries in this bucket */ + HistValue sum; /* sum of values of all entries in bucket */ +} HistogramBucket; + +/* + * struct holding entire histogram. bucket 0 = values + * below "low", bucket "buckets + 1" = values >= + * "high". Actual buckets = 1 .. "buckets" + */ +typedef struct HistogramStruct { + int buckets; /* number of buckets */ + int span; /* range of entries in each bucket except 0 and buckets+1*/ + HistValue low; /* lowest value in 1st bucket */ + HistValue high; /* value just outside last bucket */ + HistogramBucket *hb; /* array of buckets */ +} Histogram; + +#define BUCKET(h, value) (((value)>=(h)->low) ? (((value)<(h)->high) ? ((value)-(h)->low)/(h)->span+1 : (h)->buckets+1) : 0) + +#ifdef __KERNEL__ + +Histogram *histNew(HistValue low, int span, int buckets, int priority); +void histDelete(Histogram *hist); +void histAdd(Histogram *h, HistValue value); +void histPrintk(Histogram *h); +void _histPrintk(Histogram *h); +HistValue timeDiff(struct timeval *start, struct timeval *end); + +#endif /* __KERNEL__ */ + +#endif /* _HIST_H_ */ diff -u -r -N -b --exclude-from=diff_exclude linux-ref/include/linux/mutime.h linux-work/include/linux/mutime.h --- linux-ref/include/linux/mutime.h Tue Dec 23 14:36:40 1997 +++ linux-work/include/linux/mutime.h Mon Apr 20 14:08:19 1998 @@ -0,0 +1,69 @@ +/* + * UTIME: On-demand Microsecond Resolution Timers + * ---------------------------------------------- + * $ProductId: UTIME/linux/include/linux:mutime.h 1.17 $ + * + * File: include/linux/mutime.h + * Copyright (C) 1997 by the University of Kansas Center for Research, + * Inc. This software was developed by the Information and + * Telecommunication Technology Center (ITTC) at the University of + * Kansas. Partial funding for this project was provided by Sprint. This + * software may be used and distributed according to the terms of the GNU + * Public License, incorporated herein by reference. Neither ITTC nor + * Sprint accept any liability whatsoever for this product. + * + * This project was developed under the direction of Dr. Douglas Niehaus. + * + * Authors: Balaji S., Raghavan Menon + * Furquan Ansari, Jason Keimig, Apurva Sheth + * + * Please send bug-reports/suggestions/comments to utime@ittc.ukans.edu + * + * Further details about this project can be obtained at + * http://hegel.ittc.ukans.edu/projects/utime/ + * or in the file Documentation/utime.txt + */ +#ifndef _LINUX_MUTIME_H +#define _LINUX_MUTIME_H + +enum timer_chip_mode { + timer_chip_periodic, timer_chip_oneshot +}; + +#define USEC_PER_JIFFIES (1000020/HZ) +#define USEC_PER_SEC 1000000L + +#ifdef __KERNEL__ + +#undef __UTIME_DEBUG__ + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +extern unsigned long volatile jiffies; /* unit = HZ */ +extern int volatile jiffies_u; /* timer clock tics inside hz boundary */ +extern unsigned long cycles_per_sec; + +void utime_do_timer_periodic(struct pt_regs *); +void utime_do_timer_oneshot(struct pt_regs *); +extern void (*do_timer) (struct pt_regs *); +void utimer_test(void); +void load_timer(void); +void reload_timer(unsigned long expires, unsigned long usec); +inline enum timer_chip_mode get_timer_mode(void); +int change_timer_mode(enum timer_chip_mode, int); +void update_jiffies_u(void); +struct timer_list *get_unexpired_timer(void); +void utime_init(void); + +#endif /*__KERNEL */ +#endif /* _LINUX_MUTIME_H */ diff -u -r -N -b --exclude-from=diff_exclude linux-ref/include/linux/poll.h linux-work/include/linux/poll.h --- linux-ref/include/linux/poll.h Sun Dec 21 19:58:59 1997 +++ linux-work/include/linux/poll.h Mon Apr 20 14:08:19 1998 @@ -1,3 +1,7 @@ +/* Modified for UTIME: On-demand Microsecond Resolution Timers + * See Documentation/utime.txt for further details + * $ProductId: UTIME/linux/include/linux:poll.h 1.17 $ + */ #ifndef _LINUX_POLL_H #include @@ -7,7 +11,7 @@ #include #include #include - +#include /* for CONFIG_UTIME */ struct poll_table_entry { struct wait_queue wait; @@ -99,7 +103,12 @@ memset(fdset, 0, nr); } +#ifdef CONFIG_UTIME +extern int do_select(int n, fd_set_buffer *fds, unsigned long timeout, + unsigned long timeout_u); +#else extern int do_select(int n, fd_set_buffer *fds, unsigned long timeout); +#endif #endif /* KERNEL */ diff -u -r -N -b --exclude-from=diff_exclude linux-ref/include/linux/sched.h linux-work/include/linux/sched.h --- linux-ref/include/linux/sched.h Sun Dec 21 19:58:36 1997 +++ linux-work/include/linux/sched.h Mon Apr 20 14:08:19 1998 @@ -1,3 +1,7 @@ +/* Modified for UTIME: On-demand Microsecond Resolution Timers + * See Documentation/utime.txt for further details + * $ProductId: UTIME/linux/include/linux:sched.h 1.17 $ + */ #ifndef _LINUX_SCHED_H #define _LINUX_SCHED_H @@ -20,6 +24,7 @@ #include #include #include +#include /* for CONFIG_UTIME */ /* * cloning flags: @@ -264,6 +269,9 @@ int lock_depth; /* Lock depth. We can context switch in and out of holding a syscall kernel lock... */ /* Spinlocks for various pieces or per-task state. */ spinlock_t sigmask_lock; /* Protects signal and blocked */ +#ifdef CONFIG_UTIME + unsigned long timeout_u; +#endif }; /* @@ -428,7 +436,11 @@ extern unsigned long itimer_next; extern struct timeval xtime; extern int need_resched; +#ifdef CONFIG_UTIME +extern void orig_do_timer(struct pt_regs *); +#else extern void do_timer(struct pt_regs *); +#endif extern unsigned int * prof_buffer; extern unsigned long prof_len; diff -u -r -N -b --exclude-from=diff_exclude linux-ref/include/linux/timer.h linux-work/include/linux/timer.h --- linux-ref/include/linux/timer.h Sun Dec 21 19:04:50 1997 +++ linux-work/include/linux/timer.h Mon Apr 20 14:08:18 1998 @@ -1,6 +1,13 @@ +/* Modified for UTIME: On-demand Microsecond Resolution Timers + * See Documentation/utime.txt for further details + * $ProductId: UTIME/linux/include/linux:timer.h 1.17 $ + */ #ifndef _LINUX_TIMER_H #define _LINUX_TIMER_H +/* for utime + */ +#include /* * DON'T CHANGE THESE!! Most of them are hardcoded into some assembly language * as well as being defined here. @@ -82,6 +89,9 @@ unsigned long expires; unsigned long data; void (*function)(unsigned long); +#ifdef CONFIG_UTIME + unsigned long usec; /*goes from 0 to 9999*/ +#endif }; extern void add_timer(struct timer_list * timer); @@ -93,6 +103,11 @@ { timer->next = NULL; timer->prev = NULL; +#ifdef CONFIG_UTIME + /* Added for utime-Balaji + */ + timer->usec = 0; +#endif } #endif diff -u -r -N -b --exclude-from=diff_exclude linux-ref/init/main.c linux-work/init/main.c --- linux-ref/init/main.c Sun Dec 21 19:50:46 1997 +++ linux-work/init/main.c Mon Apr 20 14:08:17 1998 @@ -8,6 +8,10 @@ * Moan early if gcc is old, avoiding bogus kernels - Paul Gortmaker, May '96 * Simplified starting of init: Michael A. Griffith */ +/* Modified for UTIME: On-demand Microsecond Resolution Timers + * See Documentation/utime.txt for further details + * $ProductId: UTIME/linux/init:main.c 1.17 $ + */ #define __KERNEL_SYSCALLS__ @@ -50,6 +54,9 @@ #if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 6) #error sorry, your GCC is too old. It builds incorrect kernels. #endif +#ifdef CONFIG_UTIME +#include +#endif extern char _stext, _etext; extern char *linux_banner; @@ -858,6 +865,18 @@ /* * check for kernel options first.. */ +#ifdef CONFIG_UTIME + /* CPU cycles per second option + * if this option is not specified then the kernel + * goes and actually calibrates the cycles per second + * value + */ + if (!strncmp(line, "cps=", 4)) { + line += 4; + cycles_per_sec = simple_strtoul(line, NULL, 0); + continue; + } +#endif if (!strcmp(line,"ro")) { root_mountflags |= MS_RDONLY; continue; @@ -1041,6 +1060,12 @@ #endif #ifdef CONFIG_SYSCTL sysctl_init(); +#endif +#ifdef CONFIG_UTIME + utime_init(); +#endif +#ifdef CONFIG_UTIMER_TEST + utimer_test(); #endif /* * We count on the initial thread going ok diff -u -r -N -b --exclude-from=diff_exclude linux-ref/kernel/Makefile linux-work/kernel/Makefile --- linux-ref/kernel/Makefile Sun Nov 30 14:37:29 1997 +++ linux-work/kernel/Makefile Mon Apr 20 14:08:18 1998 @@ -13,7 +13,7 @@ O_TARGET := kernel.o O_OBJS = sched.o dma.o fork.o exec_domain.o panic.o printk.o sys.o \ module.o exit.o itimer.o info.o time.o softirq.o resource.o \ - sysctl.o acct.o + sysctl.o acct.o timers.o OX_OBJS += signal.o @@ -21,7 +21,16 @@ OX_OBJS += ksyms.o endif +ifeq ($(CONFIG_UTIME),y) +O_OBJS += utime.o +endif + +ifeq ($(CONFIG_UTIMER_TEST),y) +O_OBJS += hist.o +endif + include $(TOPDIR)/Rules.make sched.o: sched.c $(CC) $(CFLAGS) $(PROFILING) -fno-omit-frame-pointer -c $< + diff -u -r -N -b --exclude-from=diff_exclude linux-ref/kernel/hist.c linux-work/kernel/hist.c --- linux-ref/kernel/hist.c Tue Dec 23 14:36:40 1997 +++ linux-work/kernel/hist.c Mon Apr 20 14:08:18 1998 @@ -0,0 +1,127 @@ +/* + * UTIME: On-demand Microsecond Resolution Timers + * ------------------------------------------------------- + * $ProductId: UTIME/linux/kernel:hist.c 1.17 $ + * + * File: kernel/hist.c + * Copyright (C) 1997 by the University of Kansas Center for Research, + * Inc. This software was developed by the Information and + * Telecommunication Technology Center (ITTC) at the University of + * Kansas. Partial funding for this project was provided by Sprint. This + * software may be used and distributed according to the terms of the GNU + * Public License, incorporated herein by reference. Neither ITTC nor + * Sprint accept any liability whatsoever for this product. + * + * This project was developed under the direction of Dr. Douglas Niehaus. + * + * Authors: Balaji S., Raghavan Menon + * Furquan Ansari, Jason Keimig, Apurva Sheth + * + * Please send bug-reports/suggestions/comments to utime@ittc.ukans.edu + * + * Further details about this project can be obtained at + * http://hegel.ittc.ukans.edu/projects/utime/ + * or in the file Documentation/utime.txt + */ +#include +#include +#include +#include + +Histogram *histNew(HistValue low, int span, int buckets, int priority) +{ + Histogram *h; + int i; + + if (buckets > MAX_BUCKETS) + return NULL; + h = (Histogram *) kmalloc(sizeof(Histogram), priority); + if (h) { + h->low = low; + h->high = low + (HistValue) span *buckets; + h->buckets = buckets; + h->span = span; + h->hb = (HistogramBucket *) kmalloc((buckets + 2) * sizeof(HistogramBucket), priority); + if (h->hb) { + for (i = 0; i <= buckets + 1; i++) + h->hb[i].entries = h->hb[i].sum = 0; + return h; + } + kfree_s(h, sizeof(Histogram)); + return NULL; + } + return NULL; +} + +void histDelete(Histogram * hist) +{ + kfree_s(hist->hb, (hist->buckets + 2) * sizeof(HistogramBucket)); + kfree_s(hist, sizeof(Histogram)); +} + +void histAdd(Histogram * h, HistValue value) +{ + int bucket; + + bucket = BUCKET(h, value); + h->hb[bucket].entries++; + h->hb[bucket].sum += value; +} + +/* + * print out a gnuplot script to plot the histogram + */ + +void _histPrintk(Histogram * h) +{ + int i; + + if (!h) { + printk(KERN_INFO "No histogram\n"); + return; + } + printk(KERN_INFO "#set xlabel \"span = %d, low = %ld, high = %ld, buckets = %d\"\n", + h->span, h->low, h->high, h->buckets); + for (i = 0; i <= h->buckets + 1; i++) + if (h->hb[i].entries) + printk(KERN_INFO "%d %d\n", i * h->span, h->hb[i].entries); + printk(KERN_INFO "%d 0\n", i); +} + +void histPrintk(Histogram * h) +{ + int i; + + if (!h) { + printk(KERN_INFO "No histogram\n"); + return; + } + printk(KERN_INFO "less than %lu: %u", h->low, h->hb[0].entries); + if (h->hb[0].entries) + printk(KERN_INFO " (mean %lu)", h->hb[0].sum / h->hb[0].entries); + printk(KERN_INFO "\n"); + for (i = 1; i <= h->buckets; i++) + if (h->hb[i].entries) + printk(KERN_INFO "%lu to %lu: %u\n", + h->low + h->span * (i - 1), + h->low + h->span * i - 1, + h->hb[i].entries); + printk(KERN_INFO "greater than %lu: %u", + h->high, h->hb[h->buckets + 1].entries); + if (h->hb[h->buckets + 1].entries) + printk(KERN_INFO " (mean %lu)", + h->hb[h->buckets + 1].sum / h->hb[h->buckets + 1].entries); + printk(KERN_INFO "\n"); +} + +/* + * time difference in micro seconds, If HistValue is 32 + * bits wide, max time difference is around 4 billion + * micro secs (4000 seconds) + */ + +HistValue timeDiff(struct timeval *start, struct timeval *end) +{ + return (end->tv_sec - start->tv_sec) * (HistValue) (1000000) + + (end->tv_usec - start->tv_usec); +} diff -u -r -N -b --exclude-from=diff_exclude linux-ref/kernel/sched.c linux-work/kernel/sched.c --- linux-ref/kernel/sched.c Tue Dec 9 11:31:19 1997 +++ linux-work/kernel/sched.c Mon Apr 20 14:08:17 1998 @@ -15,7 +15,12 @@ * call functions (type getpid()), which just extract a field from * current-task */ +/* Modified for UTIME: On-demand Microsecond Resolution Timers + * See Documentation/utime.txt for further details + * $ProductId: UTIME/linux/kernel:sched.c 1.17 $ + */ +#include /* for utime-Balaji */ #include #include #include @@ -43,6 +48,12 @@ #include +#ifdef CONFIG_UTIME +/* include files for utime + */ +#include +#endif + /* * kernel variables */ @@ -183,6 +194,9 @@ struct task_struct * p = (struct task_struct *) __data; p->timeout = 0; +#ifdef CONFIG_UTIME + p->timeout_u = 0; +#endif wake_up_process(p); } @@ -236,123 +250,6 @@ return weight; } -/* - * Event timer code - */ -#define TVN_BITS 6 -#define TVR_BITS 8 -#define TVN_SIZE (1 << TVN_BITS) -#define TVR_SIZE (1 << TVR_BITS) -#define TVN_MASK (TVN_SIZE - 1) -#define TVR_MASK (TVR_SIZE - 1) - -struct timer_vec { - int index; - struct timer_list *vec[TVN_SIZE]; -}; - -struct timer_vec_root { - int index; - struct timer_list *vec[TVR_SIZE]; -}; - -static struct timer_vec tv5 = { 0 }; -static struct timer_vec tv4 = { 0 }; -static struct timer_vec tv3 = { 0 }; -static struct timer_vec tv2 = { 0 }; -static struct timer_vec_root tv1 = { 0 }; - -static struct timer_vec * const tvecs[] = { - (struct timer_vec *)&tv1, &tv2, &tv3, &tv4, &tv5 -}; - -#define NOOF_TVECS (sizeof(tvecs) / sizeof(tvecs[0])) - -static unsigned long timer_jiffies = 0; - -static inline void insert_timer(struct timer_list *timer, - struct timer_list **vec, int idx) -{ - if ((timer->next = vec[idx])) - vec[idx]->prev = timer; - vec[idx] = timer; - timer->prev = (struct timer_list *)&vec[idx]; -} - -static inline void internal_add_timer(struct timer_list *timer) -{ - /* - * must be cli-ed when calling this - */ - unsigned long expires = timer->expires; - unsigned long idx = expires - timer_jiffies; - - if (idx < TVR_SIZE) { - int i = expires & TVR_MASK; - insert_timer(timer, tv1.vec, i); - } else if (idx < 1 << (TVR_BITS + TVN_BITS)) { - int i = (expires >> TVR_BITS) & TVN_MASK; - insert_timer(timer, tv2.vec, i); - } else if (idx < 1 << (TVR_BITS + 2 * TVN_BITS)) { - int i = (expires >> (TVR_BITS + TVN_BITS)) & TVN_MASK; - insert_timer(timer, tv3.vec, i); - } else if (idx < 1 << (TVR_BITS + 3 * TVN_BITS)) { - int i = (expires >> (TVR_BITS + 2 * TVN_BITS)) & TVN_MASK; - insert_timer(timer, tv4.vec, i); - } else if (expires < timer_jiffies) { - /* can happen if you add a timer with expires == jiffies, - * or you set a timer to go off in the past - */ - insert_timer(timer, tv1.vec, tv1.index); - } else if (idx < 0xffffffffUL) { - int i = (expires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK; - insert_timer(timer, tv5.vec, i); - } else { - /* Can only get here on architectures with 64-bit jiffies */ - timer->next = timer->prev = timer; - } -} - -static spinlock_t timerlist_lock = SPIN_LOCK_UNLOCKED; - -void add_timer(struct timer_list *timer) -{ - unsigned long flags; - - spin_lock_irqsave(&timerlist_lock, flags); - internal_add_timer(timer); - spin_unlock_irqrestore(&timerlist_lock, flags); -} - -static inline int detach_timer(struct timer_list *timer) -{ - int ret = 0; - struct timer_list *next, *prev; - next = timer->next; - prev = timer->prev; - if (next) { - next->prev = prev; - } - if (prev) { - ret = 1; - prev->next = next; - } - return ret; -} - - -int del_timer(struct timer_list * timer) -{ - int ret; - unsigned long flags; - - spin_lock_irqsave(&timerlist_lock, flags); - ret = detach_timer(timer); - timer->next = timer->prev = 0; - spin_unlock_irqrestore(&timerlist_lock, flags); - return ret; -} - #ifdef __SMP__ #define idle_task (task[cpu_number_map[this_cpu]]) @@ -364,7 +261,6 @@ #define can_schedule(p) (1) #endif - /* * 'schedule()' is the scheduler function. It's a very simple and nice * scheduler: it's not perfect, but certainly works for most things. @@ -378,8 +274,11 @@ asmlinkage void schedule(void) { int lock_depth; - struct task_struct * prev, * next; + struct task_struct *prev, *next; unsigned long timeout; +#ifdef CONFIG_UTIME + unsigned long timeout_u; +#endif int this_cpu; need_resched = 0; @@ -400,14 +299,30 @@ move_last_runqueue(prev); } timeout = 0; +#ifdef CONFIG_UTIME + timeout_u=0; +#endif switch (prev->state) { case TASK_INTERRUPTIBLE: if (signal_pending(prev)) goto makerunnable; timeout = prev->timeout; +#ifdef CONFIG_UTIME + timeout_u = prev->timeout_u; +#endif +#ifndef CONFIG_UTIME if (timeout && (timeout <= jiffies)) { prev->timeout = 0; timeout = 0; +#else + if ((timeout||timeout_u) && + ((timeouttimeout = 0; + prev->timeout_u = 0; + timeout = 0; + timeout_u = 0; +#endif makerunnable: prev->state = TASK_RUNNING; break; @@ -417,7 +332,7 @@ case TASK_RUNNING: } { - struct task_struct * p = init_task.next_run; + struct task_struct *p = init_task.next_run; /* * This is subtle. * Note how we can enable interrupts here, even @@ -471,17 +386,28 @@ struct timer_list timer; kstat.context_swtch++; +#ifndef CONFIG_UTIME if (timeout) { +#else + if (timeout||timeout_u) { +#endif init_timer(&timer); timer.expires = timeout; +#ifdef CONFIG_UTIME + timer.usec = timeout_u; +#endif timer.data = (unsigned long) prev; timer.function = process_timeout; add_timer(&timer); } get_mmu_context(next); - switch_to(prev,next); + switch_to(prev, next); +#ifdef CONFIG_UTIME + if (timeout||timeout_u) +#else if (timeout) +#endif del_timer(&timer); } spin_unlock(&scheduler_lock); @@ -489,9 +415,9 @@ reacquire_kernel_lock(prev, smp_processor_id(), lock_depth); return; -scheduling_in_interrupt: + scheduling_in_interrupt: printk("Scheduling in interrupt\n"); - *(int *)0 = 0; + *(int *) 0 = 0; } @@ -678,69 +604,6 @@ __sleep_on(p,TASK_UNINTERRUPTIBLE); } -static inline void cascade_timers(struct timer_vec *tv) -{ - /* cascade all the timers from tv up one level */ - struct timer_list *timer; - timer = tv->vec[tv->index]; - /* - * We are removing _all_ timers from the list, so we don't have to - * detach them individually, just clear the list afterwards. - */ - while (timer) { - struct timer_list *tmp = timer; - timer = timer->next; - internal_add_timer(tmp); - } - tv->vec[tv->index] = NULL; - tv->index = (tv->index + 1) & TVN_MASK; -} - -static inline void run_timer_list(void) -{ - spin_lock_irq(&timerlist_lock); - while ((long)(jiffies - timer_jiffies) >= 0) { - struct timer_list *timer; - if (!tv1.index) { - int n = 1; - do { - cascade_timers(tvecs[n]); - } while (tvecs[n]->index == 1 && ++n < NOOF_TVECS); - } - while ((timer = tv1.vec[tv1.index])) { - void (*fn)(unsigned long) = timer->function; - unsigned long data = timer->data; - detach_timer(timer); - timer->next = timer->prev = NULL; - spin_unlock_irq(&timerlist_lock); - fn(data); - spin_lock_irq(&timerlist_lock); - } - ++timer_jiffies; - tv1.index = (tv1.index + 1) & TVR_MASK; - } - spin_unlock_irq(&timerlist_lock); -} - - -static inline void run_old_timers(void) -{ - struct timer_struct *tp; - unsigned long mask; - - for (mask = 1, tp = timer_table+0 ; mask ; tp++,mask += mask) { - if (mask > timer_active) - break; - if (!(mask & timer_active)) - continue; - if (tp->expires > jiffies) - continue; - timer_active &= ~mask; - tp->fn(); - sti(); - } -} - spinlock_t tqueue_lock; void tqueue_bh(void) @@ -1086,6 +949,30 @@ restore_flags(flags); } +static inline void run_old_timers(void) +{ + struct timer_struct *tp; + unsigned long mask; + + for (mask = 1, tp = timer_table+0 ; mask ; tp++,mask += mask) { + if (mask > timer_active) + break; + if (!(mask & timer_active)) + continue; + if (tp->expires > jiffies) + continue; + timer_active &= ~mask; + tp->fn(); +#ifdef 0 + /* This seems to be an artefact of obsolete code -- Balaji + */ + sti(); +#endif + } +} + +extern void run_timer_list(void); + static void timer_bh(void) { update_times(); @@ -1093,10 +980,14 @@ run_timer_list(); } -void do_timer(struct pt_regs * regs) +inline void orig_do_timer(struct pt_regs *regs) { - (*(unsigned long *)&jiffies)++; - lost_ticks++; +#if defined(CONFIG_UTIME) && !defined(CONFIG_M386) && !defined(CONFIG_M486) && !defined(CONFIG_APM) + /* this gets defined (void)(1) in case of a non-pentium or above + */ + set_last_timer_cc(); +#endif + mark_bh(TIMER_BH); if (!user_mode(regs)) lost_ticks_system++; @@ -1104,6 +995,32 @@ mark_bh(TQUEUE_BH); } +#ifdef CONFIG_UTIME +void do_timer_wrapper(struct pt_regs *regs) +#else +void do_timer(struct pt_regs *regs) +#endif +{ + /* jiffies is updated in update_jiffies_u unless cycles per second + * is not set -- Balaji S (for utime) + * This wrapper wont get called unless its before cycles per sec + * is initialized + */ + (*(unsigned long *) &jiffies)++; + lost_ticks++; + orig_do_timer(regs); +} + +#ifdef CONFIG_UTIME +/* pointer to the current do_timer function + * This should be pointing to wrapper_do_timer in the case cycles_per_sec is + * not set, + * should point to utime_do_timer_[oneshot/periodic] in the case + * cycles per second is set + */ +void (*do_timer) (struct pt_regs * regs) = do_timer_wrapper; +#endif + #ifndef __alpha__ /* @@ -1425,8 +1342,11 @@ { struct timespec t; unsigned long expire; +#ifdef CONFIG_UTIME + unsigned long expire_u; +#endif - if(copy_from_user(&t, rqtp, sizeof(struct timespec))) + if (copy_from_user(&t, rqtp, sizeof(struct timespec))) return -EFAULT; if (t.tv_nsec >= 1000000000L || t.tv_nsec < 0 || t.tv_sec < 0) @@ -1446,6 +1366,9 @@ return 0; } + /* this has to be modified to handle usec accuracy + */ +#ifndef CONFIG_UTIME expire = timespec_to_jiffies(&t) + (t.tv_sec || t.tv_nsec) + jiffies; current->timeout = expire; @@ -1461,6 +1384,29 @@ } return -EINTR; } + +#else + expire = t.tv_sec*HZ+jiffies; + expire_u = t.tv_nsec/1000+jiffies_u; + expire += expire_u / USEC_PER_JIFFIES; + expire_u %= USEC_PER_JIFFIES; + current->timeout = expire; + current->timeout_u = expire_u; + current->state = TASK_INTERRUPTIBLE; + schedule(); + if ((expire > jiffies)||((expire==jiffies) + && (expire_u > jiffies_u))) { + if (rmtp) { + t.tv_sec = (expire-jiffies)/HZ; + t.tv_nsec = (expire-jiffies)%HZ + + (expire_u-jiffies_u)*1000; + if (copy_to_user(rmtp, &t, + sizeof(struct timespec))) + return -EFAULT; + } + return -EINTR; + } +#endif return 0; } diff -u -r -N -b --exclude-from=diff_exclude linux-ref/kernel/timers.c linux-work/kernel/timers.c --- linux-ref/kernel/timers.c Tue Dec 23 14:36:40 1997 +++ linux-work/kernel/timers.c Mon Apr 20 14:08:18 1998 @@ -0,0 +1,413 @@ +/* Modified for UTIME: On-demand Microsecond Resolution Timers + * See Documentation/utime.txt for further details + * $ProductId: UTIME/linux/kernel:timers.c 1.17 $ + */ +/* All the timer handling stuff from sched.c has been moved to this + * file. This is to remove the clutter in sched.c + */ + +#include /* for utime-Balaji */ +#include +#include +#include + +#include + +#ifdef CONFIG_UTIME +/* include files for utime + */ +#include +#endif + +/* This has been defined to prevent the kernel from freezing under X + * Looks like there is some problem in the way the UTIME code fits + * with the Fast timers code. Comment this out in case you want + * to test UTIME with the fast timers code - Balaji + */ +#define DONT_USE_FAST_TIMERS 1 + +static spinlock_t timerlist_lock = SPIN_LOCK_UNLOCKED; + +#if !defined(DONT_USE_FAST_TIMERS) || !defined (CONFIG_UTIME) +/* + * Event timer code + */ +#define TVN_BITS 6 +#define TVR_BITS 8 +#define TVN_SIZE (1 << TVN_BITS) +#define TVR_SIZE (1 << TVR_BITS) +#define TVN_MASK (TVN_SIZE - 1) +#define TVR_MASK (TVR_SIZE - 1) + +struct timer_vec { + int index; + struct timer_list *vec[TVN_SIZE]; +}; + +struct timer_vec_root { + int index; + struct timer_list *vec[TVR_SIZE]; +}; + +static struct timer_vec tv5 = { 0 }; +static struct timer_vec tv4 = { 0 }; +static struct timer_vec tv3 = { 0 }; +static struct timer_vec tv2 = { 0 }; +static struct timer_vec_root tv1 = { 0 }; + +static struct timer_vec * const tvecs[] = { + (struct timer_vec *)&tv1, &tv2, &tv3, &tv4, &tv5 +}; + +#define NOOF_TVECS (sizeof(tvecs) / sizeof(tvecs[0])) + +static unsigned long timer_jiffies = 0; + +static inline void insert_timer(struct timer_list *timer, + struct timer_list **vec, int idx) +{ + if ((timer->next = vec[idx])) + vec[idx]->prev = timer; + vec[idx] = timer; + timer->prev = (struct timer_list *)&vec[idx]; +} + +#ifdef CONFIG_UTIME +/* If timers are inserted into tv1 (level 1) then they need to be + * sorted according to their usec value + */ +/* When we are adding timers, this gets set to 1 in case we need + * to reload the timer + * -Balaji S. for UTIME + */ +static volatile int do_reload_timer=0; + +static inline void insert_root_timer(struct timer_list *timer, + struct timer_list **vec, int idx) +{ + struct timer_list *this_timer; + + this_timer = vec[idx]; + if (!this_timer) { + vec[idx] = timer; + timer->prev = (struct timer_list *)&vec[idx]; + timer->next = NULL; + if (idx == tv1.index) + if (get_timer_mode() == timer_chip_oneshot) + do_reload_timer=1; + return; + } else if (this_timer->usec >= timer->usec) { + vec[idx] = timer; + this_timer->prev = timer; + timer->next = this_timer; + timer->prev = (struct timer_list *)&vec[idx]; + if (idx == tv1.index) + if (get_timer_mode() == timer_chip_oneshot) + do_reload_timer=1; + return; + } + + while ((this_timer->next)&&(this_timer->usec < timer->usec)) { + this_timer = this_timer->next; + } + + if (this_timer->usec >= timer->usec) { + /* timer gets added before this timer + */ + timer->prev = this_timer->prev; + timer->prev->next = timer; + timer->next = this_timer; + this_timer->prev = timer; + } else { + /* last timer ...so timer gets added after this_timer + */ + this_timer->next = timer; + timer->next = NULL; + timer->prev = this_timer; + } + return; +} + +struct timer_list *get_unexpired_timer() +{ + struct timer_list *next_timer; + + spin_lock(&timerlist_lock); + next_timer = tv1.vec[tv1.index]; + while ((next_timer) && ((next_timer->expires < jiffies) || + ((next_timer->expires == jiffies) && + (next_timer->usec <= jiffies_u)))) { + next_timer = next_timer->next; + } + spin_unlock(&timerlist_lock); + return next_timer; +} +#endif /* CONFIG_UTIME */ + +static inline void internal_add_timer(struct timer_list *timer) +{ + /* + * must be cli-ed when calling this + */ + unsigned long expires = timer->expires; + unsigned long idx = expires - timer_jiffies; + + if (idx < TVR_SIZE) { + int i = expires & TVR_MASK; +#ifdef CONFIG_UTIME + insert_root_timer(timer, tv1.vec, i); +#else + insert_timer(timer, tv1.vec, i); +#endif + } else if (idx < 1 << (TVR_BITS + TVN_BITS)) { + int i = (expires >> TVR_BITS) & TVN_MASK; + insert_timer(timer, tv2.vec, i); + } else if (idx < 1 << (TVR_BITS + 2 * TVN_BITS)) { + int i = (expires >> (TVR_BITS + TVN_BITS)) & TVN_MASK; + insert_timer(timer, tv3.vec, i); + } else if (idx < 1 << (TVR_BITS + 3 * TVN_BITS)) { + int i = (expires >> (TVR_BITS + 2 * TVN_BITS)) & TVN_MASK; + insert_timer(timer, tv4.vec, i); + } else if (expires < timer_jiffies) { + /* can happen if you add a timer with expires == jiffies, + * or you set a timer to go off in the past + */ +#ifdef CONFIG_UTIME + insert_root_timer(timer, tv1.vec, tv1.index); +#else + insert_timer(timer, tv1.vec, tv1.index); +#endif + } else if (idx < 0xffffffffUL) { + int i = (expires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK; + insert_timer(timer, tv5.vec, i); + } else { + /* Can only get here on architectures with 64-bit jiffies */ + timer->next = timer->prev = timer; + } +} + +void add_timer(struct timer_list *timer) +{ + unsigned long flags; + +#ifdef CONFIG_UTIME + if (timer->usec>=USEC_PER_JIFFIES) { + timer->expires += timer->usec/USEC_PER_JIFFIES; + timer->usec %= USEC_PER_JIFFIES; + } +#endif + spin_lock_irqsave(&timerlist_lock, flags); + internal_add_timer(timer); + /* Check whether we need to reload the timer chip + * if so reload it + */ +#ifdef CONFIG_UTIME + if (do_reload_timer) { + reload_timer(timer->expires, timer->usec); + do_reload_timer=0; + } +#endif + spin_unlock_irqrestore(&timerlist_lock, flags); +} + +static inline int detach_timer(struct timer_list *timer) +{ + int ret = 0; + struct timer_list *next, *prev; + next = timer->next; + prev = timer->prev; + if (next) { + next->prev = prev; + } + if (prev) { + ret = 1; + prev->next = next; + } + return ret; +} + +int del_timer(struct timer_list * timer) +{ + int ret; + unsigned long flags; + + spin_lock_irqsave(&timerlist_lock, flags); + ret = detach_timer(timer); + timer->next = timer->prev = 0; + spin_unlock_irqrestore(&timerlist_lock, flags); + return ret; +} + +#ifdef CONFIG_UTIME +/* this function returns non NULL as long as there are timers that + * need to be serviced + */ +static inline struct timer_list *get_timer(struct timer_list *head) +{ + if ((head) && ((head->expires < jiffies) || + ((head->expires == jiffies) && (head->usec <= jiffies_u)))) + return head; + else + return NULL; +} +#else +#define get_timer(x) (x) +#endif + +static inline void cascade_timers(struct timer_vec *tv) +{ + /* cascade all the timers from tv up one level */ + struct timer_list *timer; + timer = tv->vec[tv->index]; + /* + * We are removing _all_ timers from the list, so we don't have to + * detach them individually, just clear the list afterwards. + */ + while (timer) { + struct timer_list *tmp = timer; + timer = timer->next; + internal_add_timer(tmp); + } + tv->vec[tv->index] = NULL; + tv->index = (tv->index + 1) & TVN_MASK; +} + +void run_timer_list(void) +{ + spin_lock_irq(&timerlist_lock); + while ((long)(jiffies - timer_jiffies) >= 0) { + struct timer_list *timer; + +#if defined(CONFIG_UTIME) + if ((!tv1.index)&&(!tv1.vec[0])) { +#else + if (!tv1.index) { +#endif + int n = 1; + do { + cascade_timers(tvecs[n]); + } while (tvecs[n]->index == 1 && ++n < NOOF_TVECS); + } + while ((timer = get_timer(tv1.vec[tv1.index]))) { + void (*fn)(unsigned long) = timer->function; + unsigned long data = timer->data; + detach_timer(timer); + timer->next = timer->prev = NULL; + spin_unlock_irq(&timerlist_lock); + fn(data); + spin_lock_irq(&timerlist_lock); + } +#ifdef CONFIG_UTIME + /* increment the index only if there are no timers + * in the vector + */ + if (tv1.vec[tv1.index]) + break; +#endif + ++timer_jiffies; + tv1.index = (tv1.index + 1) & TVR_MASK; + } + spin_unlock_irq(&timerlist_lock); +} + +#else /* !defined(DONT_USE_FAST_TIMERS) || !defined(CONFIG_UTIME) */ +/* + * The head for the timer-list has a "expires" field of MAX_UINT, + * and the sorting routine counts on this.. + */ +static struct timer_list timer_head = { &timer_head, &timer_head, ~0, 0, NULL }; +#define SLOW_BUT_DEBUGGING_TIMERS 0 + +void add_timer(struct timer_list * timer) +{ + unsigned long flags; + struct timer_list *p; + +#if SLOW_BUT_DEBUGGING_TIMERS + if (timer->next || timer->prev) { + printk("add_timer() called with non-zero list from %p\n", + __builtin_return_address(0)); + return; + } +#endif + p = &timer_head; + spin_lock_irqsave(&timerlist_lock, flags); + do { + p = p->next; +#ifdef CONFIG_UTIME + } while ((timer->expires > p->expires) || + ((timer->expires==p->expires) && (timer->usec>p->usec))); +#else + } while (timer->expires > p->expires); +#endif + timer->next = p; + timer->prev = p->prev; + p->prev = timer; + timer->prev->next = timer; + spin_unlock_irqrestore(&timerlist_lock, flags); +#ifdef CONFIG_UTIME + if (timer->prev==&timer_head) { + reload_timer(timer->expires, timer->usec); + } +#endif +} + +int del_timer(struct timer_list * timer) +{ + int ret = 0; + if (timer->next) { + unsigned long flags; + struct timer_list * next; + spin_lock_irqsave(&timerlist_lock, flags); + if ((next = timer->next) != NULL) { + (next->prev = timer->prev)->next = next; + timer->next = timer->prev = NULL; + ret = 1; + } + spin_unlock_irqrestore(&timerlist_lock, flags); + } + return ret; +} + +#ifdef CONFIG_UTIME +struct timer_list *get_unexpired_timer(void) +{ + struct timer_list *next_timer; + + spin_lock(&timerlist_lock); + next_timer = timer_head.next; + while ((next_timer) && ((next_timer->expires < jiffies) || + ((next_timer->expires == jiffies) && + (next_timer->usec <= jiffies_u)))) { + next_timer = next_timer->next; + } + spin_unlock(&timerlist_lock); + return next_timer; +} +#endif + +void run_timer_list(void) +{ + struct timer_list * timer; + + spin_lock_irq(&timerlist_lock); +#ifdef CONFIG_UTIME + while ((timer = timer_head.next) != &timer_head && + ((timer->expires < jiffies) || ((timer->expires == jiffies) && + (timer->usec <= jiffies_u)))) { +#else + while ((timer = timer_head.next) != &timer_head && + (timer->expires < jiffies)) { +#endif + void (*fn)(unsigned long) = timer->function; + unsigned long data = timer->data; + timer->next->prev = timer->prev; + timer->prev->next = timer->next; + timer->next = timer->prev = NULL; + spin_unlock_irq(&timerlist_lock); + fn(data); + spin_lock_irq(&timerlist_lock); + } + spin_unlock_irq(&timerlist_lock); +} +#endif diff -u -r -N -b --exclude-from=diff_exclude linux-ref/kernel/utime.c linux-work/kernel/utime.c --- linux-ref/kernel/utime.c Tue Dec 23 14:36:40 1997 +++ linux-work/kernel/utime.c Mon Apr 20 14:08:18 1998 @@ -0,0 +1,519 @@ +/* + * UTIME: On-demand Microsecond Resolution Timers + * ------------------------------------------------------- + * $ProductId: UTIME/linux/kernel:utime.c 1.17 $ + * + * File: kernel/utime.c + * Copyright (C) 1997 by the University of Kansas Center for Research, + * Inc. This software was developed by the Information and + * Telecommunication Technology Center (ITTC) at the University of + * Kansas. Partial funding for this project was provided by Sprint. This + * software may be used and distributed according to the terms of the GNU + * Public License, incorporated herein by reference. Neither ITTC nor + * Sprint accept any liability whatsoever for this product. + * + * This project was developed under the direction of Dr. Douglas Niehaus. + * + * Authors: Balaji S., Raghavan Menon + * Furquan Ansari, Jason Keimig, Apurva Sheth + * + * Please send bug-reports/suggestions/comments to utime@ittc.ukans.edu + * + * Further details about this project can be obtained at + * http://hegel.ittc.ukans.edu/projects/utime/ + * or in the file Documentation/utime.txt + */ + +/* NOTE: Calls to time2next_event and time_elapsed should not be + * called interleaved with update_jiffies_u. ie. Dont call update_jiffies_u + * in between calls to time_elapsed and time2next_event. + */ + +#include +#include + +#include + +#define _INCLUDED_FROM_UTIME_C_ +#include +#undef _INCLUDED_FROM_UTIME_C_ + +#include + +#include +#include + +#include + +#include + +#include + +int volatile jiffies_u = 0; /* usecs inside 10 ms */ +/* This is calibrated when the kernel boots up + */ +unsigned long cycles_per_sec = 0; +unsigned long cycles_per_usec; +unsigned long cycles_per_jiffies; + +/* value to be reloaded into timer if we are in periodic mode */ +unsigned int volatile latch_reload = USEC_PER_SEC / HZ; + +/* used in update_jiffies_u to keep track of list usecs + */ +unsigned long remainder_of_usec = 0; + +/* time to the next interrupt from the last time jiffies_u was updated + */ +unsigned long volatile next_intr = 0; + +/* last time jiffies_u was updated (CPU counter value) + */ +unsigned long long volatile last_update = 0; + +spinlock_t utime_lock = SPIN_LOCK_UNLOCKED; + +enum timer_chip_mode kernel_timer_mode = timer_chip_periodic; +int jiffies_intr = 0; + +unsigned long long volatile base_cpuctr = 0ULL; +unsigned long volatile base_jiffies = 0UL; + +/* In the case of the 286-486 we need an external variable + * storing the monotonic increasing time + */ +#if defined(CONFIG_M386)||defined(CONFIG_M486)||defined(CONFIG_APM) +volatile unsigned long long new_global_time = 0; +u16 base_c0; +spinlock_t timer_chip_lock = SPIN_LOCK_UNLOCKED; +#endif + +inline enum timer_chip_mode get_timer_mode(void) +{ + return kernel_timer_mode; +} + +/* + * call with interrupts disabled + */ +extern inline unsigned long time2next_event(void) +{ + unsigned long timer_jiffies; + unsigned long timer_jiffies_u; + struct timer_list *next_timer; + + /* skip all the expired timers... + */ + /* if get_unexpired_timer returns null then the next + * timer boundary is a jiffies interrupt + */ + next_timer = get_unexpired_timer(); + if (!next_timer) { + timer_jiffies = jiffies + 1; + timer_jiffies_u = 0; + } else { + timer_jiffies = next_timer->expires; + timer_jiffies_u = next_timer->usec; + } + + if (timer_jiffies == jiffies) + return (timer_jiffies_u - jiffies_u); + else + /* aim for the HZ boundary + */ + return (USEC_PER_JIFFIES - jiffies_u); +} + +extern inline unsigned long time_elapsed(void) +{ + unsigned long jiffies_u_elapsed; + unsigned long long cpu_ctr_value; + + cpu_ctr_value = get_cpuctr(); + + jiffies_u_elapsed = arch_cycles_to_usec(cpu_ctr_value - last_update); + +#ifdef __UTIME_DEBUG__ + /* This hasnt happenned in a long long time....so i have + * put it in the ifdef + * --Balaji S. + * Correction: This happens in the case of 386/486s sometimes + */ + if (jiffies_u_elapsed > USEC_PER_JIFFIES) { + printk(KERN_CRIT "elapsed = %lu, cpu_ctr = %lu.%lu, last_update = %lu.%lu\n", + jiffies_u_elapsed, + (unsigned long) (cpu_ctr_value >> 32), + (unsigned long) cpu_ctr_value, + (unsigned long) (last_update >> 32), + (unsigned long) last_update); + dprintk_address(); + } +#endif + return jiffies_u_elapsed; +} + +static void internal_reload_timer(void) +{ + unsigned long delta_jiffies_u; /* jiffies_u till next earliest event */ + unsigned long jiffies_u_elapsed; + int new_latch_value; + +#ifdef __UTIME_DEBUG__ + if (jiffies_u >= USEC_PER_JIFFIES) { + printk(KERN_CRIT "xxxxxxxxxxx1: %d\n", jiffies_u); + dprintk_address(); + } +#endif + + if (kernel_timer_mode == timer_chip_oneshot) { + delta_jiffies_u = time2next_event(); + + /* + * Get the actual current time + */ + jiffies_u_elapsed = time_elapsed(); + + /* + * here's how much time we have left (this can be negative) + */ + new_latch_value = delta_jiffies_u - jiffies_u_elapsed; + if (new_latch_value < TIMER_DELTA) + new_latch_value = TIMER_DELTA; + else if (new_latch_value > USEC_PER_JIFFIES + TIMER_DELTA) { + printk(KERN_CRIT "utime: %lu,%lu\n", delta_jiffies_u, jiffies_u_elapsed); + dprintk_address(); + } + /* + * now setup everything + */ + reload_timer_chip(new_latch_value); + return; + } +} + +void load_timer(void) +{ + unsigned long flags; + spin_lock_irqsave(&utime_lock, flags); + internal_reload_timer(); + spin_unlock_irqrestore(&utime_lock, flags); +} + +void reload_timer(unsigned long expires, unsigned long usec) +{ +#if defined(CONFIG_M386)||defined(CONFIG_M486)||defined(CONFIG_APM) + unsigned long delta_jiffies_u; /* jiffies_u till next earliest event */ + + delta_jiffies_u = time2next_event(); + if ((next_intr >= delta_jiffies_u) && + ((next_intr - delta_jiffies_u) <= TIMER_DELTA)) { + /* + * next interrupt as currently programmed will jump the + * clock to within TIMER_DELTA jiffies_u's after the + * next earliest event is due. This is fine so do + * nothing. + */ + return; + } +#endif + + load_timer(); +} + +extern volatile unsigned long lost_ticks; + +inline void update_jiffies_u(void) +{ + unsigned long flags; + unsigned long long this_update; + unsigned long cycles_update; + + spin_lock_irqsave(&utime_lock, flags); + this_update = get_cpuctr(); + +#if defined(CONFIG_M386)||defined(CONFIG_M486)||defined(CONFIG_APM) + /* This line is very important this maintains the time from + * the last update to the time when the next interrupt would + * be generated + */ + next_intr = read_timer_chip(); +#endif + + cycles_update = (unsigned long) (this_update - last_update); + arch_update_jiffies(cycles_update); + last_update = this_update; + + spin_unlock_irqrestore(&utime_lock, flags); +} + +void utime_do_timer_oneshot(struct pt_regs *regs) +{ + update_jiffies_u(); + load_timer(); + mark_bh(TIMER_BH); + if (jiffies_intr) + orig_do_timer(regs); +} + +void utime_do_timer_periodic(struct pt_regs *regs) +{ + /*calculate the jiffies_u by using the p5 counter + */ + update_jiffies_u(); + mark_bh(TIMER_BH); + /* Call orig_do_timer in the case that its a jiffies + * interrupt + */ + if (jiffies_intr) + orig_do_timer(regs); +} + +/* + * period should be set to 0 for oneshot mode; i.e when mode is 1. + */ +int change_timer_mode(enum timer_chip_mode mode, int period) +{ + unsigned long flags; + + spin_lock_irqsave(&utime_lock, flags); + + if (mode == timer_chip_periodic) { + kernel_timer_mode = timer_chip_periodic; + latch_reload = period; + put_timer_in_periodic_mode(); + do_timer = utime_do_timer_periodic; + reload_timer_chip(latch_reload); + } else { + if (!cycles_per_sec) { + spin_unlock_irqrestore(&utime_lock, flags); + printk(KERN_DEBUG "[%s:%s:%d] timer_mode:%d" + " (cycles_per_sec not set\n", + __FILE__, __FUNCTION__, __LINE__, + kernel_timer_mode); + return 1; + } + kernel_timer_mode = timer_chip_oneshot; + latch_reload = USEC_PER_JIFFIES; + put_timer_in_oneshot_mode(); + do_timer = utime_do_timer_oneshot; + internal_reload_timer(); + } + spin_unlock_irqrestore(&utime_lock, flags); + + printk(KERN_DEBUG "[%s:%s:%d] timer_mode:%d\n", __FILE__, __FUNCTION__, \ + __LINE__, kernel_timer_mode); + return 0; +} + +void utime_init(void) +{ + /* Code for runtime calibration of utime (this will be called in case + * we did not enter boot_parameters for it) + */ + printk("Calibrating cpu "); + if (!cycles_per_sec) { + unsigned long long first_time, next_time; + unsigned long first_jiffies; + int i=0; + + /* wait for "start of" clock tick */ + first_jiffies = jiffies; + while (first_jiffies == jiffies) + /* nothing */; + + first_time = get_cpuctr(); + while (i++<5) { + first_jiffies = jiffies; + while (jiffies - first_jiffies < HZ); + printk("."); + } + next_time = get_cpuctr(); + cycles_per_sec = ((unsigned long) (next_time - first_time)) / 5; + cycles_per_sec = arch_utime_init(cycles_per_sec); + } + printk(" %lu cycles per second.\n", cycles_per_sec); + /* Now set the cycles_per_* + */ + cycles_per_usec = cycles_per_sec / USEC_PER_SEC; + cycles_per_jiffies = cycles_per_sec / HZ; + + /* This sets the last update thats used in update_jiffies_u + * If this is not set then get_cpuctr will take a big jump + */ + last_update = get_cpuctr(); +#ifdef 0 + do_timer = utime_do_timer_periodic; +#endif + base_cpuctr = last_update; + base_jiffies = jiffies; + change_timer_mode(timer_chip_oneshot,0); +} + +#ifdef CONFIG_UTIMER_TEST +/* + * utimer test routines + */ +#define _UTIMER_TEST_TIMERS_ 10 +#define MAX_TEST_TIMERS 10000 +/* define this to add random timers. + * NOTE: get_random_bytes seems to block interrupts for a long time + * hence a lot of timeouts are delayed + */ +#undef RANDOM_TIMERS + +static volatile struct timer_list test_timers[_UTIMER_TEST_TIMERS_]; +static volatile int timers_queued = 0; +static volatile int timers_added = 0, timers_finished = 0; +static spinlock_t utime_test_lock = SPIN_LOCK_UNLOCKED; + +static void target_fn(unsigned long data); + +#ifdef RANDOM_TIMERS +extern void get_random_bytes(void *buf, int nbytes); +static unsigned long random(void) +{ + char num = 0; + unsigned long result = 0; + + get_random_bytes(&num, 1); + result |= num; + get_random_bytes(&num, 1); + result |= (num << 8); + get_random_bytes(&num, 1); + result |= (num << 16); + get_random_bytes(&num, 1); + result |= (num << 24); + return result; +} +#else +static unsigned long random(void) +{ + static int x = 0; + return x++; +} +#endif + +static void add_random_timer(void) +{ + long flags; + int i; + volatile struct timer_list *timer = NULL; + + for (i = 0; i < _UTIMER_TEST_TIMERS_; i++) { + spin_lock_irqsave(&utime_test_lock, flags); + if (test_timers[i].function == NULL) { + timer = &test_timers[i]; + timers_queued++; + timers_added++; + timer->function = target_fn; + spin_unlock_irqrestore(&utime_test_lock, flags); + break; + } + spin_unlock_irqrestore(&utime_test_lock, flags); + } + if (timer) { + timer->data = (unsigned long) timer; + timer->expires = random() % 50; + timer->usec = (random() % 1000); + timer->prev = timer->next = NULL; + spin_lock_irqsave(&utime_test_lock, flags); + timer->expires += 10 + jiffies; + spin_unlock_irqrestore(&utime_test_lock, flags); + add_timer((struct timer_list *) timer); + } +} + +static Histogram *utimer_hist = NULL; +static int hist_entries = 0; + +static void stop_utimer_test(void) +{ + static int done = 0; + if (!done) { + if (utimer_hist) { + histDelete(utimer_hist); + utimer_hist = NULL; + } + done = 1; + } +} + +static void target_fn(unsigned long data) +{ + unsigned long time_now, timeout; + struct timer_list *timer = (struct timer_list *) data; + long flags; + int timers_needed; + + spin_lock_irqsave(&utime_test_lock, flags); + timers_queued--; + timers_finished++; + timer->function = NULL; + /* We want to find the current time and not the time + * when utime_do_timer_[oneshot/periodic] was called + */ + update_jiffies_u(); + time_now = jiffies * USEC_PER_JIFFIES + jiffies_u; + timeout = timer->expires * USEC_PER_JIFFIES + timer->usec; + spin_unlock_irqrestore(&utime_test_lock, flags); + if (timeout > time_now) { + printk(KERN_DEBUG "[%s:%s:%d]: Early: time_now %lu data %lu diff %ld\n", \ + __FILE__, __FUNCTION__, __LINE__, \ + time_now, timeout, timeout - time_now); + } else if (utimer_hist) { + if ((timers_finished < 1000) && (time_now - timeout > 300)) { + printk(KERN_DEBUG "time_now = %lu, timeout = %lu (%ld).\n", + time_now, timeout, time_now - timeout); + } + histAdd(utimer_hist, (HistValue) (time_now - timeout)); + hist_entries++; + if (hist_entries >= 1000) { + hist_entries = 0; + _histPrintk(utimer_hist); + } + } + if (timers_added < MAX_TEST_TIMERS) { + timers_needed = (random() % _UTIMER_TEST_TIMERS_) + 1; + while (timers_queued < timers_needed) + add_random_timer(); + } + if (timers_finished >= MAX_TEST_TIMERS) { + stop_utimer_test(); + } +} + +static void utimer_test1(unsigned long x) +{ + int i; + + utimer_hist = histNew(0, CONFIG_UTIMER_BSIZE, 400, GFP_ATOMIC); + if (!utimer_hist) { + printk(KERN_DEBUG "Could not allocate memory for histogram\n"); + return; + } + printk(KERN_DEBUG "utimer_test1: started\n"); + for (i = 0; i < _UTIMER_TEST_TIMERS_; i++) + test_timers[i].function = NULL; + + for (i = 0; i < _UTIMER_TEST_TIMERS_; i++) + add_random_timer(); +} + +void utimer_test(void) +{ + volatile struct timer_list *timer; + + timer = &test_timers[0]; + timer->function = utimer_test1; + /* we need to start utimer_test1 later, + * we need some time so that the calibration of the cpu_counter + * can take place. + * Do not reduce this time for this timer + */ + timer->expires = 6000; + timer->usec = 0; + timer->data = 0; + add_timer((struct timer_list *) timer); +} + +#endif