www.pudn.com > Linux2410_kernel.rar > ilatcurve.c
/* * FILE NAME kernel/ilatcurve.c * * BRIEF MODULE DESCRIPTION * This file implements a sample based interrupt latency curve for Linux. * * Author: David Singleton * MontaVista Software, Inc. * support@mvista.com * * Copyright 2001 MontaVista Software Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 675 Mass Ave, Cambridge, MA 02139, USA. */ #include#include #include #include #include #include #include #ifdef CONFIG_SMP #include #endif unsigned long bucketlog[BUCKETS]; unsigned long maximum_off; unsigned int ilatency_start; unsigned int ilatency_stop; unsigned int __noinstrument clock_diff(unsigned int start, unsigned int stop) { #ifdef CONFIG_CLOCK_COUNTS_DOWN #ifdef CONFIG_24BIT_COUNTER if (stop < start) { stop = 0xffffff - stop; } #endif /* CONFIG_24BIT_COUNTER */ return (start - stop); #else /* CONFIG_CLOCK_COUNTS_DOWN */ #ifdef CONFIG_24BIT_COUNTER if (start < stop) { start = 0xffffff - start; } #endif /* CONFIG_24BIT_COUNTER */ return (stop - start); #endif /* CLOCK_COUNTS_DOWN */ } void __noinstrument inline interrupt_overhead_start() { readclock(ilatency_start); } void __noinstrument inline interrupt_overhead_stop() { readclock(ilatency_stop); } unsigned long total_ilat_samples; /* total number of samples collected */ asmlinkage void __noinstrument inthoff_logentry(unsigned diff) { unsigned sampletime = diff; sampletime /= BUCKET_SIZE; if (maximum_off < diff) { maximum_off = diff; } if (sampletime < 0) { bucketlog[0]++; } else if (sampletime > LAST_BUCKET) { bucketlog[LAST_BUCKET]++; } else { bucketlog[sampletime]++; } total_ilat_samples++; return; } /* * Writing to /proc/maxoff resets the counters */ static ssize_t __noinstrument ilatcurve_write_proc(struct file * file, const char * buf, size_t count, loff_t *ppos) { extern interrupt_latency_log_t latency; total_ilat_samples = 0; memset(&bucketlog, 0, sizeof(unsigned long) * BUCKETS); memset(&latency, 0, sizeof(interrupt_latency_log_t)); latency.logging = ON; return count; } static int g_read_completed = 0; static int __noinstrument ilatcurve_read_proc( char *page_buffer, char **my_first_byte, off_t virtual_start, int length, int *eof, void *data) { int my_buffer_offset = 0; char * const my_base = page_buffer; int i; if (virtual_start == 0) { /* * Just been opened so display the header information * also stop logging BUGBUG: stop logging while we are * reading, initialize the index numbers in the log array */ unsigned int usecs; g_read_completed = 0; usecs = clock_to_usecs(clock_diff(ilatency_start, ilatency_stop));; my_buffer_offset += sprintf(my_base+my_buffer_offset, "#%lu samples logged\n#timer measured %u ticks per usec.\n" "#interrupt overhead %u microseconds\n" "#maximum interrupt off time %u microseconds\n" "#usecs samples\n", total_ilat_samples, (unsigned)ticks_per_usec, usecs, maximum_off); } else if (g_read_completed == BUCKETS) { *eof = 1; /* BUGBUG: start logging again */ return 0; } /* dump the sample log on the screen */ for (i = 0; i < (BUCKETS-1); i++) { my_buffer_offset += sprintf(my_base + my_buffer_offset, "%5u\t%8lu\n", (i + 1) * BUCKET_SIZE, bucketlog[i]); g_read_completed++; } my_buffer_offset += sprintf(my_base + my_buffer_offset, "%5u\t%8lu(greater than this time)\n", i * BUCKET_SIZE, bucketlog[LAST_BUCKET]); g_read_completed++; *my_first_byte = page_buffer; return my_buffer_offset; } int __noinstrument __init ilatcurve_init(void) { struct proc_dir_entry *entry; readclock_init(); #ifndef CONFIG_PPC ticks_per_usec = TICKS_PER_USEC; #endif #ifdef CONFIG_SMP printk("Interrupt holdoff times are not supported on SMP systems.\n"); #else printk("Interrupt holdoff times measurement enabled at %u ticks per usec.\n", ticks_per_usec); /* * this is where each platform must turn the * delta 'ticks' into microseconds. Each architecture * does it differently. */ #ifdef CONFIG_PROC_FS entry = create_proc_read_entry("ilatcurve", S_IWUSR | S_IRUGO, NULL, ilatcurve_read_proc, 0); if (entry) { entry->write_proc = (write_proc_t *)ilatcurve_write_proc; } #endif #endif return 0; } __initcall(ilatcurve_init);