//------------------------------------------------------------------- // pgfstats.c // // This module installs a kernel function that intercepts all // the page-fault exceptions and records statistics which can // be viewed by reading the pseudo-file '/proc/pgfstats'. // // NOTE: Written and tested with Linux kernel version 2.4.20. // // programmer: ALLAN CRUSE // written on: 16 JAN 2003 //------------------------------------------------------------------- #define __KERNEL__ #define MODULE #include // for init_module() #include // for create_proc_read_entry() #define SUCCESS 0 #define PGF_ID 14 static char modname[] = "pgfstats"; static unsigned long reason[ 8 ]; static unsigned short oldidtr[3], newidtr[3]; static unsigned long long *oldidt, *newidt; static unsigned long kpage, isr_orig; static void pgf_intercept( unsigned long *tos ) { int error_code = tos[ 10 ] & 0x7; reason[ error_code ] += 1; } //------- PAGE-FAULT EXCEPTION-HANDLER --------// asmlinkage void isr_page_fault( void ); asm(" .text "); asm(" .type isr_page_fault, @function "); asm("isr_page_fault: "); asm(" pushal "); asm(" pushl %ds "); asm(" pushl %es "); // asm(" movl %ss, %eax "); asm(" movl %eax, %ds "); asm(" movl %eax, %es "); // asm(" pushl %esp "); asm(" call pgf_intercept "); asm(" addl $4, %esp "); // asm(" popl %es "); asm(" popl %ds "); asm(" popal "); asm(" jmp *isr_orig "); //-----------------------------------------------// static int my_proc_read( char *buf, char **start, off_t off, int count, int *eof, void *data ) { int i, len; len = 0; len += sprintf( buf+len, "\nPage-Faults Summary\n" ); len += sprintf( buf+len, "\nReason Occurrences\n" ); for (i = 0; i < 8; i++) { len += sprintf( buf+len, " %d%d%d:", (i>>2)&1, (i>>1)&1, i&1 ); len += sprintf( buf+len, " %8d ", reason[ i ] ); len += sprintf( buf+len, "\n" ); } len += sprintf( buf+len, "\n" ); *eof = 1; return len; } static void load_IDTR( void *regimage ) { asm(" lidt %0 " : : "m" (*(unsigned short*)regimage) ); } int init_module( void ) { unsigned long long gate_desc; printk( "<1>\nInstalling \'%s\' module\n", modname ); // allocate kernel memory for the new IDT if ( !( kpage = get_free_page( GFP_KERNEL ) ) ) return -ENOMEM; // initialize our new IDT and exception-handler asm(" sidt oldidtr ; sidt newidtr "); memcpy( newidtr+1, &kpage, sizeof( kpage ) ); oldidt = (unsigned long long*)(*(unsigned long*)(oldidtr+1)); newidt = (unsigned long long*)(*(unsigned long*)(newidtr+1)); memcpy( newidt, oldidt, 256 * sizeof( unsigned long long ) ); gate_desc = oldidt[ PGF_ID ]; gate_desc &= 0xFFFF00000000FFFF; gate_desc |= ( gate_desc >> 32 ); isr_orig = (unsigned long)gate_desc; gate_desc = (unsigned long long)isr_page_fault; gate_desc &= 0x00000000FFFFFFFF; gate_desc |= ( gate_desc << 32 ); gate_desc &= 0xFFFF00000000FFFF; gate_desc |= 0x00008E0000100000; newidt[ PGF_ID ] = gate_desc; // activate the new IDT load_IDTR( newidtr ); smp_call_function( load_IDTR, newidtr, 1, 1 ); // create the proc-file create_proc_read_entry( modname, 0, NULL, my_proc_read, NULL ); return SUCCESS; } void cleanup_module( void ) { remove_proc_entry( modname, NULL ); smp_call_function( load_IDTR, oldidtr, 1, 1 ); load_IDTR( oldidtr ); if ( kpage ) free_page( kpage ); printk( "<1>Removing \'%s\' module\n", modname ); } MODULE_LICENSE("GPL");