diff -u -r -N -b --exclude-from=diff-exclude linux-ref/Documentation/Configure.help linux-work/Documentation/Configure.help --- linux-ref/Documentation/Configure.help Thu Nov 7 16:13:58 2002 +++ linux-work/Documentation/Configure.help Thu Mar 13 15:33:30 2003 @@ -75,6 +75,11 @@ # 1995-2000 by Axel Boldt and many others and are governed by the GNU # General Public License. +# +# Modified for KURT: KU Real Time Linux +# See Documentation/kurt.txt for further details +# + Prompt for development and/or incomplete code/drivers CONFIG_EXPERIMENTAL Some of the various things that Linux supports (such as network @@ -20633,6 +20638,160 @@ HP300 machines. If you are using such a system you almost certainly want this. +KU High-Resolution Timer Support +CONFIG_UTIME + This option enables microsecond resolution timer support for the + kernel. This is presently supported only on architectures that have + timestamp counters (CONFIG_X86_TSC). + +KURT Interrupt Modifications +CONFIG_KURT_IRQ_MOD + These modifications move interrupt execution decisions out of hardware + and into a software policy. This can allow for much greater accuracy + and less variance. This feature replaces cli() and sti(), which + normally operate on a hardware flag, such that they operate on a + software semaphore. + +KURT User-Mode Device Driver +CONFIG_USER_MODE_DEVICE_DRIVER + This option allows device drivers to be implemented in user-mode. + This can possibly violate the concurrency control in the Linux + kernel. User-mode device drivers should be carefully analyzed in + order to ensure that they do not conflict with the Linux kernel. + +KU: Frizz Modifications +FRIZZ + This option is for internal KURT use. + +KU: Dinkel Modifications +CONFIG_WMD + This option is for internal KURT use. + +KU: Jacob Modifications +JACOB + This option is for internal KURT use. + +High-Resolution Timer Calibration Module +CONFIG_UTIME_CALIBRATION + Enables modification of the KU high-resolution timing layer + run-time parameters. + +Data Stream Kernel Interface +CONFIG_DSTREAM + This enables the Data Stream Kernel Interface, a system monitoring + utility developed at the Information and Telecommunications + Technology Center (ITTC) at the University of Kansas. + + See http://hegel.ittc.ukans.edu/projects/datastream for + documentation on how to use the DSKI. + +DSKI Reuse SKBuffs +CONFIG_DSTREAM_REUSE_SKBUFFS + If the skbuffs which hold datastream events become full, this option + allows the oldest skbuffs to be reused in order to store new + events. This can produce gaps in the event stream that is + generated. If this option is not enabled, then new events will be + lost until an skbuff becomes available. + +DSKI Static Memory Pool +CONFIG_DSTREAM_MEM_POOL + When this option is enabled, the datastream driver will allocate a + portion of memory at initialization for storing datastream events. + This reduces kmalloc overhead during datastream event logging. + +UTIME Logging Points +CONFIG_DSTREAM_UTIME + Enables the DSKI UTIME Test Suite. + +UTIME Calibration Logging Points +CONFIG_DSTREAM_UTIME_CALIBRATION + Enables the DSKI UTIME Calibration logging points. + +UTIME Correctness Logging Points +CONFIG_DSTREAM_UTIME_CORRECT + Enables the DSKI UTIME Correctness logging points. + +UTIME Debugging Logging Points +CONFIG_DSTREAM_UTIME_DEBUG + Enables the DSKI UTIME Debugging logging points. + +UTIME Profiling Logging Points +CONFIG_DSTREAM_UTIME_PROFILE + Enables the DSKI UTIME Profiling logging points. + +KURT Test Suite +CONFIG_DSTREAM_KURT_TEST + Enables the DSKI KURT Test Suite. + +KURT Calibration Logging Points +CONFIG_DSTREAM_KURT_CALIBRATION + Enables the DSKI KURT Calibration logging points. + +KURT Correctness Logging Points +CONFIG_DSTREAM_KURT_CORRECT + Enables the DSKI KURT Correctness logging points. + +KURT Debugging Logging Points +CONFIG_DSTREAM_KURT_DEBUG + Enables the DSKI KURT Debugging logging points. + +KURT Profiling Logging Points +CONFIG_DSTREAM_KURT_PROFILE + Enables the DSKI KURT Profiling logging points. + + +Real time kernel support +CONFIG_RT + This enables the Kansas University Real-time modifications, which + provide real-time scheduling capabilities at the resolution of tens + of microseconds via the UTIME kernel patch (see the "Kernel Hacking" + section for UTIME. KURT can either be compiled into the kernel or + as a module. For more information, see Documentation/kurt.txt. + + Rather than using a straight kernel patch, with system calls etc., + this enables all KURT changes to the kernel that will allow the new + KURT module to function correctly. All system calls have been + replaced by device driver IOCTLs. Any added KURT functions have + been moved into the module code, rather than placed inside the + kernel sources. For those kernel routines where KURT makes minor + changes, we have decided to leave them within a kernel patch, as it + would be too troublesome to mess with reassigning function pointers. + +Cyclic/Dynamic scheduling +CONFIG_RT_DYNAMIC + If you wish to use KURT's dynamic scheduling capability answer 'y' + here. Standard KURT allows only one explicit real-time schedule at + a time (via the rt_schedule_events() routine). Another schedule + cannot be submitted until the previous one is complete. Dynamic + scheduling removes this restriction, allowing the user to resubmit a + new schedule at any time, which will immediately replace the + previous schedule. The dynamic schedule is executed cyclically. + +Execution Interval Enforcement +CONFIG_RT_ENFORCEMENT + This mode will force KURT to explicitly enforce all real-time + execution intervals. This is accomplished through the use of + watchdog timers, whose expiration inform the system either that a + real-time process overstepped its bounds or that a real-time process + should be temporarily suspended. These watchdogs can be generated + dynamically by the kernel (as a result of setting the "exec_time" + field of a real-time process's rtparams structure), or can be + submitted by the user inside an explicit real-time schedule. + +Parallel Bit Flipping Module +CONFIG_RT_PARBIT + This module is an example of a custom KURT timer-handling module. + Any real-time timers with the module ID of '1' will be delivered to + this module upon expiration, which will result in a bit-flip at the + parallel port. + +Resource management +CONFIG_RT_RESOURCE + If you wish to use KURT's resource management facilities answer 'y' + here. This feature provides support for requesting 'shares' of + system resources. Currently the CPU is the only supported resource. + Support for network shares will be forthcoming. + # Choice: ppctype Processor Type CONFIG_6xx diff -u -r -N -b --exclude-from=diff-exclude linux-ref/Documentation/smp.tex linux-work/Documentation/smp.tex --- linux-ref/Documentation/smp.tex Wed Jun 24 16:30:07 1998 +++ linux-work/Documentation/smp.tex Wed Nov 20 14:58:05 2002 @@ -1,8 +1,4 @@ -From: michael@Physik.Uni-Dortmund.DE (Michael Dirkmann) -thanks for your information. Attached is the tex-code of your -SMP-documentation : --=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= \documentclass[]{article} \parindent0.0cm \parskip0.2cm diff -u -r -N -b --exclude-from=diff-exclude linux-ref/Makefile linux-work/Makefile --- linux-ref/Makefile Mon Feb 25 13:37:52 2002 +++ linux-work/Makefile Tue Apr 15 13:28:49 2003 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 4 SUBLEVEL = 18 -EXTRAVERSION = +EXTRAVERSION = -KURT KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) @@ -185,6 +185,10 @@ DRIVERS-$(CONFIG_MD) += drivers/md/mddev.o DRIVERS-$(CONFIG_BLUEZ) += drivers/bluetooth/bluetooth.o DRIVERS-$(CONFIG_HOTPLUG_PCI) += drivers/hotplug/vmlinux-obj.o +DRIVERS-$(CONFIG_DSTREAM) += drivers/dstream/dstream.o +DRIVERS-$(CONFIG_RT) += drivers/kurt/kurt.o +DRIVERS-$(CONFIG_UTIME_CALIBRATION) += drivers/utime_calib/utime_calib.o +DRIVERS-$(CONFIG_GROUP_SCHEDULING) += drivers/group_sched/group_sched.o DRIVERS := $(DRIVERS-y) @@ -437,6 +441,13 @@ cd .. && tar cf - linux/ | gzip -9 > backup.gz sync +dskifamilies: + @chmod a+x $(TOPDIR)/scripts/dski/dski-parse.py + @cd $(TOPDIR)/scripts/dski && ./dski-parse.py + @cp -f $(TOPDIR)/scripts/dski/*.h $(TOPDIR)/include/linux/ + @cp -f $(TOPDIR)/scripts/dski/Config.in $(TOPDIR)/drivers/dstream + @rm -f $(TOPDIR)/scripts/dski/*.h $(TOPDIR)/scripts/dski/Config.in + sgmldocs: chmod 755 $(TOPDIR)/scripts/docgen chmod 755 $(TOPDIR)/scripts/gen-all-syms diff -u -r -N -b --exclude-from=diff-exclude linux-ref/arch/arm/config.in linux-work/arch/arm/config.in --- linux-ref/arch/arm/config.in Thu Nov 7 16:13:45 2002 +++ linux-work/arch/arm/config.in Wed Nov 20 14:58:05 2002 @@ -605,4 +605,37 @@ dep_bool 'Kernel low-level debugging functions' CONFIG_DEBUG_LL $CONFIG_EXPERIMENTAL dep_bool ' Kernel low-level debugging messages via footbridge serial port' CONFIG_DEBUG_DC21285_PORT $CONFIG_DEBUG_LL $CONFIG_FOOTBRIDGE dep_bool ' kernel low-level debugging messages via UART2' CONFIG_DEBUG_CLPS711X_UART2 $CONFIG_DEBUG_LL $CONFIG_ARCH_CLPS711X + +if [ "$CONFIG_ARCH_SA1100" = "y" ]; then + bool 'Microsecond resolution timer support' CONFIG_UTIME + if [ "$CONFIG_UTIME" = "y" ]; then + tristate 'UTIME Calibration Module' CONFIG_UTIME_CALIBRATION + bool 'UTIME Quotient High Accuracy Calc.' CONFIG_UTIME_HIGHACC + fi +fi +bool 'Data Stream Kernel Interface' CONFIG_DSTREAM +if [ "$CONFIG_DSTREAM" = "y" ]; then + bool 'UTIME Test Suite' CONFIG_DSTREAM_UTIME n + if [ "$CONFIG_DSTREAM_UTIME" = "y" ]; then + bool 'UTIME Calibration Tests' CONFIG_DSTREAM_UTIME_CALIBRATION + bool 'UTIME Correctness Tests' CONFIG_DSTREAM_UTIME_CORRECT + bool 'UTIME Debugging Tests' CONFIG_DSTREAM_UTIME_DEBUG + bool 'UTIME Profiling Tests' CONFIG_DSTREAM_UTIME_PROFILE + fi + + bool 'KURT Test Suite' CONFIG_DSTREAM_KURT_TEST n + if [ "$CONFIG_DSTREAM_KURT_TEST" = "y" ]; then + bool 'KURT Calibration Tests' CONFIG_DSTREAM_KURT_CALIBRATION + bool 'KURT Correctness Tests' CONFIG_DSTREAM_KURT_CORRECT + bool 'KURT Debugging Tests' CONFIG_DSTREAM_KURT_DEBUG + bool 'KURT Profiling Tests' CONFIG_DSTREAM_KURT_PROFILE + fi + + define_bool CONFIG_DSTREAM_USAGE_CALC y + define_bool CONFIG_DSTREAM_USAGE_CALC_THREAD y + +fi + endmenu + +source drivers/kurt/config.in \ No newline at end of file diff -u -r -N -b --exclude-from=diff-exclude linux-ref/arch/arm/kernel/Makefile linux-work/arch/arm/kernel/Makefile --- linux-ref/arch/arm/kernel/Makefile Thu Oct 25 15:53:45 2001 +++ linux-work/arch/arm/kernel/Makefile Wed Nov 20 14:58:05 2002 @@ -55,6 +55,7 @@ obj-$(CONFIG_ARTHUR) += arthur.o obj-$(CONFIG_ISA_DMA) += dma-isa.o obj-$(CONFIG_PCI) += bios32.o $(pci-$(MACHINE)) $(pci-y) +obj-$(CONFIG_UTIME) += utime.o ifneq ($(MACHINE),ebsa110) obj-y += io.o diff -u -r -N -b --exclude-from=diff-exclude linux-ref/arch/arm/kernel/setup.c linux-work/arch/arm/kernel/setup.c --- linux-ref/arch/arm/kernel/setup.c Fri Nov 16 12:07:47 2001 +++ linux-work/arch/arm/kernel/setup.c Wed Nov 20 14:58:05 2002 @@ -29,6 +29,10 @@ #include #include +#ifdef CONFIG_UTIME +#include +#endif + #ifndef MEM_SIZE #define MEM_SIZE (16*1024*1024) #endif @@ -544,6 +548,16 @@ seq_printf(m, "BogoMIPS\t: %lu.%02lu\n", loops_per_jiffy / (500000/HZ), (loops_per_jiffy / (5000/HZ)) % 100); +#ifdef CONFIG_UTIME + seq_printf(m, "UTIME cps\t: %lu ticks per second\n" + "\t\t %lu ticks per jiffie\n" + "\t\t %lu ticks per usec\n\n" + "\t\t %lu quotient\n\n", + utime_state.cycles_per_sec, + utime_state.cycles_per_jiffies, + utime_state.cycles_per_usec, + utime_state.utime_quotient); +#endif /* dump out the processor features */ seq_puts(m, "Features\t: "); diff -u -r -N -b --exclude-from=diff-exclude linux-ref/arch/arm/kernel/utime.c linux-work/arch/arm/kernel/utime.c --- linux-ref/arch/arm/kernel/utime.c Wed Dec 31 18:00:00 1969 +++ linux-work/arch/arm/kernel/utime.c Thu Mar 20 14:44:13 2003 @@ -0,0 +1,638 @@ +/* + * UTIME: On-demand Microsecond Resolution Timers + * ------------------------------------------------------- + * + * File: arch/arm/kernel/utime.c + * Copyright (C) 1999-2001 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: Will Dinkel, Michael Frisbie, Jacob Woltersdorf + * + * 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 + */ + +#define _INCLUDED_FROM_UTIME_C_ +#include +#undef _INCLUDED_FROM_UTIME_C_ +#include + +#ifdef CONFIG_RT_ENFORCEMENT +#include +#endif + +#include + +#define __UTIME_DEBUG__ + +/* The length of the cycles_per_sec calibration loop MUST be 2^x + * seconds long, or the cycles_per_sec calculation will be incorrect. + * We do this because dividing 64-bit values in the kernel is + * unwieldly, and using shifts is far easier. + * NOTE: With the latest implementation of UTIME, cycles_per_sec is + * solely used for informational purposes. All internal UTIME + * calculations are done with the use of cycles_per_jiffies and + * utime_quotient, which are calibrated by the cycles_per_jiffies + * calibration loop. + */ +#define CALIBRATE_SECS 4 +#define CALIBRATE_POWER 2 + +unsigned long last_msb_usec_high = 0; + +inline void arch_update_system_time(void) +{ + unsigned long elapsed; +#ifdef CONFIG_UTIME_HIGHACC + unsigned long long quotient_elapsed; + unsigned long usec_low, usec_high; +#else + unsigned long long usec; +#endif + + /* Here's the part we care about. We want to save the result + * from the rdtsc call into last_update. If elapsed doesn't + * fit into a 32-bit integer, we've gone several hundred years + * and Linux is still being used, or we have a serious problem. + * + * We could also worry about TSC wraparound here, but that is + * unlikely to happen in the near future. A 1GHz machine + * would have to be running for ~584 years before overflowing. + */ + utime_state.last_update = get_cycles(); +#ifdef CONFIG_DSTREAM_UTIME_PROFILE + LOGN_HIST_ENTER(UTIME_PROFILE_FAM, HIST_ARCH_UPDATE); +#endif + + elapsed = (unsigned long)(utime_state.last_update - + utime_state.cycles_at_last_jiffie); + + /* Have any jiffies elapsed since our last update? + */ + if (elapsed >= utime_state.cycles_per_jiffies) { +#ifdef CONFIG_DSTREAM_UTIME_DEBUG + INCR_COUNTER(UTIME_DEBUG_FAM, COUNTER_GOT_A_JIFFIE, 1); +#endif + utime_state.cycles_at_last_jiffie = + utime_state.cycles_at_next_jiffie; + utime_state.cycles_at_next_jiffie += + (cycles_t)utime_state.cycles_per_jiffies; + elapsed -= utime_state.cycles_per_jiffies; + utime_state.jiffies_intr++; + (*(unsigned long *)&jiffies)++; + } + /* Have we lost any jiffies since our last update? + */ + while (elapsed >= utime_state.cycles_per_jiffies) { +#ifdef CONFIG_DSTREAM_UTIME_DEBUG + INCR_COUNTER(UTIME_DEBUG_FAM, COUNTER_LOST_A_JIFFIE, 1); +#endif + utime_state.cycles_at_last_jiffie = + utime_state.cycles_at_next_jiffie; + utime_state.cycles_at_next_jiffie += + (cycles_t)utime_state.cycles_per_jiffies; + elapsed -= utime_state.cycles_per_jiffies; + utime_state.jiffies_intr++; + (*(unsigned long *)&jiffies)++; + } + + /* Now calculate the new jiffies_u with what's left in + * elapsed. Using utime_quotient, we can do a multiply here + * rather than a divide. We save cycles this way. Here's the + * formula: + * jiffies_u * 2^32 = elapsed * utime_quotient + * = elapsed * (2^32 * (1 / cycles_per_usec)) + * + * To get rid of the unecessary 2^32, we just take the high 32 + * bits of the result, which gives us our new jiffies_u. + */ +/* __asm__("mull %2" + * :"=a" (usec_low), "=d" (usec_high) + * :"g" (utime_state.utime_quotient), "0" (elapsed)); + */ + usec = elapsed; + usec *= utime_state.utime_quotient; + quotient_elapsed = usec; + usec >>= sizeof(unsigned long)<<3; + +#ifdef CONFIG_UTIME_HIGHACC +/* quotient_elapsed = (unsigned long long)usec_high << 32; + * quotient_elapsed |= usec_low; + */ + + jiffies_u = quotient_elapsed >> utime_state.quotient_power; + + // BLAHHHHHH + printk("usec_high = %lu, usec_low = %lu, quotient_elapsed = %Lu\n" + "jiffies_u = %lu\n", + usec_high, usec_low, quotient_elapsed, jiffies_u); +#else + jiffies_u = usec; +#endif + + /* If jiffies_u is equal to USEC_PER_JIFFIES, then we hit the + * jiffie right on the dot, and the number of cycles was just + * not enough to increment jiffies above. This happens + * because the calculation for jiffies_u is less precise. But + * we go ahead and register a jiffie event here and reset + * jiffies_u to 0, because a jiffie HAS expired. + */ + if (jiffies_u == USEC_PER_JIFFIES) { + utime_state.cycles_at_last_jiffie = + utime_state.cycles_at_next_jiffie; + utime_state.cycles_at_next_jiffie += + (cycles_t)utime_state.cycles_per_jiffies; + utime_state.jiffies_intr++; + (*(unsigned long *)&jiffies)++; + jiffies_u = 0; + } + +#ifdef CONFIG_DSTREAM_UTIME_PROFILE + LOGN_HIST_EXIT_CYCLES(UTIME_PROFILE_FAM, HIST_ARCH_UPDATE); +#endif +} + +/* + * call with interrupts disabled + */ +inline extern unsigned long time2next_event(void) +{ + unsigned long timer_jiffies; + unsigned long timer_jiffies_u; + struct timer_list *next_timer; +#ifdef CONFIG_RT_ENFORCEMENT + signed long diff_jiffies; + signed long diff_jiffies_u; + + if (utime_state.next_rt_watchdog) { + /* Program watchdog timers unconditionally to + * guarantee real-time execution intervals. + */ + timer_jiffies = utime_state.next_rt_watchdog->expires; + timer_jiffies_u = utime_state.next_rt_watchdog->usec; + + diff_jiffies = timer_jiffies - jiffies; + diff_jiffies_u = timer_jiffies_u - jiffies_u; + + if (diff_jiffies > 0) { + diff_jiffies_u += diff_jiffies * USEC_PER_JIFFIES; + } + + if (diff_jiffies_u < 0) { + /* Houston, we have a problem... this watchdog + * timer is late... default to a regular timer. + */ + printk(KERN_CRIT "watchdog timer is late. diff_jiffies_u = %ld\n", + diff_jiffies_u); + utime_state.next_rt_watchdog = NULL; + } else { + return (unsigned long)diff_jiffies_u; + } + } +#endif /* CONFIG_RT_ENFORCEMENT */ + + /* 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) { +#ifdef CONFIG_RT_ENFORCEMENT + /* Is this timer a real-time wakeup event for an + * EPISODIC or CONTINUOUS task? Save the fact if so. + */ + if (next_timer->flags & KURT_WAKEUP) { + utime_state.current_rt_timer = next_timer; + } +#endif /* CONFIG_RT_ENFORCEMENT */ +#ifdef CONFIG_DSTREAM_KURT_CORRECT + LOGN_EVENT(KURT_CORRECT_FAM, EVENT_NEXT_TIMER, 0, jiffies, + sizeof(struct timer_list), (char *)next_timer); +#endif + return (timer_jiffies_u - jiffies_u); + } else { + /* The next jiffie boundary is closer than the next + * timer event, so program it instead. This is how we + * reproduce the standard Linux heartbeat of 10ms. + */ + return (USEC_PER_JIFFIES - jiffies_u); + } +} + +inline extern unsigned long time_elapsed(void) +{ + unsigned long jiffies_u_elapsed; + cycles_t cpu_ctr_value; + + cpu_ctr_value = get_cycles(); + + /* Find how much time has passed since we last updated system + * time (jiffies and jiffies_u). It should be very little + * time, but we need to account for it nonetheless. + */ + jiffies_u_elapsed = arch_cycles_to_usec(cpu_ctr_value - utime_state.last_update); + + return jiffies_u_elapsed; +} + +void internal_program_next_event(void) +{ + unsigned long delta_jiffies_u; /* jiffies_u till next earliest event */ + unsigned long jiffies_u_elapsed; + int usecs_to_event; + +#ifdef CONFIG_DSTREAM_UTIME_PROFILE + LOGN_HIST_ENTER(UTIME_PROFILE_FAM, HIST_PROGRAM_NEXT_EVENT); +#endif +#ifdef CONFIG_RT_ENFORCEMENT + if (utime_state.next_rt_watchdog) { + if (timer_expired(utime_state.next_rt_watchdog)) { + /* Our watchdog timer has gone off. Proceed + * as normal. + */ +#ifdef CONFIG_DSTREAM_KURT_PROFILE + /* What timers were deferred during this last + * real-time execution interval? + */ + LOGN_DEFERRED_TIMERS(); +#endif + utime_state.next_rt_watchdog = NULL; + } else { + /* Nothing is allowed to get in the way of our + * watchdog. + */ +#ifdef CONFIG_DSTREAM_KURT_DEBUG + LOGN_EVENT(KURT_DEBUG_FAM, EVENT_PROGRAM_IN_INTERVAL, + 0, current->pid, 0, NULL); +#endif +/* printk(KERN_CRIT "Attempt to program an event " */ +/* "inside an rt interval (pid %d)!\n", */ +/* current->pid); */ + return; + } + } else if (utime_state.current_rt_timer && + timer_expired(utime_state.current_rt_timer)) { + /* Our real-time wakeup timer has expired. Its + * watchdog timer is the next event, unconditionally. + * Make sure it's a watchdog timer though. + */ + struct rtdata *timer_data = + (struct rtdata *)(utime_state.current_rt_timer->data); + + if (!timer_data) { + printk(KERN_CRIT "Ick! current_rt_timer is NOT pointing" + " to an rtdata structure (data = %lu, fn = 0x%p)\n", + utime_state.current_rt_timer->data, + utime_state.current_rt_timer->function); + if (!(utime_state.current_rt_timer->flags & KURT_WAKEUP)) { + printk(KERN_CRIT "current_rt_timer is NOT A KURT_WAKEUP" + " timer!\n"); + } + } else if (timer_data->rt_watchdog && + (timer_data->rt_watchdog->flags & + (KURT_WATCHDOG | KURT_WATCHDOG_BAD))) { + utime_state.next_rt_watchdog = timer_data->rt_watchdog; + } else { + printk(KERN_CRIT "Real-time wakeup timer does NOT" + " point to a valid watchdog timer!\n"); + } + utime_state.current_rt_timer = NULL; + } else { + /* If no real-time timer is programmed, we just search + * as normal. But if there IS one, and it's not yet + * expired, a search may find a new, earlier one. + */ + utime_state.current_rt_timer = NULL; + } +#endif /* CONFIG_RT_ENFORCEMENT */ + + /* Update time to make sure we really grab an unexpired timer. + */ + arch_update_system_time(); + +#ifdef __UTIME_DEBUG__ + if (jiffies_u >= USEC_PER_JIFFIES) { + printk(KERN_CRIT "jiffies_u too large: %d\n", jiffies_u); + dprintk_address(); + } +#endif + 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) + */ + usecs_to_event = delta_jiffies_u - jiffies_u_elapsed; + if (usecs_to_event < utime_state.timer_delta) { + usecs_to_event = utime_state.timer_delta; + } +#ifndef CONFIG_RT_ENFORCEMENT + else if (usecs_to_event > USEC_PER_JIFFIES + utime_state.timer_delta) { + printk(KERN_CRIT "utime: %lu,%lu\n", delta_jiffies_u, jiffies_u_elapsed); + dprintk_address(); + } +#endif /* CONFIG_RT_ENFORCEMENT */ + /* + * now setup everything + */ + arch_program_next_event(usecs_to_event); + +#ifdef CONFIG_DSTREAM_KURT_CORRECT + LOGN_EVENT(KURT_CORRECT_FAM, EVENT_PROGRAM_NEXT, 0, usecs_to_event, 0, NULL); +#endif +#ifdef CONFIG_DSTREAM_UTIME_PROFILE + LOGN_HIST_EXIT(UTIME_PROFILE_FAM, HIST_PROGRAM_NEXT_EVENT); +#endif + + return; +} + + +#ifdef CONFIG_UTIME_HIGHACC +static inline void calc_utime_quotient(void) { + + unsigned long cycles_per_usec; + unsigned long long quotient_numerator; + unsigned long quotient_high, quotient_low, quotient_num_high, quotient_num_low; + unsigned long long quotient; + int log_result = 1; + + /* Need cycles_per_usec value now + */ + cycles_per_usec = (utime_state.cycles_per_sec + (USEC_PER_SEC / 2)) / + ((unsigned long) USEC_PER_SEC); + + /* + * This is the way the new calculation stuff works. + * + * quotient_power = 32 + log (cycles_per_usec) + * utime_quotient = (2 ^ quotient_power) / cycles_per_usec + * + * utime_quotient is used in arch_update_jiffies to calculate the new + * jiffies_u value. It's calculated so that a division operation can be + * avoided in that frequently run piece of code. utime_quotient really + * needs to only be 1 / cycles_per_usec but we're stuck with fixed-point + * numbers. + * + * So, we choose a value for quotient_power that will keep utime_quotient + * as close as possible to 2 ^ 32 (max range of unsigned long) so we lose + * very little accuracy due to round-off during calculations. + * + * The obvious solution that meets the above requirements is to have + * quotient_power be equal to 32 plus whatever power of 2 that division + * by cycles_per_usec is reducing utime_quotient by. This is determined + * by the base 2 log of cycles_per_usec. + * + */ + + /* Caculate the base 2 log of cycles_per_usec + */ + while ((1 << log_result) <= cycles_per_usec) { + log_result++; + } + + /* Step it down if our calculated log value is too high + */ + if ((1 << log_result) > cycles_per_usec) + log_result--; + + utime_state.quotient_power = 32 + log_result; + + /* + * utime_quotient = (2 ^ quotient_power) / cycles_per_usec + */ + quotient_numerator = (unsigned long long) 1 << utime_state.quotient_power; + + /* Slight optimization to be made here. So long as quotient_power + * is greater than 32, quotient_num_low will ALWAYS be 0. + */ + quotient_num_high = quotient_numerator >> 32; + quotient_num_low = quotient_numerator & 0x00000000ffffffff; + + /* __asm__("divl %2" + * :"=a" (quotient_low), "=d" (quotient_high) + * :"r" (cycles_per_usec), "0" (quotient_num_low), "1" (quotient_num_high)); + */ + quotient = quotient_numerator; + quotient /= cycles_per_usec; + quotient_high = quotient >> 32; + quotient_low = quotient & 0x00000000ffffffff; + + utime_state.utime_quotient = quotient_low; + +#if 1 + printk("quotient_numerator = %Lu\n" + "quotient_num_high = %lu\n" + "quotient_num_low = %lu\n" + "quotient_high = %lu\n" + "quotient_low = %lu\n" + "utime_quotient = %lu\n" + "quotient_power = %lu\n", + quotient_numerator, quotient_num_high, quotient_num_low, + quotient_high, quotient_low, utime_state.utime_quotient, + utime_state.quotient_power); +#endif + +} +#endif + + +/* The job of utime_calibrate is to set the cycles_per_sec, + * cycles_per_jiffies and utime_quotient values in the utime_state + * struct. For the x86, we calibrate over a loop of CALIBRATE_SECS + * seconds to find cycles_per_sec, and over a loop of + * CALIBRATE_JIFFIES jiffies to find cycles_per_jiffies and + * utime_quotient (we can use the results of the calibrate_tsc() + * routine in arch/i386/kernel/time.c for this, or do our own + * calibration here, which is the default). For other platforms, + * they may just be set to default values. Regardless, THEY MUST + * BE SET HERE, ALL THREE OF THEM! + */ +extern unsigned long fast_gettimeoffset_quotient; + +void utime_calibrate(void) +{ + unsigned long flags; + unsigned long long quotient; + + printk("Calibrating cpu "); + if (!utime_state.cycles_per_sec) { + /* Code for runtime calibration of utime (this will be + * called in case we did not enter boot_parameters for + * it) + */ + cycles_t first_time, next_time, diff, sum = 0; + unsigned long first_jiffies, average; + int i = 0; + + /* wait for "start of" clock tick */ + first_jiffies = jiffies; + while (first_jiffies == jiffies) + /* nothing */; + + /* CALIBRATE_SECS second loop (last iteration is unrolled) + */ + first_time = utime_state.last_update; + while (i++ < CALIBRATE_SECS) { + first_jiffies = jiffies; + printk("."); + while (jiffies - first_jiffies < HZ); + } + next_time = utime_state.last_update; + printk("."); + diff = next_time - first_time; + /* We could just divide by CALIBRATE_SECS if we + * weren't doing 64-bit math. Instead, we have to + * right shift by CALIBRATE_POWER, which is found this + * way: + * CALIBRATE_SECS = 2^CALIBRATE_POWER + * We add by (CALIBRATE_SECS / 2) to round correctly. + */ + spin_lock_irqsave(&utime_state.lock, flags); + utime_state.cycles_per_sec = (unsigned long)((diff + (CALIBRATE_SECS / 2)) + >> CALIBRATE_POWER); + spin_unlock_irqrestore(&utime_state.lock, flags); + + for (i = 0; i < CALIBRATE_LOOPS; i++) { + /* wait for "start of" clock tick */ + first_jiffies = jiffies; + while (first_jiffies == jiffies) + /* nothing */; + + /* CALIBRATE_JIFFIES jiffie loop + */ + first_time = utime_state.last_update; + first_jiffies = jiffies; + while (jiffies < first_jiffies + CALIBRATE_JIFFIES) + /* nothing */; + next_time = utime_state.last_update; + diff = next_time - first_time; + + /* Was the calibration period too long? If so, panic. + */ + if (diff > 0xffffffff) { + printk("cycles_per_jiffies calibration period" + " too long!\n"); + BUG(); + } else { + sum += diff; +// printk("UTIME Cycles per Cal jiffies = %Lu\n", diff); + } + } + + /* __asm__("divl %2" + * :"=a" (average), "=d" (trash) + * :"r" (CALIBRATE_LOOPS), "A" (sum)); + */ + average = sum / CALIBRATE_LOOPS; + printk("UTIME Cycles per Cal jiffies average = %lu\n", average); + + spin_lock_irqsave(&utime_state.lock, flags); +#ifdef USE_FAST_GETTIMEOFFSET_QUOTIENT + utime_state.cycles_per_jiffies = + (cycles_per_cal_jiffies + (CALIBRATE_JIFFIES / 2)) / + CALIBRATE_JIFFIES; +#else /* default is to use UTIME's calibration */ + utime_state.cycles_per_jiffies = + (average + (CALIBRATE_JIFFIES / 2)) / + CALIBRATE_JIFFIES; +#endif + /* utime_quotient is used in arch_update_system_time() to + * calculate jiffies_u via multiplication rather than a + * divide, which saves us some cycles. The formula for it is: + * 2^32 * (1 / (cycles_per_usec)) (see + * arch/i386/kernel/time.c) + */ +#ifndef CONFIG_UTIME_HIGHACC +#ifdef USE_FAST_GETTIMEOFFSET_QUOTIENT + utime_state.utime_quotient = fast_gettimeoffset_quotient; +#else +/*__asm__("divl %2" + * :"=a" (quotient_low), "=d" (quotient_high) + * :"r" (average), "0" (0), "1" (CALIBRATE_USECS)); + */ + quotient = CALIBRATE_USECS; + quotient <<= sizeof(unsigned long)<<3; + quotient /= diff; + utime_state.utime_quotient = (unsigned long)quotient; +#endif +#endif + } else { + /* utime_quotient is used in arch_update_system_time() to + * calculate jiffies_u via multiplication rather than a + * divide, which saves us some cycles. The formula for it is: + * 2^32 * (1 / (cycles_per_usec)) + */ + spin_lock_irqsave(&utime_state.lock, flags); + utime_state.cycles_per_jiffies = + (utime_state.cycles_per_sec + (HZ / 2)) / + ((unsigned long) HZ); + +#ifndef CONFIG_UTIME_HIGHACC + /* __asm__("divl %2" + * :"=a" (quotient_low), "=d" (quotient_high) + * :"r" (utime_state.cycles_per_sec), "0" (0), "1" (USEC_PER_SEC)); + */ + quotient = USEC_PER_SEC; + quotient <<= sizeof(unsigned long)<<3; + quotient /= utime_state.cycles_per_sec; + + utime_state.utime_quotient = quotient & 0xffffffff; +#endif + } + +#ifdef CONFIG_UTIME_HIGHACC + calc_utime_quotient(); +#endif + + spin_unlock_irqrestore(&utime_state.lock, flags); +} + +int change_timer_mode() +{ + unsigned long flags; + + spin_lock_irqsave(&utime_state.lock, flags); + + if (!utime_state.cycles_per_sec) { + spin_unlock_irqrestore(&utime_state.lock, flags); + printk(KERN_DEBUG "[%s:%s:%d] timer_mode:%d" + " (cycles_per_sec not set\n", + __FILE__, __FUNCTION__, __LINE__, + utime_state.kernel_timer_mode); + return 1; + } + utime_state.kernel_timer_mode = TIMER_CHIP_ONESHOT; + put_timer_in_oneshot_mode(); + do_timer = utime_do_timer; + internal_program_next_event(); + + spin_unlock_irqrestore(&utime_state.lock, flags); + + return 0; +} + 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 Thu Nov 7 16:13:59 2002 +++ linux-work/arch/i386/config.in Tue May 27 13:22:31 2003 @@ -2,6 +2,12 @@ # For a description of the syntax of this configuration file, # see Documentation/kbuild/config-language.txt. # + +# +# Modified for KURT: KU Real Time Linux +# See Documentation/kurt.txt for further details +# + mainmenu_name "Linux Kernel Configuration" define_bool CONFIG_X86 y @@ -28,6 +34,7 @@ comment 'Processor type and features' choice 'Processor family' \ "386 CONFIG_M386 \ + TS-ELAN-AMDSC520 CONFIG_TS_M486 \ 486 CONFIG_M486 \ 586/K5/5x86/6x86/6x86MX CONFIG_M586 \ Pentium-Classic CONFIG_M586TSC \ @@ -46,6 +53,9 @@ # # Define implied options from the CPU selection here # +if [ "$CONFIG_TS_M486" = "y" ]; then + define_bool CONFIG_M486 y +fi if [ "$CONFIG_M386" = "y" ]; then define_bool CONFIG_X86_CMPXCHG n @@ -432,3 +442,64 @@ fi endmenu + +# +# KU Additional Options +# +mainmenu_option next_comment +comment 'KU Datastreams Kernel Interface' + +bool 'KU Datastreams Kernel Interface' CONFIG_DSTREAM +if [ "$CONFIG_DSTREAM" = "y" ]; then + bool ' DSKI Reuse SKBuffs' CONFIG_DSTREAM_REUSE_SKBUFFS + bool ' DSKI Static Memory Pool' CONFIG_DSTREAM_MEM_POOL + dep_bool ' DSKI Triggered Log (EXPERIMENTAL)' CONFIG_DSTREAM_TRIGGERED_LOG $CONFIG_EXPERIMENTAL + + # DSKI Family Configuration + source drivers/dstream/Config.in + + define_bool CONFIG_DSTREAM_USAGE_CALC n + define_bool CONFIG_DSTREAM_USAGE_CALC_THREAD n +fi + +endmenu + + +mainmenu_option next_comment +comment 'KU High-Resolution Timing Layer' + +if [ "$CONFIG_X86_TSC" = "y" -o "$CONFIG_TS_M486" = "y" ]; then + bool 'KU High-Resolution Timer Support' CONFIG_UTIME + if [ "$CONFIG_UTIME" = "y" ]; then + tristate ' High-Resolution Timer Calibration Module' CONFIG_UTIME_CALIBRATION + if [ "$CONFIG_SMP" = "n" ]; then + dep_bool ' KURT Interrupt Modifications (EXPERIMENTAL)' CONFIG_KURT_IRQ_MOD $CONFIG_EXPERIMENTAL + dep_bool ' KURT Highest Priority Timer Interrupt (EXPERIMENTAL)' CONFIG_HIGHEST_PRIORITY_TIMER $CONFIG_KURT_IRQ_MOD $CONFIG_EXPERIMENTAL + dep_bool ' KURT User-Mode Device Driver (EXPERIMENTAL)' CONFIG_USER_MODE_DEVICE_DRIVER $CONFIG_KURT_IRQ_MOD $CONFIG_PREEMPT $CONFIG_RT $CONFIG_EXPERIMENTAL + fi + +# Currently in a very "rough" stage of development. +# +# if [ "$CONFIG_SMP" = "y" ]; then +# bool ' Seperate UTIME timer list.' CONFIG_UTIME_SEP_TIMER_LIST n +# fi + + fi +fi + +dep_bool ' Group Scheduling (EXPERIMENTAL)' CONFIG_GROUP_SCHEDULING $CONFIG_EXPERIMENTAL +if [ "$CONFIG_GROUP_SCHEDULING" != "n" ]; then + if [ "$CONFIG_RT" != "n" ]; then + tristate ' KURT Decision Function' CONFIG_GROUP_KURT_DFUNC n + fi + # + # Other scheduler modules go right here. + # +fi +dep_bool ' KU: Frizz Modifications (EXPERIMENTAL)' FRIZZ $CONFIG_EXPERIMENTAL +dep_bool ' KU: Jacob Modifications (EXPERIMENTAL)' JACOB $CONFIG_EXPERIMENTAL +dep_bool ' KU: Hari Modifications (EXPERIMENTAL)' CONFIG_HARI $CONFIG_EXPERIMENTAL + +endmenu + +source drivers/kurt/config.in diff -u -r -N -b --exclude-from=diff-exclude linux-ref/arch/i386/kernel/Makefile linux-work/arch/i386/kernel/Makefile --- linux-ref/arch/i386/kernel/Makefile Fri Nov 9 16:21:21 2001 +++ linux-work/arch/i386/kernel/Makefile Wed Nov 20 14:58:05 2002 @@ -40,5 +40,6 @@ obj-$(CONFIG_X86_LOCAL_APIC) += mpparse.o apic.o nmi.o obj-$(CONFIG_X86_IO_APIC) += io_apic.o acpitable.o obj-$(CONFIG_X86_VISWS_APIC) += visws_apic.o +obj-$(CONFIG_UTIME) += utime.o include $(TOPDIR)/Rules.make diff -u -r -N -b --exclude-from=diff-exclude linux-ref/arch/i386/kernel/apic.c linux-work/arch/i386/kernel/apic.c --- linux-ref/arch/i386/kernel/apic.c Mon Feb 25 13:37:52 2002 +++ linux-work/arch/i386/kernel/apic.c Thu Mar 20 15:42:36 2003 @@ -30,6 +30,12 @@ #include #include +#include + +#if defined(JACOB) && defined(CONFIG_UTIME) +#include +#endif + /* Using APIC to generate smp_local_timer_interrupt? */ int using_apic_timer = 0; @@ -649,7 +655,6 @@ void __init init_apic_mappings(void) { unsigned long apic_phys; - /* * If no local APIC can be found then set up a fake all * zeroes page to simulate the local APIC and another @@ -756,7 +761,11 @@ * P5 APIC double write bug. */ +#ifdef JACOB +#define APIC_DIVISOR 1 +#else #define APIC_DIVISOR 16 +#endif void __setup_APIC_LVTT(unsigned int clocks) { @@ -770,9 +779,15 @@ * Divide PICLK by 16 */ tmp_value = apic_read(APIC_TDCR); +#ifdef JACOB + apic_write_around(APIC_TDCR, (tmp_value + & ~(APIC_TDR_DIV_1 | APIC_TDR_DIV_TMBASE)) + | APIC_TDR_DIV_1); +#else apic_write_around(APIC_TDCR, (tmp_value & ~(APIC_TDR_DIV_1 | APIC_TDR_DIV_TMBASE)) | APIC_TDR_DIV_16); +#endif apic_write_around(APIC_TMICT, clocks/APIC_DIVISOR); } @@ -913,6 +928,9 @@ __cli(); calibration_result = calibrate_APIC_clock(); +#if defined(JACOB) && defined(CONFIG_X86_LOCAL_APIC) + utime_state.apic_time_standard_per_jiffy = calibration_result; +#endif /* * Now set up the timer for real. */ @@ -968,7 +986,25 @@ { int user = user_mode(regs); int cpu = smp_processor_id(); + /* FIXME: + * Hmmm.... I'm beginning to wonder whether we really want to + * do any of this stuff at all from the local APIC handler. + * If we're using the local APIC for KURT/UTIME purposes, can + * we extend the standard Linux ifdefs to move the profiling, + * etc. back into timer_interrupt()? + * + * If so, this would allow all of this necessary "junk" to be + * handled by the executive cpu receiving interrupts from the + * ghetto PIC. + * + * Just a thought... + */ + +#if defined(JACOB) && defined(CONFIG_DSTREAM_UTIME_DEBUG) && defined(CONFIG_X86_LOCAL_APIC) + LOGN_EVENT( UTIME_DEBUG_FAM, EVENT_SMP_LOCAL_TIMER_INT, 0, + cpu, sizeof(cpu), (void *)cpu); +#endif /* * The profiling function is SMP safe. (nothing can mess * around with "current", and the profiling counters are @@ -1010,6 +1046,35 @@ */ } + +#if defined(JACOB) && defined(CONFIG_X86_LOCAL_APIC) +inline void utime_smp_local_timer_interrupt( struct pt_regs * regs ) +{ + u64 elapsed; + u64 slice_offset; + unsigned long flags; + + /* do_timer() assumes that interrupts have been disabled as it + * is normally called from the timer_interrupt() handler which + * is configured with the SA_INTERRUPT flag. + */ + spin_lock_irqsave( &xtime_lock, flags ); + utime_do_timer( regs ); + spin_unlock_irqrestore( &xtime_lock, flags ); + + /* Call the normal local apic timer interrupt handler if this + * cpu's time slice has arrived (the period designated for + * each cpu within each jiffy). + */ + elapsed = utime_state.last_update - utime_state.cycles_at_last_jiffy; + slice_offset = utime_state.time_standard_per_jiffy / (smp_num_cpus+1); + if( slice_offset >= elapsed ){ + smp_local_timer_interrupt( regs ); + } + +} +#endif + /* * Local APIC timer interrupt. This is the most natural way for doing * local interrupts, but local timer interrupts can be emulated by @@ -1020,9 +1085,15 @@ */ unsigned int apic_timer_irqs [NR_CPUS]; + void smp_apic_timer_interrupt(struct pt_regs * regs) { int cpu = smp_processor_id(); +#if defined(JACOB) && defined(CONFIG_DSTREAM_UTIME_DEBUG) && defined(CONFIG_X86_LOCAL_APIC) + + LOGN_EVENT( UTIME_DEBUG_FAM, EVENT_ENTER_SMP_APIC_TIMER_INTERRUPT, 0, cpu, sizeof(cpu), (void *)cpu ); + LOGN_EVENT( UTIME_DEBUG_FAM, EVENT_READ_LOCAL_APIC_ID, 0, GET_APIC_ID( apic_read(APIC_ID) ), sizeof(cpu), (void *)cpu ); +#endif /* * the NMI deadlock-detector uses this. @@ -1034,13 +1105,21 @@ * because timer handling can be slow. */ ack_APIC_irq(); + /* * update_process_times() expects us to have done irq_enter(). * Besides, if we don't timer interrupts ignore the global * interrupt lock, which is the WrongThing (tm) to do. */ +#if defined(JACOB) && defined(CONFIG_DSTREAM_UTIME_DEBUG) && defined(CONFIG_X86_LOCAL_APIC) + LOGN_EVENT( UTIME_DEBUG_FAM, EVENT_SMP_LOCAL_INT_FROM_SMP_APIC_TIMER_INT, 0, cpu, sizeof(cpu), (void *)cpu ); +#endif irq_enter(cpu, 0); +#if defined(JACOB) && defined(CONFIG_X86_LOCAL_APIC) + utime_smp_local_timer_interrupt( regs ); +#else smp_local_timer_interrupt(regs); +#endif irq_exit(cpu, 0); if (softirq_pending(cpu)) @@ -1097,6 +1176,7 @@ smp_processor_id(), v , v1); } + /* * This initializes the IO-APIC and APIC hardware if this is * a UP kernel. @@ -1137,3 +1217,4 @@ return 0; } + diff -u -r -N -b --exclude-from=diff-exclude linux-ref/arch/i386/kernel/entry.S linux-work/arch/i386/kernel/entry.S --- linux-ref/arch/i386/kernel/entry.S Thu Nov 7 16:13:45 2002 +++ linux-work/arch/i386/kernel/entry.S Tue Apr 8 13:29:25 2003 @@ -277,10 +277,16 @@ testl $(VM_MASK | 3),%eax # return to VM86 mode or non-supervisor? jne ret_from_sys_call #ifdef CONFIG_PREEMPT +#ifdef CONFIG_KURT_IRQ_MOD + cmpl $0, SYMBOL_NAME(kurt_blocked_irqs) + jnz restore_all +#endif cmpl $0,preempt_count(%ebx) jnz restore_all cmpl $0,need_resched(%ebx) jz restore_all +# Do we want to preempt even if a top half or bottom half is executing? +# What information do we need to decide this question? movl SYMBOL_NAME(irq_stat)+irq_stat_local_bh_count CPU_INDX,%ecx addl SYMBOL_NAME(irq_stat)+irq_stat_local_irq_count CPU_INDX,%ecx jnz restore_all @@ -681,6 +687,11 @@ .long SYMBOL_NAME(sys_ni_syscall) /* 235 reserved for removexattr */ .long SYMBOL_NAME(sys_ni_syscall) /* reserved for lremovexattr */ .long SYMBOL_NAME(sys_ni_syscall) /* reserved for fremovexattr */ +#ifdef CONFIG_PT + .long SYMBOL_NAME(sys_pt_set_config) /* 238 - Proteus */ + .long SYMBOL_NAME(sys_pt_get_config) + .long SYMBOL_NAME(sys_pt_rt_start) +#endif .rept NR_syscalls-(.-sys_call_table)/4 .long SYMBOL_NAME(sys_ni_syscall) diff -u -r -N -b --exclude-from=diff-exclude linux-ref/arch/i386/kernel/irq.c linux-work/arch/i386/kernel/irq.c --- linux-ref/arch/i386/kernel/irq.c Thu Oct 25 15:53:46 2001 +++ linux-work/arch/i386/kernel/irq.c Tue May 27 13:54:12 2003 @@ -44,7 +44,10 @@ #include #include - +#include +#ifdef CONFIG_UTIME +#include +#endif /* * Linux has a controller-independent x86 interrupt architecture. @@ -369,9 +372,18 @@ void __global_sti(void) { int cpu = smp_processor_id(); - +#if 0 + printk( "__global_sti: cpu = %d\n", cpu ); +#endif if (!local_irq_count(cpu)) +#if 0 + { + printk( "__global_sti: local_irq_count = 0\n" ); +#endif release_irqlock(cpu); +#if 0 + } +#endif __sti(); } @@ -439,6 +451,9 @@ int status; int cpu = smp_processor_id(); +#ifdef CONFIG_KURT_IRQ_MOD + preempt_disable(); +#endif irq_enter(cpu, irq); status = 1; /* Force the "do bottom halves" bit */ @@ -448,15 +463,19 @@ do { status |= action->flags; + DSKI_EVENT(KURT_DEBUG_FAM, EVENT_START_IRQ_HANDLER, 0, (unsigned long)irq, 0, NULL); action->handler(irq, action->dev_id, regs); action = action->next; } while (action); if (status & SA_SAMPLE_RANDOM) add_interrupt_randomness(irq); + __cli(); irq_exit(cpu, irq); - +#ifdef CONFIG_KURT_IRQ_MOD + preempt_enable_no_resched(); +#endif return status; } @@ -484,12 +503,18 @@ irq_desc_t *desc = irq_desc + irq; unsigned long flags; +#ifdef CONFIG_KURT_IRQ_MOD + kurt_irq_enter(); +#endif spin_lock_irqsave(&desc->lock, flags); if (!desc->depth++) { desc->status |= IRQ_DISABLED; desc->handler->disable(irq); } spin_unlock_irqrestore(&desc->lock, flags); +#ifdef CONFIG_KURT_IRQ_MOD + kurt_irq_exit(); +#endif } /** @@ -533,6 +558,9 @@ irq_desc_t *desc = irq_desc + irq; unsigned long flags; +#ifdef CONFIG_KURT_IRQ_MOD + kurt_irq_enter(); +#endif spin_lock_irqsave(&desc->lock, flags); switch (desc->depth) { case 1: { @@ -553,8 +581,705 @@ __builtin_return_address(0)); } spin_unlock_irqrestore(&desc->lock, flags); +#ifdef CONFIG_KURT_IRQ_MOD + kurt_irq_exit(); +#endif +} + +#ifdef CONFIG_KURT_IRQ_MOD +/* ############################################################################ + * ############################################################################ + */ +#include +#include +#ifdef CONFIG_GROUP_SCHEDULING +#include +#endif + +#ifdef CONFIG_USER_MODE_DEVICE_DRIVER +struct task_struct *preempted_task = NULL; +#endif + +/* Denotes when the hardware interrupt handler is running. It is also + * used to prevent critical sections from reenabling interrupts. + */ +long kurt_in_interrupt = 0; + +/* This is a bitfield which is used to mark interrupts which have + * occurred but have not been handled. + */ +unsigned volatile long kurt_pending_irqs = 0; + +/* This is used to mark interrupts that are currently executing a + * corresponding top half. + */ +unsigned volatile long kurt_current_irqs = 0; + +/* This is the global software interrupt flag. It replaces the + * hardware flag that is normally controlled by cli and sti. + */ +unsigned volatile long kurt_irqs_enabled = 1; + +/* This variable indicates the number of hardware interrupts that have + * occurred but have not executed the corresponding top half. + */ +unsigned volatile long kurt_irqs_in_progress = 0; + +/* This is used to save the state of the registers when an interrupt + * occurs. + */ +struct pt_regs *kurt_irq_regs; + +/* This is the kurt irq module. It decides which handlers to execute + * and executes both top and bottom halves. + */ +unsigned int do_delayed_IRQs(void); + +/* The flags array stores the state of the software interrupt flag + * before executing a top half. This is necessary because it can no + * longer be assumed that interrupts were enabled at the time that the + * top half was executed. + */ +unsigned long kurt_irq_flags[NR_IRQS]; + +/* This is a bitfield which is used to prevent the execution of + * specific interrupt handlers. + */ +unsigned volatile long kurt_blocked_irqs = 0; + +static inline int soft_ops_disabled(void) { + int retval; + kurt_irq_enter(); + retval = ((kurt_in_interrupt > 1) || +#ifdef CONFIG_USER_MODE_DEVICE_DRIVER + preempted_task || +#endif + test_bit(TIMER_IRQ, &kurt_current_irqs)) ? 1 : 0; + kurt_irq_exit(); + return retval; +} + +/* This function is used by spin_lock_irqsaveblock. It is responsible + * for saving flags and then clearing interrupts, which does not + * need to be atomic. The semantics of this have changed with the + * introduction of controlling interrupts separately rather than + * globally. If the spinlock has an associated interrupt that + * requires concurrency control, then the interrupt will be blocked. + * If there is no associated interrupt, then the original semantics of + * saving the global flags and disabling all interrupts applies. + */ +void spin_lock_irqsaveblock(spinlock_t *lock, unsigned long *x) +{ +#ifdef CONFIG_HIGHEST_PRIORITY_TIMER + if (lock->irq == TIMER_IRQ) { + kurt_irq_enter(); + return; + } +#endif + if (soft_ops_disabled()) + return; + if (lock->irq >= 0) { + if (test_and_set_bit(lock->irq, &kurt_blocked_irqs)) { + *x = 1; + } else { + *x = 0; + } + } else { + soft_save_flags(x); + soft_cli(); + } +} + +/* This function is used by spin_lock_irqrestoreunblock. It is used to + * restore flags to a particular state. If the spinlock has an + * associated irq, then only the flag that pertains to this irq is + * restored. Otherwise, the global irq flag is restored. + */ +void spin_unlock_irqrestoreunblock(spinlock_t *lock, unsigned long *x) +{ +#ifdef CONFIG_HIGHEST_PRIORITY_TIMER + if (lock->irq == TIMER_IRQ) { + kurt_irq_exit(); + return; + } +#endif + if (soft_ops_disabled()) + return; + if (lock->irq >= 0) { + if (*x) + spin_lock_irqblock(lock); + else + spin_unlock_irqunblock(lock); + } else + soft_restore_flags(*x); +} + +/* This function is called by spin_lock_irqblock. It is used to block + * interrupts. If the spinlock has an associated interrupt, then the + * corresponding irq is blocked. Otherwise, the global software flag + * is blocked. + */ +void spin_lock_irqblock(spinlock_t *lock) +{ +#ifdef CONFIG_HIGHEST_PRIORITY_TIMER + if (lock->irq == TIMER_IRQ) { + kurt_irq_enter(); + return; + } +#endif + if (soft_ops_disabled()) + return; + if (lock->irq >= 0) { + set_bit(lock->irq, &kurt_blocked_irqs); + } else + soft_cli(); +} + +/* This function is called by spin_unlock_irqunblock. It is used to enable + * interrupts. If the spinlock has an associated interrupt, then the + * corresponding irq is enabled. Otherwise, the global software flag + * is enabled. + */ +void spin_unlock_irqunblock(spinlock_t *lock) +{ +#ifdef CONFIG_HIGHEST_PRIORITY_TIMER + if (lock->irq == TIMER_IRQ) { + kurt_irq_exit(); + return; + } +#endif + if (soft_ops_disabled()) + return; + if (lock->irq >= 0) { + clear_bit(lock->irq, &kurt_blocked_irqs); + if (test_bit(lock->irq, &kurt_pending_irqs)) { + kurt_irq_enter(); + kurt_irqs_in_progress++; + kurt_irq_exit(); + do_delayed_IRQs(); + } + } else + soft_sti(); +} + + +/* This function is used by rw_lock_irqsaveblock. It is responsible for + * saving flags and then clearing interrupts, which does not + * need to be atomic. The semantics of this have changed with the + * introduction of controlling interrupts separately rather than + * globally. If the rwlock has an associated interrupt that + * requires concurrency control, then the interrupt will be blocked. + * If there is no associated interrupt, then the original semantics of + * saving the global flags and disabling all interrupts applies. + */ +void rw_lock_irqsaveblock(rwlock_t *lock, unsigned long *x) +{ +#ifdef CONFIG_HIGHEST_PRIORITY_TIMER + if (lock->irq == TIMER_IRQ) { + kurt_irq_enter(); + return; + } +#endif + if (soft_ops_disabled()) + return; + if (lock->irq >= 0) { + if (test_and_set_bit(lock->irq, &kurt_blocked_irqs)) { + *x = 1; + } else { + *x = 0; + } + } else { + soft_save_flags(x); + soft_cli(); + } +} + +/* This function is used by rw_lock_irqrestoreunblock. It is used to + * restore flags to a particular state. If the rwlock has an + * associated irq, then only the flag that pertains to this irq is + * restored. Otherwise, the global irq flag is restored. + */ +void rw_unlock_irqrestoreunblock(rwlock_t *lock, unsigned long *x) +{ +#ifdef CONFIG_HIGHEST_PRIORITY_TIMER + if (lock->irq == TIMER_IRQ) { + kurt_irq_exit(); + return; + } +#endif + if (soft_ops_disabled()) + return; + if (lock->irq >= 0) { + if (*x) + rw_lock_irqblock(lock); + else + rw_unlock_irqunblock(lock); + } else + soft_restore_flags(*x); +} + +/* This function is called by rw_lock_irqblock. It is used to block + * interrupts. If the rwlock has an associated interrupt, then the + * corresponding irq is blocked. Otherwise, the global software flag + * is blocked. + */ +void rw_lock_irqblock(rwlock_t *lock) +{ +#ifdef CONFIG_HIGHEST_PRIORITY_TIMER + if (lock->irq == TIMER_IRQ) { + kurt_irq_enter(); + return; + } +#endif + if (soft_ops_disabled()) + return; + if (lock->irq >= 0) { + set_bit(lock->irq, &kurt_blocked_irqs); + } else + soft_cli(); +} + +/* This function is called by rw_unlock_irqunblock. It is used to enable + * interrupts. If the rwlock has an associated interrupt, then the + * corresponding irq is enabled. Otherwise, the global software flag + * is enabled. + */ +void rw_unlock_irqunblock(rwlock_t *lock) +{ +#ifdef CONFIG_HIGHEST_PRIORITY_TIMER + if (lock->irq == TIMER_IRQ) { + kurt_irq_exit(); + return; + } +#endif + if (soft_ops_disabled()) + return; + if (lock->irq >= 0) { + clear_bit(lock->irq, &kurt_blocked_irqs); + if (test_bit(lock->irq, &kurt_pending_irqs)) { + kurt_irq_enter(); + kurt_irqs_in_progress++; + kurt_irq_exit(); + do_delayed_IRQs(); + } + } else + soft_sti(); +} + + +/* This is the software version of cli. It currently only operates on + * the global irq flag. + */ +void soft_cli(void) +{ + if (soft_ops_disabled()) + return; + kurt_irq_enter(); + if (kurt_irqs_enabled) { + DSKI_HIST_ENTER(KURT_DEBUG_FAM, HIST_CLI_STI); + DSKI_EVENT(KURT_DEBUG_FAM, EVENT_CLI, 0, kurt_pending_irqs, 0, NULL); + kurt_irqs_enabled = 0; + preempt_disable(); + } + kurt_irq_exit(); +} + + +/* This is the software version of sti. It currently only operates on + * the global irq flag. + */ +void soft_sti(void) +{ + if (soft_ops_disabled()) + return; + kurt_irq_enter(); + if (!kurt_irqs_enabled) { + DSKI_HIST_EXIT(KURT_DEBUG_FAM, HIST_CLI_STI); + kurt_irqs_enabled = 1; + preempt_enable_no_resched(); + if (kurt_pending_irqs & ~kurt_blocked_irqs) { + kurt_irqs_in_progress++; + DSKI_EVENT(KURT_DEBUG_FAM, EVENT_STI, 0, kurt_pending_irqs, 0, NULL); + kurt_irq_exit(); + do_delayed_IRQs(); + } else { + kurt_irq_exit(); + } + } else + kurt_irq_exit(); +} + +/* This function saves the current state of the global irq flag. It + * currently only operates on the global irq flag. + */ +void soft_save_flags(unsigned long *x) +{ + if (soft_ops_disabled()) + return; + hard_cli(); + *x = kurt_irqs_enabled; + hard_sti(); +} + +/* This function restores flags that were saved. If the restoring has + * no effect on the interrupt flag, then no action is taken. + * Otherwise, interrupts are enabled or disabled depending on the + * new flags. + */ +void soft_restore_flags(unsigned long x) +{ + if (soft_ops_disabled()) + return; + hard_cli(); + if (kurt_irqs_enabled != x) { + if (x) + soft_sti(); + else + soft_cli(); + } else + hard_sti(); +} + +#ifdef CONFIG_USER_MODE_DEVICE_DRIVER +/* This MUST be called inside kurt_irq_enter() */ +inline void kurt_preempt_attempt(void) +{ + if (next_rt_target && !current->kurt_preempt_count && !preempted_task && + !test_bit(TIMER_IRQ, &kurt_blocked_irqs)) { + preempted_task = current; + DSKI_EVENT(KURT_DEBUG_FAM, EVENT_RT_PREEMPTED, 0, preempted_task->pid, 0, NULL); + kurt_irq_exit(); + preempt_schedule(); + kurt_irq_enter(); + preempted_task = NULL; + if (kurt_pending_irqs & ~kurt_blocked_irqs) { + kurt_irqs_in_progress++; + kurt_irq_exit(); + do_delayed_IRQs(); + kurt_irq_enter(); + } + } +} + +void kurt_preempt_disable(void) +{ + kurt_irq_enter(); + current->kurt_preempt_count++; + kurt_irq_exit(); +} + +void kurt_preempt_enable(void) +{ + kurt_irq_enter(); + current->kurt_preempt_count--; + if ((kurt_in_interrupt <= 1) && !test_bit(TIMER_IRQ, &kurt_current_irqs)) { + kurt_preempt_attempt(); + } + kurt_irq_exit(); +} +#endif + +/* This is the kurt irq module decision function. It allows the timer + * interrupt to execute at any time. It delays other interrupts if + * there is a pending real-time process or irqs are disabled. It will + * execute bottom halves if there is no top half to execute. + */ +inline int kurt_irq_decision(void) +{ + int irq = -1; + int cpu = smp_processor_id(); + + kurt_irq_enter(); + if (test_and_clear_bit(TIMER_IRQ, &kurt_pending_irqs)) { + + if (test_bit(TIMER_IRQ, &kurt_blocked_irqs)) { + set_bit(TIMER_IRQ, &kurt_pending_irqs); + /* DSKI_EVENT(KURT_DEBUG_FAM, EVENT_BLOCKED_IRQ, 0, TIMER_IRQ, 0, NULL); */ + } else { + /* + * Select the timer interupt for service + * so it is no longer pending. + */ + irq = TIMER_IRQ; + DSKI_EVENT(KURT_DEBUG_FAM, EVENT_IRQ_SELECT_TIMER, 0, kurt_pending_irqs, 0, NULL); + } + } else if (test_bit(TIMER_IRQ, &kurt_current_irqs)) { + /* DSKI_EVENT(KURT_DEBUG_FAM, EVENT_IGNORE_IRQ_FOR_TIMER, 0, kurt_pending_irqs, 0, NULL); */ + /* + * This indicates that we are already executing a timer handler, so + * Don't execute another interrupt handler + * while the timer interrupt is active + */ +#ifdef CONFIG_RT +#ifdef CONFIG_GROUP_SCHEDULING + } else if (next_rt_target || (GRP_PROC_IS_RT(current) + && kurt_current_group[cpu] + && kurt_current_group[cpu]->rt_params + && (kurt_current_group[cpu]->rt_params->rt_mode & KURT_EXPLICIT) + && (current->state == TASK_RUNNING))) { +#else + } else if (next_rt_target || (current->rt_params && + (current->rt_params->rt_mode & KURT_EXPLICIT) && + (current->state == TASK_RUNNING))) { +#endif + /* Do not execute interrupt handler if there is a + * pending real-time event as indicated by this variable + * being set. FRIZZ: perhaps a name change???? next_rt_user_process? + */ +#ifdef CONFIG_USER_MODE_DEVICE_DRIVER + /* DSKI_EVENT(KURT_DEBUG_FAM, EVENT_IGNORE_IRQ_FOR_RT, 0, current->kurt_preempt_count, 0, NULL); */ + kurt_irqs_in_progress--; + kurt_preempt_attempt(); + kurt_irqs_in_progress++; +#else + DSKI_EVENT(KURT_DEBUG_FAM, EVENT_IGNORE_IRQ_FOR_RT, 0, current->pid, 0, NULL); +#endif /* CONFIG_USER_MODE_DEVICE_DRIVER */ +#endif /* CONFIG_RT */ + +#ifdef CONFIG_USER_MODE_DEVICE_DRIVER + } else if (kurt_irqs_enabled && !preempted_task) { +#else + } else if (kurt_irqs_enabled) { +#endif + /* + * We will service this interrupt by executing its handler, + * whether we are already in another handler or not, as this + * matches the original Linux semantics. This means the kernel + * stack grows more than it might, so this may be a reason to + * change these semantics in the future, to note the pending + * interrupt, but not always start its handler, unless timing + * constraints say we should. FRIZZ + */ + int search_irq; + DSKI_EVENT(KURT_DEBUG_FAM, EVENT_SEARCHING_FOR_IRQ, 0, kurt_pending_irqs, 0, NULL); + /* There are 16 8259A IRQs. This does not account for APIC irqs. */ + /* We search through the pending interrupt bitfield and ignore + * the timer interrupt bit(assumed to be zero) since it was checked + * previously. + */ + for (search_irq = 1; kurt_pending_irqs && (search_irq < 16); search_irq++) { + if (test_and_clear_bit(search_irq, &kurt_pending_irqs)) { + if (test_bit(search_irq, &kurt_blocked_irqs)) { + set_bit(search_irq, &kurt_pending_irqs); + DSKI_EVENT(KURT_DEBUG_FAM, EVENT_BLOCKED_IRQ, 0, search_irq, 0, NULL); + } else { + DSKI_EVENT(KURT_DEBUG_FAM, EVENT_START_IRQ_HANDLER, 0, search_irq, 0, NULL); + irq = search_irq; + break; + } + } + } + + if (irq < 0 && softirq_pending(cpu)) { + DSKI_EVENT(KURT_DEBUG_FAM, EVENT_SELECT_SOFTIRQ, 0, kurt_pending_irqs, 0, NULL); + kurt_irq_exit(); + do_softirq(); + kurt_irq_enter(); + } + } else { + DSKI_EVENT(KURT_DEBUG_FAM, EVENT_SELECT_NO_IRQ, 0, kurt_pending_irqs, 0, NULL); + } + + if (irq >= 0) { + DSKI_EVENT(KURT_DEBUG_FAM, EVENT_SELECT_IRQ, 0, irq, 0, NULL); + /* Mark the interrupt handler as currently in progress and store + * the state of the software interrupt flag. + */ + set_bit(irq, &kurt_current_irqs); + kurt_irq_flags[irq] = kurt_irqs_enabled; + if (kurt_irqs_enabled) { + /* If interrupts were enabled, then we must disable them + * now because this is the normal expectation of the interrupt + * top-half code. + */ + kurt_irqs_enabled = 0; + preempt_disable(); + DSKI_EVENT(KURT_DEBUG_FAM, EVENT_CLI, 0, kurt_pending_irqs, 0, NULL); + DSKI_HIST_ENTER(KURT_DEBUG_FAM, HIST_CLI_STI); + } + } else { + /* DSKI_EVENT(KURT_DEBUG_FAM, EVENT_SELECT_NO_IRQ, 0, kurt_pending_irqs, 0, NULL); */ + /* FRIZZ - This variable is decremented here in order to avoid + * disabling and reenabling interrupts on a hardware level. It + * signifies the number of nested calls to do_delayed_IRQs. + */ + kurt_irqs_in_progress--; + } + kurt_irq_exit(); + return irq; +} + +/* This is the kurt irq module. It executes the decision function and + * the top halves that it chooses. It finishes when the decision + * function decides not to run a top half. + */ +asmlinkage unsigned int do_delayed_IRQs(void) +{ + /* + * We ack quickly, we don't want the irq controller + * thinking we're snobs just because some other CPU has + * disabled global interrupts (we have already done the + * INT_ACK cycles, it's too late to try to pretend to the + * controller that we aren't taking the interrupt). + * + * 0 return value means that this irq is already being + * handled by some other CPU. (or is disabled) + */ + int irq; + int cpu = smp_processor_id(); + irq_desc_t *desc; + struct irqaction * action; + unsigned int status; + struct pt_regs *regs; + + while ((irq = kurt_irq_decision()) >= 0) { + desc = irq_desc + irq; + regs = kurt_irq_regs; + kstat.irqs[cpu][irq]++; +#ifdef CONFIG_KURT_IRQ_MOD + kurt_irq_enter(); +#endif + spin_lock(&desc->lock); + DSKI_HIST_TO_GROUP_EXIT(KURT_DEBUG_FAM, HIST_DELAYED_IRQ, irq); + DSKI_HIST_TO_GROUP_ENTER(KURT_DEBUG_FAM, HIST_IRQ_OVERHEAD, irq); + DSKI_EVENT(KURT_DEBUG_FAM, EVENT_DELAYED_IRQ_START, 0, (unsigned long)irq, 0, NULL); + /* + REPLAY is when Linux resends an IRQ that was dropped earlier + WAITING is used by probe to mark irqs that are being tested + */ + status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING); + status |= IRQ_PENDING; /* we _want_ to handle it */ + + /* + * If the IRQ is disabled for whatever reason, we cannot + * use the action we have. + */ + action = NULL; + if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) { + action = desc->action; + status &= ~IRQ_PENDING; /* we commit to handling */ + status |= IRQ_INPROGRESS; /* we are handling it */ + } + desc->status = status; + + /* + * If there is no IRQ handler or it was disabled, exit early. + Since we set PENDING, if another processor is handling + a different instance of this same irq, the other processor + will take care of it. + */ + if (!action) + goto out; + + /* + * Edge triggered interrupts need to remember + * pending events. + * This applies to any hw interrupts that allow a second + * instance of the same irq to arrive while we are in do_IRQ + * or in the handler. But the code here only handles the _second_ + * instance of the irq, not the third or fourth. So it is mostly + * useful for irq hardware that does not mask cleanly in an + * SMP environment. + */ + for (;;) { + spin_unlock(&desc->lock); +#ifdef CONFIG_KURT_IRQ_MOD + kurt_irq_exit(); +#endif + handle_IRQ_event(irq, regs, action); +#ifdef CONFIG_KURT_IRQ_MOD + kurt_irq_enter(); +#endif + spin_lock(&desc->lock); + + if (!(desc->status & IRQ_PENDING)) + break; + desc->status &= ~IRQ_PENDING; + } + desc->status &= ~IRQ_INPROGRESS; + + out: + /* + * The ->end() handler has to deal with interrupts which got + * disabled while the handler was running. + */ + desc->handler->end(irq); + spin_unlock(&desc->lock); + clear_bit(irq, &kurt_current_irqs); + DSKI_EVENT(KURT_DEBUG_FAM, EVENT_DELAYED_IRQ_END, 0, (unsigned long)irq, 0, NULL); + DSKI_HIST_TO_GROUP_EXIT(KURT_DEBUG_FAM, HIST_IRQ_OVERHEAD, irq); + if (!kurt_irqs_enabled && kurt_irq_flags[irq]) + DSKI_HIST_EXIT(KURT_DEBUG_FAM, HIST_CLI_STI); + if (kurt_irqs_enabled != kurt_irq_flags[irq]) { + kurt_irqs_enabled = kurt_irq_flags[irq]; + DSKI_EVENT(KURT_DEBUG_FAM, EVENT_RESTORE_DIFF_FLAG, 0, kurt_irqs_enabled, 0, NULL); + if (kurt_irqs_enabled) + preempt_enable_no_resched(); + else + preempt_disable(); + } + kurt_irq_exit(); + } + + return 1; +} + + +/* + * do_IRQ handles all normal device IRQ's (the special + * SMP cross-CPU interrupts have their own specific + * handlers). + */ +asmlinkage unsigned int do_IRQ(struct pt_regs regs) +{ + int irq = regs.orig_eax & 0xff; /* high bits used in ret_from_ code */ + irq_desc_t *desc = irq_desc + irq; + + /* preempt_disable is carried out before do_IRQ is called */ + kurt_in_interrupt++; + DSKI_HIST_ENTER(KURT_PROFILE_FAM, HIST_HWIRQ_OVERHEAD); + DSKI_HIST_TO_GROUP_ENTER(KURT_DEBUG_FAM, HIST_DELAYED_IRQ, irq); + if (irq == TIMER_IRQ) { + DSKI_HIST_ENTER(KURT_PROFILE_FAM, HIST_RT_USER_OH); + DSKI_HIST_ENTER(KURT_PROFILE_FAM, HIST_RT_KERNEL_OH); + } + if (kurt_blocked_irqs || !kurt_irqs_enabled) + DSKI_HISTOGRAM(KURT_DEBUG_FAM, HIST_CS_INTERRUPTED, irq); + + spin_lock(&desc->lock); + /* Send EOI and mask this IRQ */ + desc->handler->ack(irq); + spin_unlock(&desc->lock); + set_bit(irq, &kurt_pending_irqs); + if (!kurt_irqs_in_progress) { + kurt_irq_regs = ®s; + } + kurt_irqs_in_progress++; + DSKI_HIST_EXIT_CYCLES(KURT_PROFILE_FAM, HIST_HWIRQ_OVERHEAD); + +#ifdef CONFIG_HIGHEST_PRIORITY_TIMER + if (irq != TIMER_IRQ) { + kurt_in_interrupt--; + hard_sti(); + } + do_delayed_IRQs(); + if (irq == TIMER_IRQ) { + kurt_in_interrupt--; + hard_sti(); + } +#else + kurt_in_interrupt--; + hard_sti(); + do_delayed_IRQs(); +#endif + return 1; } +/* ############################################################################ + * ############################################################################ + */ +#else + /* * do_IRQ handles all normal device IRQ's (the special * SMP cross-CPU interrupts have their own specific @@ -578,6 +1303,12 @@ struct irqaction * action; unsigned int status; +#ifdef CONFIG_DSTREAM_KURT_DEBUG + LOGN_HIST_TO_GROUP_EXIT(KURT_DEBUG_FAM, HIST_IRQ_INTERVAL, irq); + LOGN_EVENT(KURT_DEBUG_FAM, EVENT_DO_IRQ_ENTER, 0, (unsigned long)irq, 0, NULL); + LOGN_HIST_TO_GROUP_ENTER(KURT_DEBUG_FAM, HIST_IRQ_OVERHEAD, irq); +#endif + kstat.irqs[cpu][irq]++; spin_lock(&desc->lock); desc->handler->ack(irq); @@ -639,9 +1370,16 @@ if (softirq_pending(cpu)) do_softirq(); +#ifdef CONFIG_DSTREAM_KURT_DEBUG + LOGN_HIST_TO_GROUP_EXIT(KURT_DEBUG_FAM, HIST_IRQ_OVERHEAD, irq); + LOGN_EVENT(KURT_DEBUG_FAM, EVENT_DO_IRQ_EXIT, 0, (unsigned long)irq, 0, NULL); + LOGN_HIST_TO_GROUP_ENTER(KURT_DEBUG_FAM, HIST_IRQ_INTERVAL, irq); +#endif return 1; } +#endif /* CONFIG_KURT_IRQ_MOD */ + /** * request_irq - allocate an interrupt line * @irq: Interrupt line to allocate @@ -747,6 +1485,9 @@ return; desc = irq_desc + irq; +#ifdef CONFIG_KURT_IRQ_MOD + kurt_irq_enter(); +#endif spin_lock_irqsave(&desc->lock,flags); p = &desc->action; for (;;) { @@ -764,6 +1505,9 @@ desc->handler->shutdown(irq); } spin_unlock_irqrestore(&desc->lock,flags); +#ifdef CONFIG_KURT_IRQ_MOD + kurt_irq_exit(); +#endif #ifdef CONFIG_SMP /* Wait to make sure it's not being used on another CPU */ @@ -777,6 +1521,9 @@ } printk("Trying to free free IRQ%d\n",irq); spin_unlock_irqrestore(&desc->lock,flags); +#ifdef CONFIG_KURT_IRQ_MOD + kurt_irq_exit(); +#endif return; } } @@ -815,10 +1562,16 @@ for (i = NR_IRQS-1; i > 0; i--) { desc = irq_desc + i; +#ifdef CONFIG_KURT_IRQ_MOD + kurt_irq_enter(); +#endif spin_lock_irq(&desc->lock); if (!irq_desc[i].action) irq_desc[i].handler->startup(i); spin_unlock_irq(&desc->lock); +#ifdef CONFIG_KURT_IRQ_MOD + kurt_irq_exit(); +#endif } /* Wait for longstanding interrupts to trigger. */ @@ -833,6 +1586,9 @@ for (i = NR_IRQS-1; i > 0; i--) { desc = irq_desc + i; +#ifdef CONFIG_KURT_IRQ_MOD + kurt_irq_enter(); +#endif spin_lock_irq(&desc->lock); if (!desc->action) { desc->status |= IRQ_AUTODETECT | IRQ_WAITING; @@ -840,6 +1596,9 @@ desc->status |= IRQ_PENDING; } spin_unlock_irq(&desc->lock); +#ifdef CONFIG_KURT_IRQ_MOD + kurt_irq_exit(); +#endif } /* @@ -856,6 +1615,9 @@ irq_desc_t *desc = irq_desc + i; unsigned int status; +#ifdef CONFIG_KURT_IRQ_MOD + kurt_irq_enter(); +#endif spin_lock_irq(&desc->lock); status = desc->status; @@ -869,6 +1631,9 @@ val |= 1 << i; } spin_unlock_irq(&desc->lock); +#ifdef CONFIG_KURT_IRQ_MOD + kurt_irq_exit(); +#endif } return val; @@ -901,6 +1666,9 @@ irq_desc_t *desc = irq_desc + i; unsigned int status; +#ifdef CONFIG_KURT_IRQ_MOD + kurt_irq_enter(); +#endif spin_lock_irq(&desc->lock); status = desc->status; @@ -912,6 +1680,9 @@ desc->handler->shutdown(i); } spin_unlock_irq(&desc->lock); +#ifdef CONFIG_KURT_IRQ_MOD + kurt_irq_exit(); +#endif } up(&probe_sem); @@ -951,6 +1722,9 @@ irq_desc_t *desc = irq_desc + i; unsigned int status; +#ifdef CONFIG_KURT_IRQ_MOD + kurt_irq_enter(); +#endif spin_lock_irq(&desc->lock); status = desc->status; @@ -964,6 +1738,9 @@ desc->handler->shutdown(i); } spin_unlock_irq(&desc->lock); +#ifdef CONFIG_KURT_IRQ_MOD + kurt_irq_exit(); +#endif } up(&probe_sem); @@ -1000,12 +1777,18 @@ /* * The following block of code has to be executed atomically */ +#ifdef CONFIG_KURT_IRQ_MOD + kurt_irq_enter(); +#endif spin_lock_irqsave(&desc->lock,flags); p = &desc->action; if ((old = *p) != NULL) { /* Can't share interrupts unless both agree to */ if (!(old->flags & new->flags & SA_SHIRQ)) { spin_unlock_irqrestore(&desc->lock,flags); +#ifdef CONFIG_KURT_IRQ_MOD + kurt_irq_exit(); +#endif return -EBUSY; } @@ -1025,6 +1808,9 @@ desc->handler->startup(irq); } spin_unlock_irqrestore(&desc->lock,flags); +#ifdef CONFIG_KURT_IRQ_MOD + kurt_irq_exit(); +#endif register_irq_proc(irq); return 0; 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 Mon Feb 25 13:37:53 2002 +++ linux-work/arch/i386/kernel/setup.c Wed Nov 20 14:58:05 2002 @@ -76,6 +76,9 @@ /* * This file handles the architecture-dependent parts of initialization */ +/* Modified for UTIME: On-demand Microsecond Resolution Timers + * See Documentation/utime.txt for further details + */ #include #include @@ -96,6 +99,9 @@ #ifdef CONFIG_BLK_DEV_RAM #include #endif +#ifdef CONFIG_UTIME +#include +#endif #include #include #include @@ -2845,6 +2851,16 @@ seq_printf(m, "\nbogomips\t: %lu.%02lu\n\n", c->loops_per_jiffy/(500000/HZ), (c->loops_per_jiffy/(5000/HZ)) % 100); +#ifdef CONFIG_UTIME + seq_printf(m, "UTIME cps\t: %Lu time standard ticks per second\n" + "\t\t %Lu time standard ticks per jiffy\n" + "\t\t %Lu time standard ticks per subjiffy\n\n" + "\t\t %lu quotient\n\n", + utime_state.time_standard_per_sec, + utime_state.time_standard_per_jiffy, + utime_state.time_standard_per_subjiffy, + (unsigned long)utime_state.utime_quotient); +#endif return 0; } 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 Mon Feb 25 13:37:53 2002 +++ linux-work/arch/i386/kernel/time.c Mon Apr 21 16:02:33 2003 @@ -29,6 +29,9 @@ * Fixed a xtime SMP race (we need the xtime_lock rw spinlock to * serialize accesses to xtime/lost_ticks). */ +/* Modified for UTIME: On-demand Microsecond Resolution Timers + * See Documentation/utime.txt for further details + */ #include #include @@ -55,14 +58,70 @@ #include #include +#ifdef CONFIG_UTIME +#include +#endif + #include #include +#include /* * for x86_do_profile() */ #include +#ifdef CONFIG_TS_M486 +#define SC520_SWTMRMILLI 0XDFC60 +#define SC520_SWTMRCFG 0XDFC64 + +struct sc520_swtmrmilli_reg { + __u16 milli_cnt; + __u16 micro_cnt; +}; + +union swtmr_latch { + unsigned long latch_val; + struct sc520_swtmrmilli_reg count; +}; + +int timestamp_ready = 0; +unsigned long swtmrmilli; +unsigned long swtmrcfg; + +void __init time_standard_init(void) +{ + long flags; + + write_lock_irqsave(&xtime_lock, flags); + swtmrmilli = (unsigned long) ioremap (SC520_SWTMRMILLI, + sizeof(struct sc520_swtmrmilli_reg)); + swtmrcfg = (unsigned long) ioremap (SC520_SWTMRCFG, sizeof(unsigned long)); + + writew(0x0001, (unsigned long)swtmrcfg); + timestamp_ready = 1; + write_unlock_irqrestore(&xtime_lock, flags); +} + +cycles_t get_cycles_elan520(void) +{ + long flags; + union swtmr_latch latch; + cycles_t elan520_timestamp = 0; + static unsigned long long total_millis = 0; + + write_lock_irqsave(&xtime_lock, flags); + if (timestamp_ready) { + latch.latch_val = readl(swtmrmilli); + total_millis += latch.count.milli_cnt; + elan520_timestamp = (total_millis * 1000) + latch.count.micro_cnt; + write_unlock_irqrestore(&xtime_lock, flags); + return elan520_timestamp; + } + write_unlock_irqrestore(&xtime_lock, flags); + return 0; +} +#endif unsigned long cpu_khz; /* Detected as we calibrate the TSC */ @@ -118,7 +177,7 @@ extern spinlock_t i8259A_lock; -#ifndef CONFIG_X86_TSC +#if !defined(CONFIG_X86_TSC) && !defined(CONFIG_TS_M486) /* This function must be called with interrupts disabled * It was inspired by Steve McCanne's microtime-i386 for BSD. -- jrs @@ -255,7 +314,17 @@ #else +#ifdef CONFIG_UTIME +static unsigned long do_utime_gettimeoffset(void) +{ + time_standard_t offset = get_time_standard() - utime_state.cycles_at_last_jiffy; + return time_standard_to_subjiffies(offset); +} + +#define do_gettimeoffset() do_utime_gettimeoffset() +#else #define do_gettimeoffset() do_fast_gettimeoffset() +#endif #endif @@ -392,6 +461,7 @@ */ static inline void do_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) { + // No longer need to ack the 8259 chip. #ifdef CONFIG_X86_IO_APIC if (timer_ack) { /* @@ -413,6 +483,26 @@ co_cpu_write(CO_CPU_STAT,co_cpu_read(CO_CPU_STAT) & ~CO_STAT_TIMEINTR); #endif do_timer(regs); +#if defined(JACOB) && defined(CONFIG_X86_LOCAL_APIC) + /* When using the local APIC we don't need to worry about + * running do_timer_interrupt() during non-jiffies as the PIC + * is no longer programmed in one-shot mode. + */ +#else +#ifdef CONFIG_UTIME + /* jiffies_intr is set in internal_update_system_time in + * arch/i386/kernel/utime.c if a jiffie has passed. If so, we + * fall through to the rest of the standard timer interrupt + * code. It is also set in do_timer_wrapper in + * kernel/timer.c, which gets called before UTIME has been + * initialized. + */ + if (!utime_state.jiffies_intr) + return; + utime_state.jiffies_intr--; +#endif +#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 @@ -422,9 +512,11 @@ if (!user_mode(regs)) x86_do_profile(regs->eip); #else + if (!using_apic_timer) smp_local_timer_interrupt(regs); -#endif + +#endif /* CONFIG_X86_LOCAL_APIC */ /* * If we have an externally synchronized Linux clock, then update @@ -456,6 +548,7 @@ outb_p( irq|0x80, 0x61 ); /* reset the IRQ */ } #endif + } static int use_tsc; @@ -467,7 +560,28 @@ */ static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) { +#ifndef CONFIG_UTIME int count; +#endif + +#if defined(JACOB) && defined(CONFIG_DSTREAM_UTIME_DEBUG) && defined(CONFIG_X86_LOCAL_APIC) + int cpu = smp_processor_id(); + + LOGN_EVENT(UTIME_DEBUG_FAM, EVENT_ENTER_TIMER_INTERRUPT, 0, smp_processor_id(), sizeof(cpu), (void *)cpu ); +#endif + +#ifdef CONFIG_DSTREAM_KURT_CORRECT + LOGN_EVENT(KURT_CORRECT_FAM, EVENT_TIMER_INTERRUPT, 0, current->pid, 0, NULL); +#endif + +#ifdef CONFIG_DSTREAM_UTIME_CALIBRATION + LOGN_HIST_ENTER(UTIME_CALIBRATION_FAM, HIST_EVENT_DIFFERENCE); +#endif + +#ifdef CONFIG_DSTREAM_KURT_CALIBRATION + LOGN_HIST_ENTER(KURT_CALIBRATION_FAM, HIST_TIMER_INTERRUPT); + LOGN_HIST_ENTER(KURT_CALIBRATION_FAM, HIST_EVENT_OVERHEAD); +#endif /* * Here we are in the timer irq handler. We just have irqs locally @@ -478,6 +592,11 @@ */ write_lock(&xtime_lock); +#ifdef CONFIG_UTIME + utime_state.intr_expected = 0; +#endif + +#ifndef CONFIG_UTIME if (use_tsc) { /* @@ -506,11 +625,16 @@ count = ((LATCH-1) - count) * TICK_SIZE; delay_at_last_interrupt = (count + LATCH/2) / LATCH; } +#endif /* ! CONFIG_UTIME */ do_timer_interrupt(irq, NULL, regs); write_unlock(&xtime_lock); +#ifdef CONFIG_DSTREAM_KURT_CALIBRATION + LOGN_HIST_EXIT(KURT_CALIBRATION_FAM, HIST_TIMER_INTERRUPT); +#endif + } /* not static: needed by APM */ @@ -618,6 +742,7 @@ if (endlow <= CALIBRATE_TIME) goto bad_ctc; + __asm__("divl %2" :"=a" (endlow), "=d" (endhigh) :"r" (endlow), "0" (0), "1" (CALIBRATE_TIME)); diff -u -r -N -b --exclude-from=diff-exclude linux-ref/arch/i386/kernel/utime.c linux-work/arch/i386/kernel/utime.c --- linux-ref/arch/i386/kernel/utime.c Wed Dec 31 18:00:00 1969 +++ linux-work/arch/i386/kernel/utime.c Fri Apr 18 15:54:36 2003 @@ -0,0 +1,133 @@ +/* + * UTIME: On-demand Microsecond Resolution Timers + * ------------------------------------------------------- + * + * File: arch/i386/kernel/utime.c + * Copyright (C) 1999 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, Will Dinkel, + * Jacob Woltersdorf, Michael Frisbie + * + * 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 +#include +#include +#include +#include + +#if defined(JACOB) && defined(CONFIG_X86_LOCAL_APIC) +#include +#include +#include +#include +#include +#include +#endif + + +inline void arch_program_next_event(u32 new_latch_value) +{ +#if defined(JACOB) && defined(CONFIG_X86_LOCAL_APIC) + unsigned int cycles_value; +#ifdef CONFIG_DSTREAM_UTIME_DEBUG + int cpu = smp_processor_id(); +#endif + + DSKI_EVENT(UTIME_DEBUG_FAM, EVENT_APIC_PROG_NEXT_EVENT, 0, + new_latch_value, sizeof(cpu), (void *)cpu); + + /* Convert new_latch_value (subjiffies) to cycles_value (APIC + * cycles) */ + cycles_value = (utime_state.apic_time_standard_per_jiffy / + subjiffies_per_jiffy) * new_latch_value; + + DSKI_EVENT(UTIME_DEBUG_FAM, EVENT_APIC_LATCH_VALUE, 0, + cycles_value, sizeof(cpu), (void *)cpu); + + apic_write_around(APIC_TMICT, cycles_value / UTIME_APIC_DIVISOR); +#else + extern spinlock_t i8253_lock; + + /* convert to subjiffies */ + new_latch_value = TO_LATCH(new_latch_value); + spin_lock(&i8253_lock); + outb_p(new_latch_value & 0xff, 0x40); /* LSB */ + outb(new_latch_value >> 8, 0x40); /* MSB */ + spin_unlock(&i8253_lock); +#endif + + return; +} + + +#if defined(JACOB) && defined(CONFIG_X86_LOCAL_APIC) +void utime_setup_local_apic( void * not_used ) +{ + unsigned long lvtt_value; + + /* Switch local APIC to one-shot mode + */ + lvtt_value = (SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV) & (~APIC_LVT_TIMER_PERIODIC)) | LOCAL_TIMER_VECTOR; + apic_write_around(APIC_LVTT, lvtt_value); +} +#endif + + +/* The following routines work directly with the 82C54 programmable + * interval timer. See "82C54 Programmable Interval Timer" at + * ftp://download.intel.com/design/periphrl/datashts/23124406.pdf for + * more information. + * + * We don't need to use spin_lock_irq() for locking because these + * routines are called from change_timer_mode() in kernel/utime.c + * which has already cleared interrupts when it acquired the + * xtime_lock. + * + * APIC: + * This routine is called which neither the xtime_lock held, nor + * interrupts blocked. + */ +inline void arch_put_timer_in_oneshot_mode(void) +{ +#if defined(JACOB) && defined(CONFIG_X86_LOCAL_APIC) + /* Have every CPU in the system call the + * utime_setup_local_apic() routine. Setup this CPU then tell + * all others to. + * + * NOTE: + * smp_call_function() MUST NOT be used while interrupts are + * disabled. + */ + utime_setup_local_apic( NULL ); + smp_call_function( utime_setup_local_apic, NULL, 1, 1 ); +#else + extern spinlock_t i8253_lock; + + spin_lock(&i8253_lock); + outb_p(0x38, 0x43); /* binary, mode 4, LSB/MSB, ch 0 */ + spin_unlock(&i8253_lock); + + /* Wait for the timer chip to recover */ + udelay(4); +#endif +} + + diff -u -r -N -b --exclude-from=diff-exclude linux-ref/drivers/Makefile linux-work/drivers/Makefile --- linux-ref/drivers/Makefile Mon Feb 25 13:37:56 2002 +++ linux-work/drivers/Makefile Mon Mar 3 15:32:44 2003 @@ -8,7 +8,7 @@ mod-subdirs := dio mtd sbus video macintosh usb input telephony sgi ide \ message/i2o message/fusion scsi md ieee1394 pnp isdn atm \ - fc4 net/hamradio i2c acpi bluetooth + fc4 net/hamradio i2c acpi bluetooth kurt group_sched subdir-y := parport char block net sound misc media cdrom hotplug subdir-m := $(subdir-y) @@ -39,6 +39,10 @@ subdir-$(CONFIG_ISDN) += isdn subdir-$(CONFIG_ATM) += atm subdir-$(CONFIG_FC4) += fc4 +subdir-$(CONFIG_DSTREAM) += dstream +subdir-$(CONFIG_RT) += kurt +subdir-$(CONFIG_UTIME_CALIBRATION) += utime_calib +subdir-$(CONFIG_GROUP_SCHEDULING) += group_sched # CONFIG_HAMRADIO can be set without CONFIG_NETDEVICE being set -- ch subdir-$(CONFIG_HAMRADIO) += net/hamradio diff -u -r -N -b --exclude-from=diff-exclude linux-ref/drivers/char/pc_keyb.c linux-work/drivers/char/pc_keyb.c --- linux-ref/drivers/char/pc_keyb.c Fri Nov 9 16:01:21 2001 +++ linux-work/drivers/char/pc_keyb.c Tue Jan 21 15:39:40 2003 @@ -69,7 +69,11 @@ static int aux_reconnect = 0; #endif +#ifdef CONFIG_KURT_IRQ_MOD +static spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED_IRQ(KEYBOARD_IRQ); +#else static spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED; +#endif static unsigned char handle_kbd_event(void); /* used only by send_data - set by keyboard_interrupt */ 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 Fri Dec 21 11:41:54 2001 +++ linux-work/drivers/char/sysrq.c Wed Nov 20 14:58:05 2002 @@ -1,6 +1,6 @@ /* -*- linux-c -*- * - * $Id: sysrq.c,v 1.15 1998/08/23 14:56:41 mj Exp $ + * $Id: sysrq.c,v 1.1.1.1 2002/11/20 20:58:05 woltersd Exp $ * * Linux Magic System Request Key Hacks * @@ -32,6 +32,10 @@ #include +#ifdef CONFIG_UTIME +#include +#endif + extern void reset_vc(unsigned int); extern struct list_head super_blocks; @@ -97,7 +101,21 @@ action_msg: "Resetting", }; +#if defined(CONFIG_RT) || defined(CONFIG_RT_MODULE) +extern int real_time_mode; +/* KURT sysrq handler */ +static void sysrq_handle_kurt(int key, struct pt_regs *pt_regs, + struct kbd_struct *kbd, + struct tty_struct *tty) { + real_time_mode = 0; +} +static struct sysrq_key_op sysrq_kurt_op = { + handler: sysrq_handle_kurt, + help_msg: "changekernelto_N_ormalmode", + action_msg: "Changing kernel mode to normal", +}; +#endif /* SYNC SYSRQ HANDLERS BLOCK */ @@ -368,7 +386,11 @@ #endif /* l */ &sysrq_killall_op, /* m */ &sysrq_showmem_op, +#if defined(CONFIG_RT) || defined(CONFIG_RT_MODULE) +/* n */ &sysrq_kurt_op, +#else /* n */ NULL, +#endif /* o */ NULL, /* This will often be registered as 'Off' at init time */ /* p */ &sysrq_showregs_op, diff -u -r -N -b --exclude-from=diff-exclude linux-ref/drivers/dstream/Config.in linux-work/drivers/dstream/Config.in --- linux-ref/drivers/dstream/Config.in Wed Dec 31 18:00:00 1969 +++ linux-work/drivers/dstream/Config.in Wed Apr 23 13:06:57 2003 @@ -0,0 +1,15 @@ +# DSKI Family Kernel Configuration +mainmenu_option next_comment +comment 'DSKI Family Configuration' + bool "DSKI Correctness Tests" CONFIG_DSTREAM_DSKI_CORRECT + bool "Group Scheduling Tests" CONFIG_DSTREAM_GROUP_SCHED + bool "KURT Calibration Tests" CONFIG_DSTREAM_KURT_CALIBRATION + bool "KURT Correctness Tests" CONFIG_DSTREAM_KURT_CORRECT + bool "KURT Debugging Tests" CONFIG_DSTREAM_KURT_DEBUG + bool "KURT Profiling Tests" CONFIG_DSTREAM_KURT_PROFILE + bool "Triggered Logging" CONFIG_DSTREAM_TRIGGERED_LOG + bool "UTIME Calibration Tests" CONFIG_DSTREAM_UTIME_CALIBRATION + bool "UTIME Correctness Tests" CONFIG_DSTREAM_UTIME_CORRECT + bool "UTIME Debugging Tests" CONFIG_DSTREAM_UTIME_DEBUG + bool "UTIME Profiling Tests" CONFIG_DSTREAM_UTIME_PROFILE +endmenu diff -u -r -N -b --exclude-from=diff-exclude linux-ref/drivers/dstream/Makefile linux-work/drivers/dstream/Makefile --- linux-ref/drivers/dstream/Makefile Wed Dec 31 18:00:00 1969 +++ linux-work/drivers/dstream/Makefile Wed Nov 20 14:58:05 2002 @@ -0,0 +1,23 @@ +# +# Makefile for the kernel character device drivers. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now inherited from the +# parent makes.. +# + +O_TARGET := dstream.o + +export-objs := dstream_syms.o + +obj-y := datastream.o datastream_events.o datastream_objects.o \ + datastream_counters.o datastream_histograms.o dsd_skbuff.o + +obj-$(CONFIG_MODULES) += dstream_syms.o + +obj-$(CONFIG_DSTREAM_USAGE_CALC) += datastream_cpu_usage.o + +include $(TOPDIR)/Rules.make diff -u -r -N -b --exclude-from=diff-exclude linux-ref/drivers/dstream/datastream.c linux-work/drivers/dstream/datastream.c --- linux-ref/drivers/dstream/datastream.c Wed Dec 31 18:00:00 1969 +++ linux-work/drivers/dstream/datastream.c Thu May 8 14:59:17 2003 @@ -0,0 +1,1461 @@ +/* + * Copyright (C) 1999 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: Sachin Sheth, Yulia Wijata, Sean B House + * + * Please send bug-reports/suggestions/comments to dski@ittc.ukans.edu + * + * Further details about this project can be obtained at + * http://hegel.ittc.ukans.edu/projects/datastream/ + * + * + * + * Program : datastream.c + * Authors : Furquan Ansari + * Sachin Sheth + * Yulia Wijata + * Sean B House + * Mike Frisbie + * Description : Datastreams device driver + */ +#include +#include +#include + + +/* Calibrate dski_time_standard_per_sec if UTIME is not present. */ +#ifndef CONFIG_UTIME +unsigned long dski_time_standard_per_sec = 0; +unsigned long dski_time_standard_per_usec; +#endif + +/* This is just a generic global status field for a DSKI device. + * I didn't want to mess around with anything too much, but this + * really should be moved into a device specific struct. The + * start_flag or private fields of the open_dstream struct are + * potential candidates, but I really didn't want to mess around with + * too much. + * + * At the moment it's only used to enable/disable the pid specific + * logging, but I have a feeling it would be usefull for other things + * as well. + */ +unsigned int dski_status; + +#undef dprint +#ifdef dprint +#define DPRINTK(fmt, arg...) printk("dstream: ");printk(fmt, ## arg );printk("\n") +#else +#define DPRINTK(fmt, arg...) (void)(1) +#endif + +inline int active_read(struct file *, char *, size_t, loff_t *); +int add_to_qlist(int fam_id, int event_index, struct open_dstream *remove_dstream); +void remove_dstream_events(struct open_dstream *remove_dstream); + + +static int datastream_init(void); +int datastream_open(struct inode *, struct file *); +int datastream_close(struct inode *, struct file *); +void datastream_close_timer(unsigned long arg); +ssize_t datastream_read(struct file *, char *, size_t, loff_t *); +int datastream_ioctl(struct inode *, struct file *, unsigned int, unsigned long); + +#ifndef CONFIG_UTIME +void datastream_calibrate(void); +#endif + +#ifdef CONFIG_DSTREAM_CPU_USAGE +#include +#endif + +#ifdef CONFIG_DSTREAM_MEM_POOL +extern int alloc_dski_pool(void); +#endif + +/* + * All function pointers in a dstream that requests + * registration must be valid until next reboot. + */ + +/* + * FIXME: Named like a constant, but isn't. + */ +int PID_REGISTERED[MAX_DSTREAM]; + +/* + * FIXME: Unless I'm mistaken, we don't need to disable interrupts + * while using this lock. It's only used within the scheduler + * when updating each process' usage statistics, in the various + * related ioctl calls, and in the cpu_usage calculating kernel + * thread. + * + */ +rwlock_t pid_specific_lock = RW_LOCK_UNLOCKED; + +/* + * FIXME: + * This is poorly named. It contains the count of processes used for + * pid specific logging. I added a field to the dski_status field + * that is used to determine whether or not any processes are + * registered since this is static (I could have changed this but it + * needs to get all wrapped into a global struct anyway). If this + * happens, then the field in the dski_status variable is no longer + * needed. + */ +int pid_index = 0; + +/* This is the lock for the family table. It should be used + * when adding/removing families from the table. + */ +#ifdef CONFIG_KURT_IRQ_MOD +spinlock_t dstream_family_table_lock = SPIN_LOCK_UNLOCKED_IRQ(TIMER_IRQ); +#else +spinlock_t dstream_family_table_lock = SPIN_LOCK_UNLOCKED; +#endif + +/* + * Define the various operations for the pseudo-device driver + */ +static struct file_operations datastream_ops = { + NULL, /* owner */ + NULL, /* seek */ + datastream_read, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + datastream_ioctl, /* ioctl */ + NULL, /* mmap */ + datastream_open, /* open */ + NULL, /* flush */ + datastream_close, /* release */ + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* lock */ + NULL, /* readv */ + NULL /* writev */ +}; + + +static __init int datastream_init(void) +{ + int i; + int result = 0; + +#ifdef CONFIG_DSTREAM_MEM_POOL + result = alloc_dski_pool(); + if (result < 0) { + printk("Unable to allocate DSKI skbuff pool of %d pages.\n", + DSKI_POOL_SIZE); + return result; + } +#endif + + /* This is only used when devfs isn't installed */ + result = devfs_register_chrdev(DSTREAM_MAJOR_NUM, + DSTREAM_DEVICE_NAME, + &datastream_ops); + if (result < 0) { + printk("Unable to register datastream driver on Major %d\n", + DSTREAM_MAJOR_NUM); + return result; + } + + /* Register with devfs - does nothing if devfs isn't installed */ + devfs_register(NULL, DSTREAM_DEVICE_NAME, + DEVFS_FL_DEFAULT, + DSTREAM_MAJOR_NUM, 0, + S_IFCHR | S_IWUSR | S_IRUGO, + &datastream_ops, NULL); + + printk("Datastream Driver Registered\n"); + for(i = 0; i < MAX_DSTREAM; i++) + PID_REGISTERED[i] = -1; + + dski_status = DSKI_STATUS_CLEAR; + +#ifdef CONFIG_DSTREAM_USAGE_CALC + init_cpu_usage_calc(); +#endif + + /* Calibrate dski_time_standard_per_sec if UTIME is not present. */ +#ifndef CONFIG_UTIME + datastream_calibrate(); +#endif + return 1; +} + +/* This calibrate function is only used if UTIME is not present + */ +#ifndef CONFIG_UTIME +void __init datastream_calibrate() +{ + int i; + cycles_t first_time, next_time; + unsigned long first_jiffies; + + printk("Calibrating cpu "); + + i = 0; + + /* wait for "start of" clock tick */ + first_jiffies = jiffies; + while (first_jiffies == jiffies) + /* nothing */; + + /* Four second loop (last iteration is unrolled) + */ + first_time = get_cycles(); + while (i++ < 3) { + first_jiffies = jiffies; + while (jiffies - first_jiffies < HZ); + printk("."); + } + first_jiffies = jiffies; + while ((jiffies - first_jiffies) < HZ); + next_time = get_cycles(); + printk("."); + dski_time_standard_per_sec = (unsigned long) (((next_time - first_time) + 2) >> 2); + dski_time_standard_per_usec = dski_time_standard_per_sec / ((unsigned long) USEC_PER_SEC); + + printk(" %lu cycles per second.\n", dski_time_standard_per_sec); +} +#endif + + +/* + * datastream_open is called to open the file + * /dev/dstream. Perform all initial settings for the file descriptor here. + */ +int datastream_open(struct inode * inode, struct file * file) +{ + + struct open_dstream *dev = (struct open_dstream *) + kmalloc(sizeof(struct open_dstream), GFP_KERNEL); + + /* Did we get the memory? */ + if (!dev) return -ENOMEM; + + if (file->f_flags & O_NONBLOCK) { + printk("Opening non-blocking datastream device...\n"); + } + + DPRINTK("open()"); + + file->private_data = dev; + +#ifdef CONFIG_KURT_IRQ_MOD + dev->lock = SPIN_LOCK_UNLOCKED_IRQ(TIMER_IRQ); +#else + dev->lock = SPIN_LOCK_UNLOCKED; +#endif + dev->ecount = 0; +#ifdef CONFIG_HARI + dev->event_q = kmalloc(3*sizeof(struct event_queue), GFP_KERNEL); + dev->trigger_status = kmalloc(3*sizeof(int), GFP_KERNEL); + dev->buffercount = kmalloc(3*sizeof(int), GFP_KERNEL); + //dev->trigger_event_table = kmalloc(3*sizeof(int), GFP_KERNEL); + dev->buffercount[0] = 0; + dev->buffercount[1] = 0; + dev->trigger_event_table[0][0]=-1; + dev->trigger_event_table[0][1]=-1; + dev->start_flag_temp = LOGGING_INACTIVE; + dev->stream_mode = MODE_STREAM_ALWAYS; +#else + dev->buffercount = 0; +#endif + dev->maxbuffers = 0; + dev->maxcount = 0; + dev->start_flag = LOGGING_INACTIVE; + dev->source_type = UNDEFINED; + dev->timer_set = 0; + + return 0; +} + +/* Timer expiration routine: + * When timer expires, the logging for the user queue is disabled + * Housekeeping activities are carried out after unread events are + * returned to the user + */ +void datastream_close_timer (unsigned long arg) +{ + struct open_dstream *cur = (struct open_dstream *)arg; + long flags; + + spin_lock_irqsave(&cur->lock, flags); + cur->start_flag = LOGGING_INACTIVE; + cur->timer_set = 0; + spin_unlock_irqrestore(&cur->lock, flags); +#ifdef CONFIG_HARI + cur->start_flag_temp = LOGGING_INACTIVE; + wake_up_interruptible(&(cur->event_q[0].wait_queue)); + wake_up_interruptible(&(cur->event_q[1].wait_queue)); +#else + wake_up_interruptible(&(cur->event_q.wait_queue)); +#endif + return; +} + +/* Driver close routine: + * Every instance of the driver on normal/abnormal termination will + * call this routine + */ +int datastream_close(struct inode *inode, struct file * file) +{ + struct open_dstream *cur = (struct open_dstream *)file->private_data; + long flags; + + + spin_lock_irqsave(&cur->lock, flags); + remove_dstream_events(cur); + spin_unlock_irqrestore(&cur->lock, flags); + + if (cur->timer_set) + del_timer (&(cur->duration_timer)); + +#ifdef CONFIG_HARI + kfree(cur->event_q); + kfree(cur->trigger_status); + kfree(cur->buffercount); +#endif + + kfree(cur); + return 0; +} + + +/* Driver read routine: + * This routine will return the events logged by the driver + * Depending on the datastream to be read, the appropriate routine will be + * called to read the data source. + */ +ssize_t datastream_read(struct file * file, char * buffer, + size_t count, loff_t * offset) +{ + int error; + struct open_dstream *cur = (struct open_dstream *)file->private_data; + + DPRINTK("read()"); + + + /* printk ("datastream.c: source type = %d\n", cur->source_type); */ + + if (cur->source_type == ACTIVE_SOURCE) { + /* call read for active source */ + error = active_read(file, buffer, count, offset); + + } else { + /* FIXME: This could be generalized to permit reading + * more than one object per file descriptor, but it + * probably couldn't be done here, unless you gave a + * concatenated copy of every object associated with + * this dstream. Alternatively, through ioctl, the + * user could request objects by id. + */ + /* call the appropriate read function for the passive source */ + /* printk ("Going into cur->read\n"); */ + error = (cur->read)(file, buffer, count, offset); + /* printk ("Came from cur->read\n"); */ + } + + return error; +} + + +/* Driver ioctl routine: + * This services all the ioctl calls made to the driver + */ +int datastream_ioctl(struct inode *inode, struct file * file, + unsigned int cmd, unsigned long arg) +{ + int error = 0; + struct open_dstream *cur = (struct open_dstream *)file->private_data; + struct dstream_family *fam; + + union dstream_param temp_param; + /* Hist_Change */ + struct histogram_options *hist_ptr; + struct set_hist_params *hist_params; + struct get_hist_options read_get_hist_options; + struct histogram_options read_hist_options; + + switch (cmd) { + + case SET_SOURCE_TYPE: + /* Set the source type for the datastream driver. It + * will either be active, for events, or passive, for + * objects, counters, and histograms. */ + if (arg == ACTIVE_SOURCE) { + cur->source_type = ACTIVE_SOURCE; +#ifdef CONFIG_HARI + dsd_skb_queue_head_init(&(cur->event_q[0].que)); + dsd_skb_queue_head_init(&(cur->event_q[1].que)); + cur->curr_readq=&(cur->event_q[0]); + cur->curr_logq=&(cur->event_q[0]); + cur->event_q[0].lost = 0; + cur->event_q[1].lost = 0; + cur->skb[0] = dsd_alloc_skb(); + if (cur->skb[0]) { + dsd_skb_queue_tail (&cur->event_q[0].que, cur->skb[0]); + } else { + error = -ENOMEM; + break; + } + + cur->skb[1] = dsd_alloc_skb(); + if (cur->skb[1]) { + dsd_skb_queue_tail (&cur->event_q[1].que, cur->skb[1]); + } else { + error = -ENOMEM; + break; + } + cur->buffercount[0] = 1; + cur->buffercount[1] = 1; + cur->curr_log=0; + cur->curr_read=0; + cur->current_log=cur->skb[cur->curr_log]; + cur->current_read=cur->skb[cur->curr_read]; + cur->trigger_status[0]=NOT_PRESERVED; + cur->trigger_status[1]=NOT_PRESERVED; + init_waitqueue_head(&(cur->event_q[0].wait_queue)); + init_waitqueue_head(&(cur->event_q[1].wait_queue)); +#else + dsd_skb_queue_head_init(&(cur->event_q.que)); + cur->event_q.lost = 0; + cur->skb = dsd_alloc_skb(); + if (cur->skb) { + dsd_skb_queue_tail (&cur->event_q.que, cur->skb); + } else { + error = -ENOMEM; + break; + } + cur->buffercount = 1; + init_waitqueue_head(&(cur->event_q.wait_queue)); +#endif + } else if (arg == PASSIVE_SOURCE) { + cur->source_type = PASSIVE_SOURCE; + } else { + printk("ioctl: SET_SOURCE_TYPE: invalid option = %ld\n", arg); + error = -EINVAL; + } + break; + + case ADD_EVENT: + /* Enable logging for a particular event in a + * particular family. */ + if (cur->source_type != ACTIVE_SOURCE) { + printk("ioctl: ADD_EVENT: invalid operation \n"); + error = -EINVAL; + } else { + /* set this event's flag */ + if (FAM(arg) < NUM_FAMILIES) + fam = dstream_family_table[FAM(arg)]; + else + fam = NULL; + if (fam) { + error = add_to_qlist(FAM(arg), EVCAT(arg), cur); + if (!error) { + spin_lock_irq(&fam->lock); + fam->ev_flags = SET_EVENT(fam->ev_flags, EVCAT(arg)); + spin_unlock_irq(&fam->lock); + } + } else + error = -EFAULT; + + } + break; + +#ifdef CONFIG_HARI + case SET_TRIGGER: + /* Enable triggering for a particular event in a + * particular family. */ + if (cur->source_type != ACTIVE_SOURCE) { + printk("ioctl: SET_TRIGGER: invalid operation \n"); + error = -EINVAL; + } else { + /* set this event's flag */ + if (FAM(arg) < NUM_FAMILIES) + fam = dstream_family_table[FAM(arg)]; + else + fam = NULL; + if (fam) { + if( EVENT_ISSET(fam->ev_flags, EVCAT(arg)) ) { + cur->trigger_event_table[0][0]=FAM(arg); + cur->trigger_event_table[0][1]=EVCAT(arg); + } + } else + error = -EFAULT; + + } + break; +#endif + + case SET_BUFFER_COUNT: + /* Set the number of buffers worth of events that + * should be logged. Once all are full, disable event + * logging. This does NOT control the size of the + * skbuff pool as a whole, nor the number of skbuffs + * associated with an individual stream. + */ + if (cur->source_type != ACTIVE_SOURCE) { + printk("ioctl: SET_BUFFER_COUNT: invalid operation \n"); + error = -EINVAL; + } else if (cur->maxcount > 0) { + printk("ioctl: SET_BUFFER_COUNT: Cannot set both buffer and event count\n"); + error = -EINVAL; + } else { + spin_lock_irq(&cur->lock); + cur->maxbuffers = arg; + spin_unlock_irq(&cur->lock); + } + break; + +/* UPDATE:FRIZZ - Should I go ahead and remove this? */ + case SET_EVENT_COUNT: + /* printk ("ioctl: SET_EVENT_COUNT: max_events=%ld\n", arg); */ + if (cur->source_type != ACTIVE_SOURCE) { + printk("ioctl: SET_EVENT_COUNT: invalid operation \n"); + error = -EINVAL; + } else if (cur->maxbuffers > 0) { + printk("ioctl: SET_EVENT_COUNT: Cannot set both buffer and event count\n"); + error = -EINVAL; + } else { + spin_lock_irq(&cur->lock); + cur->maxcount = arg; + spin_unlock_irq(&cur->lock); + } + break; + + case START_LOGGING: + /* Enable event logging. */ + if (cur->source_type != ACTIVE_SOURCE) { + printk("ioctl: START_LOGGING: invalid operation \n"); + error = -EINVAL; + } else { + spin_lock_irq(&cur->lock); + cur->start_flag = LOGGING_ACTIVE; +#ifdef CONFIG_HARI + cur->start_flag_temp = LOGGING_ACTIVE; +#endif + spin_unlock_irq(&cur->lock); + } + break; + + case STOP_LOGGING: + if (cur->source_type != ACTIVE_SOURCE) { + printk("ioctl: STOP_LOGGING: invalid operation \n"); + error = -EINVAL; + } else { + spin_lock_irq(&cur->lock); + cur->start_flag = LOGGING_INACTIVE; +#ifdef CONFIG_HARI + cur->start_flag_temp = LOGGING_INACTIVE; +#endif + spin_unlock_irq(&cur->lock); + } + break; + + case SET_DURATION: + if (cur->source_type != ACTIVE_SOURCE) { + printk("ioctl: SET_EVENT_COUNT: invalid operation \n"); + error = -EINVAL; + } else { + spin_lock_irq(&cur->lock); + init_timer (&(cur->duration_timer)); + cur->duration_timer.expires = jiffies + (arg*HZ); + cur->duration_timer.data = (unsigned long) cur; + cur->duration_timer.function = datastream_close_timer; + cur->timer_set = 1; + spin_unlock_irq(&cur->lock); + add_timer (&(cur->duration_timer)); + } + break; + +#ifdef CONFIG_HARI + case SET_STREAM_MODE: + if (arg == MODE_STREAM_ALWAYS) + cur->stream_mode = MODE_STREAM_ALWAYS; + else if (arg == MODE_STREAM_ON_CONDITION) + cur->stream_mode = MODE_STREAM_ON_CONDITION; + else + error = -ENOMEM; + break; +#endif + + /* counter ioctl */ + case RESET_COUNTER: + /* Reset a counter to 0 and update its timestamp. */ + + if (FAM(arg) < NUM_FAMILIES) + fam = dstream_family_table[FAM(arg)]; + else + fam = NULL; + + if (fam==NULL) { + error = -EFAULT; + } else { + if (CNTRCAT(arg) < fam->counters_num) { + spin_lock_irq(&fam->lock); + fam->cntr_options[CNTRCAT(arg)].count = 0; + fam->cntr_options[CNTRCAT(arg)].timestamp = get_cycles(); + spin_unlock_irq(&fam->lock); + } else { + error = -EFAULT; + } + } + break; + + + case READ_COUNTER: + copy_from_user (&temp_param, (struct get_cnt_options *)arg, sizeof(struct get_cnt_options)); + + if ((FAM(temp_param.get_cnt.index) >= 0) && (FAM(temp_param.get_cnt.index) < NUM_FAMILIES)) + fam = dstream_family_table[FAM(temp_param.get_cnt.index)]; + else + fam = NULL; + + if (fam == NULL) { + error = -EFAULT; + } else { + if (CNTRCAT(temp_param.get_cnt.index) >= fam->counters_num) { + error = -EINVAL; + } else { + copy_to_user ((char *)temp_param.get_cnt.counter, + (char *)&fam->cntr_options[CNTRCAT(temp_param.get_cnt.index)], + sizeof(struct counter_options)); + } + } + break; + + case START_COUNTER: + /* Enable a particular counter. */ + if (FAM(arg) < NUM_FAMILIES) + fam = dstream_family_table[FAM(arg)]; + else + fam = NULL; + + if (fam == NULL) { + error = -EFAULT; + } else { + spin_lock_irq(&fam->lock); + fam->cntr_flags = SET_COUNTER(fam->cntr_flags, CNTRCAT(arg)); + spin_unlock_irq(&fam->lock); + } + break; + + case STOP_COUNTER: + if (FAM(arg) < NUM_FAMILIES) + fam = dstream_family_table[FAM(arg)]; + else + fam = NULL; + + if (fam == NULL) { + error = -EFAULT; + } else { + spin_lock_irq(&fam->lock); + fam->cntr_flags = CLEAR_COUNTER(fam->cntr_flags, CNTRCAT(arg)); + spin_unlock_irq(&fam->lock); + } + break; + + + /* object ioctls */ + + case SET_OBJECT: + if (FAM(arg) < NUM_FAMILIES) + fam = dstream_family_table[FAM(arg)]; + else + fam = NULL; + + if (fam == NULL) { + error = -EFAULT; + } else { + (cur->read) = (fam->obj_options[CNTRCAT(arg)].read); + } + break; + + /* Hist_Change */ + /* Implementing the ioctl() calls for the histogram */ + + case START_HISTOGRAM: + /* Enable logging to a particular histogram. */ + if (FAM(arg) < NUM_FAMILIES) + fam = dstream_family_table[FAM(arg)]; + else + fam = NULL; + + if (!fam) { + error = -EFAULT; + } else { + spin_lock_irq(&fam->lock); + fam->hist_flags = SET_HISTOGRAM(fam->hist_flags, + HISTCAT(arg)); + spin_unlock_irq(&fam->lock); + } + break; + + case STOP_HISTOGRAM: + /* Disable logging to a particular histogram. */ + if (FAM(arg) < NUM_FAMILIES) + fam = dstream_family_table[FAM(arg)]; + else + fam = NULL; + + if (fam == NULL) { + error = -EFAULT; + } else { + spin_lock_irq(&fam->lock); + fam->hist_flags = CLEAR_HISTOGRAM(fam->hist_flags, + HISTCAT(arg)); + spin_unlock_irq(&fam->lock); + error = 0; + } + break; + + case RESET_HISTOGRAM: + /* Reset a particular histogram. */ + if (FAM(arg) < NUM_FAMILIES) + fam = dstream_family_table[FAM(arg)]; + else + fam = NULL; + + if(fam == NULL) { + error = -EFAULT; + } else { + if (HISTCAT(arg) < fam->histogram_num) { + hist_ptr = &fam->hist_options[HISTCAT(arg)]; + /* Trying to reset a histogram + * which has not been initialised + */ + if(hist_ptr == NULL) { + error = -EFAULT; + } else { + spin_lock_irq(&fam->lock); + memset(hist_ptr->hist_array, 0, + (hist_ptr->num_buckets*hist_ptr->num_groups*sizeof(int))); + hist_ptr->status &= ~HIST_STATUS_DIRTY; + spin_unlock_irq(&fam->lock); + error = 0; + } + } else { + error = -EFAULT; + } + } + break; + + case SET_BUCKET_PARAMS: + hist_params = (struct set_hist_params*)kmalloc + (sizeof(struct set_hist_params),GFP_KERNEL); + + if (!hist_params) { + printk ("SET_BUCKET_PARAMS: Failed kmalloc\n"); + error = -ENOMEM; + break; + } + + copy_from_user(hist_params, (struct set_hist_params *)arg, + sizeof (struct set_hist_params)); + + if ((FAM(hist_params->hist_id) >= 0) && (FAM(hist_params->hist_id) < NUM_FAMILIES)) + fam = dstream_family_table[FAM(hist_params->hist_id)]; + else + fam = NULL; + + if(!fam) { + error = -EINVAL; + kfree(hist_params); + break; + } + + if ((HISTCAT(hist_params->hist_id) < fam->histogram_num) && + (hist_ptr = &fam->hist_options[HISTCAT(hist_params->hist_id)])) { + spin_lock_irq(&fam->lock); + hist_ptr->min = hist_params->lowerbound; + hist_ptr->max = hist_params->upperbound; + hist_ptr->lowerbound = hist_params->lowerbound; + hist_ptr->upperbound = hist_params->upperbound; + hist_ptr->num_buckets = hist_params->num_buckets+2; + hist_ptr->num_events = 0; + hist_ptr->status = HIST_STATUS_CLEAR; + hist_ptr->range = (hist_params->upperbound - + hist_params->lowerbound)/hist_params->num_buckets; + hist_ptr->num_groups = hist_params->num_groups+1; + hist_ptr->hist_array = (int *)kmalloc + (((hist_ptr->num_buckets*hist_ptr->num_groups) + +hist_params->num_groups)*sizeof(int), GFP_KERNEL); + if (hist_params->groups) + copy_from_user(&hist_ptr->hist_array[hist_ptr->num_buckets* + hist_ptr->num_groups], + hist_params->groups, + hist_params->num_groups*sizeof(int)); + spin_unlock_irq(&fam->lock); + + if(hist_ptr->hist_array == NULL) { + error = -ENOMEM; + printk("SET_BUCKET_PARAMS: Failed kmalloc\n"); + kfree(hist_params); + break; + } + spin_lock_irq(&fam->lock); + + memset(hist_ptr->hist_array, 0, + hist_ptr->num_buckets*hist_ptr->num_groups*sizeof(int)); + + if (fam->enter_value) + kfree(fam->enter_value); + fam->enter_value = (cycles_t*)kmalloc(hist_ptr->num_buckets * + hist_ptr->num_groups * + sizeof(cycles_t), + GFP_KERNEL); + if (!fam->enter_value) + error = -ENOMEM; + spin_unlock_irq(&fam->lock); + } else + error = -EINVAL; + + kfree(hist_params); + break; + + case READ_HISTOGRAM: + copy_from_user (&read_get_hist_options, + (struct get_hist_options *)arg, + sizeof(struct get_hist_options)); + + copy_from_user (&read_hist_options,(struct histogram_options*) + (((struct get_hist_options*)arg)->hist_ptr), + sizeof(struct histogram_options)); + + if ((FAM(read_get_hist_options.index) >= 0) && (FAM(read_get_hist_options.index) < NUM_FAMILIES)) + fam = dstream_family_table[FAM(read_get_hist_options.index)]; + else + fam = NULL; + + if (!fam) { + error = -EFAULT; + } else { + /* FIXME: Save the HISTCAT result into a local + * variable. UPDATE:FRIZZ - Do we need HISTCAT? + */ + if ((HISTCAT(read_get_hist_options.index) < 0) || + (HISTCAT(read_get_hist_options.index) >= fam->histogram_num)) { + error = -EINVAL; + } else { + /* We don't want to overwrite the hist_array pointer */ + copy_to_user (((struct get_hist_options*)arg)->hist_ptr, + &fam->hist_options[HISTCAT(read_get_hist_options.index)], + sizeof(struct histogram_options) - sizeof(int *)); + + copy_to_user (read_hist_options.hist_array, + fam->hist_options[HISTCAT(read_get_hist_options.index)].hist_array, + ((fam->hist_options[HISTCAT(read_get_hist_options.index)].num_buckets * + fam->hist_options[HISTCAT(read_get_hist_options.index)].num_groups) + + fam->hist_options[HISTCAT(read_get_hist_options.index)].num_groups-1) * + sizeof(int)); + error = 0; + } + } + break; + + /* FIXME: CONFIG_DSTREAM_USAGE_CALC + */ + case SET_PID: + { + int i, reg; + + write_lock( &pid_specific_lock ); + if (pid_index >= MAX_DSTREAM) { + write_unlock( &pid_specific_lock ); + return -EINVAL; + } + + reg = Is_Pid_Registered(arg); + if (reg < 0) { + + for (i=0; i < MAX_DSTREAM; i++) { + if (PID_REGISTERED[i] == -1) + { + PID_REGISTERED[i] = (int)arg; + pid_index++; +#ifdef CONFIG_DSTREAM_USAGE_CALC + process_lock( i ); + proc_usage.procs[i].entering = 0; + proc_usage.procs[i].leaving = 0; + proc_usage.procs[i].period_ticks = 0; + proc_usage.procs[i].last_usage = 0; + proc_usage.procs[i].last_jiffie = jiffies; + proc_usage.procs[i].prev_tsc = get_cycles(); + process_unlock( i ); +#endif + break; + } + } + +#ifdef CONFIG_DSTREAM_USAGE_CALC + if( (proc_usage.status & CPU_USAGE_ENABLED) && (dski_status & DSKI_STATUS_NO_PID_REGISTERED) ){ + /* cpu_usage thread was enabled but sleeping since no + * threads were registered. Wake 'em up. + */ + wake_up( &proc_usage.wait ); + } +#endif + + dski_status &= ~DSKI_STATUS_NO_PID_REGISTERED; + } else { + error = -EBUSY; + } + + /* FIXME: + For the moment it's simpler to put this here rather than + having it be really optimal and placing one after the above + "for" loop and the other in the "else" clause. + */ + write_unlock( &pid_specific_lock ); + + } + break; + + case RESET_PID: + { + int index; + + DPRINTK("reset_pid( %d ) called", (int)arg); + + write_lock( &pid_specific_lock ); + + index = Is_Pid_Registered( (int)arg ); + if( index >= 0 ){ + PID_REGISTERED[index] = -1; + DPRINTK("pid_index = %d", pid_index); + pid_index--; + DPRINTK("RESET_PID -> pid_index = %d", pid_index); +#ifdef CONFIG_DSTREAM_USAGE_CALC + process_lock( index ); + proc_usage.procs[index].entering = 0; + proc_usage.procs[index].leaving = 0; + proc_usage.procs[index].period_ticks = 0; + proc_usage.procs[index].last_usage = 0; + proc_usage.procs[index].last_jiffie = 0; + proc_usage.procs[index].prev_tsc = 0; + process_unlock( index ); +#endif + DPRINTK("RESET_PID checking if last process removed\n\tpid_index = %d", pid_index); + if (pid_index <= 0){ + pid_index = 0; // Sanity + DPRINTK("no pids registered, pid_index = %d", pid_index); + dski_status |= DSKI_STATUS_NO_PID_REGISTERED; + } + error = 0; + } + else{ + DPRINTK("reset_pid could not find %d", (int)arg); + error = -EINVAL; + } + write_unlock( &pid_specific_lock ); + + } + break; + + case PID_SPECIFIC_ENABLE: + { + /* FIXME: This needs to be protected? */ + dski_status = dski_status | DSKI_STATUS_PID_SPECIFIC; + } + break; + + case PID_SPECIFIC_DISABLE: + { + /* FIXME: This needs to be protected? */ + dski_status = dski_status & ~DSKI_STATUS_PID_SPECIFIC; + } + break; + + case UNREGISTER_ALL: + { + int index; + + write_lock( &pid_specific_lock ); + for( index = 0; index < MAX_DSTREAM; index++ ){ + PID_REGISTERED[index] = -1; + } + pid_index = 0; + DPRINTK("UNREGISTER_ALL -> pid_index = %d", pid_index); + + error = 0; +#ifdef CONFIG_DSTREAM_USAGE_CALC + /* Only makes sense to go ahead and clear the usage + * statistics as well. We don't disable the usage + * calculation, however, because the user may still want + * that active, they're just doing a bit of cleanup. + */ + write_lock( &proc_usage.lock ); + cpu_usage_reset(); + write_unlock( &proc_usage.lock ); +#endif + write_unlock( &pid_specific_lock ); + + /* FIXME: PROTECT? */ + dski_status |= DSKI_STATUS_NO_PID_REGISTERED; + } + break; + +#ifdef CONFIG_DSTREAM_USAGE_CALC + + case GET_CPU_USAGE: + { + int index; + copy_from_user(&temp_param, (struct query_cpu_usage *)arg, + sizeof(struct query_cpu_usage)); + + read_lock( &pid_specific_lock ); + + index = Is_Pid_Registered(temp_param.cpu_info.pid); + if (index >= 0) { + error = 0; + process_lock( index ); + temp_param.cpu_info.last_usage = + proc_usage.procs[index].last_usage; + temp_param.cpu_info.last_usage_f = + proc_usage.procs[index].last_usage_f; + process_unlock( index ); + + read_unlock( &pid_specific_lock ); + + + DPRINTK("(%d) usage is %lu.%lu", + temp_param.cpu_info.pid, + temp_param.cpu_info.last_usage, + temp_param.cpu_info.last_usage_f); + copy_to_user((struct query_cpu_usage *)arg, &temp_param, + sizeof(temp_param.cpu_info)); + } else { + /* In this case it's probably better to have an unlock + * here and inside the "if" because if we just place one + * at the end of this "case", we'll have to wait for a + * copy_to_user(). + */ + read_unlock( &pid_specific_lock ); + error = -EINVAL; + } + } + break; + + case CALC_USAGE: + { + DPRINTK("called CALC_USAGE"); + + write_lock( &proc_usage.lock ); + if( proc_usage.status & CPU_USAGE_ENABLED ){ + // Thread already running. + error = -EINPROGRESS; + } + else { + proc_usage.status |= CPU_USAGE_ENABLED; +#ifdef CONFIG_DSTREAM_USAGE_CALC_THREAD + if( !(dski_status & DSKI_STATUS_NO_PID_REGISTERED) ){ + /* Only bother waking up if there's work to be + * done. Should just go back to sleep anyway. + */ + wake_up( &proc_usage.wait ); + } +#else + calculate_cpu_usage(); +#endif + } + write_unlock( &proc_usage.lock ); + + } + break; + + case STOP_CALC_USAGE: + { + DPRINTK("called STOP_CALC_USAGE"); + + write_lock( &proc_usage.lock ); + proc_usage.status &= ~CPU_USAGE_ENABLED; + write_unlock( &proc_usage.lock ); + } + break; + + case RESET_USAGE: + { + DPRINTK("called RESET_USAGE"); + + write_lock( &proc_usage.lock ); + cpu_usage_reset(); + write_unlock( &proc_usage.lock ); + } + break; +#endif + + default: + DPRINTK("invalid ioctl() command: %d\n", cmd); + error = -EINVAL; + } + + return error; +} + +/* + * --------- functions to register, unregister & find dstreams ---------- + */ + +/* + * All function pointers in a dstream that requests + * registration must be valid until next reboot (even if + * that dstream is unregistered in between). Functions + * should check if the dstream is registered && sock is + * connected. If dstream is unregistered set sock state to + * unconnected. + */ + + +/* Register_family adds a new family to the datastream_family_table + * structure. Note that the family_id must be less than NUM_FAMILIES + * and the family_id cannot already be in use. + */ +int register_family(int family_id, struct dstream_family *fam) +{ + + if ((family_id >= NUM_FAMILIES) || (family_id < 0)) + return -EINVAL; + if (dstream_family_table[family_id]) + return -EBUSY; + /* FIXME:HARVIND - Grab family table lock, then grab + * the per-family lock. + */ + spin_lock_irq(&dstream_family_table_lock); + dstream_family_table[family_id] = fam; + spin_unlock_irq(&dstream_family_table_lock); + return 0; +} + +/* ------------------ functions to log events --------------------- */ + + +extern __inline__ struct dsd_sk_buff *dsd_skb_peek_tail(struct dsd_sk_buff_head *list_) +{ + struct dsd_sk_buff *list = (struct dsd_sk_buff *)list_; + return (list->prev != list) ? list->prev : NULL; +} + +/* + * Logging function. + * This will be called by the MACRO for a logging events. Appropriate + * memory is allocated for the event trace to be logged. The event trace + * will be available for reading after the skbuf is full or the required + * number of event traces have been collected. + */ + + +/* FIXME:HARVIND - Change logn_event's name to something more intuitive. + * This will involve changing the namespace scripts in scripts/dski as + * well as the dstream header files. + */ + +int logn_event(int fam_id, int event_category, unsigned long set_number, + int data_len, unsigned char *data) +{ + struct dstream_family *fam; + int space_needed = sizeof(struct event_s) + data_len; + struct open_dstream *cur; + struct llist *qlist; + int i; + cycles_t tsc = get_cycles(); + struct event_s *kev; + unsigned long flags; + + /* FIXME:HARVIND - Grab the family table lock. If the family id + * is invalid, then release it and return. Otherwise, grab the + * per-family lock, get the family pointer, release the family + * table lock, and keep the per-family lock until "fam" is + * no longer needed. + */ + if ((fam_id < 0) || (fam_id >= NUM_FAMILIES)) + return -EINVAL; + else + fam = dstream_family_table[fam_id]; + + if (!fam) return -EINVAL; + + /* Get the queue list for this event. + * This list containts the datastream queues that this event must + * be logged to. + */ +/* FIXME:HARVIND - Lock the family or possibly a new per-event lock here */ + qlist = fam->ev_options[event_category].q_list; + + /* Step through each element of the events qlist. + */ + for (i=0; iev_options[event_category].q_count; i++) { + + cur = qlist->dstream_ptr; +#ifdef CONFIG_HARI + if (cur->start_flag == LOGGING_ACTIVE && cur->start_flag_temp == LOGGING_ACTIVE) { +#else + if (cur->start_flag == LOGGING_ACTIVE) { +#endif + + DPRINTK("going into logging active\n"); + /* Event logging is on and this event specifically is + * to be logged + */ + + /* We are locking the open_dstream structure that we are logging an event + * to. This is because a datastream could be in the middle of adding/removing + * events or reading events. + */ + spin_lock_irqsave(&cur->lock, flags); + /* UPDATE:FRIZZ - The following comments may + * need to be moved/deleted + */ + + /* We arrive here when logging this event requires another + * skbuff to be added to this dstream. + */ + + /* If the maximum number of buffers have been allocated, then + * either reuse the oldest one or don't log the event + */ +#ifdef CONFIG_HARI + if (!(cur->current_log = dsd_skb_peek_tail(&cur->curr_logq->que)) || + (dsd_skb_tailroom(cur->current_log) < space_needed)) { + + if (cur->maxbuffers && (cur->buffercount[cur->curr_log] >= cur->maxbuffers)) { +#ifdef CONFIG_DSTREAM_REUSE_SKBUFFS + cur->current_log = dsd_harvest_skb(&(cur->curr_logq->que)); + cur->buffercount[cur->curr_log]--; +#else + cur->curr_logq->lost++; + spin_unlock_irqrestore(&cur->lock, flags); + return -ENOMEM; +#endif + } else + cur->current_log = dsd_alloc_skb(); + + if (cur->current_log) { + cur->buffercount[cur->curr_log]++; + dsd_skb_queue_tail(&cur->curr_logq->que, cur->current_log); + DPRINTK("New skb: Waking up from sleep\n"); + } else { + spin_unlock_irqrestore(&cur->lock, flags); + return -ENOMEM; + } + } + +#else // CONFIG_HARI + if (!(cur->skb = dsd_skb_peek_tail(&cur->event_q.que)) || + (dsd_skb_tailroom(cur->skb) < space_needed)) { + + if (cur->maxbuffers && (cur->buffercount >= cur->maxbuffers)) { +#ifdef CONFIG_DSTREAM_REUSE_SKBUFFS + cur->skb = dsd_harvest_skb(&(cur->event_q.que)); + cur->buffercount--; +#else + cur->event_q.lost++; + spin_unlock_irqrestore(&cur->lock, flags); + return -ENOMEM; +#endif + } else { + cur->skb = dsd_alloc_skb(); + + if (!cur->skb) { + +#ifdef CONFIG_DSTREAM_REUSE_SKBUFFS + cur->skb = dsd_harvest_skb(&(cur->event_q.que)); + cur->buffercount--; +#else + cur->event_q.lost++; + spin_unlock_irqrestore(&cur->lock, flags); + return -ENOMEM; +#endif + } + } + + if (cur->skb) { + cur->buffercount++; + dsd_skb_queue_tail(&cur->event_q.que, cur->skb); + DPRINTK("New skb: Waking up from sleep\n"); + } else { + spin_unlock_irqrestore(&cur->lock, flags); + return -ENOMEM; + } + } +#endif //CONFIG_HARI + + /* FIXME: Consider changing this to testing whether the event + * queue is empty directly, and then peeking at it inside the + * body of the if. We think this returns NULL if there is + * no skbuff on the queue, or it depends on the convention that + * there is always an skbuff here. + */ + /* size_needed is the size of the default event + * structure plus the size of the optional data. + */ + + /* the tail skb has the space required for this + * event, BUT do a put only for the standard event + * record, because we do a second one later in order + * to get a destination pointer in order to copy the + * optional data. + */ +#ifdef CONFIG_HARI + kev = (struct event_s *)dsd_skb_put(cur->current_log, sizeof(struct event_s)); +#else + kev = (struct event_s *)dsd_skb_put(cur->skb, sizeof(struct event_s)); +#endif + + DPRINTK("skb_put successful\n"); + + cur->ecount++; + kev->event_number = cur->ecount; + kev->family = fam_id; + kev->event_category = event_category; + kev->event_set_number = set_number; + kev->time_stamp = tsc; + kev->data_len = data_len; + + /* If there is optional data, then append it to the event + */ + if (data_len) { + kev->data = data; + + /* We have to do an explicit copy here, which is why + * we've used two dsd_skb_puts. Because it gives us + * the destination pointer for the copy. + */ +#ifdef CONFIG_HARI + memcpy(dsd_skb_put(cur->current_log, data_len), data, data_len); +#else + memcpy(dsd_skb_put(cur->skb, data_len), data, data_len); +#endif + } + + spin_unlock_irqrestore(&cur->lock, flags); + + /* Go to the next dstream using this event. + */ + qlist = qlist->next; + + /* Finally, wake up any readers sleeping because + * they were doing a blocking read on an empty + * dstream. + */ +#ifdef CONFIG_HARI + if(cur->stream_mode==MODE_STREAM_ALWAYS) + wake_up_interruptible(&(cur->curr_logq->wait_queue)); + if(fam_id==cur->trigger_event_table[0][0] && event_category==cur->trigger_event_table[0][1]) + datastream_trigger(fam_id,event_category); +#else + wake_up_interruptible(&(cur->event_q.wait_queue)); +#endif + } + } + return 0; +} + + + +int Is_Pid_Registered(int pid) +{ + int i; + + if( !(dski_status & DSKI_STATUS_NO_PID_REGISTERED) ){ + for(i = 0; i < MAX_DSTREAM; i++){ + if(PID_REGISTERED[i] == pid) + return i; + } + } + + return -1; +} + + +#ifdef CONFIG_DSTREAM_KURT_PROFILE +void datastream_trigger(int fam_id, int event_category) +{ + struct dstream_family *fam; + struct open_dstream *cur; + struct llist *qlist; + int i; + long flags; + + if ((fam_id < 0) || (fam_id >= NUM_FAMILIES)) + return; + else + fam = dstream_family_table[fam_id]; + + if (!fam) return; + + //printk("inside trigger"); + /* Get the queue list for this event. + * This list containts the datastream queues that this event must + * be logged to. + */ + qlist = fam->ev_options[event_category].q_list; + + /* Step through each element of the events qlist. + */ + for (i=0; iev_options[event_category].q_count; i++) { + + cur = qlist->dstream_ptr; + + spin_lock_irqsave(&cur->lock, flags); +#ifdef CONFIG_HARI + if(cur->curr_log==1 && cur->trigger_status[0]!=NOT_PRESERVED) { + cur->trigger_status[0]=BOTH_PRESERVED; + cur->trigger_status[1]=BOTH_PRESERVED; + } + else if(cur->curr_log==0 && cur->trigger_status[1]!=NOT_PRESERVED) { + cur->trigger_status[0]=BOTH_PRESERVED; + cur->trigger_status[1]=BOTH_PRESERVED; + } + else { + if(cur->curr_log==0) { + cur->trigger_status[0]=PRESERVED; + cur->curr_log=1; + } + else if(cur->curr_log==1) { + cur->trigger_status[1]=PRESERVED; + cur->curr_log=0; + } + } + + /* Finally, wake up any readers sleeping because + * they were doing a blocking read in the MODE_STREAM_ON_CONDITION + */ + if(cur->stream_mode==MODE_STREAM_ON_CONDITION) + wake_up_interruptible(&(cur->curr_logq->wait_queue)); + + if(cur->trigger_status[0]==BOTH_PRESERVED ||cur->trigger_status[1]==BOTH_PRESERVED) { + //stop logging temporarily + cur->start_flag_temp = LOGGING_INACTIVE; + } + else { + // toggle logging + cur->current_log=cur->skb[cur->curr_log]; + cur->curr_logq=&(cur->event_q[cur->curr_log]); + } +#else + cur->start_flag = LOGGING_INACTIVE; + cur->timer_set = 0; +#endif + spin_unlock_irqrestore(&cur->lock, flags); +#ifdef CONFIG_HARI + +#else + wake_up_interruptible(&(cur->event_q.wait_queue)); +#endif + } +} +#endif + +__initcall(datastream_init); diff -u -r -N -b --exclude-from=diff-exclude linux-ref/drivers/dstream/datastream_counters.c linux-work/drivers/dstream/datastream_counters.c --- linux-ref/drivers/dstream/datastream_counters.c Wed Dec 31 18:00:00 1969 +++ linux-work/drivers/dstream/datastream_counters.c Wed Nov 20 14:58:05 2002 @@ -0,0 +1,53 @@ +/* + * Copyright (C) 1999 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: Sachin Sheth, Yulia Wijata, Sean B House + * + * Please send bug-reports/suggestions/comments to dski@ittc.ukans.edu + * + * Further details about this project can be obtained at + * http://hegel.ittc.ukans.edu/projects/datastream/ + */ +/* + * Program : datastream_counters.c + * Author : Sachin Sheth + * Yulia Wijata + * Sean B House + * Description : Functions related to counters + * + */ + +#include +#include + +#define ESUCCESS 1 +#undef dprint + +extern struct dstream_family *dstream_family_table[NUM_FAMILIES]; + +int incr_count (int fam_id, int cntr_category, long howmuch) +{ + struct dstream_family *fam; + + fam = dstream_family_table[fam_id]; + if (fam == NULL) return -EINVAL; + + fam->cntr_options[cntr_category].count += howmuch; + fam->cntr_options[cntr_category].timestamp = get_cycles(); +#ifdef dprint + printk ("Count=%d: time=%11d.%06d\n", fam->cntr_options[cntr_category].count, + fam->cntr_options[cntr_category].timestamp.tv_sec, + fam->cntr_options[cntr_category].timestamp.tv_usec); +#endif + + + return ESUCCESS; +} diff -u -r -N -b --exclude-from=diff-exclude linux-ref/drivers/dstream/datastream_cpu_usage.c linux-work/drivers/dstream/datastream_cpu_usage.c --- linux-ref/drivers/dstream/datastream_cpu_usage.c Wed Dec 31 18:00:00 1969 +++ linux-work/drivers/dstream/datastream_cpu_usa