/* * Cyclictest-Kernel * (c) 2009 Stefan Agner */ #include /* Needed by all modules */ #include /* Needed for KERN_ALERT */ #include /* Scheduling */ #include /* Kernel Threading */ #include #include #include #include /* High Resolution Timers */ #include "cyclictest.h" #include "proc.h" static char *interval = "1000000"; unsigned long long intervalus = 1000000; module_param(interval, charp, 0); MODULE_PARM_DESC(interval, "The interval in microseconds, you can use m,s,M,h,d"); static char *duration = "0"; static unsigned long long durationus = 0; module_param(duration, charp, 0); MODULE_PARM_DESC(duration, "Limit testing time in microseconds, you can use m,s,M,h,d"); wait_queue_head_t latencyvalues_queue; int shutdown = 0; struct task_struct *timer_thread; static ktime_t interval_time; static ktime_t next; static ktime_t now; static ktime_t stop; static unsigned long diff; struct timer_stat *stat; int bufmsk = 0; /* * Timerthread */ static int *timerthread(void *data) { int ret; /* daemonize("cyclictest-kernel-thread"); allow_signal(SIGKILL); */ now = ktime_get(); /* Set stop time */ stop = ktime_add(now, ns_to_ktime(durationus*NSEC_PER_USEC)); next = ktime_add(now, interval_time); while(!shutdown && !kthread_should_stop()) { set_current_state(TASK_UNINTERRUPTIBLE); ret = schedule_hrtimeout(&next, HRTIMER_MODE_ABS); if(ret == -EINTR) { printk(KERN_INFO KLOG_PREFIX "Thread aborted\n"); break; } now = ktime_get(); stat->cycles++; /* Latency Statistic */ diff = ktime_to_us(ktime_sub(now, next)); stat->act = diff; stat->avg += diff; if (diff < stat->min && stat->cycles > 1) stat->min = diff; if (diff > stat->max) stat->max = diff; if(bufmsk) stat->values[stat->cycles & bufmsk] = diff; /* Wakeup proc call */ wake_up_interruptible_sync(&latencyvalues_queue); next = ktime_add(next, interval_time); /* Stop thread when duration is set and expired */ if(durationus && (ktime_to_ns(ktime_sub(now, stop)) >= 0)) { shutdown++; printk(KERN_INFO KLOG_PREFIX "Duration reached, reload module to start again\n"); } } return 0; } /* * parse an input value as a base10 value followed by an optional * suffix. The input value is presumed to be in microseconds, unless * followed by a modifier suffix: m=miliseconds, * s=seconds, M=minutes, h=hours, d=days * * the return value is a value in microseconds */ unsigned long long parse_time_string(char *val) { char *end; unsigned long long t = simple_strtoull(val, &end, 10); if (end) { switch (*end) { case 'm': t *= USEC_PER_MSEC; break; case 's': case 'S': t *= USEC_PER_SEC; break; case 'M': t *= (unsigned long long)60*USEC_PER_SEC; break; case 'h': case 'H': t *= (unsigned long long)60*60 * USEC_PER_SEC; break; case 'd': case 'D': t *= (unsigned long long)24*60*60 * USEC_PER_SEC; break; } } return t; } /* * Kernel Module initialization */ int __init cyclictest_init(void) { struct sched_param param= { .sched_priority = 80 } ; /* Waitqueue for proc latency values */ init_waitqueue_head(&latencyvalues_queue); /* Init Space for stat structure */ stat = kmalloc(sizeof(struct timer_stat), GFP_KERNEL); stat->cycles = 0; stat->cyclesread = 0; stat->min = 10000000; stat->avg = 0; stat->max = 0; stat->act = 0; stat->values = kmalloc(VALBUF_SIZE * sizeof(long), GFP_KERNEL); /* Set the cycle 0 to 0 (same behaviour in cyclictest) */ stat->values[0] = 0; bufmsk = VALBUF_SIZE - 1; if(proc_init()) return -1; printk(KERN_INFO KLOG_PREFIX "Proc initialized\n"); /* Set timer interval */ intervalus = parse_time_string(interval); interval_time = ns_to_ktime(intervalus*NSEC_PER_USEC); /* Parse duration time... */ durationus = parse_time_string(duration); /* Create Kernel Thread */ timer_thread = kthread_create((void *)timerthread, NULL, "cyclictest"); /* Set scheduling policy and priority */ sched_setscheduler(timer_thread, SCHED_FIFO, ¶m); /* Start the thread by waking it up */ wake_up_process(timer_thread); printk(KERN_INFO KLOG_PREFIX "Cyclictest loaded\n"); return 0; } /* * Module Cleanup Method */ void __exit cyclictest_cleanup(void) { if(!shutdown) kthread_stop(timer_thread); proc_exit(); printk(KERN_INFO KLOG_PREFIX "Cyclictest unloaded\n"); } /* Define init and cleanup functions */ module_init(cyclictest_init); module_exit(cyclictest_cleanup); /* * Module informations... */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Stefan Agner "); MODULE_DESCRIPTION("Cyclictest Module");