//------------------------------------------------------------------- // tsc.c // // This character driver allows application programs to access // the current value of the cpu's Time Stamp Counter register, // as if it were a file of length 8 bytes (64 bits). // // Note: Developed and tested with Linux kernel version 2.4.20. // // programmer: ALLAN CRUSE // written on: 03 APR 2003 // revised on: 03 APR 2003 -- to incorporate a kernel timer // revised on: 08 APR 2003 -- to incorporate 'poll()' method //------------------------------------------------------------------- #define __KERNEL__ #define MODULE #include // for init_module() #include // for get_ds(), set_fs() #include // for __NR_mknod, etc #include // for poll_table //<---- new #define SUCCESS 0 #define ACCESS 0666 static char modname[] = "polltsc"; //<---- new static char devname[] = "/dev/tsc"; static int my_major = 0; static loff_t filesize = 8; static DECLARE_WAIT_QUEUE_HEAD( my_wq ); static struct timer_list my_timer; static int ready; extern void *sys_call_table[]; static int (*my_mknod)( const char *, ... ); static int (*my_chmod)( const char *, ... ); static int (*my_unlink)( const char * ); static void my_timer_action( unsigned long readyptr ) { *(int*)readyptr = 1; wake_up_interruptible( &my_wq ); } static ssize_t my_read( struct file *file, char *buf, size_t count, loff_t *pos ); static loff_t my_llseek( struct file *file, loff_t offset, int whence ); static unsigned int my_poll( struct file *file, poll_table *wait ); //<---- new static struct file_operations my_fops = { owner: THIS_MODULE, llseek: my_llseek, read: my_read, ////////// poll: my_poll, //<---- new }; int init_module( void ) { dev_t dev_id; mode_t mode; printk( "<1>\nInstalling \'%s\' module\n", modname ); my_major = register_chrdev( 0, modname, &my_fops ); if ( my_major < 0 ) return -EBUSY; my_unlink = sys_call_table[ __NR_unlink ]; my_mknod = sys_call_table[ __NR_mknod ]; my_chmod = sys_call_table[ __NR_chmod ]; set_fs( get_ds() ); my_unlink( devname ); mode = S_IFCHR; dev_id = MKDEV( my_major, 0 ); my_mknod( devname, mode, dev_id ); mode = ACCESS; my_chmod( devname, mode ); init_timer( &my_timer ); my_timer.data = (unsigned long)&ready; my_timer.function = my_timer_action; my_timer.expires = 0; add_timer( &my_timer ); return SUCCESS; } void cleanup_module( void ) { int status = del_timer_sync( &my_timer ); unregister_chrdev( my_major, modname ); set_fs( get_ds() ); my_unlink( devname ); printk( "<1>Removing \'%s\' module\n", modname ); } MODULE_LICENSE("GPL"); static unsigned char tsc_value[8]; static ssize_t my_read( struct file *file, char *buf, size_t count, loff_t *pos ) { int more; if ( *pos >= filesize ) return -EINVAL; if ( *pos + count > filesize ) count = filesize - *pos; if ( !ready ) { interruptible_sleep_on( &my_wq ); if ( signal_pending( current ) ) return -EINTR; } ready = 0; mod_timer( &my_timer, jiffies + 5 * HZ ); asm(" rdtsc "); asm(" movl %eax, tsc_value+0 "); asm(" movl %edx, tsc_value+4 "); more = copy_to_user( buf, tsc_value + *pos, count ); if ( more ) return -EFAULT; *pos += count; return count; } static loff_t my_llseek( struct file *file, loff_t offset, int whence ) { loff_t newpos; switch( whence ) { case 0: // SEEK_SET newpos = offset; break; case 1: // SEEK_CUR newpos = file->f_pos + offset; break; case 2: // SEEK_END newpos = filesize + offset; break; default: // should never occur return -EINVAL; } if ( newpos < 0 ) return -EINVAL; file->f_pos = newpos; return newpos; } static unsigned int my_poll( struct file *file, poll_table *wait ) { unsigned int mask = 0; poll_wait( file, &my_wq, wait ); if ( ready ) mask |= ( POLLIN | POLLRDNORM ); return mask; }