rt-tests/0000775001204700120500000000000012252162337013070 5ustar williamswilliamsrt-tests/src/0000700001204700120500000000000012252162340013635 5ustar williamswilliamsrt-tests/src/include/0000700001204700120500000000000012252162337015266 5ustar williamswilliamsrt-tests/src/include/rt-get_cpu.h0000600001204700120500000000215512252162337017515 0ustar williamswilliams#ifndef __RT_GET_CPU_H #define __RT_GET_CPU_H #include #include #include #include #include /* For SYS_xxx definitions */ #include #include #ifdef __NR_getcpu static inline int get_cpu_setup(void) { return 0; } static inline int get_cpu(void) { int c,s; /* Show the source of get_cpu */ #ifdef DEBUG fprintf(stderr, "__NR_getcpu\n"); #endif s = syscall(__NR_getcpu, &c, NULL, NULL); return (s == -1) ? s : c; } #elif defined(__GLIBC__) && defined(__GLIBC_MINOR__) \ && __GLIBC__>=2 && __GLIBC_MINOR__>=6 #include static inline int get_cpu_setup(void) { return 0; } static inline int get_cpu(void) { return sched_getcpu(); } #else extern int get_cpu_setup(void); extern int (*get_cpu)(void); extern int (*get_cpu_vdsop)(unsigned int *, unsigned int *, void *); static inline int getcpu_vdso(void) { unsigned int c,s; /* Show the source of get_cpu */ #ifdef DEBUG fprintf(stderr, "getcpu_vdso\n"); #endif s = get_cpu_vdsop(&c, NULL, NULL); return (s == -1) ? s : c; } #endif #endif /* __RT_GET_CPU_H */ rt-tests/src/include/rt-utils.h0000600001204700120500000000065512252162337017232 0ustar williamswilliams#ifndef __RT_UTILS_H #define __RT_UTILS_H #define _STR(x) #x #define STR(x) _STR(x) #define MAX_PATH 256 int check_privs(void); char *get_debugfileprefix(void); int mount_debugfs(char *); int get_tracers(char ***); int valid_tracer(char *); int setevent(char *event, char *val); int event_enable(char *event); int event_disable(char *event); int event_enable_all(void); int event_disable_all(void); #endif /* __RT_UTILS.H */ rt-tests/src/include/error.h0000600001204700120500000000064212252162337016574 0ustar williamswilliams#ifndef __ERROR_H #define __ERROR_H #include #include #include #include void err_exit(int err, char *fmt, ...); void err_msg(char *fmt, ...); void err_msg_n(int err, char *fmt, ...); void err_quit(char *fmt, ...); void info(char *fmt, ...); void warn(char *fmt, ...); void fatal(char *fmt, ...); void err_doit(int err, const char *fmt, va_list ap); #endif /* __ERROR_H */ rt-tests/src/include/rt-tests.h0000600001204700120500000000467412252162337017241 0ustar williamswilliams#ifndef __RT_TESTS_H__ #define __RT_TESTS_H__ #include #include #include #define __USE_GNU #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef SCHED_IDLE #define SCHED_IDLE 5 #endif #ifndef SCHED_NORMAL #define SCHED_NORMAL SCHED_OTHER #endif /* Ugly, but .... */ #define gettid() syscall(__NR_gettid) #define sigev_notify_thread_id _sigev_un._tid #ifdef __UCLIBC__ #define MAKE_PROCESS_CPUCLOCK(pid, clock) \ ((~(clockid_t) (pid) << 3) | (clockid_t) (clock)) #define CPUCLOCK_SCHED 2 static int clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *req, struct timespec *rem) { if (clock_id == CLOCK_THREAD_CPUTIME_ID) return -EINVAL; if (clock_id == CLOCK_PROCESS_CPUTIME_ID) clock_id = MAKE_PROCESS_CPUCLOCK (0, CPUCLOCK_SCHED); return syscall(__NR_clock_nanosleep, clock_id, flags, req, rem); } static int sched_setaffinity(pid_t pid, unsigned int cpusetsize, cpu_set_t *mask) { return -EINVAL; } static void CPU_SET(int cpu, cpu_set_t *set) { } static void CPU_ZERO(cpu_set_t *set) { } #else extern int clock_nanosleep(clockid_t __clock_id, int __flags, __const struct timespec *__req, struct timespec *__rem); #endif #define USEC_PER_SEC 1000000 #define NSEC_PER_SEC 1000000000 #define HIST_MAX 1000000 /* Struct to transfer parameters to the thread */ struct thread_param { int prio; int policy; int mode; int timermode; int signal; int clock; unsigned long max_cycles; struct thread_stat *stats; int bufmsk; unsigned long interval; int cpu; }; /* Struct for statistics */ struct thread_stat { unsigned long cycles; unsigned long cyclesread; long min; long max; long act; double avg; long *values; long *hist_array; pthread_t thread; int threadstarted; int tid; long reduce; long redmax; long cycleofmax; long hist_overflow; }; enum kernelversion { KV_NOT_26, /* not a 2.6 kernel */ KV_26_LT18, /* less than 2.6.18 */ KV_26_LT24, /* less than 2.6.24 */ KV_26_LT28, /* less than 2.6.28 */ KV_26_CURR, /* 2.6.28+ */ }; enum kernelversion check_kernel(void); enum { ERROR_GENERAL = -1, ERROR_NOTFOUND = -2, }; #define TIMER_RELTIME 0 #endif rt-tests/src/include/pip_stress.h0000600001204700120500000000231212252162337017632 0ustar williamswilliams#ifndef __PIP_STRESS_H #define __PIP_STRESS_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include "error.h" void low(pid_t pid); /* low priority process */ void medium(void); /* medium priority process */ void high(pid_t pid); /* high priority process */ void init_state(void); void *mmap_page(void); long process_shared_mutex_available(void); void Pthread_mutexattr_init(pthread_mutexattr_t *attr); void Pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared); void Pthread_mutexattr_setprotocol(pthread_mutexattr_t *attr, int protocol); void Pthread_mutex_init(pthread_mutex_t *mutex, pthread_mutexattr_t *attr); void Pthread_mutex_lock(pthread_mutex_t *mutex); void Pthread_mutex_unlock(pthread_mutex_t *mutex); void init_shared_pthread_mutex(pthread_mutex_t *mutex, int protocol, int policy); int set_rt_prio(pid_t pid, int prio, int policy); int get_rt_prio(pid_t pid); #define PROTRW PROT_READ|PROT_WRITE #define MMAP_FLAGS MAP_SHARED|MAP_ANONYMOUS #endif /* __PIP_STRESS_H */ rt-tests/src/signaltest/0000700001204700120500000000000012252162337016020 5ustar williamswilliamsrt-tests/src/signaltest/signaltest.c0000600001204700120500000002273212252162337020351 0ustar williamswilliams/* * RT signal roundtrip test software * * (C) 2007 Thomas Gleixner * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License Version * 2 as published by the Free Software Foundation; * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rt-utils.h" #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) /* Ugly, but .... */ #define gettid() syscall(__NR_gettid) #define USEC_PER_SEC 1000000 #define NSEC_PER_SEC 1000000000 /* Must be power of 2 ! */ #define VALBUF_SIZE 16384 /* Struct to transfer parameters to the thread */ struct thread_param { int id; int prio; int signal; unsigned long max_cycles; struct thread_stat *stats; int bufmsk; }; /* Struct for statistics */ struct thread_stat { unsigned long cycles; unsigned long cyclesread; long min; long max; long act; double avg; long *values; pthread_t thread; pthread_t tothread; int threadstarted; int tid; }; static int shutdown; static int tracelimit = 0; static int oldtrace = 0; static inline void tsnorm(struct timespec *ts) { while (ts->tv_nsec >= NSEC_PER_SEC) { ts->tv_nsec -= NSEC_PER_SEC; ts->tv_sec++; } } static inline long calcdiff(struct timespec t1, struct timespec t2) { long diff; diff = USEC_PER_SEC * ((int) t1.tv_sec - (int) t2.tv_sec); diff += ((int) t1.tv_nsec - (int) t2.tv_nsec) / 1000; return diff; } /* * signal thread * */ void *signalthread(void *param) { struct thread_param *par = param; struct sched_param schedp; sigset_t sigset; struct timespec before, after; struct thread_stat *stat = par->stats; int policy = par->prio ? SCHED_FIFO : SCHED_OTHER; int stopped = 0; int first = 1; if (tracelimit) { system("echo 1 > /proc/sys/kernel/trace_all_cpus"); system("echo 1 > /proc/sys/kernel/trace_enabled"); system("echo 1 > /proc/sys/kernel/trace_freerunning"); system("echo 0 > /proc/sys/kernel/trace_print_at_crash"); system("echo 1 > /proc/sys/kernel/trace_user_triggered"); system("echo -1 > /proc/sys/kernel/trace_user_trigger_irq"); system("echo 0 > /proc/sys/kernel/trace_verbose"); system("echo 0 > /proc/sys/kernel/preempt_thresh"); system("echo 0 > /proc/sys/kernel/wakeup_timing"); system("echo 0 > /proc/sys/kernel/preempt_max_latency"); } stat->tid = gettid(); sigemptyset(&sigset); sigaddset(&sigset, par->signal); sigprocmask(SIG_BLOCK, &sigset, NULL); memset(&schedp, 0, sizeof(schedp)); schedp.sched_priority = par->prio; sched_setscheduler(0, policy, &schedp); stat->threadstarted++; if (tracelimit) { if (oldtrace) gettimeofday(0,(struct timezone *)1); else prctl(0, 1); } clock_gettime(CLOCK_MONOTONIC, &before); while (!shutdown) { struct timespec now; long diff; int sigs; if (sigwait(&sigset, &sigs) < 0) goto out; clock_gettime(CLOCK_MONOTONIC, &after); /* * If it is the first thread, sleep after every 16 * round trips. */ if (!par->id && !(stat->cycles & 0x0F)) usleep(10000); /* Get current time */ clock_gettime(CLOCK_MONOTONIC, &now); pthread_kill(stat->tothread, SIGUSR1); /* Skip the first cycle */ if (first) { first = 0; before = now; continue; } diff = calcdiff(after, before); before = now; if (diff < stat->min) stat->min = diff; if (diff > stat->max) stat->max = diff; stat->avg += (double) diff; if (!stopped && tracelimit && (diff > tracelimit)) { stopped++; if (oldtrace) gettimeofday(0,0); else prctl(0, 0); shutdown++; } stat->act = diff; stat->cycles++; if (par->bufmsk) stat->values[stat->cycles & par->bufmsk] = diff; if (par->max_cycles && par->max_cycles == stat->cycles) break; } out: /* switch to normal */ schedp.sched_priority = 0; sched_setscheduler(0, SCHED_OTHER, &schedp); stat->threadstarted = -1; return NULL; } /* Print usage information */ static void display_help(void) { printf("signaltest V %1.2f\n", VERSION_STRING); printf("Usage:\n" "signaltest \n\n" "-b USEC --breaktrace=USEC send break trace command when latency > USEC\n" "-l LOOPS --loops=LOOPS number of loops: default=0(endless)\n" "-p PRIO --prio=PRIO priority of highest prio thread\n" "-q --quiet print only a summary on exit\n" "-t NUM --threads=NUM number of threads: default=2\n" "-m --mlockall lock current and future memory allocations\n" "-v --verbose output values on stdout for statistics\n" " format: n:c:v n=tasknum c=count v=value in us\n"); exit(0); } static int priority; static int num_threads = 2; static int max_cycles; static int verbose; static int quiet; static int lockall = 0; /* Process commandline options */ static void process_options (int argc, char *argv[]) { int error = 0; for (;;) { int option_index = 0; /** Options for getopt */ static struct option long_options[] = { {"breaktrace", required_argument, NULL, 'b'}, {"loops", required_argument, NULL, 'l'}, {"priority", required_argument, NULL, 'p'}, {"quiet", no_argument, NULL, 'q'}, {"threads", required_argument, NULL, 't'}, {"verbose", no_argument, NULL, 'v'}, {"mlockall", no_argument, NULL, 'm'}, {"help", no_argument, NULL, '?'}, {NULL, 0, NULL, 0} }; int c = getopt_long (argc, argv, "b:c:d:i:l:np:qrsmt:v", long_options, &option_index); if (c == -1) break; switch (c) { case 'b': tracelimit = atoi(optarg); break; case 'l': max_cycles = atoi(optarg); break; case 'p': priority = atoi(optarg); break; case 'q': quiet = 1; break; case 't': num_threads = atoi(optarg); break; case 'm': lockall = 1; break; case 'v': verbose = 1; break; case '?': error = 1; break; } } if (priority < 0 || priority > 99) error = 1; if (num_threads < 2) error = 1; if (error) display_help (); } static void check_kernel(void) { size_t len; char ver[256]; int fd, maj, min, sub; fd = open("/proc/version", O_RDONLY, 0666); len = read(fd, ver, 255); close(fd); ver[len-1] = 0x0; sscanf(ver, "Linux version %d.%d.%d", &maj, &min, &sub); if (maj == 2 && min == 6 && sub < 18) oldtrace = 1; } static void sighand(int sig) { shutdown = 1; } static void print_stat(struct thread_param *par, int index, int verbose) { struct thread_stat *stat = par->stats; if (!verbose) { if (quiet != 1) { printf("T:%2d (%5d) P:%2d C:%7lu " "Min:%7ld Act:%5ld Avg:%5ld Max:%8ld\n", index, stat->tid, par->prio, stat->cycles, stat->min, stat->act, stat->cycles ? (long)(stat->avg/stat->cycles) : 0, stat->max); } } else { while (stat->cycles != stat->cyclesread) { long diff = stat->values[stat->cyclesread & par->bufmsk]; printf("%8d:%8lu:%8ld\n", index, stat->cyclesread, diff); stat->cyclesread++; } } } int main(int argc, char **argv) { sigset_t sigset; int signum = SIGUSR1; struct thread_param *par; struct thread_stat *stat; int i, ret = -1; if (check_privs()) exit(-1); process_options(argc, argv); /* lock all memory (prevent paging) */ if (lockall) if (mlockall(MCL_CURRENT|MCL_FUTURE) == -1) { perror("mlockall"); goto out; } check_kernel(); sigemptyset(&sigset); sigaddset(&sigset, signum); sigprocmask (SIG_BLOCK, &sigset, NULL); signal(SIGINT, sighand); signal(SIGTERM, sighand); par = calloc(num_threads, sizeof(struct thread_param)); if (!par) goto out; stat = calloc(num_threads, sizeof(struct thread_stat)); if (!stat) goto outpar; for (i = 0; i < num_threads; i++) { if (verbose) { stat[i].values = calloc(VALBUF_SIZE, sizeof(long)); if (!stat[i].values) goto outall; par[i].bufmsk = VALBUF_SIZE - 1; } par[i].id = i; par[i].prio = priority; #if 0 if (priority) priority--; #endif par[i].signal = signum; par[i].max_cycles = max_cycles; par[i].stats = &stat[i]; stat[i].min = 1000000; stat[i].max = -1000000; stat[i].avg = 0.0; stat[i].threadstarted = 1; pthread_create(&stat[i].thread, NULL, signalthread, &par[i]); } while (!shutdown) { int allstarted = 1; for (i = 0; i < num_threads; i++) { if (stat[i].threadstarted != 2) allstarted = 0; } if (!allstarted) continue; for (i = 0; i < num_threads - 1; i++) stat[i].tothread = stat[i+1].thread; stat[i].tothread = stat[0].thread; break; } pthread_kill(stat[0].thread, signum); while (!shutdown) { char lavg[256]; int fd, len, allstopped = 0; if (!verbose && !quiet) { fd = open("/proc/loadavg", O_RDONLY, 0666); len = read(fd, &lavg, 255); close(fd); lavg[len-1] = 0x0; printf("%s \n\n", lavg); } print_stat(&par[0], 0, verbose); if(max_cycles && stat[0].cycles >= max_cycles) allstopped++; usleep(10000); if (shutdown || allstopped) break; if (!verbose && !quiet) printf("\033[%dA", 3); } ret = 0; outall: shutdown = 1; usleep(50000); if (quiet) quiet = 2; for (i = 0; i < num_threads; i++) { if (stat[i].threadstarted > 0) pthread_kill(stat[i].thread, SIGTERM); if (stat[i].threadstarted) { pthread_join(stat[i].thread, NULL); if (quiet) print_stat(&par[i], i, 0); } if (stat[i].values) free(stat[i].values); } free(stat); outpar: free(par); out: if (lockall) munlockall(); exit(ret); } rt-tests/src/svsematest/0000700001204700120500000000000012252162337016041 5ustar williamswilliamsrt-tests/src/svsematest/svsematest.80000600001204700120500000000516012252162337020334 0ustar williamswilliams.TH "svsematest" "8" "0.1" "" "" .SH "NAME" .LP \fBsvsematest\fR \- Start two threads or fork two processes and measure the latency of SYSV semaphores .SH "SYNTAX" .LP svsematest [-a|-a PROC] [-b USEC] [-d DIST] [-f] [-i INTV] [-l loops] [-p PRIO] [-t|-t NUM] .br .SH "DESCRIPTION" .LP The program \fBsvsematest\fR starts two threads or, optionally, forks two processes that are synchronized via SYSV semaphores and measures the latency between releasing a semaphore on one side and getting it on the other side. .SH "OPTIONS" .TP .B \-a, \-\-affinity[=PROC] Run on procesor number PROC. If PROC is not specified, run on current processor. .TP .B \-b, \-\-breaktrace=USEC Send break trace command when latency > USEC. This is a debugging option to control the latency tracer in the realtime preemption patch. It is useful to track down unexpected large latencies of a system. .TP .B \-d, \-\-distance=DIST Set the distance of thread intervals in microseconds (default is 500 us). When cylictest is called with the -t option and more than one thread is created, then this distance value is added to the interval of the threads: Interval(thread N) = Interval(thread N-1) + DIST .TP .B \-f, \-\-fork Instead of creating threads (which is the default), fork new processes .TP .B \-i, \-\-interval=INTV Set the base interval of the thread(s) in microseconds (default is 1000 us). This sets the interval of the first thread. See also -d. .TP .B \-l, \-\-loops=LOOPS Set the number of loops. The default is 0 (endless). This option is useful for automated tests with a given number of test cycles. svsematest is stopped once the number of timer intervals has been reached. .TP .B \-p, \-\-prio=PRIO Set the priority of the process. .TP .B \-t, \-\-threads[=NUM] Set the number of test threads (default is 1, if this option is not given). If NUM is specified, create NUM test threads. If NUM is not specifed, NUM is set to the number of available CPUs. .SH "EXAMPLES" The following example was running on a 4-way CPU: .LP .nf # svsematest -a -t -p99 -i100 -d25 -l1000000 #0: ID13110, P99, CPU0, I100; #1: ID13111, P99, CPU0, Cycles 1000000 #2: ID13112, P98, CPU1, I125; #3: ID13113, P98, CPU1, Cycles 813573 #4: ID13114, P97, CPU2, I150; #5: ID13115, P97, CPU2, Cycles 667285 #6: ID13116, P96, CPU3, I175; #7: ID13117, P96, CPU3, Cycles 591403 #1 -> #0, Min 1, Cur 2, Avg 2, Max 12 #3 -> #2, Min 1, Cur 3, Avg 2, Max 12 #5 -> #4, Min 1, Cur 3, Avg 3, Max 12 #7 -> #6, Min 1, Cur 2, Avg 3, Max 11 .fi .SH "AUTHORS" .LP Carsten Emde .SH "SEE ALSO" semop(2) .LP rt-tests/src/svsematest/svsematest.c0000600001204700120500000004237412252162337020417 0ustar williamswilliams/* * svsematest.c * * Copyright (C) 2009 Carsten Emde * * 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 program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, * USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rt-utils.h" #include "rt-get_cpu.h" #include "error.h" #define gettid() syscall(__NR_gettid) #define USEC_PER_SEC 1000000 #define SEM_WAIT_FOR_RECEIVER 0 #define SEM_WAIT_FOR_SENDER 1 #define SEM_LOCK -1 #define SEM_UNLOCK 1 enum { AFFINITY_UNSPECIFIED, AFFINITY_SPECIFIED, AFFINITY_USEALL }; struct params { int num; int num_threads; int cpu; int priority; int affinity; int semid; int sender; int samples; int max_cycles; int tracelimit; int tid; pid_t pid; int shutdown; int stopped; struct timespec delay; unsigned int mindiff, maxdiff; double sumdiff; struct timeval unblocked, received, diff; pthread_t threadid; struct params *neighbor; char error[MAX_PATH * 2]; }; static int mustfork; static int wasforked; static int wasforked_sender = -1; static int wasforked_threadno = -1; static int tracelimit; void *semathread(void *param) { int mustgetcpu = 0; struct params *par = param; cpu_set_t mask; int policy = SCHED_FIFO; struct sched_param schedp; struct sembuf sb = { 0, 0, 0}; sigset_t sigset; sigemptyset(&sigset); pthread_sigmask(SIG_SETMASK, &sigset, NULL); memset(&schedp, 0, sizeof(schedp)); schedp.sched_priority = par->priority; sched_setscheduler(0, policy, &schedp); if (par->cpu != -1) { CPU_ZERO(&mask); CPU_SET(par->cpu, &mask); if(sched_setaffinity(0, sizeof(mask), &mask) == -1) snprintf(par->error, sizeof(par->error), "WARNING: Could not set CPU affinity " "to CPU #%d\n", par->cpu); } else { int max_cpus = sysconf(_SC_NPROCESSORS_CONF); if (max_cpus > 1) mustgetcpu = 1; else par->cpu = 0; } if (!wasforked) par->tid = gettid(); while (!par->shutdown) { if (par->sender) { sb.sem_num = SEM_WAIT_FOR_SENDER; sb.sem_op = SEM_UNLOCK; /* * Unlocking the semaphore: * Start of latency measurement ... */ gettimeofday(&par->unblocked, NULL); semop(par->semid, &sb, 1); par->samples++; if(par->max_cycles && par->samples >= par->max_cycles) par->shutdown = 1; if (mustgetcpu) par->cpu = get_cpu(); sb.sem_num = SEM_WAIT_FOR_RECEIVER; sb.sem_op = SEM_LOCK; semop(par->semid, &sb, 1); sb.sem_num = SEM_WAIT_FOR_SENDER; sb.sem_op = SEM_LOCK; semop(par->semid, &sb, 1); } else { /* Receiver */ struct params *neighbor; if (wasforked) neighbor = par + par->num_threads; else neighbor = par->neighbor; sb.sem_num = SEM_WAIT_FOR_SENDER; sb.sem_op = SEM_LOCK; semop(par->semid, &sb, 1); /* * ... We got the lock: * End of latency measurement */ gettimeofday(&par->received, NULL); par->samples++; if (par->max_cycles && par->samples >= par->max_cycles) par->shutdown = 1; if (mustgetcpu) par->cpu = get_cpu(); timersub(&par->received, &neighbor->unblocked, &par->diff); if (par->diff.tv_usec < par->mindiff) par->mindiff = par->diff.tv_usec; if (par->diff.tv_usec > par->maxdiff) par->maxdiff = par->diff.tv_usec; par->sumdiff += (double) par->diff.tv_usec; if (par->tracelimit && par->maxdiff > par->tracelimit) { char tracing_enabled_file[MAX_PATH]; strcpy(tracing_enabled_file, get_debugfileprefix()); strcat(tracing_enabled_file, "tracing_enabled"); int tracing_enabled = open(tracing_enabled_file, O_WRONLY); if (tracing_enabled >= 0) { write(tracing_enabled, "0", 1); close(tracing_enabled); } else snprintf(par->error, sizeof(par->error), "Could not access %s\n", tracing_enabled_file); par->shutdown = 1; neighbor->shutdown = 1; } sb.sem_num = SEM_WAIT_FOR_RECEIVER; sb.sem_op = SEM_UNLOCK; semop(par->semid, &sb, 1); nanosleep(&par->delay, NULL); sb.sem_num = SEM_WAIT_FOR_SENDER; sb.sem_op = SEM_UNLOCK; semop(par->semid, &sb, 1); } } if (par->sender) { sb.sem_num = SEM_WAIT_FOR_SENDER; sb.sem_op = SEM_UNLOCK; semop(par->semid, &sb, 1); sb.sem_num = SEM_WAIT_FOR_RECEIVER; sb.sem_op = SEM_UNLOCK; semop(par->semid, &sb, 1); } par->stopped = 1; return NULL; } union semun { int val; /* Value for SETVAL */ struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */ unsigned short *array; /* Array for GETALL, SETALL */ struct seminfo *__buf; /* Buffer for IPC_INFO (Linux-specific) */ }; static void display_help(void) { printf("svsematest V %1.2f\n", VERSION_STRING); puts("Usage: svsematest "); puts("Function: test SYSV semaphore latency"); puts( "Options:\n" "-a [NUM] --affinity run thread #N on processor #N, if possible\n" " with NUM pin all threads to the processor NUM\n" "-b USEC --breaktrace=USEC send break trace command when latency > USEC\n" "-d DIST --distance=DIST distance of thread intervals in us default=500\n" "-f --fork fork new processes instead of creating threads\n" "-i INTV --interval=INTV base interval of thread in us default=1000\n" "-l LOOPS --loops=LOOPS number of loops: default=0(endless)\n" "-p PRIO --prio=PRIO priority\n" "-S --smp SMP testing: options -a -t and same priority\n" " of all threads\n" "-t --threads one thread per available processor\n" "-t [NUM] --threads=NUM number of threads:\n" " without NUM, threads = max_cpus\n" " without -t default = 1\n"); exit(1); } static int setaffinity = AFFINITY_UNSPECIFIED; static int affinity; static int priority; static int num_threads = 1; static int max_cycles; static int interval = 1000; static int distance = 500; static int smp; static int sameprio; static void process_options (int argc, char *argv[]) { int error = 0; int max_cpus = sysconf(_SC_NPROCESSORS_CONF); int thistracelimit = 0; for (;;) { int option_index = 0; /** Options for getopt */ static struct option long_options[] = { {"affinity", optional_argument, NULL, 'a'}, {"breaktrace", required_argument, NULL, 'b'}, {"distance", required_argument, NULL, 'd'}, {"fork", optional_argument, NULL, 'f'}, {"interval", required_argument, NULL, 'i'}, {"loops", required_argument, NULL, 'l'}, {"priority", required_argument, NULL, 'p'}, {"smp", no_argument, NULL, 'S'}, {"threads", optional_argument, NULL, 't'}, {"help", no_argument, NULL, '?'}, {NULL, 0, NULL, 0} }; int c = getopt_long (argc, argv, "a::b:d:f::i:l:p:St::", long_options, &option_index); if (c == -1) break; switch (c) { case 'a': if (smp) { warn("-a ignored due to --smp\n"); break; } if (optarg != NULL) { affinity = atoi(optarg); setaffinity = AFFINITY_SPECIFIED; } else if (optind= max_cpus) { fprintf(stderr, "ERROR: CPU #%d not found, " "only %d CPUs available\n", affinity, max_cpus); error = 1; } } if (num_threads < 1 || num_threads > 255) error = 1; if (priority < 0 || priority > 99) error = 1; if (priority && smp) sameprio = 1; tracelimit = thistracelimit; } if (error) display_help (); } static int volatile mustshutdown; static void sighand(int sig) { mustshutdown = 1; } int main(int argc, char *argv[]) { char *myfile; int i, totalsize = 0; int max_cpus = sysconf(_SC_NPROCESSORS_CONF); int oldsamples = 1; key_t key; union semun args; struct params *receiver = NULL; struct params *sender = NULL; sigset_t sigset; void *param = NULL; char f_opt[8]; struct timespec launchdelay, maindelay; myfile = getenv("_"); if (myfile == NULL) myfile = argv[0]; process_options(argc, argv); if (check_privs()) return 1; if (mlockall(MCL_CURRENT|MCL_FUTURE) == -1) { perror("mlockall"); return 1; } get_cpu_setup(); if (mustfork) { int shmem; /* * In fork mode (-f), the shared memory contains two * subsequent arrays, receiver[num_threads] and * sender[num_threads]. */ totalsize = num_threads * sizeof(struct params) * 2; shm_unlink("/sigwaittest"); shmem = shm_open("/sigwaittest", O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR); if (shmem < 0) { fprintf(stderr, "Could not create shared memory\n"); return 1; } ftruncate(shmem, totalsize); param = mmap(0, totalsize, PROT_READ|PROT_WRITE, MAP_SHARED, shmem, 0); if (param == MAP_FAILED) { fprintf(stderr, "Could not map shared memory\n"); close(shmem); return 1; } receiver = (struct params *) param; sender = receiver + num_threads; } else if (wasforked) { struct stat buf; int shmem, totalsize, expect_totalsize; if (wasforked_threadno == -1 || wasforked_sender == -1) { fprintf(stderr, "Invalid fork option\n"); return 1; } shmem = shm_open("/sigwaittest", O_RDWR, S_IRUSR|S_IWUSR); if (fstat(shmem, &buf)) { fprintf(stderr, "Could not determine shared memory size\n"); close(shmem); return 1; } totalsize = buf.st_size; param = mmap(0, totalsize, PROT_READ|PROT_WRITE, MAP_SHARED, shmem, 0); close(shmem); if (param == MAP_FAILED) { fprintf(stderr, "Could not map shared memory\n"); return 1; } receiver = (struct params *) param; expect_totalsize = receiver->num_threads * sizeof(struct params) * 2; if (totalsize != expect_totalsize) { fprintf(stderr, "Memory size problem (expected %d, " "found %d\n", expect_totalsize, totalsize); munmap(param, totalsize); return 1; } sender = receiver + receiver->num_threads; if (wasforked_sender) semathread(sender + wasforked_threadno); else semathread(receiver + wasforked_threadno); munmap(param, totalsize); return 0; } signal(SIGINT, sighand); signal(SIGTERM, sighand); sigemptyset(&sigset); pthread_sigmask(SIG_SETMASK, &sigset, NULL); if (!mustfork && !wasforked) { receiver = calloc(num_threads, sizeof(struct params)); sender = calloc(num_threads, sizeof(struct params)); if (receiver == NULL || sender == NULL) goto nomem; } launchdelay.tv_sec = 0; launchdelay.tv_nsec = 10000000; /* 10 ms */ maindelay.tv_sec = 0; maindelay.tv_nsec = 50000000; /* 50 ms */ for (i = 0; i < num_threads; i++) { struct sembuf sb = { 0, 0, 0}; receiver[i].mindiff = UINT_MAX; receiver[i].maxdiff = 0; receiver[i].sumdiff = 0.0; if ((key = ftok(myfile, i)) == -1) { perror("ftok"); goto nosem; } if ((receiver[i].semid = semget(key, 2, 0666 | IPC_CREAT)) == -1) { perror("semget"); goto nosem; } args.val = 1; if (semctl(receiver[i].semid, SEM_WAIT_FOR_RECEIVER, SETVAL, args) == -1) { perror("semctl sema #0"); goto nosem; } if (semctl(receiver[i].semid, SEM_WAIT_FOR_SENDER, SETVAL, args) == -1) { perror("semctl sema #1"); goto nosem; } sb.sem_num = SEM_WAIT_FOR_RECEIVER; sb.sem_op = SEM_LOCK; semop(receiver[i].semid, &sb, 1); sb.sem_num = SEM_WAIT_FOR_SENDER; sb.sem_op = SEM_LOCK; semop(receiver[i].semid, &sb, 1); receiver[i].cpu = i; switch (setaffinity) { case AFFINITY_UNSPECIFIED: receiver[i].cpu = -1; break; case AFFINITY_SPECIFIED: receiver[i].cpu = affinity; break; case AFFINITY_USEALL: receiver[i].cpu = i % max_cpus; break; } receiver[i].priority = priority; receiver[i].tracelimit = tracelimit; if (priority > 1 && !sameprio) priority--; receiver[i].delay.tv_sec = interval / USEC_PER_SEC; receiver[i].delay.tv_nsec = (interval % USEC_PER_SEC) * 1000; interval += distance; receiver[i].max_cycles = max_cycles; receiver[i].sender = 0; receiver[i].neighbor = &sender[i]; if (mustfork) { pid_t pid = fork(); if (pid == -1) { fprintf(stderr, "Could not fork\n"); return 1; } else if (pid == 0) { char *args[3]; receiver[i].num_threads = num_threads; receiver[i].pid = getpid(); sprintf(f_opt, "-fr%d", i); args[0] = argv[0]; args[1] = f_opt; args[2] = NULL; execvp(args[0], args); fprintf(stderr, "Could not execute receiver child process " "#%d\n", i); } } else pthread_create(&receiver[i].threadid, NULL, semathread, &receiver[i]); nanosleep(&launchdelay, NULL); memcpy(&sender[i], &receiver[i], sizeof(receiver[0])); sender[i].sender = 1; sender[i].neighbor = &receiver[i]; if (mustfork) { pid_t pid = fork(); if (pid == -1) { fprintf(stderr, "Could not fork\n"); return 1; } else if (pid == 0) { char *args[3]; sender[i].num_threads = num_threads; sender[i].pid = getpid(); sprintf(f_opt, "-fs%d", i); args[0] = argv[0]; args[1] = f_opt; args[2] = NULL; execvp(args[0], args); fprintf(stderr, "Could not execute sender child process " "#%d\n", i); } } else pthread_create(&sender[i].threadid, NULL, semathread, &sender[i]); } while (!mustshutdown) { int printed; int errorlines = 0; for (i = 0; i < num_threads; i++) mustshutdown |= receiver[i].shutdown | sender[i].shutdown; if (receiver[0].samples > oldsamples || mustshutdown) { for (i = 0; i < num_threads; i++) { int receiver_pid, sender_pid; if (mustfork) { receiver_pid = receiver[i].pid; sender_pid = sender[i].pid; } else { receiver_pid = receiver[i].tid; sender_pid = sender[i].tid; } printf("#%1d: ID%d, P%d, CPU%d, I%ld; #%1d: " "ID%d, P%d, CPU%d, Cycles %d\n", i*2, receiver_pid, receiver[i].priority, receiver[i].cpu, receiver[i].delay.tv_nsec / 1000, i*2+1, sender_pid, sender[i].priority, sender[i].cpu, sender[i].samples); } for (i = 0; i < num_threads; i++) { if (receiver[i].mindiff == -1) printf("#%d -> #%d (not yet ready)\n", i*2+1, i*2); else printf("#%d -> #%d, Min %4d, Cur %4d, " "Avg %4d, Max %4d\n", i*2+1, i*2, receiver[i].mindiff, (int) receiver[i].diff.tv_usec, (int) ((receiver[i].sumdiff / receiver[i].samples) + 0.5), receiver[i].maxdiff); if (receiver[i].error[0] != '\0') { printf("%s", receiver[i].error); receiver[i].error[0] = '\0'; errorlines++; } if (sender[i].error[0] != '\0') { printf("%s", sender[i].error); sender[i].error[0] = '\0'; errorlines++; } } printed = 1; } else printed = 0; sigemptyset(&sigset); sigaddset(&sigset, SIGTERM); sigaddset(&sigset, SIGINT); pthread_sigmask(SIG_SETMASK, &sigset, NULL); nanosleep(&maindelay, NULL); sigemptyset(&sigset); pthread_sigmask(SIG_SETMASK, &sigset, NULL); if (printed && !mustshutdown) printf("\033[%dA", num_threads*2 + errorlines); } for (i = 0; i < num_threads; i++) { receiver[i].shutdown = 1; sender[i].shutdown = 1; } nanosleep(&receiver[0].delay, NULL); for (i = 0; i < num_threads; i++) { if (!receiver[i].stopped) { if (mustfork) kill(receiver[i].pid, SIGTERM); else pthread_kill(receiver[i].threadid, SIGTERM); } if (!sender[i].stopped) { if (mustfork) kill(sender[i].pid, SIGTERM); else pthread_kill(sender[i].threadid, SIGTERM); } } nosem: for (i = 0; i < num_threads; i++) semctl(receiver[i].semid, -1, IPC_RMID); nomem: if (mustfork) { munmap(param, totalsize); shm_unlink("/sigwaittest"); } return 0; } rt-tests/src/svsematest/Makefile0000600001204700120500000000034412252162340017476 0ustar williamswilliamsCFLAGS += -Wall -O2 LDFLAGS += -lpthread -lrt all: svsematest @echo Done svsematest.o: svsematest.c svsematest: clean: @rm -f *.o tar: clean @rm -f svsematest $(shell bn=`basename $$PWD`; cd ..; tar -zcf $$bn.tgz $$bn) rt-tests/src/cyclictest/0000700001204700120500000000000012252162340016003 5ustar williamswilliamsrt-tests/src/cyclictest/foo.c0000600001204700120500000000126612252162340016741 0ustar williamswilliams#include #include #include #include #define ALLOCSZ 256 int mkcmdlinestr(int argc, char **argv) { int i; int bufsize = ALLOCSZ; int elementsz; char *str; if ((str = malloc(ALLOCSZ)) == NULL) { perror("malloc"); exit(-1); } str[0] = '\0'; for (i=0; i < argc; i++) { elementsz = strlen(argv[i]) + 2; if ((strlen(str) + elementsz) >= bufsize) { char *ptr; int sz = ALLOCSZ; while (sz < elementsz) sz *= 2; ptr = realloc(str, bufsize+sz); if (ptr == NULL) { perror("realloc"); exit(-2); } str = ptr; bufsize += sz; } strcat(strcat(str, argv[i]), " "); } str[strlen(str) - 1] = '\0'; return str; } rt-tests/src/cyclictest/rt_numa.h0000664001204700120500000001233012252162340017634 0ustar williamswilliams/* * A numa library for cyclictest. * The functions here are designed to work whether cyclictest has been * compiled with numa support or not, and whether the user uses the --numa * option or not. * They should also work correctly with older versions of the numactl lib * such as the one found on RHEL5, or with the newer version 2 and above. * * (C) 2010 John Kacur * (C) 2010 Clark Williams * */ #ifndef _RT_NUMA_H #define _RT_NUMA_H #include "rt-utils.h" #include "error.h" static int numa = 0; #ifdef NUMA #include #ifndef LIBNUMA_API_VERSION #define LIBNUMA_API_VERSION 1 #endif #if LIBNUMA_API_VERSION < 2 struct bitmask { unsigned long size; /* number of bits in the map */ unsigned long *maskp; }; #define BITS_PER_LONG (8*sizeof(long)) #endif static void * threadalloc(size_t size, int node) { if (node == -1) return malloc(size); return numa_alloc_onnode(size, node); } static void threadfree(void *ptr, size_t size, int node) { if (node == -1) free(ptr); else numa_free(ptr, size); } static void rt_numa_set_numa_run_on_node(int node, int cpu) { int res; res = numa_run_on_node(node); if (res) warn("Could not set NUMA node %d for thread %d: %s\n", node, cpu, strerror(errno)); return; } static void numa_on_and_available() { if (numa && numa_available() == -1) fatal("--numa specified and numa functions not available.\n"); } #if LIBNUMA_API_VERSION >= 2 static int rt_numa_numa_node_of_cpu(int cpu) { int node; node = numa_node_of_cpu(cpu); if (node == -1) fatal("invalid cpu passed to numa_node_of_cpu(%d)\n", cpu); return node; } #else /* LIBNUMA_API_VERSION == 1 */ static int rt_numa_numa_node_of_cpu(int cpu) { unsigned char cpumask[256]; int node, idx, bit; int max_node, max_cpus; max_node = numa_max_node(); max_cpus = sysconf(_SC_NPROCESSORS_CONF); if (cpu > max_cpus) { errno = EINVAL; return -1; } /* calculate bitmask index and relative bit position of cpu */ idx = cpu / 8; bit = cpu % 8; for (node = 0; node <= max_node; node++) { if (numa_node_to_cpus(node, (void *) cpumask, sizeof(cpumask))) return -1; if (cpumask[idx] & (1<= 2 return numa_bitmask_isbitset(mask,i); #else long bit = mask->maskp[i/BITS_PER_LONG] & (1<<(i % BITS_PER_LONG)); return (bit != 0); #endif } /** Returns number of bits set in mask. */ static inline unsigned int rt_numa_bitmask_count(const struct bitmask *mask) { unsigned int num_bits = 0, i; for (i = 0; i < mask->size; i++) { if (rt_numa_bitmask_isbitset(mask, i)) num_bits++; } /* Could stash this instead of recomputing every time. */ return num_bits; } static inline struct bitmask* rt_numa_parse_cpustring(const char* s, int max_cpus) { #if LIBNUMA_API_VERSION >= 2 #ifdef HAVE_PARSE_CPUSTRING_ALL /* Currently not defined anywhere. No autotools build. */ return numa_parse_cpustring_all(s); #else /* We really need numa_parse_cpustring_all(), so we can assign threads * to cores which are part of an isolcpus set, but early 2.x versions of * libnuma do not have this function. A work around should be to run * your command with e.g. taskset -c 9-15 */ return numa_parse_cpustring(s); #endif #else /* LIBNUMA_API_VERSION == 1 */ int cpu; struct bitmask *mask = NULL; cpu = atoi(s); if (0 <= cpu && cpu < max_cpus) { mask = malloc(sizeof(*mask)); if (mask) { /* Round up to integral number of longs to contain * max_cpus bits */ int nlongs = (max_cpus+BITS_PER_LONG-1)/BITS_PER_LONG; mask->maskp = calloc(nlongs, sizeof(long)); if (mask->maskp) { mask->maskp[cpu/BITS_PER_LONG] |= (1UL << (cpu % BITS_PER_LONG)); mask->size = max_cpus; } else { free(mask); mask = NULL; } } } return mask; #endif } static inline void rt_bitmask_free(struct bitmask *mask) { #if LIBNUMA_API_VERSION >= 2 numa_bitmask_free(mask); #else /* LIBNUMA_API_VERSION == 1 */ free(mask->maskp); free(mask); #endif } #else /* ! NUMA */ struct bitmask { }; static inline void *threadalloc(size_t size, int n) { return malloc(size); } static inline void threadfree(void *ptr, size_t s, int n) { free(ptr); } static inline void rt_numa_set_numa_run_on_node(int n, int c) { } static inline void numa_on_and_available() { }; static inline int rt_numa_numa_node_of_cpu(int cpu) { return -1; } static void *rt_numa_numa_alloc_onnode(size_t s, int n, int c) { return NULL; } static inline unsigned int rt_numa_bitmask_isbitset( const struct bitmask *affinity_mask, unsigned long i) { return 0; } static inline struct bitmask* rt_numa_parse_cpustring(const char* s, int m) { return NULL; } static inline unsigned int rt_numa_bitmask_count(const struct bitmask *mask) { return 0; } static inline void rt_bitmask_free(struct bitmask *mask) { return; } #endif /* NUMA */ #endif /* _RT_NUMA_H */ rt-tests/src/cyclictest/commit.txt0000600001204700120500000000045312252162340020040 0ustar williamswilliams[cyclictest] fixes for ftrace options Miscellaneous fixes for ftrace option handling. Add an enumeration to indicate when the -T/--tracer option is specified. Change from using the tracing_enabled file to tracing_on to turn tracing on/off. Add the latency-format option for nicer latency output. rt-tests/src/cyclictest/fifotst0000600001204700120500000002213012252162340017404 0ustar williamswilliamsELF>@@@8 @@@@@@88@8@@@ `` ((`(`TT@T@DDPtdp p @p @LLQtdRtd``/lib64/ld-linux-x86-64.so.2GNU GNU'nTY_8: b&:t9-&LoW_ RfFE>`__gmon_start__libc.so.6exitsignalunlink__errno_locationstderrusleepcloseopenfprintfmkfifostrerror__libc_start_mainwriteGLIBC_2.2.5ui ` `` `(`0`8`@`H`P`X` `` h` p` x` `HHU HtH5B %D @%B h%: h%2 h%* h%" h% h% h% hp% h`% h P% h @% h 0% h % h 1I^HHPTI @Hp @H@7f@`UH-`HHw]øHt]``UH-`HHHH?HHHu]úHt]`=I uUH~]6 @H= tHtU `H]{vfUH} ]UHH H}H HǸE}yEUHH H]UHH }Hu@@HT HE}uHH" H# - @HǸX:H HE}yZu mHH I @HǸ7_Ea @EWb UEHl$Ld$H- L% H\$Ll$Lt$L|$H8L)AIHI1Ht@LLDAHH9uH\$Hl$Ld$Ll$ Lt$(L|$0H8fHH/tmp/fifotstError creating %s fifo: %s Error opening fifo: %s Got an event! ;LP@h,?y @hzRx *zRx $FJ w?;*3$"DdAC N dW:AC u qAC P fjAC $Jf@X p@P@ @  @``o@@@@ `PH@@0 o@oo@(`@@@@@&@6@F@V@f@v@@@@ @GCC: (GNU) 4.7.2 20121109 (Red Hat 4.7.2-8).symtab.strtab.shstrtab.interp.note.ABI-tag.note.gnu.build-id.gnu.hash.dynsym.dynstr.gnu.version.gnu.version_r.rela.dyn.rela.plt.init.text.fini.rodata.eh_frame_hdr.eh_frame.init_array.fini_array.jcr.dynamic.got.got.plt.data.bss.comment8@8#T@T 1t@t$Do@$N @V@@@^o@ ko@ z@0H@HP @@@T @  @ `p @p L @ 4`` ` (`(````0,P- !X8@T@t@@@@@@@ @ H@ @ @ @ @ @p @ @`` `(`````  ` @. @A P@W`f` p@` @ ``(`` ` @/Ma } `` @``) 8 @E @T p @d`i @p`y` @j @: @ `  @* @0D`crtstuff.c__JCR_LIST__deregister_tm_clonesregister_tm_clones__do_global_dtors_auxcompleted.6137__do_global_dtors_aux_fini_array_entryframe_dummy__frame_dummy_init_array_entryfifotst.c__FRAME_END____JCR_END____init_array_end_DYNAMIC__init_array_start_GLOBAL_OFFSET_TABLE___libc_csu_fini__errno_location@@GLIBC_2.2.5unlink@@GLIBC_2.2.5_ITM_deregisterTMCloneTabledata_startwrite@@GLIBC_2.2.5_edata_finistopclose@@GLIBC_2.2.5__libc_start_main@@GLIBC_2.2.5__data_startsignal@@GLIBC_2.2.5fprintf@@GLIBC_2.2.5mkfifo@@GLIBC_2.2.5__gmon_start____dso_handle_IO_stdin_used__libc_csu_init_end_startfifopath__bss_startmainopenfifocleanupopen@@GLIBC_2.2.5_Jv_RegisterClassesexit@@GLIBC_2.2.5__TMC_END___ITM_registerTMCloneTablesighandstrerror@@GLIBC_2.2.5sleep@@GLIBC_2.2.5_initusleep@@GLIBC_2.2.5stderr@@GLIBC_2.2.5rt-tests/src/cyclictest/cyclictest.c0000664001204700120500000015436012252162340020342 0ustar williamswilliams/* * High resolution timer test software * * (C) 2013 Clark Williams * (C) 2013 John Kacur * (C) 2008-2012 Clark Williams * (C) 2005-2007 Thomas Gleixner * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License Version * 2 as published by the Free Software Foundation. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rt_numa.h" #include "rt-utils.h" #define DEFAULT_INTERVAL 1000 #define DEFAULT_DISTANCE 500 #ifndef SCHED_IDLE #define SCHED_IDLE 5 #endif #ifndef SCHED_NORMAL #define SCHED_NORMAL SCHED_OTHER #endif #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) /* Ugly, but .... */ #define gettid() syscall(__NR_gettid) #define sigev_notify_thread_id _sigev_un._tid #ifdef __UCLIBC__ #define MAKE_PROCESS_CPUCLOCK(pid, clock) \ ((~(clockid_t) (pid) << 3) | (clockid_t) (clock)) #define CPUCLOCK_SCHED 2 static int clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *req, struct timespec *rem) { if (clock_id == CLOCK_THREAD_CPUTIME_ID) return -EINVAL; if (clock_id == CLOCK_PROCESS_CPUTIME_ID) clock_id = MAKE_PROCESS_CPUCLOCK (0, CPUCLOCK_SCHED); return syscall(__NR_clock_nanosleep, clock_id, flags, req, rem); } int sched_setaffinity (__pid_t __pid, size_t __cpusetsize, __const cpu_set_t *__cpuset) { return -EINVAL; } #undef CPU_SET #undef CPU_ZERO #define CPU_SET(cpu, cpusetp) #define CPU_ZERO(cpusetp) #else extern int clock_nanosleep(clockid_t __clock_id, int __flags, __const struct timespec *__req, struct timespec *__rem); #endif #define USEC_PER_SEC 1000000 #define NSEC_PER_SEC 1000000000 #define HIST_MAX 1000000 #define MODE_CYCLIC 0 #define MODE_CLOCK_NANOSLEEP 1 #define MODE_SYS_ITIMER 2 #define MODE_SYS_NANOSLEEP 3 #define MODE_SYS_OFFSET 2 #define TIMER_RELTIME 0 /* Must be power of 2 ! */ #define VALBUF_SIZE 16384 #define KVARS 32 #define KVARNAMELEN 32 #define KVALUELEN 32 int enable_events; static char *policyname(int policy); enum { NOTRACE, CTXTSWITCH, IRQSOFF, PREEMPTOFF, PREEMPTIRQSOFF, WAKEUP, WAKEUPRT, LATENCY, FUNCTION, CUSTOM, }; /* Struct to transfer parameters to the thread */ struct thread_param { int prio; int policy; int mode; int timermode; int signal; int clock; unsigned long max_cycles; struct thread_stat *stats; int bufmsk; unsigned long interval; int cpu; int node; int tnum; }; /* Struct for statistics */ struct thread_stat { unsigned long cycles; unsigned long cyclesread; long min; long max; long act; double avg; long *values; long *hist_array; long *outliers; pthread_t thread; int threadstarted; int tid; long reduce; long redmax; long cycleofmax; long hist_overflow; long num_outliers; }; static int shutdown; static int tracelimit = 0; static int notrace = 0; static int ftrace = 0; static int kernelversion; static int verbose = 0; static int oscope_reduction = 1; static int lockall = 0; static int tracetype = NOTRACE; static int histogram = 0; static int histofall = 0; static int duration = 0; static int use_nsecs = 0; static int refresh_on_max; static int force_sched_other; static int priospread = 0; static int check_clock_resolution; static int ct_debug; static int use_fifo = 0; static pthread_t fifo_threadid; static int aligned = 0; static int offset = 0; static pthread_cond_t refresh_on_max_cond = PTHREAD_COND_INITIALIZER; static pthread_mutex_t refresh_on_max_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t break_thread_id_lock = PTHREAD_MUTEX_INITIALIZER; static pid_t break_thread_id = 0; static uint64_t break_thread_value = 0; static pthread_barrier_t align_barr; static pthread_barrier_t globalt_barr; static struct timespec globalt; /* Backup of kernel variables that we modify */ static struct kvars { char name[KVARNAMELEN]; char value[KVALUELEN]; } kv[KVARS]; static char *procfileprefix = "/proc/sys/kernel/"; static char *fileprefix; static char tracer[MAX_PATH]; static char fifopath[MAX_PATH]; static char **traceptr; static int traceopt_count; static int traceopt_size; static struct thread_param **parameters; static struct thread_stat **statistics; static void print_stat(FILE *fp, struct thread_param *par, int index, int verbose, int quiet); static int latency_target_fd = -1; static int32_t latency_target_value = 0; /* Latency trick * if the file /dev/cpu_dma_latency exists, * open it and write a zero into it. This will tell * the power management system not to transition to * a high cstate (in fact, the system acts like idle=poll) * When the fd to /dev/cpu_dma_latency is closed, the behavior * goes back to the system default. * * Documentation/power/pm_qos_interface.txt */ static void set_latency_target(void) { struct stat s; int ret; if (stat("/dev/cpu_dma_latency", &s) == 0) { latency_target_fd = open("/dev/cpu_dma_latency", O_RDWR); if (latency_target_fd == -1) return; ret = write(latency_target_fd, &latency_target_value, 4); if (ret == 0) { printf("# error setting cpu_dma_latency to %d!: %s\n", latency_target_value, strerror(errno)); close(latency_target_fd); return; } printf("# /dev/cpu_dma_latency set to %dus\n", latency_target_value); } } enum kernelversion { KV_NOT_SUPPORTED, KV_26_LT18, KV_26_LT24, KV_26_33, KV_30 }; enum { ERROR_GENERAL = -1, ERROR_NOTFOUND = -2, }; static char functiontracer[MAX_PATH]; static char traceroptions[MAX_PATH]; static int trace_fd = -1; static int tracemark_fd = -1; static int kernvar(int mode, const char *name, char *value, size_t sizeofvalue) { char filename[128]; int retval = 1; int path; size_t len_prefix = strlen(fileprefix), len_name = strlen(name); if (len_prefix + len_name + 1 > sizeof(filename)) { errno = ENOMEM; return 1; } memcpy(filename, fileprefix, len_prefix); memcpy(filename + len_prefix, name, len_name + 1); path = open(filename, mode); if (path >= 0) { if (mode == O_RDONLY) { int got; if ((got = read(path, value, sizeofvalue)) > 0) { retval = 0; value[got-1] = '\0'; } } else if (mode == O_WRONLY) { if (write(path, value, sizeofvalue) == sizeofvalue) retval = 0; } close(path); } return retval; } static void setkernvar(const char *name, char *value) { int i; char oldvalue[KVALUELEN]; if (kernelversion < KV_26_33) { if (kernvar(O_RDONLY, name, oldvalue, sizeof(oldvalue))) fprintf(stderr, "could not retrieve %s\n", name); else { for (i = 0; i < KVARS; i++) { if (!strcmp(kv[i].name, name)) break; if (kv[i].name[0] == '\0') { strncpy(kv[i].name, name, sizeof(kv[i].name)); strncpy(kv[i].value, oldvalue, sizeof(kv[i].value)); break; } } if (i == KVARS) fprintf(stderr, "could not backup %s (%s)\n", name, oldvalue); } } if (kernvar(O_WRONLY, name, value, strlen(value))) fprintf(stderr, "could not set %s to %s\n", name, value); } static void restorekernvars(void) { int i; for (i = 0; i < KVARS; i++) { if (kv[i].name[0] != '\0') { if (kernvar(O_WRONLY, kv[i].name, kv[i].value, strlen(kv[i].value))) fprintf(stderr, "could not restore %s to %s\n", kv[i].name, kv[i].value); } } } static inline void tsnorm(struct timespec *ts) { while (ts->tv_nsec >= NSEC_PER_SEC) { ts->tv_nsec -= NSEC_PER_SEC; ts->tv_sec++; } } static inline int64_t calcdiff(struct timespec t1, struct timespec t2) { int64_t diff; diff = USEC_PER_SEC * (long long)((int) t1.tv_sec - (int) t2.tv_sec); diff += ((int) t1.tv_nsec - (int) t2.tv_nsec) / 1000; return diff; } static inline int64_t calcdiff_ns(struct timespec t1, struct timespec t2) { int64_t diff; diff = NSEC_PER_SEC * (int64_t)((int) t1.tv_sec - (int) t2.tv_sec); diff += ((int) t1.tv_nsec - (int) t2.tv_nsec); return diff; } void traceopt(char *option) { char *ptr; if (traceopt_count + 1 > traceopt_size) { traceopt_size += 16; printf("expanding traceopt buffer to %d entries\n", traceopt_size); traceptr = realloc(traceptr, sizeof(char*) * traceopt_size); if (traceptr == NULL) fatal ("Error allocating space for %d trace options\n", traceopt_count+1); } ptr = malloc(strlen(option)+1); if (ptr == NULL) fatal("error allocating space for trace option %s\n", option); printf("adding traceopt %s\n", option); strcpy(ptr, option); traceptr[traceopt_count++] = ptr; } static int trace_file_exists(char *name) { struct stat sbuf; char *tracing_prefix = get_debugfileprefix(); char path[MAX_PATH]; strcat(strcpy(path, tracing_prefix), name); return stat(path, &sbuf) ? 0 : 1; } #define TRACEBUFSIZ 1024 static __thread char tracebuf[TRACEBUFSIZ]; static void tracemark(char *fmt, ...) { va_list ap; int len; /* bail out if we're not tracing */ /* or if the kernel doesn't support trace_mark */ if (tracemark_fd < 0) return; va_start(ap, fmt); len = vsnprintf(tracebuf, TRACEBUFSIZ, fmt, ap); va_end(ap); write(tracemark_fd, tracebuf, len); } void tracing(int on) { if (on) { switch (kernelversion) { case KV_26_LT18: gettimeofday(0,(struct timezone *)1); break; case KV_26_LT24: prctl(0, 1); break; case KV_26_33: case KV_30: write(trace_fd, "1", 1); break; default: break; } } else { switch (kernelversion) { case KV_26_LT18: gettimeofday(0,0); break; case KV_26_LT24: prctl(0, 0); break; case KV_26_33: case KV_30: write(trace_fd, "0", 1); break; default: break; } } } static int settracer(char *tracer) { if (valid_tracer(tracer)) { setkernvar("current_tracer", tracer); return 0; } return -1; } static void setup_tracer(void) { if (!tracelimit || notrace) return; if (mount_debugfs(NULL)) fatal("could not mount debugfs"); if (kernelversion >= KV_26_33) { char testname[MAX_PATH]; fileprefix = get_debugfileprefix(); if (!trace_file_exists("tracing_enabled") && !trace_file_exists("tracing_on")) warn("tracing_enabled or tracing_on not found\n" "debug fs not mounted, " "TRACERs not configured?\n", testname); } else fileprefix = procfileprefix; if (kernelversion >= KV_26_33) { int ret; if (trace_file_exists("tracing_enabled") && !trace_file_exists("tracing_on")) setkernvar("tracing_enabled", "1"); /* ftrace_enabled is a sysctl variable */ /* turn it on if you're doing anything but nop or event tracing */ fileprefix = procfileprefix; if (tracetype) setkernvar("ftrace_enabled", "1"); else setkernvar("ftrace_enabled", "0"); fileprefix = get_debugfileprefix(); /* * Set default tracer to nop. * this also has the nice side effect of clearing out * old traces. */ ret = settracer("nop"); switch (tracetype) { case NOTRACE: /* no tracer specified, use events */ enable_events = 1; break; case FUNCTION: ret = settracer("function"); break; case IRQSOFF: ret = settracer("irqsoff"); break; case PREEMPTOFF: ret = settracer("preemptoff"); break; case PREEMPTIRQSOFF: ret = settracer("preemptirqsoff"); break; case CTXTSWITCH: if (valid_tracer("sched_switch")) ret = settracer("sched_switch"); else { if ((ret = event_enable("sched/sched_wakeup"))) break; ret = event_enable("sched/sched_switch"); } break; case WAKEUP: ret = settracer("wakeup"); break; case WAKEUPRT: ret = settracer("wakeup_rt"); break; default: if (strlen(tracer)) { ret = settracer(tracer); if (strcmp(tracer, "events") == 0 && ftrace) ret = settracer(functiontracer); } else { printf("cyclictest: unknown tracer!\n"); ret = 0; } break; } if (enable_events) /* turn on all events */ event_enable_all(); if (ret) fprintf(stderr, "Requested tracer '%s' not available\n", tracer); setkernvar(traceroptions, "print-parent"); setkernvar(traceroptions, "latency-format"); if (verbose) { setkernvar(traceroptions, "sym-offset"); setkernvar(traceroptions, "sym-addr"); setkernvar(traceroptions, "verbose"); } else { setkernvar(traceroptions, "nosym-offset"); setkernvar(traceroptions, "nosym-addr"); setkernvar(traceroptions, "noverbose"); } if (traceopt_count) { int i; for (i = 0; i < traceopt_count; i++) setkernvar(traceroptions, traceptr[i]); } setkernvar("tracing_max_latency", "0"); if (trace_file_exists("latency_hist")) setkernvar("latency_hist/wakeup/reset", "1"); /* open the tracing on file descriptor */ if (trace_fd == -1) { char path[MAX_PATH]; strcpy(path, fileprefix); if (trace_file_exists("tracing_on")) strcat(path, "tracing_on"); else strcat(path, "tracing_enabled"); if ((trace_fd = open(path, O_WRONLY)) == -1) fatal("unable to open %s for tracing", path); } /* open the tracemark file descriptor */ if (tracemark_fd == -1) { char path[MAX_PATH]; strcat(strcpy(path, fileprefix), "trace_marker"); if ((tracemark_fd = open(path, O_WRONLY)) == -1) warn("unable to open trace_marker file: %s\n", path); } } else { setkernvar("trace_all_cpus", "1"); setkernvar("trace_freerunning", "1"); setkernvar("trace_print_on_crash", "0"); setkernvar("trace_user_triggered", "1"); setkernvar("trace_user_trigger_irq", "-1"); setkernvar("trace_verbose", "0"); setkernvar("preempt_thresh", "0"); setkernvar("wakeup_timing", "0"); setkernvar("preempt_max_latency", "0"); if (ftrace) setkernvar("mcount_enabled", "1"); setkernvar("trace_enabled", "1"); setkernvar("latency_hist/wakeup_latency/reset", "1"); } tracing(1); } /* * parse an input value as a base10 value followed by an optional * suffix. The input value is presumed to be in seconds, unless * followed by a modifier suffix: m=minutes, h=hours, d=days * * the return value is a value in seconds */ int parse_time_string(char *val) { char *end; int t = strtol(val, &end, 10); if (end) { switch (*end) { case 'm': case 'M': t *= 60; break; case 'h': case 'H': t *= 60*60; break; case 'd': case 'D': t *= 24*60*60; break; } } return t; } /* * Raise the soft priority limit up to prio, if that is less than or equal * to the hard limit * if a call fails, return the error * if successful return 0 * if fails, return -1 */ static int raise_soft_prio(int policy, const struct sched_param *param) { int err; int policy_max; /* max for scheduling policy such as SCHED_FIFO */ int soft_max; int hard_max; int prio; struct rlimit rlim; prio = param->sched_priority; policy_max = sched_get_priority_max(policy); if (policy_max == -1) { err = errno; err_msg("WARN: no such policy\n"); return err; } err = getrlimit(RLIMIT_RTPRIO, &rlim); if (err) { err = errno; err_msg_n(err, "WARN: getrlimit failed\n"); return err; } soft_max = (rlim.rlim_cur == RLIM_INFINITY) ? policy_max : rlim.rlim_cur; hard_max = (rlim.rlim_max == RLIM_INFINITY) ? policy_max : rlim.rlim_max; if (prio > soft_max && prio <= hard_max) { rlim.rlim_cur = prio; err = setrlimit(RLIMIT_RTPRIO, &rlim); if (err) { err = errno; err_msg_n(err, "WARN: setrlimit failed\n"); /* return err; */ } } else { err = -1; } return err; } /* * Check the error status of sched_setscheduler * If an error can be corrected by raising the soft limit priority to * a priority less than or equal to the hard limit, then do so. */ static int setscheduler(pid_t pid, int policy, const struct sched_param *param) { int err = 0; try_again: err = sched_setscheduler(pid, policy, param); if (err) { err = errno; if (err == EPERM) { int err1; err1 = raise_soft_prio(policy, param); if (!err1) goto try_again; } } return err; } /* * timer thread * * Modes: * - clock_nanosleep based * - cyclic timer based * * Clock: * - CLOCK_MONOTONIC * - CLOCK_REALTIME * */ void *timerthread(void *param) { struct thread_param *par = param; struct sched_param schedp; struct sigevent sigev; sigset_t sigset; timer_t timer; struct timespec now, next, interval, stop; struct itimerval itimer; struct itimerspec tspec; struct thread_stat *stat = par->stats; int stopped = 0; cpu_set_t mask; pthread_t thread; /* if we're running in numa mode, set our memory node */ if (par->node != -1) rt_numa_set_numa_run_on_node(par->node, par->cpu); if (par->cpu != -1) { CPU_ZERO(&mask); CPU_SET(par->cpu, &mask); thread = pthread_self(); if(pthread_setaffinity_np(thread, sizeof(mask), &mask) == -1) warn("Could not set CPU affinity to CPU #%d\n", par->cpu); } interval.tv_sec = par->interval / USEC_PER_SEC; interval.tv_nsec = (par->interval % USEC_PER_SEC) * 1000; stat->tid = gettid(); sigemptyset(&sigset); sigaddset(&sigset, par->signal); sigprocmask(SIG_BLOCK, &sigset, NULL); if (par->mode == MODE_CYCLIC) { sigev.sigev_notify = SIGEV_THREAD_ID | SIGEV_SIGNAL; sigev.sigev_signo = par->signal; sigev.sigev_notify_thread_id = stat->tid; timer_create(par->clock, &sigev, &timer); tspec.it_interval = interval; } memset(&schedp, 0, sizeof(schedp)); schedp.sched_priority = par->prio; if (setscheduler(0, par->policy, &schedp)) fatal("timerthread%d: failed to set priority to %d\n", par->cpu, par->prio); /* Get current time */ if(aligned){ pthread_barrier_wait(&globalt_barr); if(par->tnum==0) clock_gettime(par->clock, &globalt); pthread_barrier_wait(&align_barr); now = globalt; if(offset) { now.tv_nsec += offset * par->tnum; tsnorm(&now); } } else clock_gettime(par->clock, &now); next = now; next.tv_sec += interval.tv_sec; next.tv_nsec += interval.tv_nsec; tsnorm(&next); if (duration) { memset(&stop, 0, sizeof(stop)); /* grrr */ stop = now; stop.tv_sec += duration; } if (par->mode == MODE_CYCLIC) { if (par->timermode == TIMER_ABSTIME) tspec.it_value = next; else { tspec.it_value = interval; } timer_settime(timer, par->timermode, &tspec, NULL); } if (par->mode == MODE_SYS_ITIMER) { itimer.it_interval.tv_sec = interval.tv_sec; itimer.it_interval.tv_usec = interval.tv_nsec / 1000; itimer.it_value = itimer.it_interval; setitimer (ITIMER_REAL, &itimer, NULL); } stat->threadstarted++; while (!shutdown) { uint64_t diff; int sigs, ret; /* Wait for next period */ switch (par->mode) { case MODE_CYCLIC: case MODE_SYS_ITIMER: if (sigwait(&sigset, &sigs) < 0) goto out; break; case MODE_CLOCK_NANOSLEEP: if (par->timermode == TIMER_ABSTIME) { if ((ret = clock_nanosleep(par->clock, TIMER_ABSTIME, &next, NULL))) { if (ret != EINTR) warn("clock_nanosleep failed. errno: %d\n", errno); goto out; } } else { if ((ret = clock_gettime(par->clock, &now))) { if (ret != EINTR) warn("clock_gettime() failed: %s", strerror(errno)); goto out; } if ((ret = clock_nanosleep(par->clock, TIMER_RELTIME, &interval, NULL))) { if (ret != EINTR) warn("clock_nanosleep() failed. errno: %d\n", errno); goto out; } next.tv_sec = now.tv_sec + interval.tv_sec; next.tv_nsec = now.tv_nsec + interval.tv_nsec; tsnorm(&next); } break; case MODE_SYS_NANOSLEEP: if ((ret = clock_gettime(par->clock, &now))) { if (ret != EINTR) warn("clock_gettime() failed: errno %d\n", errno); goto out; } if (nanosleep(&interval, NULL)) { if (errno != EINTR) warn("nanosleep failed. errno: %d\n", errno); goto out; } next.tv_sec = now.tv_sec + interval.tv_sec; next.tv_nsec = now.tv_nsec + interval.tv_nsec; tsnorm(&next); break; } if ((ret = clock_gettime(par->clock, &now))) { if (ret != EINTR) warn("clock_getttime() failed. errno: %d\n", errno); goto out; } if (use_nsecs) diff = calcdiff_ns(now, next); else diff = calcdiff(now, next); if (diff < stat->min) stat->min = diff; if (diff > stat->max) { stat->max = diff; if (refresh_on_max) pthread_cond_signal(&refresh_on_max_cond); } stat->avg += (double) diff; if (duration && (calcdiff(now, stop) >= 0)) shutdown++; if (!stopped && tracelimit && (diff > tracelimit)) { stopped++; tracemark("hit latency threshold (%d > %d)", diff, tracelimit); tracing(0); shutdown++; pthread_mutex_lock(&break_thread_id_lock); if (break_thread_id == 0) break_thread_id = stat->tid; break_thread_value = diff; pthread_mutex_unlock(&break_thread_id_lock); } stat->act = diff; if (par->bufmsk) stat->values[stat->cycles & par->bufmsk] = diff; /* Update the histogram */ if (histogram) { if (diff >= histogram) { stat->hist_overflow++; if (stat->num_outliers < histogram) stat->outliers[stat->num_outliers++] = stat->cycles; } else stat->hist_array[diff]++; } stat->cycles++; next.tv_sec += interval.tv_sec; next.tv_nsec += interval.tv_nsec; if (par->mode == MODE_CYCLIC) { int overrun_count = timer_getoverrun(timer); next.tv_sec += overrun_count * interval.tv_sec; next.tv_nsec += overrun_count * interval.tv_nsec; } tsnorm(&next); if (par->max_cycles && par->max_cycles == stat->cycles) break; } out: if (par->mode == MODE_CYCLIC) timer_delete(timer); if (par->mode == MODE_SYS_ITIMER) { itimer.it_value.tv_sec = 0; itimer.it_value.tv_usec = 0; itimer.it_interval.tv_sec = 0; itimer.it_interval.tv_usec = 0; setitimer (ITIMER_REAL, &itimer, NULL); } /* switch to normal */ schedp.sched_priority = 0; sched_setscheduler(0, SCHED_OTHER, &schedp); stat->threadstarted = -1; return NULL; } /* Print usage information */ static void display_help(int error) { char tracers[MAX_PATH]; char *prefix; prefix = get_debugfileprefix(); if (prefix[0] == '\0') strcpy(tracers, "unavailable (debugfs not mounted)"); else { fileprefix = prefix; if (kernvar(O_RDONLY, "available_tracers", tracers, sizeof(tracers))) strcpy(tracers, "none"); } printf("cyclictest V %1.2f\n", VERSION_STRING); printf("Usage:\n" "cyclictest \n\n" #if LIBNUMA_API_VERSION >= 2 "-a [CPUSET] --affinity Run thread #N on processor #N, if possible, or if CPUSET\n" " given, pin threads to that set of processors in round-\n" " robin order. E.g. -a 2 pins all threads to CPU 2,\n" " but -a 3-5,0 -t 5 will run the first and fifth\n" " threads on CPU (0),thread #2 on CPU 3, thread #3\n" " on CPU 4, and thread #5 on CPU 5.\n" #else "-a [NUM] --affinity run thread #N on processor #N, if possible\n" " with NUM pin all threads to the processor NUM\n" #endif "-A USEC --aligned=USEC align thread wakeups to a specific offset\n" "-b USEC --breaktrace=USEC send break trace command when latency > USEC\n" "-B --preemptirqs both preempt and irqsoff tracing (used with -b)\n" "-c CLOCK --clock=CLOCK select clock\n" " 0 = CLOCK_MONOTONIC (default)\n" " 1 = CLOCK_REALTIME\n" "-C --context context switch tracing (used with -b)\n" "-d DIST --distance=DIST distance of thread intervals in us default=500\n" "-D --duration=t specify a length for the test run\n" " default is in seconds, but 'm', 'h', or 'd' maybe added\n" " to modify value to minutes, hours or days\n" "-e --latency=PM_QOS write PM_QOS to /dev/cpu_dma_latency\n" "-E --event event tracing (used with -b)\n" "-f --ftrace function trace (when -b is active)\n" "-F --fifo= create a named pipe at path and write stats to it\n" "-h --histogram=US dump a latency histogram to stdout after the run\n" " (with same priority about many threads)\n" " US is the max time to be be tracked in microseconds\n" "-H --histofall=US same as -h except with an additional summary column\n" "-i INTV --interval=INTV base interval of thread in us default=1000\n" "-I --irqsoff Irqsoff tracing (used with -b)\n" "-l LOOPS --loops=LOOPS number of loops: default=0(endless)\n" "-m --mlockall lock current and future memory allocations\n" "-M --refresh_on_max delay updating the screen until a new max latency is hit\n" "-n --nanosleep use clock_nanosleep\n" "-N --nsecs print results in ns instead of us (default us)\n" "-o RED --oscope=RED oscilloscope mode, reduce verbose output by RED\n" "-O TOPT --traceopt=TOPT trace option\n" "-p PRIO --prio=PRIO priority of highest prio thread\n" "-P --preemptoff Preempt off tracing (used with -b)\n" "-q --quiet print only a summary on exit\n" "-Q --priospread spread priority levels starting at specified value\n" "-r --relative use relative timer instead of absolute\n" "-R --resolution check clock resolution, calling clock_gettime() many\n" " times. list of clock_gettime() values will be\n" " reported with -X\n" "-s --system use sys_nanosleep and sys_setitimer\n" "-S --smp Standard SMP testing: options -a -t -n and\n" " same priority of all threads\n" "-t --threads one thread per available processor\n" "-t [NUM] --threads=NUM number of threads:\n" " without NUM, threads = max_cpus\n" " without -t default = 1\n" "-T TRACE --tracer=TRACER set tracing function\n" " configured tracers: %s\n" "-u --unbuffered force unbuffered output for live processing\n" "-U --numa Standard NUMA testing (similar to SMP option)\n" " thread data structures allocated from local node\n" "-v --verbose output values on stdout for statistics\n" " format: n:c:v n=tasknum c=count v=value in us\n" "-w --wakeup task wakeup tracing (used with -b)\n" "-W --wakeuprt rt task wakeup tracing (used with -b)\n" "-X --dbg_cyclictest print info useful for debugging cyclictest\n" "-y POLI --policy=POLI policy of realtime thread, POLI may be fifo(default) or rr\n" " format: --policy=fifo(default) or --policy=rr\n", tracers ); if (error) exit(EXIT_FAILURE); exit(EXIT_SUCCESS); } static int use_nanosleep; static int timermode = TIMER_ABSTIME; static int use_system; static int priority; static int policy = SCHED_OTHER; /* default policy if not specified */ static int num_threads = 1; static int max_cycles; static int clocksel = 0; static int quiet; static int interval = DEFAULT_INTERVAL; static int distance = -1; static struct bitmask *affinity_mask = NULL; static int smp = 0; enum { AFFINITY_UNSPECIFIED, AFFINITY_SPECIFIED, AFFINITY_USEALL }; static int setaffinity = AFFINITY_UNSPECIFIED; static int clocksources[] = { CLOCK_MONOTONIC, CLOCK_REALTIME, }; static unsigned int is_cpumask_zero(const struct bitmask *mask) { return (rt_numa_bitmask_count(mask) == 0); } static int cpu_for_thread(int thread_num, int max_cpus) { unsigned int m, cpu, i, num_cpus; num_cpus = rt_numa_bitmask_count(affinity_mask); m = thread_num % num_cpus; /* there are num_cpus bits set, we want position of m'th one */ for (i = 0, cpu = 0; i < max_cpus; i++) { if (rt_numa_bitmask_isbitset(affinity_mask, i)) { if (cpu == m) return i; cpu++; } } fprintf(stderr, "Bug in cpu mask handling code.\n"); return 0; } static void parse_cpumask(const char *option, const int max_cpus) { affinity_mask = rt_numa_parse_cpustring(option, max_cpus); if (affinity_mask) { if (is_cpumask_zero(affinity_mask)) { rt_bitmask_free(affinity_mask); affinity_mask = NULL; } } if (!affinity_mask) display_help(1); if (verbose) { printf("%s: Using %u cpus.\n", __func__, rt_numa_bitmask_count(affinity_mask)); } } static void handlepolicy(char *polname) { if (strncasecmp(polname, "other", 5) == 0) policy = SCHED_OTHER; else if (strncasecmp(polname, "batch", 5) == 0) policy = SCHED_BATCH; else if (strncasecmp(polname, "idle", 4) == 0) policy = SCHED_IDLE; else if (strncasecmp(polname, "fifo", 4) == 0) policy = SCHED_FIFO; else if (strncasecmp(polname, "rr", 2) == 0) policy = SCHED_RR; else /* default policy if we don't recognize the request */ policy = SCHED_OTHER; } static char *policyname(int policy) { char *policystr = ""; switch(policy) { case SCHED_OTHER: policystr = "other"; break; case SCHED_FIFO: policystr = "fifo"; break; case SCHED_RR: policystr = "rr"; break; case SCHED_BATCH: policystr = "batch"; break; case SCHED_IDLE: policystr = "idle"; break; } return policystr; } enum option_values { OPT_AFFINITY=1, OPT_NOTRACE, OPT_BREAKTRACE, OPT_PREEMPTIRQ, OPT_CLOCK, OPT_CONTEXT, OPT_DISTANCE, OPT_DURATION, OPT_LATENCY, OPT_EVENT, OPT_FTRACE, OPT_FIFO, OPT_HISTOGRAM, OPT_HISTOFALL, OPT_INTERVAL, OPT_IRQSOFF, OPT_LOOPS, OPT_MLOCKALL, OPT_REFRESH, OPT_NANOSLEEP, OPT_NSECS, OPT_OSCOPE, OPT_TRACEOPT, OPT_PRIORITY, OPT_PREEMPTOFF, OPT_QUIET, OPT_PRIOSPREAD, OPT_RELATIVE, OPT_RESOLUTION, OPT_SYSTEM, OPT_SMP, OPT_THREADS, OPT_TRACER, OPT_UNBUFFERED, OPT_NUMA, OPT_VERBOSE, OPT_WAKEUP, OPT_WAKEUPRT, OPT_DBGCYCLIC, OPT_POLICY, OPT_HELP, OPT_NUMOPTS, OPT_ALIGNED, }; /* Process commandline options */ static void process_options (int argc, char *argv[], int max_cpus) { int error = 0; int option_affinity = 0; for (;;) { int option_index = 0; /* * Options for getopt * Ordered alphabetically by single letter name */ static struct option long_options[] = { {"affinity", optional_argument, NULL, OPT_AFFINITY}, {"notrace", no_argument, NULL, OPT_NOTRACE }, {"aligned", optional_argument, NULL, OPT_ALIGNED }, {"breaktrace", required_argument, NULL, OPT_BREAKTRACE }, {"preemptirqs", no_argument, NULL, OPT_PREEMPTIRQ }, {"clock", required_argument, NULL, OPT_CLOCK }, {"context", no_argument, NULL, OPT_CONTEXT }, {"distance", required_argument, NULL, OPT_DISTANCE }, {"duration", required_argument, NULL, OPT_DURATION }, {"latency", required_argument, NULL, OPT_LATENCY }, {"event", no_argument, NULL, OPT_EVENT }, {"ftrace", no_argument, NULL, OPT_FTRACE }, {"fifo", required_argument, NULL, OPT_FIFO }, {"histogram", required_argument, NULL, OPT_HISTOGRAM }, {"histofall", required_argument, NULL, OPT_HISTOFALL }, {"interval", required_argument, NULL, OPT_INTERVAL }, {"irqsoff", no_argument, NULL, OPT_IRQSOFF }, {"loops", required_argument, NULL, OPT_LOOPS }, {"mlockall", no_argument, NULL, OPT_MLOCKALL }, {"refresh_on_max", no_argument, NULL, OPT_REFRESH }, {"nanosleep", no_argument, NULL, OPT_NANOSLEEP }, {"nsecs", no_argument, NULL, OPT_NSECS }, {"oscope", required_argument, NULL, OPT_OSCOPE }, {"traceopt", required_argument, NULL, OPT_TRACEOPT }, {"priority", required_argument, NULL, OPT_PRIORITY }, {"preemptoff", no_argument, NULL, OPT_PREEMPTOFF }, {"quiet", no_argument, NULL, OPT_QUIET }, {"priospread", no_argument, NULL, OPT_PRIOSPREAD }, {"relative", no_argument, NULL, OPT_RELATIVE }, {"resolution", no_argument, NULL, OPT_RESOLUTION }, {"system", no_argument, NULL, OPT_SYSTEM }, {"smp", no_argument, NULL, OPT_SMP }, {"threads", optional_argument, NULL, OPT_THREADS }, {"tracer", required_argument, NULL, OPT_TRACER }, {"unbuffered", no_argument, NULL, OPT_UNBUFFERED }, {"numa", no_argument, NULL, OPT_NUMA }, {"verbose", no_argument, NULL, OPT_VERBOSE }, {"wakeup", no_argument, NULL, OPT_WAKEUP }, {"wakeuprt", no_argument, NULL, OPT_WAKEUPRT }, {"dbg_cyclictest", no_argument, NULL, OPT_DBGCYCLIC }, {"policy", required_argument, NULL, OPT_POLICY }, {"help", no_argument, NULL, OPT_HELP }, {NULL, 0, NULL, 0} }; int c = getopt_long(argc, argv, "a::A::b:Bc:Cd:D:Efh:H:i:Il:MnNo:O:p:PmqrRsSt::uUvD:wWT:", long_options, &option_index); if (c == -1) break; switch (c) { case 'a': case OPT_AFFINITY: option_affinity = 1; if (smp || numa) break; if (optarg != NULL) { parse_cpumask(optarg, max_cpus); setaffinity = AFFINITY_SPECIFIED; } else if (optind ARRAY_SIZE(clocksources)) error = 1; if (oscope_reduction < 1) error = 1; if (oscope_reduction > 1 && !verbose) { warn("-o option only meaningful, if verbose\n"); error = 1; } if (histogram < 0) error = 1; if (histogram > HIST_MAX) histogram = HIST_MAX; if (histogram && distance != -1) warn("distance is ignored and set to 0, if histogram enabled\n"); if (distance == -1) distance = DEFAULT_DISTANCE; if (priority < 0 || priority > 99) error = 1; if (priospread && priority == 0) { fprintf(stderr, "defaulting realtime priority to %d\n", num_threads+1); priority = num_threads+1; } if (priority && (policy != SCHED_FIFO && policy != SCHED_RR)) { fprintf(stderr, "policy and priority don't match: setting policy to SCHED_FIFO\n"); policy = SCHED_FIFO; } if ((policy == SCHED_FIFO || policy == SCHED_RR) && priority == 0) { fprintf(stderr, "defaulting realtime priority to %d\n", num_threads+1); priority = num_threads+1; } if (num_threads < 1) error = 1; if (aligned) { pthread_barrier_init(&globalt_barr, NULL, num_threads); pthread_barrier_init(&align_barr, NULL, num_threads); } if (error) { if (affinity_mask) rt_bitmask_free(affinity_mask); display_help(1); } } static int check_kernel(void) { struct utsname kname; int maj, min, sub, kv, ret; ret = uname(&kname); if (ret) { fprintf(stderr, "uname failed: %s. Assuming not 2.6\n", strerror(errno)); return KV_NOT_SUPPORTED; } sscanf(kname.release, "%d.%d.%d", &maj, &min, &sub); if (maj == 2 && min == 6) { if (sub < 18) kv = KV_26_LT18; else if (sub < 24) kv = KV_26_LT24; else if (sub < 28) { kv = KV_26_33; strcpy(functiontracer, "ftrace"); strcpy(traceroptions, "iter_ctrl"); } else { kv = KV_26_33; strcpy(functiontracer, "function"); strcpy(traceroptions, "trace_options"); } } else if (maj == 3) { kv = KV_30; strcpy(functiontracer, "function"); strcpy(traceroptions, "trace_options"); } else kv = KV_NOT_SUPPORTED; return kv; } static int check_timer(void) { struct timespec ts; if (clock_getres(CLOCK_MONOTONIC, &ts)) return 1; return (ts.tv_sec != 0 || ts.tv_nsec != 1); } static void sighand(int sig) { if (sig == SIGUSR1) { int i; int oldquiet = quiet; quiet = 0; printf("#---------------------------\n"); printf("# cyclictest current status:\n"); for (i = 0; i < num_threads; i++) print_stat(stdout, parameters[i], i, 0, 0); printf("#---------------------------\n"); quiet = oldquiet; return; } shutdown = 1; if (refresh_on_max) pthread_cond_signal(&refresh_on_max_cond); if (tracelimit && !notrace) tracing(0); } static void print_tids(struct thread_param *par[], int nthreads) { int i; printf("# Thread Ids:"); for (i = 0; i < nthreads; i++) printf(" %05d", par[i]->stats->tid); printf("\n"); } static void print_hist(struct thread_param *par[], int nthreads) { int i, j; unsigned long long int log_entries[nthreads+1]; unsigned long maxmax, alloverflows; bzero(log_entries, sizeof(log_entries)); printf("# Histogram\n"); for (i = 0; i < histogram; i++) { unsigned long long int allthreads = 0; printf("%06d ", i); for (j = 0; j < nthreads; j++) { unsigned long curr_latency=par[j]->stats->hist_array[i]; printf("%06lu", curr_latency); if (j < nthreads - 1) printf("\t"); log_entries[j] += curr_latency; allthreads += curr_latency; } if (histofall && nthreads > 1) { printf("\t%06llu", allthreads); log_entries[nthreads] += allthreads; } printf("\n"); } printf("# Total:"); for (j = 0; j < nthreads; j++) printf(" %09llu", log_entries[j]); if (histofall && nthreads > 1) printf(" %09llu", log_entries[nthreads]); printf("\n"); printf("# Min Latencies:"); for (j = 0; j < nthreads; j++) printf(" %05lu", par[j]->stats->min); printf("\n"); printf("# Avg Latencies:"); for (j = 0; j < nthreads; j++) printf(" %05lu", par[j]->stats->cycles ? (long)(par[j]->stats->avg/par[j]->stats->cycles) : 0); printf("\n"); printf("# Max Latencies:"); maxmax = 0; for (j = 0; j < nthreads; j++) { printf(" %05lu", par[j]->stats->max); if (par[j]->stats->max > maxmax) maxmax = par[j]->stats->max; } if (histofall && nthreads > 1) printf(" %05lu", maxmax); printf("\n"); printf("# Histogram Overflows:"); alloverflows = 0; for (j = 0; j < nthreads; j++) { printf(" %05lu", par[j]->stats->hist_overflow); alloverflows += par[j]->stats->hist_overflow; } if (histofall && nthreads > 1) printf(" %05lu", alloverflows); printf("\n"); printf("# Histogram Overflow at cycle number:\n"); for (i = 0; i < nthreads; i++) { printf("# Thread %d:", i); for (j = 0; j < par[i]->stats->num_outliers; j++) printf(" %05lu", par[i]->stats->outliers[j]); if (par[i]->stats->num_outliers < par[i]->stats->hist_overflow) printf(" # %05lu others", par[i]->stats->hist_overflow - par[i]->stats->num_outliers); printf("\n"); } printf("\n"); } static void print_stat(FILE *fp, struct thread_param *par, int index, int verbose, int quiet) { struct thread_stat *stat = par->stats; if (!verbose) { if (quiet != 1) { char *fmt; if (use_nsecs) fmt = "T:%2d (%5d) P:%2d I:%ld C:%7lu " "Min:%7ld Act:%8ld Avg:%8ld Max:%8ld\n"; else fmt = "T:%2d (%5d) P:%2d I:%ld C:%7lu " "Min:%7ld Act:%5ld Avg:%5ld Max:%8ld\n"; fprintf(fp, fmt, index, stat->tid, par->prio, par->interval, stat->cycles, stat->min, stat->act, stat->cycles ? (long)(stat->avg/stat->cycles) : 0, stat->max); } } else { while (stat->cycles != stat->cyclesread) { long diff = stat->values [stat->cyclesread & par->bufmsk]; if (diff > stat->redmax) { stat->redmax = diff; stat->cycleofmax = stat->cyclesread; } if (++stat->reduce == oscope_reduction) { fprintf(fp, "%8d:%8lu:%8ld\n", index, stat->cycleofmax, stat->redmax); stat->reduce = 0; stat->redmax = 0; } stat->cyclesread++; } } } /* * thread that creates a named fifo and hands out run stats when someone * reads from the fifo. */ void *fifothread(void *param) { int ret; int fd; FILE *fp; int i; if (use_fifo == 0) return NULL; unlink(fifopath); ret = mkfifo(fifopath, 0666); if (ret) { fprintf(stderr, "Error creating fifo %s: %s\n", fifopath, strerror(errno)); return NULL; } while (!shutdown) { fd = open(fifopath, O_WRONLY|O_NONBLOCK); if (fd < 0) { usleep(500000); continue; } fp = fdopen(fd, "w"); for (i=0; i < num_threads; i++) print_stat(fp, parameters[i], i, 0, 0); fclose(fp); usleep(250); } unlink(fifopath); return NULL; } int main(int argc, char **argv) { sigset_t sigset; int signum = SIGALRM; int mode; int max_cpus = sysconf(_SC_NPROCESSORS_CONF); int i, ret = -1; int status; process_options(argc, argv, max_cpus); if (check_privs()) exit(EXIT_FAILURE); if (verbose) printf("Max CPUs = %d\n", max_cpus); /* Checks if numa is on, program exits if numa on but not available */ numa_on_and_available(); /* lock all memory (prevent swapping) */ if (lockall) if (mlockall(MCL_CURRENT|MCL_FUTURE) == -1) { perror("mlockall"); goto out; } /* use the /dev/cpu_dma_latency trick if it's there */ set_latency_target(); kernelversion = check_kernel(); if (kernelversion == KV_NOT_SUPPORTED) warn("Running on unknown kernel version...YMMV\n"); setup_tracer(); if (check_timer()) warn("High resolution timers not available\n"); if (check_clock_resolution) { int clock; uint64_t diff; int k; uint64_t min_non_zero_diff = UINT64_MAX; struct timespec now; struct timespec prev; uint64_t reported_resolution = UINT64_MAX; struct timespec res; struct timespec *time; int times; clock = clocksources[clocksel]; if (clock_getres(clock, &res)) { warn("clock_getres failed"); } else { reported_resolution = (NSEC_PER_SEC * res.tv_sec) + res.tv_nsec; } /* * Calculate how many calls to clock_gettime are needed. * Then call it that many times. * Goal is to collect timestamps for ~ 0.001 sec. * This will reliably capture resolution <= 500 usec. */ times = 1000; clock_gettime(clock, &prev); for (k=0; k < times; k++) { clock_gettime(clock, &now); } diff = calcdiff_ns(now, prev); if (diff == 0) { /* * No clock rollover occurred. * Use the default value for times. */ times = -1; } else { int call_time; call_time = diff / times; /* duration 1 call */ times = NSEC_PER_SEC / call_time; /* calls per second */ times /= 1000; /* calls per msec */ if (times < 1000) times = 1000; } /* sanity check */ if ((times <= 0) || (times > 100000)) times = 100000; time = calloc(times, sizeof(*time)); for (k=0; k < times; k++) { clock_gettime(clock, &time[k]); } if (ct_debug) { info("For %d consecutive calls to clock_gettime():\n", times); info("time, delta time (nsec)\n"); } prev = time[0]; for (k=1; k < times; k++) { diff = calcdiff_ns(time[k], prev); prev = time[k]; if (diff && (diff < min_non_zero_diff)) { min_non_zero_diff = diff; } if (ct_debug) info("%ld.%06ld %5llu\n", time[k].tv_sec, time[k].tv_nsec, (unsigned long long)diff); } free(time); if (verbose || (min_non_zero_diff && (min_non_zero_diff > reported_resolution))) { /* * Measured clock resolution includes the time to call * clock_gettime(), so it will be slightly larger than * actual resolution. */ warn("reported clock resolution: %llu nsec\n", (unsigned long long)reported_resolution); warn("measured clock resolution approximately: %llu nsec\n", (unsigned long long)min_non_zero_diff); } } mode = use_nanosleep + use_system; sigemptyset(&sigset); sigaddset(&sigset, signum); sigprocmask (SIG_BLOCK, &sigset, NULL); signal(SIGINT, sighand); signal(SIGTERM, sighand); signal(SIGUSR1, sighand); parameters = calloc(num_threads, sizeof(struct thread_param *)); if (!parameters) goto out; statistics = calloc(num_threads, sizeof(struct thread_stat *)); if (!statistics) goto outpar; for (i = 0; i < num_threads; i++) { pthread_attr_t attr; int node; struct thread_param *par; struct thread_stat *stat; status = pthread_attr_init(&attr); if (status != 0) fatal("error from pthread_attr_init for thread %d: %s\n", i, strerror(status)); node = -1; if (numa) { void *stack; void *currstk; size_t stksize; /* find the memory node associated with the cpu i */ node = rt_numa_numa_node_of_cpu(i); /* get the stack size set for for this thread */ if (pthread_attr_getstack(&attr, &currstk, &stksize)) fatal("failed to get stack size for thread %d\n", i); /* if the stack size is zero, set a default */ if (stksize == 0) stksize = PTHREAD_STACK_MIN * 2; /* allocate memory for a stack on appropriate node */ stack = rt_numa_numa_alloc_onnode(stksize, node, i); /* set the thread's stack */ if (pthread_attr_setstack(&attr, stack, stksize)) fatal("failed to set stack addr for thread %d to 0x%x\n", i, stack+stksize); } /* allocate the thread's parameter block */ parameters[i] = par = threadalloc(sizeof(struct thread_param), node); if (par == NULL) fatal("error allocating thread_param struct for thread %d\n", i); memset(par, 0, sizeof(struct thread_param)); /* allocate the thread's statistics block */ statistics[i] = stat = threadalloc(sizeof(struct thread_stat), node); if (stat == NULL) fatal("error allocating thread status struct for thread %d\n", i); memset(stat, 0, sizeof(struct thread_stat)); /* allocate the histogram if requested */ if (histogram) { int bufsize = histogram * sizeof(long); stat->hist_array = threadalloc(bufsize, node); stat->outliers = threadalloc(bufsize, node); if (stat->hist_array == NULL || stat->outliers == NULL) fatal("failed to allocate histogram of size %d on node %d\n", histogram, i); memset(stat->hist_array, 0, bufsize); memset(stat->outliers, 0, bufsize); } if (verbose) { int bufsize = VALBUF_SIZE * sizeof(long); stat->values = threadalloc(bufsize, node); if (!stat->values) goto outall; memset(stat->values, 0, bufsize); par->bufmsk = VALBUF_SIZE - 1; } par->prio = priority; if (priority && (policy == SCHED_FIFO || policy == SCHED_RR)) par->policy = policy; else { par->policy = SCHED_OTHER; force_sched_other = 1; } if (priospread) priority--; par->clock = clocksources[clocksel]; par->mode = mode; par->timermode = timermode; par->signal = signum; par->interval = interval; if (!histogram) /* same interval on CPUs */ interval += distance; if (verbose) printf("Thread %d Interval: %d\n", i, interval); par->max_cycles = max_cycles; par->stats = stat; par->node = node; par->tnum = i; switch (setaffinity) { case AFFINITY_UNSPECIFIED: par->cpu = -1; break; case AFFINITY_SPECIFIED: par->cpu = cpu_for_thread(i, max_cpus); if (verbose) printf("Thread %d using cpu %d.\n", i, par->cpu); break; case AFFINITY_USEALL: par->cpu = i % max_cpus; break; } stat->min = 1000000; stat->max = 0; stat->avg = 0.0; stat->threadstarted = 1; status = pthread_create(&stat->thread, &attr, timerthread, par); if (status) fatal("failed to create thread %d: %s\n", i, strerror(status)); } if (use_fifo) status = pthread_create(&fifo_threadid, NULL, fifothread, NULL); while (!shutdown) { char lavg[256]; int fd, len, allstopped = 0; static char *policystr = NULL; static char *slash = NULL; static char *policystr2; if (!policystr) policystr = policyname(policy); if (!slash) { if (force_sched_other) { slash = "/"; policystr2 = policyname(SCHED_OTHER); } else slash = policystr2 = ""; } if (!verbose && !quiet) { fd = open("/proc/loadavg", O_RDONLY, 0666); len = read(fd, &lavg, 255); close(fd); lavg[len-1] = 0x0; printf("policy: %s%s%s: loadavg: %s \n\n", policystr, slash, policystr2, lavg); } for (i = 0; i < num_threads; i++) { print_stat(stdout, parameters[i], i, verbose, quiet); if(max_cycles && statistics[i]->cycles >= max_cycles) allstopped++; } usleep(10000); if (shutdown || allstopped) break; if (!verbose && !quiet) printf("\033[%dA", num_threads + 2); if (refresh_on_max) { pthread_mutex_lock(&refresh_on_max_lock); pthread_cond_wait(&refresh_on_max_cond, &refresh_on_max_lock); pthread_mutex_unlock(&refresh_on_max_lock); } } ret = EXIT_SUCCESS; outall: shutdown = 1; usleep(50000); if (quiet) quiet = 2; for (i = 0; i < num_threads; i++) { if (statistics[i]->threadstarted > 0) pthread_kill(statistics[i]->thread, SIGTERM); if (statistics[i]->threadstarted) { pthread_join(statistics[i]->thread, NULL); if (quiet && !histogram) print_stat(stdout, parameters[i], i, 0, 0); } if (statistics[i]->values) threadfree(statistics[i]->values, VALBUF_SIZE*sizeof(long), parameters[i]->node); } if (histogram) { print_hist(parameters, num_threads); for (i = 0; i < num_threads; i++) { threadfree(statistics[i]->hist_array, histogram*sizeof(long), parameters[i]->node); threadfree(statistics[i]->outliers, histogram*sizeof(long), parameters[i]->node); } } if (tracelimit) { print_tids(parameters, num_threads); if (break_thread_id) { printf("# Break thread: %d\n", break_thread_id); printf("# Break value: %llu\n", (unsigned long long)break_thread_value); } } for (i=0; i < num_threads; i++) { if (!statistics[i]) continue; threadfree(statistics[i], sizeof(struct thread_stat), parameters[i]->node); } outpar: for (i = 0; i < num_threads; i++) { if (!parameters[i]) continue; threadfree(parameters[i], sizeof(struct thread_param), parameters[i]->node); } out: /* ensure that the tracer is stopped */ if (tracelimit && !notrace) tracing(0); /* close any tracer file descriptors */ if (tracemark_fd >= 0) close(tracemark_fd); if (trace_fd >= 0) close(trace_fd); if (enable_events) /* turn off all events */ event_disable_all(); /* turn off the function tracer */ fileprefix = procfileprefix; if (tracetype && !notrace) setkernvar("ftrace_enabled", "0"); fileprefix = get_debugfileprefix(); /* unlock everything */ if (lockall) munlockall(); /* Be a nice program, cleanup */ if (kernelversion < KV_26_33) restorekernvars(); /* close the latency_target_fd if it's open */ if (latency_target_fd >= 0) close(latency_target_fd); if (affinity_mask) rt_bitmask_free(affinity_mask); exit(ret); } rt-tests/src/cyclictest/cyclictest.80000664001204700120500000002127412252162340020264 0ustar williamswilliams.\" Hey, EMACS: -*- nroff -*- .TH CYCLICTEST 8 "december 20, 2007" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME cyclictest \- High resolution test program .SH SYNOPSIS .B cyclictest .RI "[ \-hfmnqrsvMS ] [\-a " proc " ] [\-A " align " ] [\-b " usec " ] [\-c " clock " ] [\-d " dist " ] \ [\-h " histogram " ] [\-i " intv " ] [\-l " loop " ] [\-o " red " ] [\-p " prio " ] \ [\-t " num " ] [\-D " time "] [\-w] [\-W] [\-y " policy " ] [ \-S | \-U ]" .\" .SH DESCRIPTION .\" This manual page documents briefly the .\" .B cyclictest commands. .\" .PP .\" \fI\fP escape sequences to invode bold face and italics, respectively. .\" \fBcyclictest\fP is a program that... .SH OPTIONS These programs follow the usual GNU command line syntax, with long options starting with two dashes ('\-\-'). .br A summary of options is included below. .\" For a complete description, see the Info files. .TP .B \-a, \-\-affinity[=PROC-SET] Run threads on the set of procesors given by PROC-SET. If PROC-SET is not specified, all processors will be used. Threads will be assigned to processors in the set in numeric order, in a round-robin fashion. .br The set of processors can be specified as A,B,C, or A-C, or A-B,D-F, and so on*. The ! character can be used to negate a set. For example, !B-D means to use all available CPUs except B through D. The cpu numbers are the same as shown in the .I processor field in /proc/cpuinfo. See numa(3) for more information on specifying CPU sets. * Support for CPU sets requires libnuma version >= 2. For libnuma v1, PROC-SET, if specified, must be a single CPU number. .TP .B \-A, \-\-align=USEC Align measurement thread wakeups to a specific offset in microseconds .TP .B \-b, \-\-breaktrace=USEC Send break trace command when latency > USEC. This is a debugging option to control the latency tracer in the realtime preemption patch. It is useful to track down unexpected large latencies on a system. This option does only work with following kernel config options. For kernel < 2.6.24: .br * CONFIG_PREEMPT_RT=y .br * CONFIG_WAKEUP_TIMING=y .br * CONFIG_LATENCY_TRACE=y .br * CONFIG_CRITICAL_PREEMPT_TIMING=y .br * CONFIG_CRITICAL_IRQSOFF_TIMING=y .sp 1 For kernel >= 2.6.24: .br * CONFIG_PREEMPT_RT=y .br * CONFIG_FTRACE .br * CONFIG_IRQSOFF_TRACER=y .br * CONFIG_PREEMPT_TRACER=y .br * CONFIG_SCHED_TRACER=y .br * CONFIG_WAKEUP_LATENCY_HIST kernel configuration options enabled. The USEC parameter to the \-b option defines a maximum latency value, which is compared against the actual latencies of the test. Once the measured latency is higher than the given maximum, the kernel tracer and cyclictest is stopped. The trace can be read from /proc/latency_trace. Please be aware that the tracer adds significant overhead to the kernel, so the latencies will be much higher than on a kernel with latency tracing disabled. .TP .B \-c, \-\-clock=CLOCK Selects the clock, which is used: * 0 selects CLOCK_MONOTONIC, which is the monotonic increasing system time (default). * 1 selects CLOCK_REALTIME, which is the time of day time. CLOCK_REALTIME can be set by settimeofday, while CLOCK_MONOTONIC can not be modified by the user. This option has no influence when the \-s option is given. .TP .B \-C, \-\-context context switch tracing (used with \-b) .TP .B \-d, \-\-distance=DIST Set the distance of thread intervals in microseconds (default is 500us). When cyclictest is called with the \-t option and more than one thread is created, then this distance value is added to the interval of the threads: Interval(thread N) = Interval(thread N\-1) + DIST .TP .B \-E, \-\-event event tracing (used with \-b) .TP .B \-f, \-\-ftrace Enable function tracing using ftrace as tracer. This option is available only with \-b. .TP .B \-h, \-\-histogram=MAXLATENCYINUS Dump latency histogram to stdout. US means the max time to be be tracked in microseconds. When you use \-h option to get histogram data, Cyclictest runs many threads with same priority without priority\-\-. .TP .B \-H, \-\-histofall=MAXLATENCYINUS Same as -h except that an additional histogram column is displayed at the right that contains summary data of all thread histograms. If cyclictest runs a single thread only, the -H option is equivalent to -h. .TP .B \-i, \-\-interval=INTV Set the base interval of the thread(s) in microseconds (default is 1000us). This sets the interval of the first thread. See also \-d. .TP .B \-l, \-\-loops=LOOPS Set the number of loops. The default is 0 (endless). This option is useful for automated tests with a given number of test cycles. Cyclictest is stopped once the number of timer intervals has been reached. .TP .B \-n, \-\-nanosleep Use clock_nanosleep instead of posix interval timers. Setting this option runs the tests with clock_nanosleep instead of posix interval timers. .TP .B \-N, \-\-nsecs Show results in nanoseconds instead of microseconds, which is the default unit. .TP .B \-o, \-\-oscope=RED Oscilloscope mode, reduce verbose output by RED. .TP .B \-O, \-\-traceopt=TRACING_OPTION Used to pass tracing options to ftrace tracers. May be invoked mutiple times for multiple trace options. For example trace options look at /sys/kernel/debug/tracing/trace_options .TP .B \-p, \-\-prio=PRIO Set the priority of the first thread. The given priority is set to the first test thread. Each further thread gets a lower priority: Priority(Thread N) = max(Priority(Thread N\-1) \- 1, 0) .TP .B \-q, \-\-quiet Run the tests quiet and print only a summary on exit. Useful for automated tests, where only the summary output needs to be captured. .TP .B \-r, \-\-relative Use relative timers instead of absolute. The default behaviour of the tests is to use absolute timers. This option is there for completeness and should not be used for reproducible tests. .TP .B \-s, \-\-system Use sys_nanosleep and sys_setitimer instead of posix timers. Note, that \-s can only be used with one thread because itimers are per process and not per thread. \-s in combination with \-n uses the nanosleep syscall and is not restricted to one thread. .TP .B \-T, \-\-tracer=TRACEFUNC set the ftrace tracer function. Used with the \-b option. Must be one of the trace functions available from /kernel/debug/tracing/available_tracers .TP .B \-t, \-\-threads[=NUM] Set the number of test threads (default is 1). Create NUM test threads. If NUM is not specified, NUM is set to the number of available CPUs. See \-d, \-i and \-p for further information. .TP .B \-m, \-\-mlockall Lock current and future memory allocations to prevent being paged out .TP .B \-v, \-\-verbose Output values on stdout for statistics. This option is used to gather statistical information about the latency distribution. The output is sent to stdout. The output format is: n:c:v where n=task number c=count v=latency value in us. Use this option in combination with \-l .TP .B \\-D, \-\-duration=TIME Run the test for the specified time, which defaults to seconds. Append 'm', 'h', or 'd' to specify minutes, hours or days .TP .B \\-w, \-\-wakeup task wakeup tracing (used with \-b) .TP .B \\-W, \-\-wakeuprt rt-task wakeup tracing (used with \-b) .TP .B \\-y, \-\-policy=NAME set the scheduler policy of the measurement threads where NAME is one of: other, normal, batch, idle, fifo, rr .TP .B \\-M, \-\-refresh_on_max delay updating the screen until a new max latency is hit (useful for running cyclictest on low-bandwidth connections) .TP .B \\-S, \-\-smp Set options for standard testing on SMP systems. Equivalent to using the options: "\-t \-a \-n" as well keeping any specified priority equal across all threads .TP .B \\-U, \-\-numa Similar to the above \-\-smp option, this implies the "\-t \-a \-n" options, as well as a constant measurement interval, but also forces memory allocations using the numa(3) policy library. Thread stacks and data structures are allocated from the NUMA node local to the core to which the thread is bound. Requires the underlying kernel to have NUMA support compiled in. .SH SEE ALSO .BR numactl (8), .\" .br .\" The programs are documented fully by .\" .IR "The Rise and Fall of a Fooish Bar" , .\" available via the Info system. .SH AUTHOR cyclictest was written by Thomas Gleixner . .PP This manual page was written by Alessio Igor Bogani , for the Debian project (but may be used by others). rt-tests/src/cyclictest/fifotst.c0000600001204700120500000000214112252162340017625 0ustar williamswilliams#include #include #include #include #include #include #include #include #include #include int stop = 0; #define SLEEPTIME 500000 void sighand(int sig) { stop = 1; } char *fifopath = "/tmp/fifotst"; int openfifo(char *path) { int fd = open(fifopath, O_WRONLY | O_NONBLOCK); if (fd < 0) return -1; return fd; } void cleanup() { unlink(fifopath); } int main(int argc, char **argv) { int ret; int fd; struct pollfd pollfds[1]; cleanup(); signal(SIGINT, sighand); signal(SIGTERM, sighand); ret = mkfifo(fifopath, 0666); if (ret) { fprintf(stderr, "Error creating %s fifo: %s\n", fifopath, strerror(errno)); cleanup(); exit(errno); } while (!stop) { fd = openfifo(fifopath); if (fd < 0) { if (errno == ENXIO) { usleep(SLEEPTIME); continue; } else { fprintf(stderr, "Error opening fifo: %s\n", strerror(errno)); cleanup(); exit(errno); } } write(fd, "Got an event!\n", 14); close(fd); sleep(1); } close(fd); cleanup(); exit(0); } rt-tests/src/cyclictest/foo0000600001204700120500000001664412252162340016526 0ustar williamswilliamsELF>P@@X @8@@@@@@@@@@    ` `( @ @ `@ `@@DDPtd8 8 @8 @,,Qtd/lib64/ld-linux-x86-64.so.2GNU GNUa)P .C 5&<__gmon_start__libc.so.6exitperrorreallocprintfmallocstrcat__libc_start_mainGLIBC_2.2.5ui U ` ` ` ` ` ` ` `HRH5  %  @%  h% h% h% h% h% h% h1I^HHPTI@H@H4@wHHI HtHÐUHSH= uK0 `Hz H( `HHH9s$fDHHU ( `HG H9r3 H[]fff.H=  UHtHt]8 `]ÐUHSHH}HuE HEH}u @HEE+EHHHEHHEH¸HMHHHHEHEHEH¸HMHHHHPEHH‹EHH9r[E e܋E;E|E܋UHcHEHHHEH}u @HEHEEE @EHHHEHHHEHHHHHEH¸HMHHHHHfEE;EHEHEH¸HMHHHHHHE @HUHHǸHH[]Hl$Ld$H- L% Ll$Lt$L|$H\$H8L)AIHI[Ht1@LLDAHH9uH\$Hl$Ld$Ll$ Lt$(L|$0H8ÐUHSHHX Ht `DHHHuH[]ÐHHmallocrealloc resultng cmdline str: <%s> ;,HphzRx $HFJ w?;*3$"$DAC X $l8Q_@X @ @o`@X@@ a  `@@ o@oo@@ `@@@@&@6@F@GCC: (GNU) 4.6.2 20111027 (Red Hat 4.6.2-1).symtab.strtab.shstrtab.interp.note.ABI-tag.note.gnu.build-id.gnu.hash.dynsym.dynstr.gnu.version.gnu.version_r.rela.dyn.rela.plt.init.text.fini.rodata.eh_frame_hdr.eh_frame.ctors.dtors.jcr.dynamic.got.got.plt.data.bss.comment@#@ 1<@<$Do`@`N @VX@Xa^o@ko@ z@@ @@P@P@@=8 @8 ,h @h  ` ( `( 8 `8 @ `@  `  ` P( `( 0 `, 0, ,X x. PT@@<@`@@X@@@ @ @ @ @ P@@@8 @h @ `( `8 `@ ` ` `( `0 ` |@ `*( `88 `E @[0 `j8 `x @ ` @8 ` @ `@ ` ` `  @ ( `%, `,@2F0 `Sr( `  @@ @@ ` P@, ` 4@ (<N @call_gmon_startcrtstuff.c__CTOR_LIST____DTOR_LIST____JCR_LIST____do_global_dtors_auxcompleted.5895dtor_idx.5897frame_dummy__CTOR_END____FRAME_END____JCR_END____do_global_ctors_auxfoo.c__init_array_end_DYNAMIC__init_array_start_GLOBAL_OFFSET_TABLE___libc_csu_finidata_start_edata_finiprintf@@GLIBC_2.2.5__DTOR_END____libc_start_main@@GLIBC_2.2.5__data_start__gmon_start____dso_handle_IO_stdin_used__libc_csu_initmalloc@@GLIBC_2.2.5_end_startrealloc@@GLIBC_2.2.5__bss_startmainperror@@GLIBC_2.2.5_Jv_RegisterClassesstrcat@@GLIBC_2.2.5exit@@GLIBC_2.2.5_initrt-tests/src/backfire/0000700001204700120500000000000012252162340015403 5ustar williamswilliamsrt-tests/src/backfire/.gitignore0000600001204700120500000000007112252162340017373 0ustar williamswilliamsModule.symvers backfire.ko backfire.mod.c modules.order rt-tests/src/backfire/backfire.40000600001204700120500000000202112252162340017233 0ustar williamswilliams.TH "backfire" "4" "0.1" "" "Driver" .SH "NAME" .LP backfire \- send a signal from driver to user .SH "DESCRIPTION" .LP The \fBbackfire\fR driver reads a numerical string that is sent to the \fB/dev/backfire\fR device and sends the corresponding signal to the calling user program. Reading from \fB/dev/backfire\fR returns the time of the day when the most recent sent request was serviced or 0, if a sent request was not yet received. The time of the day is displayed in seconds since 1970-01-01 00:00:00 UTC followed by the fraction of the second in microseconds separated by a comma. .SH "PURPOSE" .LP The \fBbackfire\fR driver is normally used in combination with the program \fBsendme\fR to benchmark the performance of the kernel's signal sending capabilities. .SH "EXAMPLES" .LP .nf head -1 /dev/backfire 0,0 trap "echo Got signal 7" 7 echo 7 >/dev/backfire Got signal 7 head -1 /dev/backfire 1234567890,123456 .fi .LP .SH "AUTHORS" .LP Carsten Emde .SH "SEE ALSO" .LP sendme(8) rt-tests/src/backfire/sendme.80000600001204700120500000000262612252162340016757 0ustar williamswilliams.TH "sendme" "8" "0.2" "" "" .SH "NAME" .LP \fBsendme\fR \- Send a signal from driver to user and measure time intervals .SH "SYNTAX" .LP sendme [-a|-a PROC] [-b USEC] [-l loops] [-p PRIO] .br .SH "DESCRIPTION" .LP The program \fBsendme\fR uses the \fBbackfire\fR driver to send a signal from driver to user. It then reads the timestamp from the driver and calculates the time intervals to call the driver and to receive the signal from the driver. .SH "OPTIONS" .TP .B \-a, \-\-affinity[=PROC] Run on procesor number PROC. If PROC is not specified, run on current processor. .TP .B \-b, \-\-breaktrace=USEC Send break trace command when latency > USEC. This is a debugging option to control the latency tracer in the realtime preemption patch. It is useful to track down unexpected large latencies on a system. .TP .B \-l, \-\-loops=LOOPS Set the number of loops. The default is 0 (endless). This option is useful for automated tests with a given number of test cycles. Sendme is stopped once the number of timer intervals has been reached. .TP .B \-p, \-\-prio=PRIO Set the priority of the process. .SH "FILES" backfire.ko .SH "EXAMPLES" .LP .nf # modprobe backfire # sendme -a -p99 -l1000000 Samples: 1000000 To: Min 0, Cur 0, Avg 1, Max 11 From: Min 2, Cur 3, Avg 3, Max 43 .fi .SH "AUTHORS" .LP Carsten Emde .SH "SEE ALSO" .LP backfire(4) rt-tests/src/backfire/.tmp_versions/0000700001204700120500000000000012252162340020211 5ustar williamswilliamsrt-tests/src/backfire/.backfire.o.d0000600001204700120500000000000012252162340017621 0ustar williamswilliamsrt-tests/src/backfire/backfire.c0000600001204700120500000000736212252162340017327 0ustar williamswilliams/* * backfire - send signal back to caller * * Copyright (C) 2007 Carsten Emde * * 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 program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, * USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #define BACKFIRE_MINOR MISC_DYNAMIC_MINOR static spinlock_t backfire_state_lock = SPIN_LOCK_UNLOCKED; static int backfire_open_cnt; /* #times opened */ static int backfire_open_mode; /* special open modes */ static struct timeval sendtime; /* when the most recent signal was sent */ #define BACKFIRE_WRITE 1 /* opened for writing (exclusive) */ #define BACKFIRE_EXCL 2 /* opened with O_EXCL */ /* * These are the file operation function for user access to /dev/backfire */ static ssize_t backfire_read(struct file *file, char *buf, size_t count, loff_t *ppos) { return snprintf(buf, count, "%d,%d\n", (int) sendtime.tv_sec, (int) sendtime.tv_usec); } static ssize_t backfire_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { int signo; struct pid *pid; if (sscanf(buf, "%d", &signo) >= 1) { if (signo > 0 && signo < 32) { pid = get_pid(task_pid(current)); do_gettimeofday(&sendtime); kill_pid(pid, signo, 1); } else printk(KERN_ERR "Invalid signal no. %d\n", signo); } return strlen(buf); } static int backfire_open(struct inode *inode, struct file *file) { spin_lock(&backfire_state_lock); if ((backfire_open_cnt && (file->f_flags & O_EXCL)) || (backfire_open_mode & BACKFIRE_EXCL)) { spin_unlock(&backfire_state_lock); return -EBUSY; } if (file->f_flags & O_EXCL) backfire_open_mode |= BACKFIRE_EXCL; if (file->f_mode & 2) backfire_open_mode |= BACKFIRE_WRITE; backfire_open_cnt++; spin_unlock(&backfire_state_lock); return 0; } static int backfire_release(struct inode *inode, struct file *file) { spin_lock(&backfire_state_lock); backfire_open_cnt--; if (backfire_open_cnt == 1 && backfire_open_mode & BACKFIRE_EXCL) backfire_open_mode &= ~BACKFIRE_EXCL; if (file->f_mode & 2) backfire_open_mode &= ~BACKFIRE_WRITE; spin_unlock(&backfire_state_lock); return 0; } static struct file_operations backfire_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .open = backfire_open, .read = backfire_read, .write = backfire_write, .release = backfire_release, }; static struct miscdevice backfire_dev = { BACKFIRE_MINOR, "backfire", &backfire_fops }; static int __init backfire_init(void) { int ret; ret = misc_register(&backfire_dev); if (ret) printk(KERN_ERR "backfire: can't register dynamic misc device\n"); else printk(KERN_INFO "backfire driver misc device %d\n", backfire_dev.minor); return ret; } static void __exit backfire_exit(void) { misc_deregister(&backfire_dev); } module_init(backfire_init); module_exit(backfire_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Carsten Emde "); MODULE_DESCRIPTION("Send signal back to caller"); rt-tests/src/backfire/sendme.c0000600001204700120500000001715512252162340017035 0ustar williamswilliams/* * sendme.c * * Copyright (C) 2009 Carsten Emde * * 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 program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, * USA. */ #include #include #include #include #include #include #include #include #include #include #include #include "rt-utils.h" #include "rt-get_cpu.h" #include #include #include #include #include #define USEC_PER_SEC 1000000 #define NSEC_PER_SEC 1000000000 #define SIGTEST SIGHUP enum { AFFINITY_UNSPECIFIED, AFFINITY_SPECIFIED, AFFINITY_USECURRENT }; static int setaffinity = AFFINITY_UNSPECIFIED; static int affinity; static int tracelimit; static int priority; static int shutdown; static int max_cycles; static volatile struct timeval after; static int interval = 1000; static int kernvar(int mode, const char *name, char *value, size_t sizeofvalue) { char filename[128]; char *fileprefix = get_debugfileprefix(); int retval = 1; int path; size_t len_prefix = strlen(fileprefix), len_name = strlen(name); if (len_prefix + len_name + 1 > sizeof(filename)) { errno = ENOMEM; return 1; } memcpy(filename, fileprefix, len_prefix); memcpy(filename + len_prefix, name, len_name + 1); path = open(filename, mode); if (path >= 0) { if (mode == O_RDONLY) { int got; if ((got = read(path, value, sizeofvalue)) > 0) { retval = 0; value[got-1] = '\0'; } } else if (mode == O_WRONLY) { if (write(path, value, sizeofvalue) == sizeofvalue) retval = 0; } close(path); } return retval; } void signalhandler(int signo) { struct timeval tv; gettimeofday(&tv, NULL); after = tv; if (signo == SIGINT || signo == SIGTERM) shutdown = 1; } void stop_tracing(void) { kernvar(O_WRONLY, "tracing_enabled", "0", 1); } static void display_help(void) { printf("sendme V %1.2f\n", VERSION_STRING); puts("Usage: sendme "); puts("Function: send a signal from driver to userspace"); puts( "Options:\n" "-a [NUM] --affinity pin to current processor\n" " with NUM pin to processor NUM\n" "-b USEC --breaktrace=USEC send break trace command when latency > USEC\n" "-i INTV --interval=INTV base interval of thread in us default=1000\n" "-l LOOPS --loops=LOOPS number of loops: default=0(endless)\n" "-p PRIO --prio=PRIO priority\n"); exit(1); } static void process_options (int argc, char *argv[]) { int error = 0; int max_cpus = sysconf(_SC_NPROCESSORS_CONF); for (;;) { int option_index = 0; /** Options for getopt */ static struct option long_options[] = { {"affinity", optional_argument, NULL, 'a'}, {"breaktrace", required_argument, NULL, 'b'}, {"interval", required_argument, NULL, 'i'}, {"loops", required_argument, NULL, 'l'}, {"priority", required_argument, NULL, 'p'}, {"help", no_argument, NULL, '?'}, {NULL, 0, NULL, 0} }; int c = getopt_long (argc, argv, "a::b:i:l:p:", long_options, &option_index); if (c == -1) break; switch (c) { case 'a': if (optarg != NULL) { affinity = atoi(optarg); setaffinity = AFFINITY_SPECIFIED; } else if (optind < argc && atoi(argv[optind])) { affinity = atoi(argv[optind]); setaffinity = AFFINITY_SPECIFIED; } else setaffinity = AFFINITY_USECURRENT; break; case 'b': tracelimit = atoi(optarg); break; case 'i': interval = atoi(optarg); break; case 'l': max_cycles = atoi(optarg); break; case 'p': priority = atoi(optarg); break; case '?': error = 1; break; } } if (setaffinity == AFFINITY_SPECIFIED) { if (affinity < 0) error = 1; if (affinity >= max_cpus) { fprintf(stderr, "ERROR: CPU #%d not found, only %d CPUs available\n", affinity, max_cpus); error = 1; } } if (priority < 0 || priority > 99) error = 1; if (error) display_help (); } int main(int argc, char *argv[]) { int path; cpu_set_t mask; int policy = SCHED_FIFO; struct sched_param schedp; struct flock fl; int retval = 0; process_options(argc, argv); if (check_privs()) return 1; if (mlockall(MCL_CURRENT|MCL_FUTURE) == -1) { perror("mlockall"); return 1; } memset(&schedp, 0, sizeof(schedp)); schedp.sched_priority = priority; sched_setscheduler(0, policy, &schedp); if (setaffinity != AFFINITY_UNSPECIFIED) { CPU_ZERO(&mask); if (setaffinity == AFFINITY_USECURRENT) { get_cpu_setup(); affinity = get_cpu(); } CPU_SET(affinity, &mask); if (sched_setaffinity(0, sizeof(mask), &mask) == -1) fprintf(stderr, "WARNING: Could not set CPU affinity " "to CPU #%d\n", affinity); } path = open("/dev/backfire", O_RDWR); if (path < 0) { fprintf(stderr, "ERROR: Could not access backfire device, " "try 'modprobe backfire'\n" "If the module backfire can't be loaded, " "it may need to be built first.\n" "Execute 'cd src/backfire; make' in the " "rt-tests directory (requires rt-tests\n" "sources and kernel-devel package).\n"); return 1; } fl.l_type = F_WRLCK; fl.l_whence = SEEK_SET; fl.l_start = 0; fl.l_len = 1; if (fcntl(path, F_SETLK, &fl) == -1) { fprintf(stderr, "ERRROR: backfire device locked\n"); retval = 1; } else { char sigtest[8]; char timestamp[32]; struct timeval before, sendtime, diff; unsigned int diffno = 0; unsigned int mindiff1 = UINT_MAX, maxdiff1 = 0; unsigned int mindiff2 = UINT_MAX, maxdiff2 = 0; double sumdiff1 = 0.0, sumdiff2 = 0.0; if (tracelimit) kernvar(O_WRONLY, "tracing_enabled", "1", 1); sprintf(sigtest, "%d", SIGTEST); signal(SIGTEST, signalhandler); signal(SIGINT, signalhandler); signal(SIGTERM, signalhandler); while (1) { struct timespec ts; ts.tv_sec = interval / USEC_PER_SEC; ts.tv_nsec = (interval % USEC_PER_SEC) * 1000; gettimeofday(&before, NULL); write(path, sigtest, strlen(sigtest)); while (after.tv_sec == 0); read(path, timestamp, sizeof(timestamp)); if (sscanf(timestamp, "%lu,%lu\n", &sendtime.tv_sec, &sendtime.tv_usec) != 2) break; diffno++; if(max_cycles && diffno >= max_cycles) shutdown = 1; printf("Samples: %8d\n", diffno); timersub(&sendtime, &before, &diff); if (diff.tv_usec < mindiff1) mindiff1 = diff.tv_usec; if (diff.tv_usec > maxdiff1) maxdiff1 = diff.tv_usec; sumdiff1 += (double) diff.tv_usec; printf("To: Min %4d, Cur %4d, Avg %4d, Max %4d\n", mindiff1, (int) diff.tv_usec, (int) ((sumdiff1 / diffno) + 0.5), maxdiff1); timersub(&after, &sendtime, &diff); if (diff.tv_usec < mindiff2) mindiff2 = diff.tv_usec; if (diff.tv_usec > maxdiff2) maxdiff2 = diff.tv_usec; sumdiff2 += (double) diff.tv_usec; printf("From: Min %4d, Cur %4d, Avg %4d, Max %4d\n", mindiff2, (int) diff.tv_usec, (int) ((sumdiff2 / diffno) + 0.5), maxdiff2); after.tv_sec = 0; if ((tracelimit && diff.tv_usec > tracelimit) || shutdown) { if (tracelimit) stop_tracing(); break; } nanosleep(&ts, NULL); printf("\033[3A"); } } close(path); return retval; } rt-tests/src/backfire/version.h0000600001204700120500000000002612252162340017241 0ustar williamswilliams#define VERSION "0.5" rt-tests/src/backfire/Makefile0000600001204700120500000000065312252162340017051 0ustar williamswilliams# If KERNELRELEASE is defined, we've been invoked from the # kernel build system and can use its language ifneq ($(KERNELRELEASE),) obj-m := backfire.o # otherwise we were called directly from the command # line; invoke the kernel build system. else KERNELDIR ?= /lib/modules/$(shell uname -r)/build modules modules_install clean:: make -C $(KERNELDIR) M=$(CURDIR) $@ clean:: rm -f *.o Module.markers modules.order endif rt-tests/src/backfire/Makefile.module0000600001204700120500000000021212252162340020324 0ustar williamswilliamsobj-m := backfire.o KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules rt-tests/src/pmqtest/0000700001204700120500000000000012252162340015332 5ustar williamswilliamsrt-tests/src/pmqtest/pmqtest.80000600001204700120500000000637612252162340017136 0ustar williamswilliams.TH "pmqtest" "8" "0.1" "" "" .SH "NAME" .LP \fBpmqtest\fR \- Start pairs of threads and measure the latency of interprocess communication with POSIX messages queues .SH "SYNTAX" .LP pmqtest [-a|-a PROC] [-b USEC] [-d DIST] [-i INTV] [-l loops] [-p PRIO] [-S] [-t|-t NUM] [-T TO] .br .SH "DESCRIPTION" .LP The program \fBpmqtest\fR starts pairs of threads that are synchronized via mq_send/mw_receive() and measures the latency between sending and receiving the message. .SH "OPTIONS" .TP .B \-a, \-\-affinity[=PROC] Run on procesor number PROC. If PROC is not specified, run on current processor. .TP .B \-b, \-\-breaktrace=USEC Send break trace command when latency > USEC. This is a debugging option to control the latency tracer in the realtime preemption patch. It is useful to track down unexpected large latencies of a system. .TP .B \-d, \-\-distance=DIST Set the distance of thread intervals in microseconds (default is 500 us). When pmqtest is called with the -t option and more than one thread is created, then this distance value is added to the interval of the threads: Interval(thread N) = Interval(thread N-1) + DIST .TP .B \-f, \-\-forcetimeout=TO Set an artificial delay of the send function to force timeout of the receiver, requires the -T option .TP .B \-i, \-\-interval=INTV Set the base interval of the thread(s) in microseconds (default is 1000 us). This sets the interval of the first thread. See also -d. .TP .B \-l, \-\-loops=LOOPS Set the number of loops. The default is 0 (endless). This option is useful for automated tests with a given number of test cycles. pmqtest is stopped once the number of timer intervals has been reached. .TP .B \-p, \-\-prio=PRIO Set the priority of the process. .TP .B \-S, \-\-smp Test mode for symmetric multi-processing, implies -a and -t and uses the same priority on all threads. .TP .B \-t, \-\-threads[=NUM] Set the number of test threads (default is 1, if this option is not given). If NUM is specified, create NUM test threads. If NUM is not specifed, NUM is set to the number of available CPUs. .TP .B \-T, \-\-timeout=TO Use mq_timedreceive() instead of mq_receive() and specify timeout TO in seconds. .SH "EXAMPLES" The following example was running on an 8-way processor: .LP .nf # pmqtest -Sp99 -i100 -d0 #0: ID10047, P99, CPU0, I100; #1: ID10048, P99, CPU0, Cycles 153695 #2: ID10049, P99, CPU1, I100; #3: ID10050, P99, CPU1, Cycles 154211 #4: ID10051, P99, CPU2, I100; #5: ID10052, P99, CPU2, Cycles 156823 #6: ID10053, P99, CPU3, I100; #7: ID10054, P99, CPU3, Cycles 158202 #8: ID10055, P99, CPU4, I100; #9: ID10056, P99, CPU4, Cycles 153399 #10: ID10057, P99, CPU5, I100; #11: ID10058, P99, CPU5, Cycles 153992 #12: ID10059, P99, CPU6, I100; #13: ID10060, P99, CPU6, Cycles 156576 #14: ID10061, P99, CPU7, I100; #15: ID10062, P99, CPU7, Cycles 157957 #1 -> #0, Min 1, Cur 8, Avg 5, Max 18 #3 -> #2, Min 1, Cur 4, Avg 5, Max 18 #5 -> #4, Min 1, Cur 5, Avg 5, Max 19 #7 -> #6, Min 1, Cur 4, Avg 4, Max 17 #9 -> #8, Min 1, Cur 9, Avg 5, Max 18 #11 -> #10, Min 1, Cur 8, Avg 5, Max 18 #13 -> #12, Min 1, Cur 4, Avg 5, Max 29 #15 -> #14, Min 1, Cur 8, Avg 4, Max 17 .fi .SH "AUTHORS" .LP Carsten Emde .SH "SEE ALSO" .LP mq_send(3p), mq_receive(3p) rt-tests/src/pmqtest/Makefile0000600001204700120500000000032012252162340016767 0ustar williamswilliamsCFLAGS += -Wall -O2 LDFLAGS += -lpthread all: pmqtest @echo Done pmqtest.o: pmqtest.c pmqtest: clean: @rm -f *.o tar: clean @rm -f pmqtest $(shell bn=`basename $$PWD`; cd ..; tar -zcf $$bn.tgz $$bn) rt-tests/src/pmqtest/pmqtest.c0000600001204700120500000003626712252162340017213 0ustar williamswilliams/* * pmqtest.c * * Copyright (C) 2009 Carsten Emde * * 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 program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, * USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rt-utils.h" #include "rt-get_cpu.h" #include "error.h" #include #define gettid() syscall(__NR_gettid) #define USEC_PER_SEC 1000000 #define SYNCMQ_NAME "/syncmsg%d" #define TESTMQ_NAME "/testmsg%d" #define MSG_SIZE 8 #define MSEC_PER_SEC 1000 #define NSEC_PER_SEC 1000000000 char *syncmsg = "Syncing"; char *testmsg = "Testing"; enum { AFFINITY_UNSPECIFIED, AFFINITY_SPECIFIED, AFFINITY_USEALL }; struct params { int num; int cpu; int priority; int affinity; int sender; int samples; int max_cycles; int tracelimit; int tid; int shutdown; int stopped; struct timespec delay; unsigned int mindiff, maxdiff; double sumdiff; struct timeval sent, received, diff; pthread_t threadid; int timeout; int forcetimeout; int timeoutcount; mqd_t syncmq, testmq; char recvsyncmsg[MSG_SIZE]; char recvtestmsg[MSG_SIZE]; struct params *neighbor; char error[MAX_PATH * 2]; }; void *pmqthread(void *param) { int mustgetcpu = 0; struct params *par = param; cpu_set_t mask; int policy = SCHED_FIFO; struct sched_param schedp; struct timespec ts; memset(&schedp, 0, sizeof(schedp)); schedp.sched_priority = par->priority; sched_setscheduler(0, policy, &schedp); if (par->cpu != -1) { CPU_ZERO(&mask); CPU_SET(par->cpu, &mask); if(sched_setaffinity(0, sizeof(mask), &mask) == -1) fprintf(stderr, "WARNING: Could not set CPU affinity " "to CPU #%d\n", par->cpu); } else mustgetcpu = 1; par->tid = gettid(); while (!par->shutdown) { if (par->sender) { /* Optionally force receiver timeout */ if (par->forcetimeout) { struct timespec senddelay; senddelay.tv_sec = par->forcetimeout; senddelay.tv_nsec = 0; clock_nanosleep(CLOCK_MONOTONIC, 0, &senddelay, NULL); } /* Send message: Start of latency measurement ... */ gettimeofday(&par->sent, NULL); if (mq_send(par->testmq, testmsg, strlen(testmsg), 1) != 0) { fprintf(stderr, "could not send test message\n"); par->shutdown = 1; } par->samples++; if(par->max_cycles && par->samples >= par->max_cycles) par->shutdown = 1; if (mustgetcpu) par->cpu = get_cpu(); /* Wait until receiver ready */ if (par->timeout) { clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec += par->timeout; if (mq_timedreceive(par->syncmq, par->recvsyncmsg, MSG_SIZE, NULL, &ts) != strlen(syncmsg)) { fprintf(stderr, "could not receive sync message\n"); par->shutdown = 1; } } if (mq_receive(par->syncmq, par->recvsyncmsg, MSG_SIZE, NULL) != strlen(syncmsg)) { perror("could not receive sync message"); par->shutdown = 1; } if (!par->shutdown && strcmp(syncmsg, par->recvsyncmsg)) { fprintf(stderr, "ERROR: Sync message mismatch detected\n"); fprintf(stderr, " %s != %s\n", syncmsg, par->recvsyncmsg); par->shutdown = 1; } } else { /* Receiver */ if (par->timeout) { clock_gettime(CLOCK_REALTIME, &ts); par->timeoutcount = 0; ts.tv_sec += par->timeout; do { if (mq_timedreceive(par->testmq, par->recvtestmsg, MSG_SIZE, NULL, &ts) != strlen(testmsg)) { if (!par->forcetimeout || errno != ETIMEDOUT) { perror("could not receive test message"); par->shutdown = 1; break; } if (errno == ETIMEDOUT) { par->timeoutcount++; clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec += par->timeout; } } else break; } while (1); } else { if (mq_receive(par->testmq, par->recvtestmsg, MSG_SIZE, NULL) != strlen(testmsg)) { perror("could not receive test message"); par->shutdown = 1; } } /* ... Received the message: End of latency measurement */ gettimeofday(&par->received, NULL); if (!par->shutdown && strcmp(testmsg, par->recvtestmsg)) { fprintf(stderr, "ERROR: Test message mismatch detected\n"); fprintf(stderr, " %s != %s\n", testmsg, par->recvtestmsg); par->shutdown = 1; } par->samples++; timersub(&par->received, &par->neighbor->sent, &par->diff); if (par->diff.tv_usec < par->mindiff) par->mindiff = par->diff.tv_usec; if (par->diff.tv_usec > par->maxdiff) par->maxdiff = par->diff.tv_usec; par->sumdiff += (double) par->diff.tv_usec; if (par->tracelimit && par->maxdiff > par->tracelimit) { char tracing_enabled_file[MAX_PATH]; strcpy(tracing_enabled_file, get_debugfileprefix()); strcat(tracing_enabled_file, "tracing_enabled"); int tracing_enabled = open(tracing_enabled_file, O_WRONLY); if (tracing_enabled >= 0) { write(tracing_enabled, "0", 1); close(tracing_enabled); } else snprintf(par->error, sizeof(par->error), "Could not access %s\n", tracing_enabled_file); par->shutdown = 1; par->neighbor->shutdown = 1; } if (par->max_cycles && par->samples >= par->max_cycles) par->shutdown = 1; if (mustgetcpu) par->cpu = get_cpu(); clock_nanosleep(CLOCK_MONOTONIC, 0, &par->delay, NULL); /* Tell receiver that we are ready for the next measurement */ if (mq_send(par->syncmq, syncmsg, strlen(syncmsg), 1) != 0) { fprintf(stderr, "could not send sync message\n"); par->shutdown = 1; } } } par->stopped = 1; return NULL; } static void display_help(void) { printf("pmqtest V %1.2f\n", VERSION_STRING); puts("Usage: pmqtest "); puts("Function: test POSIX message queue latency"); puts( "Options:\n" "-a [NUM] --affinity run thread #N on processor #N, if possible\n" " with NUM pin all threads to the processor NUM\n" "-b USEC --breaktrace=USEC send break trace command when latency > USEC\n" "-d DIST --distance=DIST distance of thread intervals in us default=500\n" "-f TO --forcetimeout=TO force timeout of mq_timedreceive(), requires -T\n" "-i INTV --interval=INTV base interval of thread in us default=1000\n" "-l LOOPS --loops=LOOPS number of loops: default=0(endless)\n" "-p PRIO --prio=PRIO priority\n" "-S --smp SMP testing: options -a -t and same priority\n" " of all threads\n" "-t --threads one thread per available processor\n" "-t [NUM] --threads=NUM number of threads:\n" " without NUM, threads = max_cpus\n" " without -t default = 1\n" "-T TO --timeout=TO use mq_timedreceive() instead of mq_receive()\n" " with timeout TO in seconds\n"); exit(1); } static int setaffinity = AFFINITY_UNSPECIFIED; static int affinity; static int tracelimit; static int priority; static int num_threads = 1; static int max_cycles; static int interval = 1000; static int distance = 500; static int smp; static int sameprio; static int timeout; static int forcetimeout; static void process_options (int argc, char *argv[]) { int error = 0; int max_cpus = sysconf(_SC_NPROCESSORS_CONF); for (;;) { int option_index = 0; /** Options for getopt */ static struct option long_options[] = { {"affinity", optional_argument, NULL, 'a'}, {"breaktrace", required_argument, NULL, 'b'}, {"distance", required_argument, NULL, 'd'}, {"forcetimeout", required_argument, NULL, 'f'}, {"interval", required_argument, NULL, 'i'}, {"loops", required_argument, NULL, 'l'}, {"priority", required_argument, NULL, 'p'}, {"smp", no_argument, NULL, 'S'}, {"threads", optional_argument, NULL, 't'}, {"timeout", required_argument, NULL, 'T'}, {"help", no_argument, NULL, '?'}, {NULL, 0, NULL, 0} }; int c = getopt_long (argc, argv, "a::b:d:f:i:l:p:St::T:", long_options, &option_index); if (c == -1) break; switch (c) { case 'a': if (smp) { warn("-a ignored due to --smp\n"); break; } if (optarg != NULL) { affinity = atoi(optarg); setaffinity = AFFINITY_SPECIFIED; } else if (optind= max_cpus) { fprintf(stderr, "ERROR: CPU #%d not found, only %d CPUs available\n", affinity, max_cpus); error = 1; } } if (num_threads < 0 || num_threads > 255) error = 1; if (priority < 0 || priority > 99) error = 1; if (num_threads < 1) error = 1; if (forcetimeout && !timeout) error = 1; if (priority && smp) sameprio = 1; if (error) display_help (); } static int volatile shutdown; static void sighand(int sig) { shutdown = 1; } int main(int argc, char *argv[]) { int i; int max_cpus = sysconf(_SC_NPROCESSORS_CONF); struct params *receiver = NULL; struct params *sender = NULL; sigset_t sigset; int oldsamples = INT_MAX; int oldtimeoutcount = INT_MAX; int first = 1; int errorlines = 0; struct timespec maindelay; int oflag = O_CREAT|O_RDWR; struct mq_attr mqstat; memset(&mqstat, 0, sizeof(mqstat)); mqstat.mq_maxmsg = 1; mqstat.mq_msgsize = 8; mqstat.mq_flags = 0; process_options(argc, argv); if (check_privs()) return 1; if (mlockall(MCL_CURRENT|MCL_FUTURE) == -1) { perror("mlockall"); return 1; } sigemptyset(&sigset); sigaddset(&sigset, SIGTERM); sigaddset(&sigset, SIGINT); pthread_sigmask(SIG_SETMASK, &sigset, NULL); signal(SIGINT, sighand); signal(SIGTERM, sighand); receiver = calloc(num_threads, sizeof(struct params)); sender = calloc(num_threads, sizeof(struct params)); if (receiver == NULL || sender == NULL) goto nomem; for (i = 0; i < num_threads; i++) { char mqname[16]; sprintf(mqname, SYNCMQ_NAME, i); receiver[i].syncmq = mq_open(mqname, oflag, 0777, &mqstat); if (receiver[i].syncmq == (mqd_t) -1) { fprintf(stderr, "could not open POSIX message queue #1\n"); return 1; } sprintf(mqname, TESTMQ_NAME, i); receiver[i].testmq = mq_open(mqname, oflag, 0777, &mqstat); if (receiver[i].testmq == (mqd_t) -1) { fprintf(stderr, "could not open POSIX message queue #2\n"); return 1; } receiver[i].mindiff = UINT_MAX; receiver[i].maxdiff = 0; receiver[i].sumdiff = 0.0; receiver[i].num = i; receiver[i].cpu = i; switch (setaffinity) { case AFFINITY_UNSPECIFIED: receiver[i].cpu = -1; break; case AFFINITY_SPECIFIED: receiver[i].cpu = affinity; break; case AFFINITY_USEALL: receiver[i].cpu = i % max_cpus; break; } receiver[i].priority = priority; receiver[i].tracelimit = tracelimit; if (priority > 1 && !sameprio) priority--; receiver[i].delay.tv_sec = interval / USEC_PER_SEC; receiver[i].delay.tv_nsec = (interval % USEC_PER_SEC) * 1000; interval += distance; receiver[i].max_cycles = max_cycles; receiver[i].sender = 0; receiver[i].neighbor = &sender[i]; receiver[i].timeout = timeout; receiver[i].forcetimeout = forcetimeout; pthread_create(&receiver[i].threadid, NULL, pmqthread, &receiver[i]); memcpy(&sender[i], &receiver[i], sizeof(receiver[0])); sender[i].sender = 1; sender[i].neighbor = &receiver[i]; pthread_create(&sender[i].threadid, NULL, pmqthread, &sender[i]); } maindelay.tv_sec = 0; maindelay.tv_nsec = 50000000; /* 50 ms */ sigemptyset(&sigset); pthread_sigmask(SIG_SETMASK, &sigset, NULL); do { int newsamples = 0, newtimeoutcount = 0; int minsamples = INT_MAX; for (i = 0; i < num_threads; i++) { newsamples += receiver[i].samples; newtimeoutcount += receiver[i].timeoutcount; if (receiver[i].samples < minsamples) minsamples = receiver[i].samples; } if (minsamples > 1 && (shutdown || newsamples > oldsamples || newtimeoutcount > oldtimeoutcount)) { if (!first) printf("\033[%dA", num_threads*2 + errorlines); first = 0; for (i = 0; i < num_threads; i++) { printf("#%1d: ID%d, P%d, CPU%d, I%ld; #%1d: ID%d, P%d, CPU%d, TO %d, Cycles %d \n", i*2, receiver[i].tid, receiver[i].priority, receiver[i].cpu, receiver[i].delay.tv_nsec / 1000, i*2+1, sender[i].tid, sender[i].priority, sender[i].cpu, receiver[i].timeoutcount, sender[i].samples); } for (i = 0; i < num_threads; i++) { printf("#%d -> #%d, Min %4d, Cur %4d, Avg %4d, Max %4d\n", i*2+1, i*2, receiver[i].mindiff, (int) receiver[i].diff.tv_usec, (int) ((receiver[i].sumdiff / receiver[i].samples) + 0.5), receiver[i].maxdiff); if (receiver[i].error[0] != '\0') { printf("%s", receiver[i].error); errorlines++; receiver[i].error[0] = '\0'; } if (sender[i].error[0] != '\0') { printf("%s", sender[i].error); errorlines++; receiver[i].error[0] = '\0'; } } } else { if (minsamples < 1) printf("Collecting ...\n\033[1A"); } fflush(NULL); oldsamples = 0; oldtimeoutcount = 0; for (i = 0; i < num_threads; i++) { oldsamples += receiver[i].samples; oldtimeoutcount += receiver[i].timeoutcount; } nanosleep(&maindelay, NULL); for (i = 0; i < num_threads; i++) shutdown |= receiver[i].shutdown | sender[i].shutdown; } while (!shutdown); for (i = 0; i < num_threads; i++) { receiver[i].shutdown = 1; sender[i].shutdown = 1; } for (i = 0; i < num_threads; i++) { if (!receiver[i].stopped) pthread_kill(receiver[i].threadid, SIGTERM); if (!sender[i].stopped) pthread_kill(sender[i].threadid, SIGTERM); } nanosleep(&maindelay, NULL); for (i = 0; i < num_threads; i++) { char mqname[16]; mq_close(receiver[i].syncmq); sprintf(mqname, SYNCMQ_NAME, i); mq_unlink(mqname); mq_close(receiver[i].testmq); sprintf(mqname, TESTMQ_NAME, i); mq_unlink(mqname); } nomem: return 0; } rt-tests/src/sigwaittest/0000700001204700120500000000000012252162340016204 5ustar williamswilliamsrt-tests/src/sigwaittest/sigwaittest.c0000600001204700120500000003657112252162340020735 0ustar williamswilliams/* * sigwaittest.c * * Copyright (C) 2009 Carsten Emde * * 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 program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, * USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rt-utils.h" #include "rt-get_cpu.h" #include #define gettid() syscall(__NR_gettid) #define USEC_PER_SEC 1000000 enum { AFFINITY_UNSPECIFIED, AFFINITY_SPECIFIED, AFFINITY_USEALL }; struct params { int num; int num_threads; int cpu; int priority; int affinity; int sender; int samples; int max_cycles; int tracelimit; int tid; pid_t pid; int shutdown; int stopped; struct timespec delay; unsigned int mindiff, maxdiff; double sumdiff; struct timeval unblocked, received, diff; pthread_t threadid; struct params *neighbor; char error[MAX_PATH * 2]; }; static int mustfork; static int wasforked; static int wasforked_sender = -1; static int wasforked_threadno = -1; static int tracelimit; void *semathread(void *param) { int mustgetcpu = 0; struct params *par = param; cpu_set_t mask; int policy = SCHED_FIFO; struct sched_param schedp; memset(&schedp, 0, sizeof(schedp)); schedp.sched_priority = par->priority; sched_setscheduler(0, policy, &schedp); if (par->cpu != -1) { CPU_ZERO(&mask); CPU_SET(par->cpu, &mask); if(sched_setaffinity(0, sizeof(mask), &mask) == -1) fprintf(stderr, "WARNING: Could not set CPU affinity " "to CPU #%d\n", par->cpu); } else { int max_cpus = sysconf(_SC_NPROCESSORS_CONF); if (max_cpus > 1) mustgetcpu = 1; else par->cpu = 0; } if (!wasforked) par->tid = gettid(); while (!par->shutdown) { int sig; int first = 1; sigset_t sigset; struct params *neighbor = NULL; if (par->sender) { if (wasforked) neighbor = par - par->num_threads; else neighbor = par->neighbor; if (first) { sigemptyset(&sigset); sigaddset(&sigset, SIGUSR1); pthread_sigmask(SIG_SETMASK, &sigset, NULL); first = 0; } /* Sending signal: Start of latency measurement ... */ gettimeofday(&par->unblocked, NULL); if (wasforked) kill(neighbor->pid, SIGUSR2); else pthread_kill(neighbor->threadid, SIGUSR2); par->samples++; if(par->max_cycles && par->samples >= par->max_cycles) par->shutdown = 1; if (mustgetcpu) { par->cpu = get_cpu(); } sigwait(&sigset, &sig); } else { /* Receiver */ if (wasforked) neighbor = par + par->num_threads; else neighbor = par->neighbor; if (first) { sigemptyset(&sigset); sigaddset(&sigset, SIGUSR2); pthread_sigmask(SIG_SETMASK, &sigset, NULL); first = 0; } sigwait(&sigset, &sig); /* ... Signal received: End of latency measurement */ gettimeofday(&par->received, NULL); par->samples++; if (par->max_cycles && par->samples >= par->max_cycles) par->shutdown = 1; if (mustgetcpu) { par->cpu = get_cpu(); } /* * Latency is the time spent between sending and * receiving the signal. */ timersub(&par->received, &neighbor->unblocked, &par->diff); if (par->diff.tv_usec < par->mindiff) par->mindiff = par->diff.tv_usec; if (par->diff.tv_usec > par->maxdiff) par->maxdiff = par->diff.tv_usec; par->sumdiff += (double) par->diff.tv_usec; if (par->tracelimit && par->maxdiff > par->tracelimit) { char tracing_enabled_file[MAX_PATH]; strcpy(tracing_enabled_file, get_debugfileprefix()); strcat(tracing_enabled_file, "tracing_enabled"); int tracing_enabled = open(tracing_enabled_file, O_WRONLY); if (tracing_enabled >= 0) { write(tracing_enabled, "0", 1); close(tracing_enabled); } else snprintf(par->error, sizeof(par->error), "Could not access %s\n", tracing_enabled_file); par->shutdown = 1; neighbor->shutdown = 1; } nanosleep(&par->delay, NULL); if (wasforked) kill(neighbor->pid, SIGUSR1); else pthread_kill(neighbor->threadid, SIGUSR1); } } par->stopped = 1; return NULL; } static void display_help(void) { printf("sigwaittest V %1.2f\n", VERSION_STRING); puts("Usage: sigwaittest "); puts("Function: test sigwait() latency"); puts( "Options:\n" "-a [NUM] --affinity run thread #N on processor #N, if possible\n" " with NUM pin all threads to the processor NUM\n" "-b USEC --breaktrace=USEC send break trace command when latency > USEC\n" "-d DIST --distance=DIST distance of thread intervals in us default=500\n" "-f --fork fork new processes instead of creating threads\n" "-i INTV --interval=INTV base interval of thread in us default=1000\n" "-l LOOPS --loops=LOOPS number of loops: default=0(endless)\n" "-p PRIO --prio=PRIO priority\n" "-t --threads one thread per available processor\n" "-t [NUM] --threads=NUM number of threads:\n" " without NUM, threads = max_cpus\n" " without -t default = 1\n"); exit(1); } static int setaffinity = AFFINITY_UNSPECIFIED; static int affinity; static int priority; static int num_threads = 1; static int max_cycles; static int interval = 1000; static int distance = 500; static void process_options (int argc, char *argv[]) { int error = 0; int max_cpus = sysconf(_SC_NPROCESSORS_CONF); int thistracelimit = 0; for (;;) { int option_index = 0; /** Options for getopt */ static struct option long_options[] = { {"affinity", optional_argument, NULL, 'a'}, {"breaktrace", required_argument, NULL, 'b'}, {"distance", required_argument, NULL, 'd'}, {"fork", optional_argument, NULL, 'f'}, {"interval", required_argument, NULL, 'i'}, {"loops", required_argument, NULL, 'l'}, {"priority", required_argument, NULL, 'p'}, {"threads", optional_argument, NULL, 't'}, {"help", no_argument, NULL, '?'}, {NULL, 0, NULL, 0} }; int c = getopt_long (argc, argv, "a::b:d:f::i:l:p:t::", long_options, &option_index); if (c == -1) break; switch (c) { case 'a': if (optarg != NULL) { affinity = atoi(optarg); setaffinity = AFFINITY_SPECIFIED; } else if (optind= max_cpus) { fprintf(stderr, "ERROR: CPU #%d not found, " "only %d CPUs available\n", affinity, max_cpus); error = 1; } } if (num_threads < 1 || num_threads > 255) error = 1; if (priority < 0 || priority > 99) error = 1; tracelimit = thistracelimit; } if (error) display_help (); } static int volatile mustshutdown; static void sighand(int sig) { mustshutdown = 1; } int main(int argc, char *argv[]) { int i, totalsize = 0; int max_cpus = sysconf(_SC_NPROCESSORS_CONF); int oldsamples = 1; struct params *receiver = NULL; struct params *sender = NULL; sigset_t sigset; void *param = NULL; char f_opt[8]; struct timespec launchdelay, maindelay; process_options(argc, argv); if (check_privs()) return 1; if (mlockall(MCL_CURRENT|MCL_FUTURE) == -1) { perror("mlockall"); return 1; } get_cpu_setup(); /* init get_cpu() */ if (mustfork) { int shmem; /* * In fork mode (-f), the shared memory contains two * subsequent arrays, receiver[num_threads] and * sender[num_threads]. */ totalsize = num_threads * sizeof(struct params) * 2; shm_unlink("/sigwaittest"); shmem = shm_open("/sigwaittest", O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR); if (shmem < 0) { fprintf(stderr, "Could not create shared memory\n"); return 1; } ftruncate(shmem, totalsize); param = mmap(0, totalsize, PROT_READ|PROT_WRITE, MAP_SHARED, shmem, 0); if (param == MAP_FAILED) { fprintf(stderr, "Could not map shared memory\n"); close(shmem); return 1; } receiver = (struct params *) param; sender = receiver + num_threads; } else if (wasforked) { struct stat buf; int shmem, totalsize, expect_totalsize; if (wasforked_threadno == -1 || wasforked_sender == -1) { fprintf(stderr, "Invalid fork option\n"); return 1; } shmem = shm_open("/sigwaittest", O_RDWR, S_IRUSR|S_IWUSR); if (fstat(shmem, &buf)) { fprintf(stderr, "Could not determine shared memory size\n"); close(shmem); return 1; } totalsize = buf.st_size; param = mmap(0, totalsize, PROT_READ|PROT_WRITE, MAP_SHARED, shmem, 0); close(shmem); if (param == MAP_FAILED) { fprintf(stderr, "Could not map shared memory\n"); return 1; } receiver = (struct params *) param; expect_totalsize = receiver->num_threads * sizeof(struct params) * 2; if (totalsize != expect_totalsize) { fprintf(stderr, "Memory size problem (expected %d, " "found %d\n", expect_totalsize, totalsize); munmap(param, totalsize); return 1; } sender = receiver + receiver->num_threads; if (wasforked_sender) semathread(sender + wasforked_threadno); else semathread(receiver + wasforked_threadno); munmap(param, totalsize); return 0; } signal(SIGINT, sighand); signal(SIGTERM, sighand); sigemptyset(&sigset); pthread_sigmask(SIG_SETMASK, &sigset, NULL); if (!mustfork && !wasforked) { receiver = calloc(num_threads, sizeof(struct params)); sender = calloc(num_threads, sizeof(struct params)); if (receiver == NULL || sender == NULL) goto nomem; } launchdelay.tv_sec = 0; launchdelay.tv_nsec = 10000000; /* 10 ms */ maindelay.tv_sec = 0; maindelay.tv_nsec = 50000000; /* 50 ms */ for (i = 0; i < num_threads; i++) { receiver[i].mindiff = UINT_MAX; receiver[i].maxdiff = 0; receiver[i].sumdiff = 0.0; receiver[i].num = i; receiver[i].cpu = i; receiver[i].priority = priority; receiver[i].tracelimit = tracelimit; if (priority > 0) priority--; switch (setaffinity) { case AFFINITY_UNSPECIFIED: receiver[i].cpu = -1; break; case AFFINITY_SPECIFIED: receiver[i].cpu = affinity; break; case AFFINITY_USEALL: receiver[i].cpu = i % max_cpus; break; } receiver[i].delay.tv_sec = interval / USEC_PER_SEC; receiver[i].delay.tv_nsec = (interval % USEC_PER_SEC) * 1000; interval += distance; receiver[i].max_cycles = max_cycles; receiver[i].sender = 0; receiver[i].neighbor = &sender[i]; if (mustfork) { pid_t pid = fork(); if (pid == -1) { fprintf(stderr, "Could not fork\n"); return 1; } else if (pid == 0) { char *args[3]; receiver[i].num_threads = num_threads; receiver[i].pid = getpid(); sprintf(f_opt, "-fr%d", i); args[0] = argv[0]; args[1] = f_opt; args[2] = NULL; execvp(args[0], args); fprintf(stderr, "Could not execute receiver child process " "#%d\n", i); } } else pthread_create(&receiver[i].threadid, NULL, semathread, &receiver[i]); nanosleep(&launchdelay, NULL); memcpy(&sender[i], &receiver[i], sizeof(receiver[0])); sender[i].sender = 1; sender[i].neighbor = &receiver[i]; if (mustfork) { pid_t pid = fork(); if (pid == -1) { fprintf(stderr, "Could not fork\n"); return 1; } else if (pid == 0) { char *args[3]; sender[i].num_threads = num_threads; sender[i].pid = getpid(); sprintf(f_opt, "-fs%d", i); args[0] = argv[0]; args[1] = f_opt; args[2] = NULL; execvp(args[0], args); fprintf(stderr, "Could not execute sender child process " "#%d\n", i); } } else pthread_create(&sender[i].threadid, NULL, semathread, &sender[i]); } while (!mustshutdown) { int printed; int errorlines = 0; for (i = 0; i < num_threads; i++) mustshutdown |= receiver[i].shutdown | sender[i].shutdown; if (receiver[0].samples > oldsamples || mustshutdown) { for (i = 0; i < num_threads; i++) { int receiver_pid, sender_pid; if (mustfork) { receiver_pid = receiver[i].pid; sender_pid = sender[i].pid; } else { receiver_pid = receiver[i].tid; sender_pid = sender[i].tid; } printf("#%1d: ID%d, P%d, CPU%d, I%ld; #%1d: " "ID%d, P%d, CPU%d, Cycles %d\n", i*2, receiver_pid, receiver[i].priority, receiver[i].cpu, receiver[i].delay.tv_nsec / 1000, i*2+1, sender_pid, sender[i].priority, sender[i].cpu, sender[i].samples); } for (i = 0; i < num_threads; i++) { if (receiver[i].mindiff == -1) printf("#%d -> #%d (not yet ready)\n", i*2+1, i*2); else printf("#%d -> #%d, Min %4d, Cur %4d, " "Avg %4d, Max %4d\n", i*2+1, i*2, receiver[i].mindiff, (int) receiver[i].diff.tv_usec, (int) ((receiver[i].sumdiff / receiver[i].samples) + 0.5), receiver[i].maxdiff); if (receiver[i].error[0] != '\0') { printf("%s", receiver[i].error); receiver[i].error[0] = '\0'; errorlines++; } if (sender[i].error[0] != '\0') { printf("%s", sender[i].error); sender[i].error[0] = '\0'; errorlines++; } } printed = 1; } else printed = 0; sigemptyset(&sigset); sigaddset(&sigset, SIGTERM); sigaddset(&sigset, SIGINT); pthread_sigmask(SIG_SETMASK, &sigset, NULL); nanosleep(&maindelay, NULL); sigemptyset(&sigset); pthread_sigmask(SIG_SETMASK, &sigset, NULL); if (printed && !mustshutdown) printf("\033[%dA", num_threads*2 + errorlines); } for (i = 0; i < num_threads; i++) { receiver[i].shutdown = 1; sender[i].shutdown = 1; } nanosleep(&receiver[0].delay, NULL); for (i = 0; i < num_threads; i++) { if (!receiver[i].stopped) { if (mustfork) kill(receiver[i].pid, SIGTERM); else pthread_kill(receiver[i].threadid, SIGTERM); } if (!sender[i].stopped) { if (mustfork) kill(sender[i].pid, SIGTERM); else pthread_kill(sender[i].threadid, SIGTERM); } } nomem: if (mustfork) { munmap(param, totalsize); shm_unlink("/sigwaittest"); } return 0; } rt-tests/src/sigwaittest/Makefile0000600001204700120500000000035212252162340017646 0ustar williamswilliamsCFLAGS += -Wall -O2 LDFLAGS += -lpthread -lrt all: sigwaittest @echo Done sigwaittest.o: sigwaittest.c sigwaittest: clean: @rm -f *.o tar: clean @rm -f sigwaittest $(shell bn=`basename $$PWD`; cd ..; tar -zcf $$bn.tgz $$bn) rt-tests/src/sigwaittest/sigwaittest.80000600001204700120500000000516712252162340020657 0ustar williamswilliams.TH "sigwaittest" "8" "0.1" "" "" .SH "NAME" .LP \fBsigwaittest\fR \- Start two threads or fork two processes and measure the latency between sending and receiving a signal .SH "SYNTAX" .LP sigwaittest [-a|-a PROC] [-b USEC] [-d DIST] [-f] [-i INTV] [-l loops] [-p PRIO] [-t|-t NUM] .br .SH "DESCRIPTION" .LP The program \fBsigwaittest\fR starts two threads or, optionally, forks two processes that are synchonized via signals and measures the latency between sending a signal and returning from sigwait(). .SH "OPTIONS" .TP .B \-a, \-\-affinity[=PROC] Run on procesor number PROC. If PROC is not specified, run on current processor. .TP .B \-b, \-\-breaktrace=USEC Send break trace command when latency > USEC. This is a debugging option to control the latency tracer in the realtime preemption patch. It is useful to track down unexpected large latencies of a system. .TP .B \-d, \-\-distance=DIST Set the distance of thread intervals in microseconds (default is 500 us). When cylictest is called with the -t option and more than one thread is created, then this distance value is added to the interval of the threads: Interval(thread N) = Interval(thread N-1) + DIST .TP .B \-f, \-\-fork Instead of creating threads (which is the default), fork new processes .TP .B \-i, \-\-interval=INTV Set the base interval of the thread(s) in microseconds (default is 1000 us). This sets the interval of the first thread. See also -d. .TP .B \-l, \-\-loops=LOOPS Set the number of loops. The default is 0 (endless). This option is useful for automated tests with a given number of test cycles. sigwaittest is stopped once the number of timer intervals has been reached. .TP .B \-p, \-\-prio=PRIO Set the priority of the process. .TP .B \-t, \-\-threads[=NUM] Set the number of test threads (default is 1, if this option is not given). If NUM is specified, create NUM test threads. If NUM is not specifed, NUM is set to the number of available CPUs. .SH "EXAMPLES" The following example was running on a 4-way CPU: .LP .nf # sigwaittest -a -t -p99 -i100 -d25 -l1000000 #0: ID11510, P99, CPU0, I100; #1: ID11511, P99, CPU0, Cycles 1000000 #2: ID11512, P98, CPU1, I125; #3: ID11513, P98, CPU1, Cycles 817484 #4: ID11514, P97, CPU2, I150; #5: ID11515, P97, CPU2, Cycles 668213 #6: ID11516, P96, CPU3, I175; #7: ID11517, P96, CPU3, Cycles 597344 #1 -> #0, Min 1, Cur 2, Avg 3, Max 30 #3 -> #2, Min 1, Cur 26, Avg 3, Max 42 #5 -> #4, Min 1, Cur 46, Avg 4, Max 67 #7 -> #6, Min 1, Cur 2, Avg 3, Max 74 .fi .SH "AUTHORS" .LP Carsten Emde .SH "SEE ALSO" .LP kill(2), sigwait(3) rt-tests/src/pi_tests/0000700001204700120500000000000012252162340015467 5ustar williamswilliamsrt-tests/src/pi_tests/tst-mutexpi10.c0000600001204700120500000004551612252162340020314 0ustar williamswilliams/* Classic Priority Inversion deadlock test case Copyright (C) 2006 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Clark Williams, 2006 The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* This program tests Priority Inheritence mutexes and their ability to avoid Priority Inversion deadlocks The basic premise here is to set up a deadlock scenario and confirm that PI mutexes resolve the situation. Three worker threads will be created from the main thread: low, medium and high priority threads that use SCHED_FIFO as their scheduling policy. The low priority thread claims a mutex and then starts "working". The medium priority thread starts and preempts the low priority thread. Then the high priority thread runs and attempts to claim the mutex owned by the low priority thread. Without priority inheritance, this will deadlock the program. With priority inheritance, the low priority thread receives a priority boost, finishes it's "work" and releases the mutex, which allows the high priority thread to run and finish and then the medium priority thread finishes. That's the theory, anyway... CW - 2006 */ #include #include #include #include #include #include #include #include #include /* test timeout */ #define TIMEOUT 2 /* determine if the C library supports Priority Inheritance mutexes */ #if defined(_POSIX_THREAD_PRIO_INHERIT) && _POSIX_THREAD_PRIO_INHERIT != -1 #define HAVE_PI_MUTEX 1 #else #define HAVE_PI_MUTEX 0 #endif int use_pi_mutex = HAVE_PI_MUTEX; #define SUCCESS 0 #define FAILURE 1 /* the number of times we cause a priority inversion situation */ int inversions = 1; /* the file handle used by the error reporting routine */ FILE *errout; #define MAIN do_test #define TEST_FUNCTION do_test(argc, argv) #define CMDLINE_OPTIONS { "verbose", no_argument, NULL, 1002 }, \ { "no-pi", no_argument, NULL, 1003}, #define CMDLINE_PROCESS \ case 1002: verbose=1; break; \ case 1003: use_pi_mutex = 0; break; #define CLEANUP_HANDLER cleanup() int verbose = 0; /* define priorities for the threads */ #define SKEL_PRIO(x) (x) #define MAIN_PRIO(x) (x - 1) #define HIGH_PRIO(x) (x - 2) #define MED_PRIO(x) (x - 3) #define LOW_PRIO(x) (x - 4) enum thread_names { LOW = 0, MEDIUM, HIGH, NUM_WORKER_THREADS }; pthread_mutex_t mutex; pthread_mutexattr_t mutex_attr; pthread_barrier_t all_threads_ready; pthread_barrier_t all_threads_done; /* state barriers */ pthread_barrier_t start_barrier; pthread_barrier_t locked_barrier; pthread_barrier_t elevate_barrier; pthread_barrier_t finish_barrier; volatile int deadlocked = 0; volatile int high_has_run = 0; volatile int low_unlocked = 0; cpu_set_t cpu_mask; struct thread_parameters { pthread_t tid; int inversions; } thread_parameters[NUM_WORKER_THREADS]; /* forward prototypes */ void *low_priority (void *arg); void *med_priority (void *arg); void *high_priority (void *arg); int setup_thread_attr (pthread_attr_t * attr, int prio, cpu_set_t * mask); int set_cpu_affinity (cpu_set_t * mask); void error (char *, ...); void info (char *, ...); void prepare (int argc, char **argv) { struct sched_param thread_param; int max = sched_get_priority_max (SCHED_FIFO); int status; errout = stdout; /* boost test skeleton to max priority (so we keep running) :) */ thread_param.sched_priority = SKEL_PRIO (max); status = pthread_setschedparam (pthread_self (), SCHED_FIFO, &thread_param); if (status) error ("main: boosting to max priority: 0x%x\n", status); } #define PREPARE prepare int initialize_barriers (void) { int status; status = pthread_barrier_init (&all_threads_ready, NULL, NUM_WORKER_THREADS + 1); if (status) { error ("initialize_barriers: failed to initialize all_threads_ready\n"); return FAILURE; } status = pthread_barrier_init (&all_threads_done, NULL, NUM_WORKER_THREADS + 1); if (status) { error ("initialize_barriers: failed to initialize all_threads_done\n"); return FAILURE; } status = pthread_barrier_init (&start_barrier, NULL, NUM_WORKER_THREADS); if (status) { error ("initialize_barriers: failed to initialize start_barrier\n"); return FAILURE; } status = pthread_barrier_init (&locked_barrier, NULL, 2); if (status) { error ("initializing_barriers: failed to intialize locked_barrier\n"); return FAILURE; } status = pthread_barrier_init (&elevate_barrier, NULL, 2); if (status) { error ("initializing_barriers: failed to initialize elevate_barrier\n"); return FAILURE; } status = pthread_barrier_init (&finish_barrier, NULL, NUM_WORKER_THREADS); if (status) { error ("initializing_barriers: failed to initialize finish_barrier\n"); return FAILURE; } return SUCCESS; } void cleanup (void) { int i; int status; for (i = 0; i < NUM_WORKER_THREADS; i++) { status = pthread_kill (thread_parameters[i].tid, SIGQUIT); if (status) error ("cleanup: error sending SIGQUIT to thread %d\n", thread_parameters[i].tid); } } void handler (int signal) { info ("handler: %s fired\n", sys_siglist[signal]); cleanup (); if (signal == SIGALRM) { error ("handler: DEADLOCK detected!\n"); deadlocked = 1; } } int MAIN (int argc, char **argv) { int status; int prio_max; pthread_attr_t thread_attr; struct sched_param thread_param; errout = stdout; /* initialize default attributes for the mutex */ status = pthread_mutexattr_init (&mutex_attr); if (status) { error ("main: initializing mutex attribute: 0x%x\n", status); return FAILURE; } if (use_pi_mutex) { /* set priority inheritance attribute for mutex */ status = pthread_mutexattr_setprotocol (&mutex_attr, PTHREAD_PRIO_INHERIT); if (status) { error ("main: setting mutex attribute policy: 0x%x\n", status); return FAILURE; } } info ("main: Priority Inheritance turned %s\n", use_pi_mutex ? "on" : "off"); /* initialize our mutex */ status = pthread_mutex_init (&mutex, &mutex_attr); if (status) { error ("main: initializing mutex: 0x%x\n", status); return FAILURE; } /* set up our barriers */ status = initialize_barriers (); if (status) return FAILURE; /* set up CPU affinity so we only use one processor */ if (set_cpu_affinity (&cpu_mask)) return FAILURE; /* boost us to max priority (so we keep running) :) */ prio_max = sched_get_priority_max (SCHED_FIFO); thread_param.sched_priority = MAIN_PRIO (prio_max); status = pthread_setschedparam (pthread_self (), SCHED_FIFO, &thread_param); if (status) { error ("main: boosting to max priority: 0x%x\n", status); /* Don't fail if we don't have the right privledges */ return SUCCESS; } /* start the low priority thread */ info ("main: creating low priority thread\n"); setup_thread_attr (&thread_attr, LOW_PRIO (prio_max), &cpu_mask); thread_parameters[LOW].inversions = inversions; status = pthread_create (&thread_parameters[LOW].tid, &thread_attr, low_priority, &thread_parameters[LOW]); if (status != 0) { error ("main: creating low_priority thread: 0x%x\n", status); return FAILURE; } /* create the medium priority thread */ info ("main: creating medium priority thread\n"); setup_thread_attr (&thread_attr, MED_PRIO (prio_max), &cpu_mask); thread_parameters[MEDIUM].inversions = inversions; status = pthread_create (&thread_parameters[MEDIUM].tid, &thread_attr, med_priority, &thread_parameters[MEDIUM]); if (status != 0) { error ("main: creating med_priority thread: 0x%x\n", status); return FAILURE; } /* create the high priority thread */ info ("main: creating high priority thread\n"); if (setup_thread_attr (&thread_attr, HIGH_PRIO (prio_max), &cpu_mask)) return FAILURE; thread_parameters[HIGH].inversions = inversions; status = pthread_create (&thread_parameters[HIGH].tid, &thread_attr, high_priority, &thread_parameters[HIGH]); if (status != 0) { error ("main: creating high_priority thread: 0x%x\n", status); cleanup (); return FAILURE; } signal (SIGINT, handler); info ("main: releasing all threads\n"); status = pthread_barrier_wait (&all_threads_ready); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error ("main: pthread_barrier_wait(all_threads_ready): 0x%x\n", status); cleanup (); return FAILURE; } info ("main: all threads initialized\n"); info ("main: waiting for threads to finish\n"); status = pthread_barrier_wait (&all_threads_done); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error ("main: pthread_barrier_wait(all_threads_done): 0x%x\n", status); cleanup (); return FAILURE; } info ("main: all threads terminated!\n"); if (deadlocked) { info ("main: test failed\n"); return FAILURE; } info ("main: test passed\n"); return SUCCESS; } int setup_thread_attr (pthread_attr_t * attr, int prio, cpu_set_t * mask) { int status; struct sched_param thread_param; status = pthread_attr_init (attr); if (status) { error ("setup_thread_attr: initializing thread attribute: 0x%x\n", status); return FAILURE; } status = pthread_attr_setschedpolicy (attr, SCHED_FIFO); if (status) { error ("setup_thread_attr: setting attribute policy to SCHED_FIFO: 0x%x\n", status); return FAILURE; } status = pthread_attr_setinheritsched (attr, PTHREAD_EXPLICIT_SCHED); if (status) { error ("setup_thread_attr: setting explicit scheduling inheritance: 0x%x\n", status); return FAILURE; } thread_param.sched_priority = prio; status = pthread_attr_setschedparam (attr, &thread_param); if (status) { error ("setup_thread_attr: setting scheduler param: 0x%x\n", status); return FAILURE; } status = pthread_attr_setaffinity_np (attr, sizeof (cpu_set_t), mask); if (status) { error ("setup_thread_attr: setting affinity attribute: 0x%x\n", status); return FAILURE; } return SUCCESS; } int set_cpu_affinity (cpu_set_t * cpu_set) { int status, i; cpu_set_t current_mask, new_mask; /* Now set our CPU affinity to only run one one processor */ status = sched_getaffinity (0, sizeof (cpu_set_t), ¤t_mask); if (status) { error ("set_cpu_affinity: getting CPU affinity mask: 0x%x\n", status); return FAILURE; } for (i = 0; i < sizeof (cpu_set_t) * 8; i++) { if (CPU_ISSET (i, ¤t_mask)) break; } if (i >= sizeof (cpu_set_t) * 8) { error ("set_cpu_affinity: No schedulable CPU found!\n"); return FAILURE; } CPU_ZERO (&new_mask); CPU_SET (i, &new_mask); status = sched_setaffinity (0, sizeof (cpu_set_t), &new_mask); if (status) { error ("set_cpu_affinity: setting CPU affinity mask: 0x%x\n", status); return FAILURE; } info ("set_cpu_affinity: using processr %d\n", i); *cpu_set = new_mask; return SUCCESS; } void report_threadinfo (char *name) { int status; struct sched_param thread_param; int thread_policy; status = pthread_getschedparam (pthread_self (), &thread_policy, &thread_param); if (status) { error ("report_threadinfo: failed to get scheduler param: 0x%x\n", status); pthread_mutex_unlock (&mutex); exit (FAILURE); } info ("%s: running as %s thread at priority %d\n", name, thread_policy == SCHED_FIFO ? "FIFO" : thread_policy == SCHED_RR ? "RR" : "OTHER", thread_param.sched_priority); } void * low_priority (void *arg) { int status; struct thread_parameters *p = (struct thread_parameters *) arg; report_threadinfo ("low_priority"); info ("low_priority: entering ready state\n"); /* wait for all threads to be ready */ status = pthread_barrier_wait (&all_threads_ready); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error ("low_priority: pthread_barrier_wait(all_threads_ready): %x", status); return NULL; } info ("low_priority: starting inversion loop (%d)\n", p->inversions); while (p->inversions-- > 0) { /* initial state */ info ("low_priority: entering start wait (%d)\n", p->inversions + 1); status = pthread_barrier_wait (&start_barrier); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error ("low_priority: pthread_barrier_wait(start): %x\n", status); return NULL; } info ("low_priority: claiming mutex\n"); pthread_mutex_lock (&mutex); info ("low_priority: mutex locked\n"); info ("low_priority: entering locked wait\n"); status = pthread_barrier_wait (&locked_barrier); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error ("low_priority: pthread_barrier_wait(locked): %x\n", status); return NULL; } /* wait for priority boost */ info ("low_priority: entering elevated wait\n"); low_unlocked = 0; /* prevent race with med_priority */ status = pthread_barrier_wait (&elevate_barrier); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error ("low_priority: pthread_barrier_wait(elevate): %x\n", status); return NULL; } low_unlocked = 1; /* release the mutex */ info ("low_priority: unlocking mutex\n"); pthread_mutex_unlock (&mutex); /* finish state */ info ("low_priority: entering finish wait\n"); status = pthread_barrier_wait (&finish_barrier); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error ("low_priority: pthread_barrier_wait(elevate): %x\n", status); return NULL; } } /* let main know we're done */ info ("low_priority: entering exit state\n"); status = pthread_barrier_wait (&all_threads_done); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error ("low_priority: pthread_barrier_wait(all_threads_done): %x", status); return NULL; } info ("low_priority: exiting\n"); return NULL; } void * med_priority (void *arg) { int status; struct thread_parameters *p = (struct thread_parameters *) arg; report_threadinfo ("med_priority"); info ("med_priority: entering ready state\n"); /* wait for all threads to be ready */ status = pthread_barrier_wait (&all_threads_ready); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error ("med_priority: pthread_barrier_wait(all_threads_ready): %x", status); return NULL; } info ("med_priority: starting inversion loop (%d)\n", p->inversions); while (p->inversions-- > 0) { /* start state */ info ("med_priority: entering start state (%d)\n", p->inversions + 1); status = pthread_barrier_wait (&start_barrier); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error ("med_priority: pthread_barrier_wait(start): %x", status); return NULL; } info ("med_priority: entering elevate state\n"); do { status = pthread_barrier_wait (&elevate_barrier); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error ("med_priority: pthread_barrier_wait(elevate): %x", status); return NULL; } } while (!high_has_run && !low_unlocked); info ("med_priority: entering finish state\n"); status = pthread_barrier_wait (&finish_barrier); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error ("med_priority: pthread_barrier_wait(finished): %x", status); return NULL; } } info ("med_priority: entering exit state\n"); status = pthread_barrier_wait (&all_threads_done); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error ("med_priority: pthread_barrier_wait(all_threads_done): %x", status); return NULL; } info ("med_priority: exiting\n"); return NULL; } void * high_priority (void *arg) { int status; struct thread_parameters *p = (struct thread_parameters *) arg; report_threadinfo ("high_priority"); info ("high_priority: entering ready state\n"); /* wait for all threads to be ready */ status = pthread_barrier_wait (&all_threads_ready); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error ("high_priority: pthread_barrier_wait(all_threads_ready): %x", status); return NULL; } info ("high_priority: starting inversion loop (%d)\n", p->inversions); while (p->inversions-- > 0) { high_has_run = 0; info ("high_priority: entering start state (%d)\n", p->inversions + 1); status = pthread_barrier_wait (&start_barrier); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error ("high_priority: pthread_barrier_wait(start): %x", status); return NULL; } info ("high_priority: entering running state\n"); status = pthread_barrier_wait (&locked_barrier); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error ("high_priority: pthread_barrier_wait(running): %x", status); return NULL; } info ("high_priority: locking mutex\n"); pthread_mutex_lock (&mutex); info ("high_priority: got mutex\n"); high_has_run = 1; info ("high_priority: unlocking mutex\n"); pthread_mutex_unlock (&mutex); info ("high_priority: entering finish state\n"); status = pthread_barrier_wait (&finish_barrier); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error ("high_priority: pthread_barrier_wait(finish): %x", status); return NULL; } } info ("high_priority: entering exit state\n"); status = pthread_barrier_wait (&all_threads_done); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error ("high_priority: pthread_barrier_wait(all_threads_done): %x", status); return NULL; } info ("high_priority: exiting\n"); return NULL; } void error (char *fmt, ...) { va_list ap; fputs ("ERROR: ", errout); va_start (ap, fmt); vfprintf (errout, fmt, ap); va_end (ap); } void info (char *fmt, ...) { if (verbose) { va_list ap; va_start (ap, fmt); vprintf (fmt, ap); va_end (ap); } } #include "../test-skeleton.c" rt-tests/src/pi_tests/pi_stress.80000600001204700120500000000510712252162340017600 0ustar williamswilliams.\" Process this file with .\" groff -man -Tascii pi_stress.8 .\" .\"{{{}}} .\"{{{ Title .TH pi_stress 8 "Nov 27, 2006" "" "Linux System Administrator's Manual" .\"}}} .\"{{{ Name .SH NAME pi_stress \- a stress test for POSIX Priority Inheritance mutexes .\"}}} .\"{{{ Synopsis .\" Usage: pi_stress [-i n ] [-g n] [-v] [-d] [-s] [-r] [-p] [-u] [-m] .SH SYNOPSIS .B pi_stress .RB [ \-i|\-\-inversions .IR inversions ] .RB [ \-t|\-\-duration .IR seconds ] .RB [ \-g|\-\-groups .IR groups .RB [ \-d|\-\-debug ] .RB [ \-v|\-\-verbose ] .RB [ \-s|\-\-signal ] .RB [ \-r|\-\-rr ] .RB [ \-p|\-\-prompt ] .RB [ \-m|\-\-mlockall ] .RB [ \-u|\-\-uniprocessor ] .br .\" help .B pi_stress .RB \-h|\-\-help .SH DESCRIPTION .B pi_stress is a program used to stress the .IR priority-inheritance code paths for POSIX mutexes, in both the Linux kernel and the C library. It runs as a realtime-priority task and launches .IR "inversion machine" thread groups. Each inversion group causes a .IR "priorty inversion" condition that will deadlock if .IR "priority inheritance" doesn't work. .SH OPTIONS .IP "\-i n|\-\-inversions=n" Run for .I n number of inversion conditions. This is the total number of inversions for all inversion groups. Default is \-1 for infinite. .IP "\-t n|\-\-duration=n" Run the test for .I n seconds and then terminate. .IP "\-g n|\-\-groups=n" The number of inversion groups to run. Defaults to 10. .IP \-d|\-\-debug Run in debug mode; lots of extra prints .IP \-v|\-\-verbose Run with verbose messages .IP \-s|\-\-signal Terminate on receipt of SIGTERM (Ctrl-C). Default is to terminate on any keypress. .IP \-r|\-\-rr Run inversion group threads as SCHED_RR (round-robin). The default is to run the inversion threads as SCHED_FIFO. .IP \-p|\-\-prompt Prompt before actually starting the stress test .IP \-u|\-\-uniprocessor Run all threads on one processor. The default is to run all inversion group threads on one processor and the admin threads (reporting thread, keyboard reader, etc.) on a different processor. .IP \-m|\-\-mlockall Call mlockall to lock current and future memory allocations and prevent being paged out .IP \-h|\-\-help Display a short help message and options. .SH CAVEATS The pi_stress test threads run as SCHED_FIFO or SCHED_RR threads, which means that they can starve critical system threads. It is advisable to change the scheduling policy of critical system threads to be SCHED_FIFO prior to running pi_stress and use a priority of 10 or higher, to prevent those threads from being starved by the stress test. .SH BUGS No documented bugs. .SH AUTHOR Clark Williams rt-tests/src/pi_tests/pi_stress.c0000600001204700120500000010276612252162340017664 0ustar williamswilliams/* pi_stress - Priority Inheritance stress test Copyright (C) 2006, 2007 Clark Williams 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 program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* This program stress tests pthreads priority inheritance mutexes The logic is built upon the state machine that performs the "classic_pi" deadlock scenario. A state machine or "inversion group" is a group of three threads as described below. The basic premise here is to set up a deadlock scenario and confirm that PI mutexes resolve the situation. Three worker threads will be created from the main thread: low, medium and high priority threads that use SCHED_FIFO as their scheduling policy. The low priority thread claims a mutex and then starts "working". The medium priority thread starts and preempts the low priority thread. Then the high priority thread runs and attempts to claim the mutex owned by the low priority thread. Without priority inheritance, this will deadlock the program. With priority inheritance, the low priority thread receives a priority boost, finishes it's "work" and releases the mutex, which allows the high priority thread to run and finish and then the medium priority thread finishes. That's the theory, anyway... CW - 2006 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* conversions */ #define USEC_PER_SEC 1000000 #define NSEC_PER_SEC 1000000000 #define USEC_TO_NSEC(u) ((u) * 1000) #define USEC_TO_SEC(u) ((u) / USEC_PER_SEC) #define NSEC_TO_USEC(n) ((n) / 1000) #define SEC_TO_NSEC(s) ((s) * NSEC_PER_SEC) #define SEC_TO_USEC(s) ((s) * USEC_PER_SEC) /* test timeout */ #define TIMEOUT 2 /* determine if the C library supports Priority Inheritance mutexes */ #if defined(_POSIX_THREAD_PRIO_INHERIT) && _POSIX_THREAD_PRIO_INHERIT != -1 #define HAVE_PI_MUTEX 1 #else #define HAVE_PI_MUTEX 0 #endif #if HAVE_PI_MUTEX == 0 #error "Can't run this test without PI Mutex support" #endif #define SUCCESS 0 #define FAILURE 1 /* cursor control */ #define UP_ONE "\033[1A" #define DOWN_ONE "\033[1B" /* the length of the test */ /* default is infinite */ int duration = -1; /* times for starting and finishing the stress test */ time_t start, finish; /* the number of groups to create */ int ngroups = 0; /* the number of times a group causes a priority inversion situation */ /* default to infinite */ int inversions = -1; /* turn on lots of prints */ int verbose = 0; /* turn on debugging prints */ int debugging = 0; int quiet = 0; /* turn off all prints, default = 0 (off) */ /* prompt to start test */ int prompt = 0; /* report interval */ unsigned long report_interval = (unsigned long)SEC_TO_USEC(0.75); int shutdown = 0; /* global indicating we should shut down */ pthread_mutex_t shutdown_mtx; /* associated mutex */ /* indicate if errors have occured */ int have_errors = 0; /* indicated that keyboard interrupt has happened */ int interrupted = 0; /* force running on one cpu */ int uniprocessor = 0; /* lock all memory */ int lockall = 0; /* command line options */ struct option options[] = { {"duration", required_argument, NULL, 't'}, {"verbose", no_argument, NULL, 'v'}, {"quiet", no_argument, NULL, 'q'}, {"groups", required_argument, NULL, 'g'}, {"inversions", required_argument, NULL, 'i'}, {"rr", no_argument, NULL, 'r'}, {"uniprocessor", no_argument, NULL, 'u'}, {"prompt", no_argument, NULL, 'p'}, {"debug", no_argument, NULL, 'd'}, {"version", no_argument, NULL, 'V'}, {"mlockall", no_argument, NULL, 'm'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0}, }; /* max priority for the scheduling policy */ int prio_min; /* define priorities for the threads */ #define MAIN_PRIO() (prio_min + 3) #define HIGH_PRIO() (prio_min + 2) #define MED_PRIO() (prio_min + 1) #define LOW_PRIO() (prio_min + 0) #define NUM_TEST_THREADS 3 #define NUM_ADMIN_THREADS 1 #define TIMER_SIGNAL (SIGRTMIN+1) pthread_barrier_t all_threads_ready; pthread_barrier_t all_threads_done; cpu_set_t test_cpu_mask, admin_cpu_mask; int policy = SCHED_FIFO; struct group_parameters { /* group id (index) */ int id; /* cpu this group is bound to */ long cpu; /* threads in the group */ pthread_t low_tid; pthread_t med_tid; pthread_t high_tid; /* number of machine iterations to perform */ int inversions; /* group mutex */ pthread_mutex_t mutex; /* state barriers */ pthread_barrier_t start_barrier; pthread_barrier_t locked_barrier; pthread_barrier_t elevate_barrier; pthread_barrier_t finish_barrier; /* Either everyone goes through the loop, or else no-ones does */ pthread_barrier_t loop_barr; pthread_mutex_t loop_mtx; /* Protect access to int loop */ int loop; /* boolean, loop or not, connected to shutdown */ /* state variables */ volatile int watchdog; /* total number of inversions performed */ unsigned long total; /* total watchdog hits */ int watchdog_hits; } *groups; /* number of consecutive watchdog hits before quitting */ #define WATCHDOG_LIMIT 5 /* number of online processors */ long num_processors = 0; /* forward prototypes */ void *low_priority(void *arg); void *med_priority(void *arg); void *high_priority(void *arg); void *reporter(void *arg); void *watchdog(void *arg); int setup_thread_attr(pthread_attr_t * attr, int prio, cpu_set_t * mask, int schedpolicy); int set_cpu_affinity(cpu_set_t * test_mask, cpu_set_t * admin_mask); void error(char *, ...); void info(char *, ...); void debug(char *, ...); void process_command_line(int argc, char **argv); void usage(void); int block_signals(void); int allow_sigterm(void); void set_shutdown_flag(void); int initialize_group(struct group_parameters *group); int create_group(struct group_parameters *group); unsigned long total_inversions(void); void banner(void); void summary(void); void wait_for_termination(void); int barrier_init(pthread_barrier_t * b, const pthread_barrierattr_t * attr, unsigned count, const char *name); int main(int argc, char **argv) { int status; struct sched_param thread_param; int i; int retval = FAILURE; int core; int nthreads; /* Make sure we see all message, even those on stdout. */ setvbuf(stdout, NULL, _IONBF, 0); /* get the number of processors */ num_processors = sysconf(_SC_NPROCESSORS_ONLN); /* calculate the number of inversion groups to run */ ngroups = num_processors == 1 ? 1 : num_processors - 1; /* process command line arguments */ process_command_line(argc, argv); /* lock memory */ if (lockall) if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1) { error("mlockall failed\n"); return FAILURE; } /* boost main's priority (so we keep running) :) */ prio_min = sched_get_priority_min(policy); thread_param.sched_priority = MAIN_PRIO(); status = pthread_setschedparam(pthread_self(), policy, &thread_param); if (status) { error("main: boosting to max priority: 0x%x\n", status); return FAILURE; } /* block unwanted signals */ block_signals(); /* allocate our groups array */ groups = calloc(ngroups, sizeof(struct group_parameters)); if (groups == NULL) { error("main: failed to allocate %d groups\n", ngroups); return FAILURE; } /* set up CPU affinity masks */ if (set_cpu_affinity(&test_cpu_mask, &admin_cpu_mask)) return FAILURE; nthreads = ngroups * NUM_TEST_THREADS + NUM_ADMIN_THREADS; /* set up our ready barrier */ if (barrier_init(&all_threads_ready, NULL, nthreads, "all_threads_ready")) return FAILURE; /* set up our done barrier */ if (barrier_init(&all_threads_done, NULL, nthreads, "all_threads_done")) return FAILURE; /* create the groups */ info("Creating %d test groups\n", ngroups); for (core = 0; core < num_processors; core++) if (CPU_ISSET(core, &test_cpu_mask)) break; for (i = 0; i < ngroups; i++) { groups[i].id = i; groups[i].cpu = core++; if (core >= num_processors) core = 0; if (create_group(&groups[i]) != SUCCESS) return FAILURE; } /* prompt if requested */ if (prompt) { printf("Press return to start test: "); getchar(); } /* report */ banner(); start = time(NULL); /* turn loose the threads */ info("Releasing all threads\n"); status = pthread_barrier_wait(&all_threads_ready); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error("main: pthread_barrier_wait(all_threads_ready): 0x%x\n", status); set_shutdown_flag(); return FAILURE; } reporter(NULL); if (!quiet) { fputs(DOWN_ONE, stdout); printf("Stopping test\n"); } set_shutdown_flag(); /* wait for all threads to notice the shutdown flag */ if (have_errors == 0 && interrupted == 0) { info("waiting for all threads to complete\n"); status = pthread_barrier_wait(&all_threads_done); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error ("main: pthread_barrier_wait(all_threads_ready): 0x%x\n", status); return FAILURE; } info("All threads terminated!\n"); retval = SUCCESS; } else kill(0, SIGTERM); finish = time(NULL); summary(); if (lockall) munlockall(); exit(retval); } int setup_thread_attr(pthread_attr_t * attr, int prio, cpu_set_t * mask, int schedpolicy) { int status; struct sched_param thread_param; status = pthread_attr_init(attr); if (status) { error ("setup_thread_attr: initializing thread attribute: 0x%x\n", status); return FAILURE; } status = pthread_attr_setschedpolicy(attr, schedpolicy); if (status) { error ("setup_thread_attr: setting attribute policy to %s: 0x%x\n", schedpolicy == SCHED_FIFO ? "SCHED_FIFO" : "SCHED_RR", status); return FAILURE; } status = pthread_attr_setinheritsched(attr, PTHREAD_EXPLICIT_SCHED); if (status) { error ("setup_thread_attr: setting explicit scheduling inheritance: 0x%x\n", status); return FAILURE; } thread_param.sched_priority = prio; status = pthread_attr_setschedparam(attr, &thread_param); if (status) { error("setup_thread_attr: setting scheduler param: 0x%x\n", status); return FAILURE; } status = pthread_attr_setaffinity_np(attr, sizeof(cpu_set_t), mask); if (status) { error("setup_thread_attr: setting affinity attribute: 0x%x\n", status); return FAILURE; } return SUCCESS; } int set_cpu_affinity(cpu_set_t * test_mask, cpu_set_t * admin_mask) { int status, i, admin_proc; cpu_set_t current_mask; /* handle uniprocessor case */ if (num_processors == 1 || uniprocessor) { CPU_ZERO(admin_mask); CPU_ZERO(test_mask); CPU_SET(0, admin_mask); CPU_SET(0, test_mask); info("admin and test threads running on one processor\n"); return SUCCESS; } /* first set our main thread to run on the first scheduleable processor we can find */ status = sched_getaffinity(0, sizeof(cpu_set_t), ¤t_mask); if (status) { error("failed getting CPU affinity mask: 0x%x\n", status); return FAILURE; } for (i = 0; i < num_processors; i++) { if (CPU_ISSET(i, ¤t_mask)) break; } if (i >= num_processors) { error("No schedulable CPU found for main!\n"); return FAILURE; } admin_proc = i; CPU_ZERO(admin_mask); CPU_SET(admin_proc, admin_mask); status = sched_setaffinity(0, sizeof(cpu_set_t), admin_mask); if (status) { error("set_cpu_affinity: setting CPU affinity mask: 0x%x\n", status); return FAILURE; } info("Admin thread running on processor: %d\n", i); /* Set test affinity so that tests run on the non-admin processors */ CPU_ZERO(test_mask); for (i = admin_proc + 1; i < num_processors; i++) CPU_SET(i, test_mask); if (admin_proc + 1 == num_processors - 1) info("Test threads running on processor: %ld\n", num_processors - 1); else info("Test threads running on processors: %d-%d\n", admin_proc + 1, (int)num_processors - 1); return SUCCESS; } /* clear all watchdog counters */ void watchdog_clear(void) { int i; for (i = 0; i < ngroups; i++) groups[i].watchdog = 0; } /* check for zero watchdog counters */ int watchdog_check(void) { int i; int failures = 0; struct group_parameters *g; for (i = 0; i < ngroups; i++) { g = &groups[i]; if (g->watchdog == 0) { /* don't report deadlock if group is finished */ if (g->inversions == g->total) continue; if (++g->watchdog_hits >= WATCHDOG_LIMIT) { error ("WATCHDOG triggered: group %d is deadlocked!\n", i); failures++; } } else g->watchdog_hits = 0; } return failures ? FAILURE : SUCCESS; } int pending_interrupt(void) { sigset_t pending; if (sigpending(&pending) < 0) { error("from sigpending: %s\n", strerror(errno)); return 0; } return interrupted = sigismember(&pending, SIGINT); } static inline void tsnorm(struct timespec *ts) { while (ts->tv_nsec >= NSEC_PER_SEC) { ts->tv_nsec -= NSEC_PER_SEC; ts->tv_sec++; } } /* * this routine serves two purposes: * 1. report progress * 2. check for deadlocks */ void *reporter(void *arg) { int status; int end = 0; struct timespec ts; ts.tv_sec = 0; ts.tv_nsec = USEC_TO_NSEC(report_interval); tsnorm(&ts); if (duration >= 0) end = duration + time(NULL); /* sleep initially to let everything get up and running */ status = clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL); if (status) { error("from clock_nanosleep: %s\n", strerror(status)); return NULL; } debug("reporter: starting report loop\n"); info("Press Control-C to stop test\nCurrent Inversions: \n"); for (;;) { pthread_mutex_lock(&shutdown_mtx); if (shutdown) { pthread_mutex_unlock(&shutdown_mtx); break; } pthread_mutex_unlock(&shutdown_mtx); /* wait for our reporting interval */ status = clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL); if (status) { error("from clock_nanosleep: %s\n", strerror(status)); break; } /* check for signaled shutdown */ if (!quiet) { pthread_mutex_lock(&shutdown_mtx); if (shutdown == 0) { fputs(UP_ONE, stdout); printf("Current Inversions: %lu\n", total_inversions()); } } pthread_mutex_unlock(&shutdown_mtx); /* if we specified a duration, see if it has expired */ if (end && time(NULL) > end) { info("duration reached (%d seconds)\n", duration); set_shutdown_flag(); continue; } /* check for a pending SIGINT */ if (pending_interrupt()) { info("Keyboard Interrupt!\n"); break; } /* check watchdog stuff */ if ((watchdog_check())) { error("reporter stopping due to watchdog event\n"); set_shutdown_flag(); break; } /* clear watchdog counters */ watchdog_clear(); } debug("reporter: finished\n"); set_shutdown_flag(); return NULL; } int verify_cpu(int cpu) { int status; int err; cpu_set_t mask; CPU_ZERO(&mask); status = sched_getaffinity(0, sizeof(cpu_set_t), &mask); if (status == -1) { err = errno; fprintf(stderr, "sched_getaffinity %s\n", strerror(err)); exit(-1); } if (CPU_ISSET(cpu, &mask)) return SUCCESS; return FAILURE; } void *low_priority(void *arg) { int status; int unbounded; unsigned long count = 0; struct group_parameters *p = (struct group_parameters *)arg; pthread_barrier_t *loop_barr = &p->loop_barr; pthread_mutex_t *loop_mtx = &p->loop_mtx; int *loop = &p->loop; allow_sigterm(); if (verify_cpu(p->cpu) != SUCCESS) { error("low_priority[%d]: not bound to %ld\n", p->id, p->cpu); return NULL; } debug("low_priority[%d]: entering ready state\n", p->id); /* wait for all threads to be ready */ status = pthread_barrier_wait(&all_threads_ready); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error ("low_priority[%d]: pthread_barrier_wait(all_threads_ready): %x", p->id, status); return NULL; } unbounded = (p->inversions < 0); debug("low_priority[%d]: starting inversion loop\n", p->id); for (;;) { /* We can't set the 'loop' boolean here, because some flags may have already reached the loop_barr */ if (!unbounded && (p->total >= p->inversions)) { set_shutdown_flag(); } /* Either all threads go through the loop_barr, or none do */ pthread_mutex_lock(loop_mtx); if (*loop == 0) { pthread_mutex_unlock(loop_mtx); break; } pthread_mutex_unlock(loop_mtx); status = pthread_barrier_wait(loop_barr); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error("%s[%d]: pthread_barrier_wait(loop): %x\n", __func__, p->id, status); return NULL; } /* Only one Thread needs to check the shutdown status */ if (status == PTHREAD_BARRIER_SERIAL_THREAD) { pthread_mutex_lock(&shutdown_mtx); if (shutdown) { pthread_mutex_lock(loop_mtx); *loop = 0; pthread_mutex_unlock(loop_mtx); } pthread_mutex_unlock(&shutdown_mtx); } /* initial state */ debug("low_priority[%d]: entering start wait (%d)\n", p->id, count++); status = pthread_barrier_wait(&p->start_barrier); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error ("low_priority[%d]: pthread_barrier_wait(start): %x\n", p->id, status); return NULL; } debug("low_priority[%d]: claiming mutex\n", p->id); pthread_mutex_lock(&p->mutex); debug("low_priority[%d]: mutex locked\n", p->id); debug("low_priority[%d]: entering locked wait\n", p->id); status = pthread_barrier_wait(&p->locked_barrier); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error ("low_priority[%d]: pthread_barrier_wait(locked): %x\n", p->id, status); return NULL; } /* wait for priority boost */ debug("low_priority[%d]: entering elevated wait\n", p->id); status = pthread_barrier_wait(&p->elevate_barrier); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error ("low_priority[%d]: pthread_barrier_wait(elevate): %x\n", p->id, status); return NULL; } /* release the mutex */ debug("low_priority[%d]: unlocking mutex\n", p->id); pthread_mutex_unlock(&p->mutex); /* finish state */ debug("low_priority[%d]: entering finish wait\n", p->id); status = pthread_barrier_wait(&p->finish_barrier); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error ("low_priority[%d]: pthread_barrier_wait(elevate): %x\n", p->id, status); return NULL; } } set_shutdown_flag(); debug("low_priority[%d]: entering done barrier\n", p->id); /* wait for all threads to finish */ status = pthread_barrier_wait(&all_threads_done); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error ("low_priority[%d]: pthread_barrier_wait(all_threads_done): %x", p->id, status); return NULL; } debug("low_priority[%d]: exiting\n", p->id); return NULL; } void *med_priority(void *arg) { int status; int unbounded; unsigned long count = 0; struct group_parameters *p = (struct group_parameters *)arg; pthread_barrier_t *loop_barr = &p->loop_barr; pthread_mutex_t *loop_mtx = &p->loop_mtx; int *loop = &p->loop; allow_sigterm(); if (verify_cpu(p->cpu) != SUCCESS) { error("med_priority[%d]: not bound to %ld\n", p->id, p->cpu); return NULL; } debug("med_priority[%d]: entering ready state\n", p->id); /* wait for all threads to be ready */ status = pthread_barrier_wait(&all_threads_ready); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error ("med_priority[%d]: pthread_barrier_wait(all_threads_ready): %x", p->id, status); return NULL; } unbounded = (p->inversions < 0); debug("med_priority[%d]: starting inversion loop\n", p->id); for (;;) { if (!unbounded && (p->total >= p->inversions)) { set_shutdown_flag(); } /* Either all threads go through the loop_barr, or none do */ pthread_mutex_lock(loop_mtx); if (*loop == 0) { pthread_mutex_unlock(loop_mtx); break; } pthread_mutex_unlock(loop_mtx); status = pthread_barrier_wait(loop_barr); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error("%s[%d]: pthread_barrier_wait(loop): %x\n", __func__, p->id, status); return NULL; } /* Only one Thread needs to check the shutdown status */ if (status == PTHREAD_BARRIER_SERIAL_THREAD) { pthread_mutex_lock(&shutdown_mtx); if (shutdown) { pthread_mutex_lock(loop_mtx); *loop = 0; pthread_mutex_unlock(loop_mtx); } pthread_mutex_unlock(&shutdown_mtx); } /* start state */ debug("med_priority[%d]: entering start state (%d)\n", p->id, count++); status = pthread_barrier_wait(&p->start_barrier); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error ("med_priority[%d]: pthread_barrier_wait(start): %x", p->id, status); return NULL; } debug("med_priority[%d]: entering elevate state\n", p->id); status = pthread_barrier_wait(&p->elevate_barrier); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error ("med_priority[%d]: pthread_barrier_wait(elevate): %x", p->id, status); return NULL; } debug("med_priority[%d]: entering finish state\n", p->id); status = pthread_barrier_wait(&p->finish_barrier); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error ("med_priority[%d]: pthread_barrier_wait(finished): %x", p->id, status); return NULL; } } set_shutdown_flag(); debug("med_priority[%d]: entering done barrier\n", p->id); /* wait for all threads to finish */ if (have_errors == 0) { status = pthread_barrier_wait(&all_threads_done); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error ("med_priority[%d]: pthread_barrier_wait(all_threads_done): %x", p->id, status); return NULL; } } /* exit */ debug("med_priority[%d]: exiting\n", p->id); return NULL; } void *high_priority(void *arg) { int status; int unbounded; unsigned long count = 0; struct group_parameters *p = (struct group_parameters *)arg; pthread_barrier_t *loop_barr = &p->loop_barr; pthread_mutex_t *loop_mtx = &p->loop_mtx; int *loop = &p->loop; allow_sigterm(); if (verify_cpu(p->cpu) != SUCCESS) { error("high_priority[%d]: not bound to %ld\n", p->id, p->cpu); return NULL; } debug("high_priority[%d]: entering ready state\n", p->id); /* wait for all threads to be ready */ status = pthread_barrier_wait(&all_threads_ready); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error ("high_priority[%d]: pthread_barrier_wait(all_threads_ready): %x", p->id, status); return NULL; } unbounded = (p->inversions < 0); debug("high_priority[%d]: starting inversion loop\n", p->id); for (;;) { if (!unbounded && (p->total >= p->inversions)) { set_shutdown_flag(); } /* Either all threads go through the loop_barr, or none do */ pthread_mutex_lock(loop_mtx); if (*loop == 0) { pthread_mutex_unlock(loop_mtx); break; } pthread_mutex_unlock(loop_mtx); status = pthread_barrier_wait(loop_barr); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error("%s[%d]: pthread_barrier_wait(loop): %x\n", __func__, p->id, status); return NULL; } /* Only one Thread needs to check the shutdown status */ if (status == PTHREAD_BARRIER_SERIAL_THREAD) { pthread_mutex_lock(&shutdown_mtx); if (shutdown) { pthread_mutex_lock(loop_mtx); *loop = 0; pthread_mutex_unlock(loop_mtx); } pthread_mutex_unlock(&shutdown_mtx); } debug("high_priority[%d]: entering start state (%d)\n", p->id, count++); status = pthread_barrier_wait(&p->start_barrier); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error ("high_priority[%d]: pthread_barrier_wait(start): %x", p->id, status); return NULL; } debug("high_priority[%d]: entering running state\n", p->id); status = pthread_barrier_wait(&p->locked_barrier); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error ("high_priority[%d]: pthread_barrier_wait(running): %x", p->id, status); return NULL; } debug("high_priority[%d]: locking mutex\n", p->id); pthread_mutex_lock(&p->mutex); debug("high_priority[%d]: got mutex\n", p->id); debug("high_priority[%d]: unlocking mutex\n", p->id); pthread_mutex_unlock(&p->mutex); debug("high_priority[%d]: entering finish state\n", p->id); status = pthread_barrier_wait(&p->finish_barrier); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error ("high_priority[%d]: pthread_barrier_wait(finish): %x", status); return NULL; } /* update the group stats */ p->total++; /* update the watchdog counter */ p->watchdog++; } set_shutdown_flag(); debug("high_priority[%d]: entering done barrier\n", p->id); if (have_errors == 0) { /* wait for all threads to finish */ status = pthread_barrier_wait(&all_threads_done); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error ("high_priority[%d]: pthread_barrier_wait(all_threads_done): %x", p->id, status); return NULL; } } /* exit */ debug("high_priority[%d]: exiting\n", p->id); return NULL; } void error(char *fmt, ...) { va_list ap; fputs("ERROR: ", stderr); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); have_errors = 1; } void info(char *fmt, ...) { if (verbose) { va_list ap; va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); } } void debug(char *fmt, ...) { if (debugging) { va_list ap; fputs("DEBUG: ", stderr); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); } } void usage(void) { printf("usage: pi_stress \n"); printf(" options:\n"); printf("\t--verbose\t- lots of output\n"); printf("\t--quiet\t\t- surpress running output\n"); printf ("\t--duration=- length of the test run in seconds [infinite]\n"); printf("\t--groups=\t- set the number of inversion groups [%d]\n", ngroups); printf ("\t--inversions=- number of inversions per group [infinite]\n"); printf("\t--report=\t- output to file [/dev/null]\n"); printf("\t--rr\t\t- use SCHED_RR for test threads [SCHED_FIFO]\n"); printf("\t--prompt\t- prompt before starting the test\n"); printf ("\t--uniprocessor\t- force all threads to run on one processor\n"); printf("\t--mlockall\t- lock current and future memory\n"); printf("\t--debug\t\t- turn on debug prints\n"); printf("\t--version\t- print version number on output\n"); printf("\t--help\t\t- print this message\n"); } /* block all signals (called from main) */ int block_signals(void) { int status; sigset_t sigset; /* mask off all signals */ status = sigfillset(&sigset); if (status) { error("setting up full signal set %s\n", strerror(status)); return FAILURE; } status = pthread_sigmask(SIG_BLOCK, &sigset, NULL); if (status) { error("setting signal mask: %s\n", strerror(status)); return FAILURE; } return SUCCESS; } /* allow SIGTERM delivery (called from worker threads) */ int allow_sigterm(void) { int status; sigset_t sigset; status = sigemptyset(&sigset); if (status) { error("creating empty signal set: %s\n", strerror(status)); return FAILURE; } status = sigaddset(&sigset, SIGTERM); if (status) { error("adding SIGTERM to signal set: %s\n", strerror(status)); return FAILURE; } status = pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); if (status) { error("unblocking SIGTERM: %s\n", strerror(status)); return FAILURE; } return SUCCESS; } /* clean up before exiting */ void set_shutdown_flag(void) { pthread_mutex_lock(&shutdown_mtx); if (shutdown == 0) { /* tell anyone that's looking that we're done */ info("setting shutdown flag\n"); shutdown = 1; } pthread_mutex_unlock(&shutdown_mtx); } /* set up a test group */ int initialize_group(struct group_parameters *group) { int status; pthread_mutexattr_t mutex_attr; group->inversions = inversions; /* setup default attributes for the group mutex */ /* (make it a PI mutex) */ status = pthread_mutexattr_init(&mutex_attr); if (status) { error("initializing mutex attribute: %s\n", strerror(status)); return FAILURE; } /* set priority inheritance attribute for mutex */ status = pthread_mutexattr_setprotocol(&mutex_attr, PTHREAD_PRIO_INHERIT); if (status) { error("setting mutex attribute policy: %s\n", strerror(status)); return FAILURE; } /* initialize the group mutex */ status = pthread_mutex_init(&group->mutex, &mutex_attr); if (status) { error("initializing mutex: %s\n", strerror(status)); return FAILURE; } /* initialize the group barriers */ if (barrier_init(&group->start_barrier, NULL, NUM_TEST_THREADS, "start_barrier")) return FAILURE; if (barrier_init(&group->locked_barrier, NULL, 2, "locked_barrier")) return FAILURE; if (barrier_init(&group->elevate_barrier, NULL, 2, "elevate_barrier")) return FAILURE; if (barrier_init (&group->finish_barrier, NULL, NUM_TEST_THREADS, "finish_barrier")) return FAILURE; if (barrier_init(&group->loop_barr, NULL, NUM_TEST_THREADS, "loop_barrier")) return FAILURE; if ((status = pthread_mutex_init(&group->loop_mtx, NULL)) != 0) { error("pthread_mutex_init, status = %d\n", status); return FAILURE; } if ((status = pthread_mutex_lock(&group->loop_mtx)) != 0) { error("pthread_mutex_lock, status = %d\n", status); return FAILURE; } group->loop = 1; if ((status = pthread_mutex_unlock(&group->loop_mtx)) != 0) { error("pthread_mutex_unlock, status = %d\n", status); return FAILURE; } return SUCCESS; } /* setup and create a groups threads */ int create_group(struct group_parameters *group) { int status; pthread_attr_t thread_attr; cpu_set_t mask; /* initialize group structure */ status = initialize_group(group); if (status) { error("initializing group %d\n", group->id); return FAILURE; } CPU_ZERO(&mask); CPU_SET(group->cpu, &mask); debug("group %d bound to cpu %ld\n", group->id, group->cpu); /* start the low priority thread */ debug("creating low priority thread\n"); if (setup_thread_attr(&thread_attr, LOW_PRIO(), &mask, policy)) return FAILURE; status = pthread_create(&group->low_tid, &thread_attr, low_priority, group); if (status != 0) { error("creating low_priority thread: %s\n", strerror(status)); return FAILURE; } /* create the medium priority thread */ debug("creating medium priority thread\n"); if (setup_thread_attr(&thread_attr, MED_PRIO(), &mask, policy)) return FAILURE; status = pthread_create(&group->med_tid, &thread_attr, med_priority, group); if (status != 0) { error("creating med_priority thread: %s\n", strerror(status)); return FAILURE; } /* create the high priority thread */ debug("creating high priority thread\n"); if (setup_thread_attr(&thread_attr, HIGH_PRIO(), &mask, policy)) return FAILURE; status = pthread_create(&group->high_tid, &thread_attr, high_priority, group); if (status != 0) { error("creating high_priority thread: %s\n", strerror(status)); set_shutdown_flag(); return FAILURE; } return SUCCESS; } void process_command_line(int argc, char **argv) { int opt; while ((opt = getopt_long(argc, argv, "+", options, NULL)) != -1) { switch (opt) { case '?': case 'h': usage(); exit(0); case 't': duration = strtol(optarg, NULL, 10); break; case 'v': verbose = 1; quiet = 0; break; case 'q': verbose = 0; quiet = 1; break; case 'i': inversions = strtol(optarg, NULL, 10); info("doing %d inversion per group\n", inversions); break; case 'g': ngroups = strtol(optarg, NULL, 10); info("number of groups set to %d\n", ngroups); break; case 'r': policy = SCHED_RR; break; case 'p': prompt = 1; break; case 'd': debugging = 1; break; case 'V': printf("pi_stress v%1.2f ", VERSION_STRING); printf("(%s %s)\n", __DATE__, __TIME__); exit(0); case 'u': uniprocessor = 1; break; case 'm': lockall = 1; break; } } } /* total the number of inversions that have been performed */ unsigned long total_inversions(void) { int i; unsigned long total = 0; for (i = 0; i < ngroups; i++) total += groups[i].total; return total; } void banner(void) { if (quiet) return; printf("Starting PI Stress Test\n"); printf("Number of thread groups: %d\n", ngroups); if (duration >= 0) printf("Duration of test run: %d seconds\n", duration); else printf("Duration of test run: infinite\n"); if (inversions < 0) printf("Number of inversions per group: unlimited\n"); else printf("Number of inversions per group: %d\n", inversions); printf("Test threads using scheduler policy: %s\n", policy == SCHED_FIFO ? "SCHED_FIFO" : "SCHED_RR"); printf(" Admin thread priority: %d\n", MAIN_PRIO()); printf("%d groups of 3 threads will be created\n", ngroups); printf(" High thread priority: %d\n", HIGH_PRIO()); printf(" Med thread priority: %d\n", MED_PRIO()); printf(" Low thread priority: %d\n\n", LOW_PRIO()); } void summary(void) { time_t interval = finish - start; struct tm *t = gmtime(&interval); printf("Total inversion performed: %lu\n", total_inversions()); printf("Test Duration: %d days, %d hours, %d minutes, %d seconds\n", t->tm_yday, t->tm_hour, t->tm_min, t->tm_sec); } int barrier_init(pthread_barrier_t * b, const pthread_barrierattr_t * attr, unsigned count, const char *name) { int status; if ((status = pthread_barrier_init(b, attr, count)) != 0) { error("barrier_init: failed to initialize: %s\n", name); error("status = %d\n", status); return FAILURE; } return SUCCESS; } rt-tests/src/pi_tests/pip_stress.c0000600001204700120500000002343712252162340020041 0ustar williamswilliams/* Pip stress - Priority Inheritance with processes Copyright (C) 2009, John Kacur 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 program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* * This program demonstrates the technique of using priority inheritance (PI) * mutexes with processes instead of threads. * The way to do this is to obtain some shared memory - in this case with * mmap that backs a pthread_mutex_t since this will support PI. * Pay particular attention to how this is intialized to support processes. * Function init_shared_pthread_mutex() does this by setting the * pthread_mutexattr to PTHREAD_PROCESS_SHARED and the mutex protocol to * PTHREAD_PRIO_INHERIT. * In this program we purposely try to invoke a classic priority inversion. * A low priority process grabs the mutex and does some work. * A high priority process comes a long and is blocked since the mutex is taken. * A medium priority process that doesn't require the mutex then takes the * processor. Because the processes are restricted to one cpu, the low priority * processes never makes any progress because the medium priority process * runs in an infinite loop. This is a priority inversion because the * medium priority process is running at the expensive of the high priority * process. However, since we have used PRIO_INHERIT and are running on a * machine that supports preemption, the high priority process will lend it's * priority to the low priority process which will preempt the medium priority * process. The low priority process will then release the mutex which the * high priority process can obtain. When the high priority process gets to run * it kills the medium priority process. * The state structure keeps track of the progress. Although this program * is set up to likely trigger an inversion, there is no guarantee that * scheduling will make that happen. After the program completes it reports * whether a priority inversion occurred or not. In either case this program * demonstrates how to use priority inheritance mutexes with processes. * In fact, you would be better off to avoid scenarios in which a priority * inversion occurs if possible - this program tries to trigger them just * to show that it works. If you are having difficulty triggering an inversion, * merely increase the time that the low priority process sleeps while * holding the lock. (usleep); * Also note that you have to run as a user with permission to change * scheduling priorities. */ #include "pip_stress.h" pthread_mutex_t *resource; /* This records the state to determine whether a priority inversion occured */ struct State { int low_owns_resource; int high_started; int high_owns_resource; int medium_started; int inversion; pthread_mutex_t *mutex; }; struct State *statep; const int policy = SCHED_FIFO; const int prio_min; /* Initialized for the minimum priority of policy */ int main(void) { void *mptr; /* memory pointer */ pid_t pid1, pid2; cpu_set_t set, *setp = &set; int res; int *minimum_priority = (int*)&prio_min; *minimum_priority = sched_get_priority_min(policy); if (check_privs()) exit(-1); mptr = mmap_page(); /* Get a page of shared memory */ resource = (pthread_mutex_t*)mptr; /* point our lock to it */ mptr += sizeof(pthread_mutex_t); /* advance the memory pointer */ /* Initialize our mutex via the resource pointer */ init_shared_pthread_mutex(resource, PTHREAD_PRIO_INHERIT, policy); statep = (struct State*)mptr; mptr += sizeof(struct State); init_state(); /* Initialize the state structure */ statep->mutex = (pthread_mutex_t*)mptr; /* point the next lock to it */ mptr += sizeof(pthread_mutex_t); /* advance the memory pointer */ /* Initialize our State mutex */ init_shared_pthread_mutex(statep->mutex, PTHREAD_PRIO_NONE, policy); set_rt_prio(0, prio_min, policy); /* We restrict this program to the first cpu, inorder to increase * the likelihood of a priority inversion */ CPU_ZERO(setp); CPU_SET(0, setp); res = sched_setaffinity(0, sizeof(set), setp); if (res == -1) { int err = errno; err_msg("sched_setaffinity: "); err_exit(err, NULL); } pid1 = fork(); if (pid1 == -1) { perror("fork"); exit(1); } else if (pid1 != 0) { /* parent code */ low(pid1); } else { /* child code */ pid2 = fork(); /* parent code */ if (pid2 == -1) { perror("fork: "); exit(-1); } else if (pid2 != 0) { /* parent code */ high(pid2); } else { /* child code */ medium(); } } exit(0); } /* Initialize the structure that tracks when a priority inversion occurs */ void init_state(void) { /* Init the State structure */ statep->low_owns_resource = 0; statep->high_started = 0; statep->high_owns_resource = 0; statep->medium_started = 0; /* Assume an inversion will occur until proven false */ statep->inversion = 1; } /* @pid = high priority process pid */ void low(pid_t pid) { int status; Pthread_mutex_lock(resource); Pthread_mutex_lock(statep->mutex); statep->low_owns_resource = 1; if (statep->high_owns_resource || statep->medium_started) { statep->inversion = 0; } Pthread_mutex_unlock(statep->mutex); usleep(500); Pthread_mutex_unlock(resource); waitpid(pid, &status, 0); } void medium(void) { set_rt_prio(0, prio_min+1, policy); Pthread_mutex_lock(statep->mutex); statep->medium_started = 1; if (!statep->high_started) statep->inversion = 0; Pthread_mutex_unlock(statep->mutex); for(;;); /* infinite loop */ } /* @pid = medium priority process pid */ void high(pid_t pid) { int status; set_rt_prio(0, prio_min+2, policy); /* Must come after raising the priority */ Pthread_mutex_lock(statep->mutex); statep->high_started = 1; Pthread_mutex_unlock(statep->mutex); Pthread_mutex_lock(resource); Pthread_mutex_lock(statep->mutex); statep->high_owns_resource = 1; if (!statep->low_owns_resource || !statep->medium_started) { statep->inversion = 0; } Pthread_mutex_unlock(statep->mutex); Pthread_mutex_unlock(resource); kill(pid, SIGKILL); /* kill the medium thread */ waitpid(pid, &status, 0); Pthread_mutex_lock(statep->mutex); if (statep->inversion) printf("Successfully used priority inheritance to handle an inversion\n"); else { printf("No inversion incurred\n"); } Pthread_mutex_unlock(statep->mutex); } /* mmap a page of anonymous shared memory */ void *mmap_page(void) { void *mptr; long pgsize = sysconf(_SC_PAGE_SIZE); mptr = mmap(NULL, pgsize, PROTRW, MMAP_FLAGS, 0, 0); if (mptr == MAP_FAILED) { perror("In function mmap_page - mmap"); exit(-1); } return mptr; } long process_shared_mutex_available(void) { long res = -1; /* undefined */ #ifdef _POSIX_THREAD_PROCESS_SHARED res = sysconf(_SC_THREAD_PROCESS_SHARED); if (res == -1) { int err = errno; /* save the error number */ err_msg("%s: sysconf(_SC_THREAD_PROCESS_SHARED): "); err_exit(err, NULL); } #else #error _POSIX_THREAD_PROCESS_SHARED is not defined #endif return res; } void Pthread_mutexattr_init(pthread_mutexattr_t *attr) { int err; err = pthread_mutexattr_init(attr); if (err) { err_msg("%s: pthread_mutexattr_init(): ", __func__); err_exit(err, NULL); } } void Pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared) { int err; err = pthread_mutexattr_setpshared(attr, pshared); if (err) { err_msg("%s: pthread_mutexattr_setpshared(): ", __func__); err_exit(err, NULL); } } void Pthread_mutexattr_setprotocol(pthread_mutexattr_t *attr, int protocol) { int err; err = pthread_mutexattr_setprotocol(attr, protocol); if (err) { err_msg("%s: pthread_mutexattr_setprotocol(): ", __func__); err_exit(err, NULL); } } void Pthread_mutex_init(pthread_mutex_t *mutex, pthread_mutexattr_t *attr) { int err; err = pthread_mutex_init(mutex, attr); if (err) { err_msg("%s: pthread_mutex_init(): ", __func__); err_exit(err, NULL); } } void Pthread_mutex_lock(pthread_mutex_t *mutex) { int err; err = pthread_mutex_lock(mutex); if (err) { err_msg("%s: pthread_mutex_lock(): ", __func__); err_exit(err, NULL); } } void Pthread_mutex_unlock(pthread_mutex_t *mutex) { int err; err = pthread_mutex_unlock(mutex); if (err) { err_msg("%s: pthread_mutex_unlock(): ", __func__); err_exit(err, NULL); } } void init_shared_pthread_mutex(pthread_mutex_t *mutex, int protocol, int policy) { pthread_mutexattr_t attr; process_shared_mutex_available(); Pthread_mutexattr_init(&attr); Pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); Pthread_mutexattr_setprotocol(&attr, protocol); Pthread_mutex_init(mutex, &attr); } /* Set the priority and policy of a process */ int set_rt_prio(pid_t pid, int prio, int policy) { int err; struct sched_param param; struct sched_param *pparam = ¶m; pparam->sched_priority = prio; err = sched_setscheduler(pid, policy, pparam); if (err) { err = errno; /* save the errno */ err_msg_n(err, "%s: sched_setscheduler(): ", __func__); err_msg("%s: prio = %d\n", __func__, prio); err_msg("%s: pparam->sched_priority = %d\n", __func__, pparam->sched_priority); err_msg("%s: policy = %d\n", __func__, policy); } return err; /* 0 on success */ } int get_rt_prio(pid_t pid) { int err; struct sched_param param; err = sched_getparam(pid, ¶m); if (err) { err = errno; /* save the errno */ err_msg_n(err, "%s: get_rt_prio(): ", __func__); return -1; } return param.sched_priority; } rt-tests/src/pi_tests/COPYING0000600001204700120500000004307012252162340016530 0ustar williamswilliams GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy 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 program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 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. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. rt-tests/src/pi_tests/classic_pi.odg0000600001204700120500000003026612252162340020304 0ustar williamswilliamsPK<5.++mimetypeapplication/vnd.oasis.opendocument.graphicsPK<5Configurations2/statusbar/PK<5'Configurations2/accelerator/current.xmlPKPK<5Configurations2/floater/PK<5Configurations2/popupmenu/PK<5Configurations2/progressbar/PK<5Configurations2/menubar/PK<5Configurations2/toolbar/PK<5Configurations2/images/Bitmaps/PK<5ϣ[[-Pictures/10000000000000200000002000309F1C.pngPNG  IHDR DPLTE3f333f3333f3ffffff3f̙3f3f333f333333333f33333333f33f3ff3f3f3f3333f33̙33333f3333333f3333f3ffffff3f33ff3f3f3f3fff3ffffffffffff3ffff̙fff3fffffff3ffffff3f333f3333f3ffffff3f̙̙3̙f̙̙̙̙3f3f̙333f3̙333f3fff̙fff3f̙̙3f̙3f̙3f333f3333f3ffffff3f̙3f3fOIDATxc'F*U0RU<4IENDB`PK<5 content.xml[n6+ ]gPtP@43@DD%D~IIEٖ%{ؘ,^>xkN^0Htec? H+?zʻłx$:qI Y1:W4%(#,Ff?KRWV3Y{&UH|6ʲu_\e?Y(E\*/ƯY.z5$\Yy:Ӵf6&HRTZ/]PhCi@ҍpΏSA9[Ք〈01Hirw,;hW3v3*f F(_u}`CW4,ۢʧ$ Ж$ `5tҊkI{sT}CIUQ׌'!ҘԘ_Aĉ: xxW8B2WVI(n"^(&]JSVm AV :OHwP) fe"7)ZbE6]=%E8E'qF@[)e$Мl' j3G* +p(TQH nݰ­F+"A p$^&,cP@#D1U?ϕ_)M6uAYNgf'1Nzu4_BE^jW]urJDNxopC^E2H\~TJ=.:_;Q#' L{9%uDڑHh9%>[F+$FXDe_*1&91Q|ܫ}`2ßW.QbD $"+lqCOA.gŐ߿xdP̳ՊG) +nfvUhR;f e" `>0M$H&KnY#yT Sceǐ9C7ɐ5"CM2dƐd!&qF7C֍ fhוrìucOI/;ђSˢ9+6*U+3Wr=^u+-q4'u/joy#FIW!71+YE7[ +D.:GV%{>imJ?KKo jӼ9eʠƃh޴_88@x5&0E,tP/enF qAb#3:<ȭu=B6c kuϚ6=Pxaݱ[Z]x! K^vcLhG tNcн9*Q'ko8bM4 : 4ho&an GeneoYMk6+f[Q-X<%G`8kO 10c]-B \),#㟓O8'9= L-ټN"TkF:PK*^2J8PK<5 styles.xml\_60tcoeS\}hiZm3D~{wfHl+'ެd!Ό4̐3滇$3s9s&, E͝o?[绷߼52aT\cO@8͗x2] |҄K.EJhis/̈PqlK+ #oK?Y3ґ k1T!ZP$U@Ruwt7 qbjjpXe5W,f[&Lѡ!RZ$+&CC=LX\a7eZu]-L3]e wYd&Tm{}D_g!o PlۖBԪ vnys\[ܻ;{x=qX#..Ѐw{tǃ') =ZZV*~[]n߾,HqD_#ʱ?׎DǶW}\I񉑈歙z׈GЉR'A{$ U,Tؗ$f`k9I̒5-rC31ck  myTTLB@Ka dH P"(>GLVg[lY"; 7Sid%`&_&0h%Q)_0Lћ5sVUd9 U]G9#Ď臗dѨB[_GG1$_lX Hh8sO%}XL2K! P yZ4TJ2%ǺVFcW4VPQU)feN-kPoRiTM=6&akI;TMFGJcpșVBÌXv<ҙӌ5ܹ̘KbY NgujCb\ MpE׋t0\mZViXakJ ͧiҳ>7%Z*!63aI !=~pYP 7r]RoLFzɣ$ʙ"mH}'ga\aa*☩!^! ?N_*>̳u>쓕sҴIrO{1cX7L2z%+^_!蚒t2uB!OݾHn.P0H^] ًyyb_h^\}bE.n_%vφ gM/ {(udˌiOjY-1_dXmѸrj17E|(S?"i5Vb%e,ݖ!4[XLh TU\FO˘Geɮ~زoFAEu8=&~#{Xܳ$RD_hqbqMhz@~# Վ+HT__љ]FԿ9TwOt^DrIh}J8pstK3,<'4ݴs &*H˕ϩݏI>+ng[5XoY׷K2C| j4{g3Ig6i3=v_}}cych +)u|~V&aSqϴFl0G64NGptO3]A9Ԛ: :RWW&ǺzǺj걮걮Fh/^WX:ǫ%z6ՄLmtfg}ie4S>_?~<3ct^5Za,ɜ-:<!W ]; /E4a4/׈W3uavkj>H%"Tmk{Q迾StDZ,PlzЫsqLH%)1,(;J{寤a\jEO) l3[ar{g$$4Wo/M8~] Doc! 21Uj.Ji,bܚw &mz4s6@vPKuHPK<5>88meta.xml OpenOffice.org/2.0$Linux OpenOffice.org_project/680m4$Build-9070Clark Williams2006-09-26T11:52:53Clark Williams2006-09-28T15:47:30Clark Williams2006-09-26T15:00:39en-US7PT11H38M19SPK<5Thumbnails/thumbnail.pngiPNG  IHDR|]IDATxQ8 @sX0*0;z O!d[IJt6iFNZʇGwo@r-)<)<)<Uۉݳ<]Q^{j-}.>8V8'S9ymyt9^-[|=zEܤo@(\:LO:tޖv{E,FQO$ V Qt.$Mdd' ?&4#]7M߇բ[w,itKg~}t[f4=I'k@ F F F F pZ +VH ww/?SZSLN2KdS/.{B to^;/k;WEyXJkyk@X<.!B;Upc4.T1Ǖcֱ;^vPɘHUJki͡Ž_mxm n@4._f^6i{;C/tǴY`c%g5x:jOKFb,&GGD<- tѠ;65˱s92?X0s־=+Es)3XǮ ?ٳs1˖uQn٥:B(OĽS^Td<(~hȾ|=ϗ<2 \*cDEI }4A(Dnʖ1%U!.jnӹQN HjּU'y,ٝRk#. °\*TCxOFpFuY:VN6J2^2n^QxEwy.6ݡiNt|<K!> H 5Du8~煮GW8Dt ى.CNȧN1t.n<)4/˨HsjV#]"zU!M~)wM8Y |&ÔݡZڙkQn.Xc^$÷zRtDrfgr"FʘZ;BHX$|/AoH=W<+cʽۡFtIf9jNqӤ_ ڙ[WՎt]=r\~^FMKiIw?3*w'B _i<ޤV8nƥyFL.ܸzx{;%QttG550爦' -5@hK4fJVq0Fqרv3Z'{}wflo]I+ӞdSN'mQ)\s}J6͐^y oa5l"ܲNZbHxF=iӨ!V4iOkևtN X#]Y#,Uz+8iL:] R]| 'H}rE GHCwLIDN١=RPpm;/XMxWEBiA˳Τ;ּ Iۙ+:x̛Ղ6Ewxm1/eWaZ;6*rYQt B^q5XDyH^hqRXfRh&kJ8^b&M77vP+i3o @T+̓_8t[V}"]v݊szZdA: B%יi>sMDxdf/3WȆځr(\Ov+י  aWf[e{u%$[]Y`Dfי~%)enP.sRA.>p{6M3rEYO)}ZmX) ^rϛCP.6kSϗ2[Aޡ0jkÿswuۍKWkEWH@Od}Y]gWo~=JA0~fsS,}yf W}g ~Q.]&F@F./÷ r/g=gq:\ppa|QkcDK`AB>pa0ARL߬h ɋ|Qx) ];w~`!S ֑RąwtS#>dwt3.͢<Ff5gݣbUCXw$?H4PUu <^d覬cRҌ rԉU9on։C{g$4,IYoOOahnbc$GDOۘP}#5UAiI&zQBrgfnC!!PubAH\8cfuٮf$Q#m>t493C]a?y4)@sȎ,Ã6hm+Ht}(P ]DPN8O>?$vzoa?WS2bp~@l+y8UQ:06A س4m'IYt6D7Ы2oITQ29z/|0B~dc0"J2jg]Z ;Jt 1(ͯ6@ < 2y{ ?Ԝ!e2O祕[H#k$#<8շHlkMγΥ8Pj&x7,S%bB양ذ_CjKp<_5(}j&sÁӼ_,}0!rK0'cvf c]? EeW"wvWɁįKa`mJ5-w'0ns9)`4q{y>pyan}>{WY^Ns閻/[ 亭Eq;6k7bY{=ZVpJ/<40kI~}mNKa 0Lg~B8~)d⍠c"vJN.YPKv)%PK<5META-INF/manifest.xmlAo  u }$aߏfu) |A<*k*6/f,#mL[6aӲF5T^;Y\g6XFt%:0CeoZ=ewp4q?gwJtvP1VRPM]\\PQ^&h;AqG S64 .8{!2xo3f2+V$;낋 ${zt'݇ NdpՉ)ä5"Ba2w}LWBQ|>~A<{7E"7Hx_tRc"Uw.YNdtwӵ;q &~KxWPKyIePK<5.++mimetypePK<5QConfigurations2/statusbar/PK<5'Configurations2/accelerator/current.xmlPK<5Configurations2/floater/PK<5Configurations2/popupmenu/PK<5NConfigurations2/progressbar/PK<5Configurations2/menubar/PK<5Configurations2/toolbar/PK<5Configurations2/images/Bitmaps/PK<5ϣ[[-1Pictures/10000000000000200000002000309F1C.pngPK<5*^2J8 content.xmlPK<5uH Z styles.xmlPK<5>883meta.xmlPK<5dThumbnails/thumbnail.pngPK<5v)% r$settings.xmlPK<5yIe*META-INF/manifest.xmlPKIW,rt-tests/src/pi_tests/pi_tests.spec0000600001204700120500000000466612252162340020213 0ustar williamswilliamsSummary: Programs used to test Priority Inheritance Mutexes Name: pi_tests Version: 1.17 Release: 1%{?dist} License: GPL Group: Development/Tools URL: http://people.redhat.com/~williams/tests Source0: %{name}-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root %define mandir usr/share/man %description The pi_tests package contains programs used to test the functionality of the priority inheritance attribute of POSIX mutexes on the Linux kernel %prep %setup -q %build make all %install rm -rf $RPM_BUILD_ROOT mkdir $RPM_BUILD_ROOT install -D -m 755 classic_pi $RPM_BUILD_ROOT/usr/sbin/classic_pi install -D -m 755 pi_stress $RPM_BUILD_ROOT/usr/sbin/pi_stress install -D -m 644 pi_stress.8 $RPM_BUILD_ROOT/%{mandir}/man8/pi_stress.8 gzip $RPM_BUILD_ROOT/%{mandir}/man8/pi_stress.8 install -D -m 644 COPYING $RPM_BUILD_ROOT/usr/share/doc/%{name}-%{version}/COPYING install -D -m 644 README $RPM_BUILD_ROOT/usr/share/doc/%{name}-%{version}/README %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root,-) /usr/sbin/classic_pi /usr/sbin/pi_stress %doc /usr/share/man/man8/pi_stress.8.gz /usr/share/doc/%{name}-%{version}/COPYING /usr/share/doc/%{name}-%{version}/README %changelog * Wed Jan 2 2008 Clark Williams - 1.17-1 - fixed logic error with watchdog_check() * Wed Jul 11 2007 Clark Williams - 1.16-1 - bumped version for packaging in RHEL-RT - added usage() to classic_pi - removed --signal option in pi_stress * Wed Jul 11 2007 Clark Williams - 1.15-1 - added rpmlint target to Makefile - fixed rpmlint complaints in specfile * Wed Dec 12 2006 Clark Williams - 1.14-1 - changed how pi_stress shutdown is handled - fixed dealing with --iterations shutdown * Thu Dec 7 2006 Clark Williams - 1.13-1 - Added README and COPYING files * Wed Dec 6 2006 Clark Williams - 1.12-1 - changed shutdown variable to volatile - bumped version * Mon Dec 4 2006 Clark Williams - 1.11-1 - Modified priorities to be just above minimum (as opposed to just below max) * Thu Nov 30 2006 Clark Williams - 1.10-1 - added timing logic to pi_stress.c - refactored function order in pi_stress.c * Wed Nov 29 2006 Clark Williams - 1.9-1 - added --duration logic to pi_stress.c * Mon Nov 27 2006 Clark Williams - 1.8-1 - Initial packaging. rt-tests/src/pi_tests/README0000600001204700120500000000552012252162340016353 0ustar williamswilliamsDESCRIPTION The pi_stress program is a stress test that is intended to exercise kernel and C library code paths for POSIX mutexes using the Priority Inheritance attribute (PTHREAD_PRIO_INHERIT). The program consists of an admin thread (main), a reporter thread and some number of groups of three threads called "inversion groups". These thread groups are called that because they cause a condition called Priority Inversion, where a high priority thread is blocked due to a low-priority thread holding a shared resource. Priority inversion with no contravening logic is a deadlock condition. Each inversion group consists of three threads: 1. A high-priority thread 2. A medium-priority thread 3. A low-priority thread The threads run through a state machine designed to guarantee that a low-priority thread holds a mutex while a medium priority thread runs (keeping the low-priority thread from releasing the mutex). The high-priority thread attempts to acquire the mutex and is blocked because of the low-priority thread holding it. If priority inheritence is working, the low-priority thread will receive a priority boost (will inherit the high-priority thread's priority) and will then run and release the mutex, averting a deadlock. On a multi-processor system, the admin and reporter threads are run one one processor while the inversion groups are run on another processor. STEPS TO RUN Install the pi_tests rpm. If you are running Fedora Core, the simplest way do to that is to fetchthe rt-userspace.repo file from: http://people.redhat.com/~williams/rt-userspace.repo and install that file in /etc/yum.repos.d, then run: $ yum install pi_stress If you are not running Fedora, you can fetch the SRPM and build it, or fetch the tarball from: http://people.redhat.com/~williams/tests Once you've installed it, to see the options provided by pi_stress you can run it with the --help option to get a usage message or read the man page for more detail. Since the stress test runs as a number of realtime threads, it's usually a good idea to run the test while logged into the test machine from another system. Its always a good idea to have a window up that's either a serial console to the test machine or something that's continually printing the contents of the test system's syslog (e.g. 'tail -f /var/log/messages'). Login to the test machine and run pi_stress as root: $ sudo /usr/sbin/pi_stress This will run the test with the defaults of: - no stop time - 10 inversion groups A banner will be printed indicating what defaults are in use and then a display will be updated showing the current number of inversions that have been performed. To stop the test, press any key and a summary will be printed to show how many inversions were performed and how long the test ran. To run the test for a specifed time, use the --duraton= option. rt-tests/src/pi_tests/Makefile0000600001204700120500000000351312252162340017133 0ustar williamswilliamsPACKAGE := pi_tests SPEC := $(PACKAGE).spec VERSION := $(shell awk '/^Version:/ { print $$2 }' $(SPEC)) RELEASE := $(shell awk '/Release:/ { print $$2 }' ${PACKAGE}.spec) SRPM := $(PACKAGE)-$(VERSION)-$(RELEASE).src.rpm HERE := $(shell pwd) TARGET := $(shell uname -p) RPMARGS := --define "_sourcedir $(HERE)" \ --define "_builddir $(HERE)/BUILD" \ --define "_rpmdir $(HERE)/RPMS" \ --define "_srcrpmdir $(HERE)/SRPMS" CC := gcc OPT := -g -O CFLAGS := $(OPT) -Wall -D_GNU_SOURCE -DVERSION=\"$(VERSION)\" LIBS := -lpthread -lrt SRC := classic_pi.c allprios.c tst-mutexpi10.c pi_stress.c sigtest.c DOC := pi_stress.8 README COPYING TARGETS := $(subst .c,,$(SRC)) all: classic_pi pi_stress classic_pi: classic_pi.c $(CC) $(CFLAGS) -o $@ $< $(LIBS) pi_stress: pi_stress.c $(CC) $(CFLAGS) -o $@ $< $(LIBS) tst-mutexpi10: tst-mutexpi10.c $(CC) $(CFLAGS) -o $@ $< $(LIBS) #tst-mutexpi10.c: classic_pi.c # indent --gnu-style classic_pi.c -o tst-mutexpi10.c allprios: allprios.c $(CC) $(CFLAGS) -o $@ $< $(LIBS) tarball: pi_tests-$(VERSION).tar.gz libctest: tst-mutexpi10 sigtest: sigtest.c $(CC) $(CFLAGS) -o $@ $< $(LIBS) pi_tests-$(VERSION).tar.gz: clean $(SRC) $(DOC) Makefile pi_tests.spec rm -rf $@ pi_tests-$(VERSION) mkdir pi_tests-$(VERSION) cp $(SRC) $(DOC) Makefile pi_tests.spec pi_tests-$(VERSION) tar -czvf pi_tests-$(VERSION).tar.gz pi_tests-$(VERSION) clean: rm -rf *~ *.[oi] $(TARGETS) *.tar.gz pi_tests-* rpm: rpmdirs $(SPEC) tarball rm -rf RPMS/$(TARGET) rpmbuild -ba --target $(TARGET) $(RPMARGS) $(SPEC) rpmdirs: @[ -d BUILD ] || mkdir BUILD @[ -d RPMS ] || mkdir RPMS @[ -d SRPMS ] || mkdir SRPMS rpmlint lint: rpm rpmlint -vi RPMS/*/* rpmlint -vi SRPMS/* DESTINATION := people.redhat.com:~williams/public_html/tests push: rpm scp pi_tests-$(VERSION).tar.gz $(DESTINATION) scp SRPMS/* $(DESTINATION) rt-tests/src/pi_tests/classic_pi.c0000600001204700120500000004325412252162340017756 0ustar williamswilliams/* classic_pi - Classic Priority Inversion deadlock test case Copyright (C) 2006, 2007 Clark Williams 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 program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* This program tests Priority Inheritence mutexes and their ability to avoid Priority Inversion deadlocks The basic premise here is to set up a deadlock scenario and confirm that PI mutexes resolve the situation. Three worker threads will be created from the main thread: low, medium and high priority threads that use SCHED_FIFO as their scheduling policy. The low priority thread claims a mutex and then starts "working". The medium priority thread starts and preempts the low priority thread. Then the high priority thread runs and attempts to claim the mutex owned by the low priority thread. Without priority inheritance, this will deadlock the program. With priority inheritance, the low priority thread receives a priority boost, finishes it's "work" and releases the mutex, which allows the high priority thread to run and finish and then the medium priority thread finishes. That's the theory, anyway... CW - 2006 */ #include #include #include #include #include #include #include #include #include #include /* test timeout */ #define TIMEOUT 2 /* determine if the C library supports Priority Inheritance mutexes */ #if defined(_POSIX_THREAD_PRIO_INHERIT) && _POSIX_THREAD_PRIO_INHERIT != -1 #define HAVE_PI_MUTEX 1 #else #define HAVE_PI_MUTEX 0 #endif int use_pi_mutex = HAVE_PI_MUTEX; #define SUCCESS 0 #define FAILURE 1 /* the number of times we cause a priority inversion situation */ int inversions = 1; int verbose = 0; struct option options [] = { { "verbose", no_argument, NULL, 'v' }, { "quiet", no_argument, NULL, 'q' }, { "no-pi", no_argument, NULL, 'n'}, { "inversions" , required_argument, NULL, 'i'}, }; /* define priorities for the threads */ #define SKEL_PRIO(x) (x) #define MAIN_PRIO(x) (x - 1) #define HIGH_PRIO(x) (x - 2) #define MED_PRIO(x) (x - 3) #define LOW_PRIO(x) (x - 4) enum thread_names {LOW=0, MEDIUM, HIGH, NUM_WORKER_THREADS}; pthread_mutex_t mutex; pthread_mutexattr_t mutex_attr; pthread_barrier_t all_threads_ready; pthread_barrier_t all_threads_done; // state barriers pthread_barrier_t start_barrier; pthread_barrier_t locked_barrier; pthread_barrier_t elevate_barrier; pthread_barrier_t finish_barrier; volatile int deadlocked = 0; volatile int high_has_run = 0; volatile int low_unlocked = 0; cpu_set_t cpu_mask; struct thread_parameters { pthread_t tid; int inversions; } thread_parameters[NUM_WORKER_THREADS]; /* forward prototypes */ void *low_priority(void *arg); void *med_priority(void *arg); void *high_priority(void *arg); int setup_thread_attr(pthread_attr_t *attr, int prio, cpu_set_t *mask); int set_cpu_affinity(cpu_set_t *mask); void error(char *, ...); void info(char *, ...); int initialize_barriers(void) { int status; status = pthread_barrier_init(&all_threads_ready, NULL, NUM_WORKER_THREADS+1); if (status) { error("initialize_barriers: failed to initialize all_threads_ready\n"); return FAILURE; } status = pthread_barrier_init(&all_threads_done, NULL, NUM_WORKER_THREADS+1); if (status) { error("initialize_barriers: failed to initialize all_threads_done\n"); return FAILURE; } status = pthread_barrier_init(&start_barrier, NULL, NUM_WORKER_THREADS); if (status) { error("initialize_barriers: failed to initialize start_barrier\n"); return FAILURE; } status = pthread_barrier_init(&locked_barrier, NULL, 2); if (status) { error("initializing_barriers: failed to intialize locked_barrier\n"); return FAILURE; } status = pthread_barrier_init(&elevate_barrier, NULL, 2); if (status) { error("initializing_barriers: failed to initialize elevate_barrier\n"); return FAILURE; } status = pthread_barrier_init(&finish_barrier, NULL, NUM_WORKER_THREADS); if (status) { error("initializing_barriers: failed to initialize finish_barrier\n"); return FAILURE; } return SUCCESS; } void cleanup(void) { int i; int status; for (i = 0; i < NUM_WORKER_THREADS; i++) { status = pthread_kill(thread_parameters[i].tid, SIGQUIT); if (status) error("cleanup: error sending SIGQUIT to thread %d\n", thread_parameters[i].tid); } } void handler(int signal) { info("handler: %s fired\n", sys_siglist[signal]); cleanup(); if (signal == SIGALRM) { error("handler: DEADLOCK detected!\n"); deadlocked = 1; } } void usage(void) { printf ("classic_pi [options]\n"); printf (" options:\n"); printf (" -v|--verbose\n"); printf (" -q|--quiet\n"); printf (" -n|--no-pi\n"); printf (" -i |--inversions=\n"); } int main(int argc, char **argv) { int status; int prio_max; pthread_attr_t thread_attr; struct sched_param thread_param; int opt; /* Make sure we see all message, even those on stdout. */ setvbuf (stdout, NULL, _IONBF, 0); /* process command line arguments */ while ((opt = getopt_long(argc, argv, "+", options, NULL)) != -1) { switch (opt) { case '?': usage(); exit(1); case 'v': verbose = 1; break; case 'q': verbose = 0; break; case 'n': use_pi_mutex = 0; break; case 'i': inversions = strtol(optarg, NULL, 10); info("main: doing %d inversion loops\n", inversions); break; } } /* initialize default attributes for the mutex */ status = pthread_mutexattr_init(&mutex_attr); if (status) { error("main: initializing mutex attribute: 0x%x\n", status); return FAILURE; } if (use_pi_mutex) { /* set priority inheritance attribute for mutex */ status = pthread_mutexattr_setprotocol(&mutex_attr, PTHREAD_PRIO_INHERIT); if (status) { error("main: setting mutex attribute policy: 0x%x\n", status); return FAILURE; } } info("main: Priority Inheritance turned %s\n", use_pi_mutex ? "on" : "off"); /* initialize our mutex */ status = pthread_mutex_init(&mutex, &mutex_attr); if (status) { error("main: initializing mutex: 0x%x\n", status); return FAILURE; } /* set up our barriers */ status = initialize_barriers(); if (status) return FAILURE; /* set up CPU affinity so we only use one processor */ if (set_cpu_affinity(&cpu_mask)) return FAILURE; /* boost us to max priority (so we keep running) :) */ prio_max = sched_get_priority_max(SCHED_FIFO); thread_param.sched_priority = MAIN_PRIO(prio_max); status = pthread_setschedparam(pthread_self(), SCHED_FIFO, &thread_param); if (status) { error("main: boosting to max priority: 0x%x\n", status); /* Don't fail if we don't have the right privledges */ return SUCCESS; } /* start the low priority thread */ info("main: creating low priority thread\n"); setup_thread_attr(&thread_attr, LOW_PRIO(prio_max), &cpu_mask); thread_parameters[LOW].inversions = inversions; status = pthread_create(&thread_parameters[LOW].tid, &thread_attr, low_priority, &thread_parameters[LOW]); if (status != 0) { error("main: creating low_priority thread: 0x%x\n", status); return FAILURE; } /* create the medium priority thread */ info("main: creating medium priority thread\n"); setup_thread_attr(&thread_attr, MED_PRIO(prio_max), &cpu_mask); thread_parameters[MEDIUM].inversions = inversions; status = pthread_create(&thread_parameters[MEDIUM].tid, &thread_attr, med_priority, &thread_parameters[MEDIUM]); if (status != 0) { error("main: creating med_priority thread: 0x%x\n", status); return FAILURE; } /* create the high priority thread */ info("main: creating high priority thread\n"); if (setup_thread_attr(&thread_attr, HIGH_PRIO(prio_max), &cpu_mask)) return FAILURE; thread_parameters[HIGH].inversions = inversions; status = pthread_create(&thread_parameters[HIGH].tid, &thread_attr, high_priority, &thread_parameters[HIGH]); if (status != 0) { error("main: creating high_priority thread: 0x%x\n", status); cleanup(); return FAILURE; } signal(SIGINT, handler); signal(SIGALRM, handler); info("main: releasing all threads\n"); status = pthread_barrier_wait(&all_threads_ready); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error("main: pthread_barrier_wait(all_threads_ready): 0x%x\n", status); cleanup(); return FAILURE; } info("main: all threads initialized, waiting for mutex to be claimed\n"); alarm(TIMEOUT * inversions); info("main: waiting for threads to finish\n"); status = pthread_barrier_wait(&all_threads_done); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error("main: pthread_barrier_wait(all_threads_done): 0x%x\n", status); cleanup(); return FAILURE; } alarm(0); info("main: all threads terminated!\n"); if (deadlocked) { info("main: test failed\n"); return FAILURE; } info("main: test passed\n"); return SUCCESS; } int setup_thread_attr(pthread_attr_t *attr, int prio, cpu_set_t *mask) { int status; struct sched_param thread_param; status = pthread_attr_init(attr); if (status) { error("setup_thread_attr: initializing thread attribute: 0x%x\n", status); return FAILURE; } status = pthread_attr_setschedpolicy(attr, SCHED_FIFO); if (status) { error("setup_thread_attr: setting attribute policy to SCHED_FIFO: 0x%x\n", status); return FAILURE; } status = pthread_attr_setinheritsched(attr, PTHREAD_EXPLICIT_SCHED); if (status) { error("setup_thread_attr: setting explicit scheduling inheritance: 0x%x\n", status); return FAILURE; } thread_param.sched_priority = prio; status = pthread_attr_setschedparam(attr, &thread_param); if (status) { error("setup_thread_attr: setting scheduler param: 0x%x\n", status); return FAILURE; } status = pthread_attr_setaffinity_np(attr, sizeof(cpu_set_t), mask); if (status) { error("setup_thread_attr: setting affinity attribute: 0x%x\n", status); return FAILURE; } return SUCCESS; } int set_cpu_affinity(cpu_set_t *cpu_set) { int status, i; cpu_set_t current_mask, new_mask; /* Now set our CPU affinity to only run one one processor */ status = sched_getaffinity(0, sizeof(cpu_set_t), ¤t_mask); if (status) { error("set_cpu_affinity: getting CPU affinity mask: 0x%x\n", status); return FAILURE; } for (i = 0; i < sizeof(cpu_set_t) * 8; i++) { if (CPU_ISSET(i, ¤t_mask)) break; } if (i >= sizeof(cpu_set_t) * 8) { error("set_cpu_affinity: No schedulable CPU found!\n"); return FAILURE; } CPU_ZERO(&new_mask); CPU_SET(i, &new_mask); status = sched_setaffinity(0, sizeof(cpu_set_t), &new_mask); if (status) { error("set_cpu_affinity: setting CPU affinity mask: 0x%x\n", status); return FAILURE; } info("set_cpu_affinity: using processr %d\n", i); *cpu_set = new_mask; return SUCCESS; } void report_threadinfo(char *name) { int status; struct sched_param thread_param; int thread_policy; status = pthread_getschedparam(pthread_self(), &thread_policy, &thread_param); if (status) { error("report_threadinfo: failed to get scheduler param: 0x%x\n", status); exit(FAILURE); } info("%s: running as %s thread at priority %d\n", name, thread_policy == SCHED_FIFO ? "FIFO" : thread_policy == SCHED_RR ? "RR" : "OTHER", thread_param.sched_priority); } void *low_priority(void *arg) { int status; struct thread_parameters *p = (struct thread_parameters *)arg; report_threadinfo("low_priority"); info("low_priority: entering ready state\n"); /* wait for all threads to be ready */ status = pthread_barrier_wait(&all_threads_ready); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error("low_priority: pthread_barrier_wait(all_threads_ready): %x", status); return NULL; } info("low_priority: starting inversion loop (%d)\n", p->inversions); while (p->inversions-- > 0) { /* initial state */ info("low_priority: entering start wait (%d)\n", p->inversions+1); status = pthread_barrier_wait(&start_barrier); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error("low_priority: pthread_barrier_wait(start): %x\n", status); return NULL; } info("low_priority: claiming mutex\n"); pthread_mutex_lock(&mutex); info("low_priority: mutex locked\n"); info("low_priority: entering locked wait\n"); status = pthread_barrier_wait(&locked_barrier); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error("low_priority: pthread_barrier_wait(locked): %x\n", status); return NULL; } /* wait for priority boost */ info("low_priority: entering elevated wait\n"); low_unlocked = 0; /* prevent race with med_priority */ status = pthread_barrier_wait(&elevate_barrier); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error("low_priority: pthread_barrier_wait(elevate): %x\n", status); return NULL; } low_unlocked = 1; /* release the mutex */ info("low_priority: unlocking mutex\n"); pthread_mutex_unlock(&mutex); /* finish state */ info("low_priority: entering finish wait\n"); status = pthread_barrier_wait(&finish_barrier); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error("low_priority: pthread_barrier_wait(elevate): %x\n", status); return NULL; } } /* let main know we're done */ info("low_priority: entering exit state\n"); status = pthread_barrier_wait(&all_threads_done); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error("low_priority: pthread_barrier_wait(all_threads_done): %x", status); return NULL; } info("low_priority: exiting\n"); return NULL; } void *med_priority(void *arg) { int status; struct thread_parameters *p = (struct thread_parameters *)arg; report_threadinfo("med_priority"); info("med_priority: entering ready state\n"); /* wait for all threads to be ready */ status = pthread_barrier_wait(&all_threads_ready); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error("med_priority: pthread_barrier_wait(all_threads_ready): %x", status); return NULL; } info("med_priority: starting inversion loop (%d)\n", p->inversions); while (p->inversions-- > 0) { /* start state */ info("med_priority: entering start state (%d)\n", p->inversions+1); status = pthread_barrier_wait(&start_barrier); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error("med_priority: pthread_barrier_wait(start): %x", status); return NULL; } info("med_priority: entering elevate state\n"); do { status = pthread_barrier_wait(&elevate_barrier); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error("med_priority: pthread_barrier_wait(elevate): %x", status); return NULL; } } while (!high_has_run && !low_unlocked); info("med_priority: entering finish state\n"); status = pthread_barrier_wait(&finish_barrier); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error("med_priority: pthread_barrier_wait(finished): %x", status); return NULL; } } info("med_priority: entering exit state\n"); status = pthread_barrier_wait(&all_threads_done); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error("med_priority: pthread_barrier_wait(all_threads_done): %x", status); return NULL; } info("med_priority: exiting\n"); return NULL; } void *high_priority(void *arg) { int status; struct thread_parameters *p = (struct thread_parameters *)arg; report_threadinfo("high_priority"); info("high_priority: entering ready state\n"); /* wait for all threads to be ready */ status = pthread_barrier_wait(&all_threads_ready); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error("high_priority: pthread_barrier_wait(all_threads_ready): %x", status); return NULL; } info("high_priority: starting inversion loop (%d)\n", p->inversions); while (p->inversions-- > 0) { high_has_run = 0; info("high_priority: entering start state (%d)\n", p->inversions+1); status = pthread_barrier_wait(&start_barrier); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error("high_priority: pthread_barrier_wait(start): %x", status); return NULL; } info("high_priority: entering running state\n"); status = pthread_barrier_wait(&locked_barrier); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error("high_priority: pthread_barrier_wait(running): %x", status); return NULL; } info("high_priority: locking mutex\n"); pthread_mutex_lock(&mutex); info("high_priority: got mutex\n"); high_has_run = 1; info("high_priority: unlocking mutex\n"); pthread_mutex_unlock(&mutex); info("high_priority: entering finish state\n"); status = pthread_barrier_wait(&finish_barrier); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error("high_priority: pthread_barrier_wait(finish): %x", status); return NULL; } } info("high_priority: entering exit state\n"); status = pthread_barrier_wait(&all_threads_done); if (status && status != PTHREAD_BARRIER_SERIAL_THREAD) { error("high_priority: pthread_barrier_wait(all_threads_done): %x", status); return NULL; } info("high_priority: exiting\n"); return NULL; } void error(char *fmt, ...) { va_list ap; fputs("ERROR: ", stderr); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); } void info(char *fmt, ...) { if (verbose) { va_list ap; va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); } } rt-tests/src/pi_tests/sigtest.c0000600001204700120500000000626212252162340017325 0ustar williamswilliams/* sigtest - simple little program to verify signal behavior Copyright (C) 2006, 2007 Clark Williams 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 program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #define TIMER_SIGNAL (SIGRTMIN+1) #define SUCCESS 0 #define FAILURE -1 int setup_timer(timer_t *timer) { int status; struct sigevent sigev; memset(&sigev, 0, sizeof(sigev)); sigev.sigev_notify = SIGEV_SIGNAL; sigev.sigev_signo = TIMER_SIGNAL; status = timer_create(CLOCK_MONOTONIC, &sigev, timer); if (status) { fprintf(stderr,"error from timer_create: %s\n", strerror(errno)); return FAILURE; } return SUCCESS; } int start_timer(timer_t t, int sec, int nsec) { int status; struct itimerspec it; // set up as a one-shot memset(&it, 0, sizeof(it)); it.it_value.tv_sec = sec; it.it_value.tv_nsec = nsec; status = timer_settime(t, 0, &it, NULL); if (status) fprintf(stderr,"starting timer: %s\n", strerror(errno)); return status; } int wait_for_signal(void) { int signo; sigset_t sigset; if (sigemptyset(&sigset)) { fprintf(stderr,"creating empty signal wait set: %s\n", strerror(errno)); return -1; } if (sigaddset(&sigset, SIGINT)) { fprintf(stderr,"adding SIGINT to signal set: %s\n", strerror(errno)); return -1; } if (sigaddset(&sigset, TIMER_SIGNAL)) { fprintf(stderr,"adding TIMER_SIGNAL to signal set: %s\n", strerror(errno)); return -1; } if (sigwait(&sigset, &signo)) { fprintf(stderr,"waiting for signal: %s\n", strerror(errno)); return -1; } return signo; } int block_signals(void) { int status; sigset_t sigset; // mask off all signals status = sigfillset(&sigset); if (status) { fprintf(stderr,"setting up full signal set %d\n", status); return FAILURE; } status = pthread_sigmask(SIG_BLOCK, &sigset, NULL); if (status) { fprintf(stderr,"setting signal mask: %d\n", status); return FAILURE; } return SUCCESS; } int main(int argc, char **argv) { int status; timer_t timer; unsigned long count = 0; int stop_test = 0; block_signals(); setup_timer(&timer); printf("Press Ctrl-C to stop\n"); while (stop_test == 0) { if (start_timer(timer, 0, 500000000)) { stop_test = 1; continue; } status = wait_for_signal(); if (status == SIGINT) { fputs("\033[1B", stdout); stop_test = 1; } else if (status == TIMER_SIGNAL) { printf("count: %lu\n", ++count); fputs("\033[1A", stdout); } else { fprintf(stderr, "WTF?\n"); return -1; } } return 0; } rt-tests/src/hackbench/0000700001204700120500000000000012252162340015543 5ustar williamswilliamsrt-tests/src/hackbench/hackbench.c0000664001204700120500000002621212252162340017634 0ustar williamswilliams/* * This is the latest version of hackbench.c, that tests scheduler and * unix-socket (or pipe) performance. * * Usage: hackbench [-pipe] [process|thread] [loops] * * Build it with: * gcc -g -Wall -O2 -o hackbench hackbench.c -lpthread * * Downloaded from http://people.redhat.com/mingo/cfs-scheduler/tools/hackbench.c * February 19 2010. * */ /* Test groups of 20 processes spraying to 20 receivers */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static unsigned int datasize = 100; static unsigned int loops = 100; static unsigned int num_groups = 10; static unsigned int num_fds = 20; static unsigned int fifo = 0; /* * 0 means thread mode and others mean process (default) */ #define THREAD_MODE 0 #define PROCESS_MODE 1 static unsigned int process_mode = PROCESS_MODE; static int use_pipes = 0; struct sender_context { unsigned int num_fds; int ready_out; int wakefd; int out_fds[0]; }; struct receiver_context { unsigned int num_packets; int in_fds[2]; int ready_out; int wakefd; }; typedef union { pthread_t threadid; pid_t pid; long long error; } childinfo_t; childinfo_t *child_tab = NULL; unsigned int total_children = 0; unsigned int signal_caught = 0; static jmp_buf jmpbuf; inline static void sneeze(const char *msg) { /* Avoid calling these functions when called from a code path * which involves sigcatcher(), as they are not reentrant safe. */ if( !signal_caught ) { fprintf(stderr, "%s (error: %s)\n", msg, strerror(errno)); } } static void barf(const char *msg) { sneeze(msg); exit(1); } static void print_usage_exit() { printf("Usage: hackbench [-p|--pipe] [-s|--datasize ] [-l|--loops ]\n" "\t\t [-g|--groups ]\n" "\t\t [-T|--threads] [-P|--process] [--help]\n"); exit(1); } static void fdpair(int fds[2]) { if (use_pipes) { if (pipe(fds) == 0) return; } else { if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0) return; } barf("Creating fdpair"); } /* Block until we're ready to go */ static void ready(int ready_out, int wakefd) { char dummy = '*'; struct pollfd pollfd = { .fd = wakefd, .events = POLLIN }; /* Tell them we're ready. */ if (write(ready_out, &dummy, 1) != 1) barf("CLIENT: ready write"); /* Wait for "GO" signal */ if (poll(&pollfd, 1, -1) != 1) barf("poll"); } static void reset_worker_signals(void) { signal(SIGTERM, SIG_DFL); signal(SIGINT, SIG_DFL); } /* Sender sprays loops messages down each file descriptor */ static void *sender(struct sender_context *ctx) { char data[datasize]; unsigned int i, j; reset_worker_signals(); ready(ctx->ready_out, ctx->wakefd); memset(&data, '-', datasize); /* Now pump to every receiver. */ for (i = 0; i < loops; i++) { for (j = 0; j < ctx->num_fds; j++) { int ret, done = 0; again: ret = write(ctx->out_fds[j], data + done, sizeof(data)-done); if (ret < 0) barf("SENDER: write"); done += ret; if (done < sizeof(data)) goto again; } } return NULL; } /* One receiver per fd */ static void *receiver(struct receiver_context* ctx) { unsigned int i; reset_worker_signals(); if (process_mode == PROCESS_MODE) close(ctx->in_fds[1]); /* Wait for start... */ ready(ctx->ready_out, ctx->wakefd); /* Receive them all */ for (i = 0; i < ctx->num_packets; i++) { char data[datasize]; int ret, done = 0; again: ret = read(ctx->in_fds[0], data + done, datasize - done); if (ret < 0) barf("SERVER: read"); done += ret; if (done < datasize) goto again; } if (ctx) { free(ctx); } return NULL; } static childinfo_t create_worker(void *ctx, void *(*func)(void *)) { pthread_attr_t attr; int err; childinfo_t child; pid_t childpid; memset(&child, 0, sizeof(child)); switch (process_mode) { case PROCESS_MODE: /* process mode */ /* Fork the sender/receiver child. */ switch ((childpid = fork())) { case -1: sneeze("fork()"); child.error = -1; return child; case 0: (*func) (ctx); exit(0); } child.pid = childpid; break; case THREAD_MODE: /* threaded mode */ if (pthread_attr_init(&attr) != 0) { sneeze("pthread_attr_init()"); child.error = -1; return child; } #ifndef __ia64__ if (pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN) != 0) { sneeze("pthread_attr_setstacksize()"); child.error = -1; return child; } #endif if ((err=pthread_create(&child.threadid, &attr, func, ctx)) != 0) { sneeze("pthread_create failed()"); child.error = -1; return child; } break; } return child; } void signal_workers(childinfo_t *children, unsigned int num_children) { int i; printf("signaling %d worker threads to terminate\n", num_children); for (i=0; i < num_children; i++) { kill(children[i].pid, SIGTERM); } } unsigned int reap_workers(childinfo_t *child, unsigned int totchld, unsigned int dokill) { unsigned int i, rc = 0; int status, err; void *thr_status; if (dokill) { fprintf(stderr, "sending SIGTERM to all child processes\n"); signal(SIGTERM, SIG_IGN); signal_workers(child, totchld); } for( i = 0; i < totchld; i++ ) { int pid; switch( process_mode ) { case PROCESS_MODE: /* process mode */ fflush(stdout); pid = wait(&status); if (pid == -1 && errno == ECHILD) break; if (!WIFEXITED(status)) rc++; break; case THREAD_MODE: /* threaded mode */ err = pthread_join(child[i].threadid, &thr_status); if( err != 0 ) { sneeze("pthread_join()"); rc++; } break; } } return rc; } /* One group of senders and receivers */ static unsigned int group(childinfo_t *child, unsigned int tab_offset, unsigned int num_fds, int ready_out, int wakefd) { unsigned int i; struct sender_context* snd_ctx = malloc (sizeof(struct sender_context) +num_fds*sizeof(int)); if (!snd_ctx) { sneeze("malloc() [sender ctx]"); return 0; } for (i = 0; i < num_fds; i++) { int fds[2]; struct receiver_context* ctx = malloc (sizeof(*ctx)); if (!ctx) { sneeze("malloc() [receiver ctx]"); return (i > 0 ? i-1 : 0); } /* Create the pipe between client and server */ fdpair(fds); ctx->num_packets = num_fds*loops; ctx->in_fds[0] = fds[0]; ctx->in_fds[1] = fds[1]; ctx->ready_out = ready_out; ctx->wakefd = wakefd; child[tab_offset+i] = create_worker(ctx, (void *)(void *)receiver); if( child[tab_offset+i].error < 0 ) { return (i > 0 ? i-1 : 0); } snd_ctx->out_fds[i] = fds[1]; if (process_mode == PROCESS_MODE) close(fds[0]); } /* Now we have all the fds, fork the senders */ for (i = 0; i < num_fds; i++) { snd_ctx->ready_out = ready_out; snd_ctx->wakefd = wakefd; snd_ctx->num_fds = num_fds; child[tab_offset+num_fds+i] = create_worker(snd_ctx, (void *)(void *)sender); if( child[tab_offset+num_fds+i].error < 0 ) { return (num_fds+i)-1; } } /* Close the fds we have left */ if (process_mode == PROCESS_MODE) for (i = 0; i < num_fds; i++) close(snd_ctx->out_fds[i]); /* Return number of children to reap */ return num_fds * 2; } static void process_options (int argc, char *argv[]) { int error = 0; while( 1 ) { int optind = 0; static struct option longopts[] = { {"pipe", no_argument, NULL, 'p'}, {"datasize", required_argument, NULL, 's'}, {"loops", required_argument, NULL, 'l'}, {"groups", required_argument, NULL, 'g'}, {"fds", required_argument, NULL, 'f'}, {"threads", no_argument, NULL, 'T'}, {"processes", no_argument, NULL, 'P'}, {"fifo", no_argument, NULL, 'F'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0} }; int c = getopt_long(argc, argv, "ps:l:g:f:TPFh", longopts, &optind); if (c == -1) { break; } switch (c) { case 'p': use_pipes = 1; break; case 's': if (!(argv[optind] && (datasize = atoi(optarg)) > 0)) { fprintf(stderr, "%s: --datasize|-s requires an integer > 0\n", argv[0]); error = 1; } break; case 'l': if (!(argv[optind] && (loops = atoi(optarg)) > 0)) { fprintf(stderr, "%s: --loops|-l requires an integer > 0\n", argv[0]); error = 1; } break; case 'g': if (!(argv[optind] && (num_groups = atoi(optarg)) > 0)) { fprintf(stderr, "%s: --groups|-g requires an integer > 0\n", argv[0]); error = 1; } break; case 'f': if (!(argv[optind] && (num_fds = atoi(optarg)) > 0)) { fprintf(stderr, "%s: --fds|-f requires an integer > 0\n", argv[0]); error = 1; } break; case 'T': process_mode = THREAD_MODE; break; case 'P': process_mode = PROCESS_MODE; break; case 'F': fifo = 1; break; case 'h': print_usage_exit(); default: error = 1; } } if( error ) { exit(1); } } void sigcatcher(int sig) { /* All caught signals will cause the program to exit */ signal_caught = 1; fprintf(stderr, "Signal %d caught, longjmp'ing out!\n", sig); signal(sig, SIG_IGN); longjmp(jmpbuf, 1); } int main(int argc, char *argv[]) { unsigned int i; struct timeval start, stop, diff; int readyfds[2], wakefds[2]; char dummy; struct sched_param sp; process_options (argc, argv); printf("Running in %s mode with %d groups using %d file descriptors each (== %d tasks)\n", (process_mode == THREAD_MODE ? "threaded" : "process"), num_groups, 2*num_fds, num_groups*(num_fds*2)); printf("Each sender will pass %d messages of %d bytes\n", loops, datasize); fflush(NULL); child_tab = calloc(num_fds * 2 * num_groups, sizeof(childinfo_t)); if (!child_tab) barf("main:malloc()"); fdpair(readyfds); fdpair(wakefds); /* Catch some signals */ signal(SIGINT, sigcatcher); signal(SIGTERM, sigcatcher); signal(SIGHUP, SIG_IGN); if (setjmp(jmpbuf) == 0) { total_children = 0; for (i = 0; i < num_groups; i++) { int c = group(child_tab, total_children, num_fds, readyfds[1], wakefds[0]); if( c != (num_fds*2) ) { fprintf(stderr, "%i children started. Expected %i\n", c, num_fds*2); reap_workers(child_tab, total_children + c, 1); barf("Creating workers"); } total_children += c; } if (fifo) { /* make main a realtime task so that we can manage the workers */ sp.sched_priority = 1; if (sched_setscheduler(0, SCHED_FIFO, &sp) < 0) barf("can't change to fifo in main"); } /* Wait for everyone to be ready */ for (i = 0; i < total_children; i++) if (read(readyfds[0], &dummy, 1) != 1) { reap_workers(child_tab, total_children, 1); barf("Reading for readyfds"); } gettimeofday(&start, NULL); /* Kick them off */ if (write(wakefds[1], &dummy, 1) != 1) { reap_workers(child_tab, total_children, 1); barf("Writing to start senders"); } } else { fprintf(stderr, "longjmp'ed out, reaping children\n"); signal(SIGINT, SIG_IGN); signal(SIGTERM, SIG_IGN); } /* Reap them all */ reap_workers(child_tab, total_children, signal_caught); gettimeofday(&stop, NULL); /* Print time... */ timersub(&stop, &start, &diff); printf("Time: %lu.%03lu\n", diff.tv_sec, diff.tv_usec/1000); free(child_tab); exit(0); } rt-tests/src/hackbench/hackbench.80000600001204700120500000000730512252162340017551 0ustar williamswilliams.TH "hackbench" "8" "February 23, 2010" "" "" .SH "NAME" hackbench \- scheduler benchmark/stress test .SH "SYNOPSIS" .B hackbench .RI "[\-p|\-\-pipe] [\-s|\-\-datasize " "] " .RI "[\-l|\-\-loops " "] " .RI "[\-g|\-\-groups " "] " .RI "[\-f|\-\-fds ] " .RI "[\-T|\-\-threads] [\-P|\-\-process] [\-\-help]" .SH "DESCRIPTION" Hackbench is both a benchmark and a stress test for the Linux kernel scheduler. It's main job is to create a specified number of pairs of schedulable entities (either threads or traditional processes) which communicate via either sockets or pipes and time how long it takes for each pair to send data back and forth. .SH "OPTIONS" These programs follow the usual GNU command line syntax, with long options starting with two dashes ("\-\-"). .br A summary of options is included below. .TP .B \-p, \-\-pipe Sends the data via a pipe instead of the socket (default) .TP .B \-s, \-\-datasize= Sets the amount of data to send in each message .TP .B \-l, \-\-loops= How many messages each sender/receiver pair should send .TP .B \-g, \-\-groups= Defines how many groups of senders and receivers should be started .TP .B \-f, \-\-fds= Defines how many file descriptors each child should use. Note that the effective number will be twice the amount you set here, as the sender and receiver children will each open the given amount of file descriptors. .TP .B \-T, \-\-threads Each sender/receiver child will be a POSIX thread of the parent. .TP .B \-P, \-\-process Hackbench will use fork() on all children (default behaviour) .TP .B \-\-help .br Shows a simple help screen .SH "EXAMPLES" .LP Running hackbench without any options will give default behaviour, using fork() and sending data between senders and receivers via sockets. .LP user@host: ~ $ hackbench .br Running in process mode with 10 groups using 40 file descriptors each (== 400 tasks) .br Each sender will pass 100 messages of 100 bytes .br Time: 0.890 .LP To use pipes between senders and receivers and using threads instead of fork(), run .LP user@host: ~ $ hackbench \-\-pipe \-\-threads (or hackbench \-p \-T) .br Running in threaded mode with 10 groups using 40 file descriptors each (== 400 tasks) .br Each sender will pass 100 messages of 100 bytes .br Time: 0.497 .LP Set the datasize to 512 bytes, do 200 messages per sender/receiver pairs and use 15 groups using 25 file descriptors per child, in process mode. .LP user@host: ~ $ hackbench \-s 512 \-l 200 \-g 15 \-f 25 \-P .br Running in process mode with 15 groups using 50 file descriptors each (== 750 tasks) .br Each sender will pass 200 messages of 512 bytes .br Time: 4.497 .SH "AUTHORS" .LP hackbench was written by Rusty Russell with contributions from Yanmin Zhang , Ingo Molnar and David Sommerseth This manual page was written by Clark Williams and David Sommerseth .SH "HISTORY" This version of hackbench is based on the code downloaded from http://people.redhat.com/mingo/cfs\-scheduler/tools/hackbench.c. Yanmin Zhang merged the original hackbench code from .br http://devresources.linuxfoundation.org/craiger/hackbench/src/hackbench.c which uses fork() and a modified version from .br http://www.bullopensource.org/posix/pi\-futex/hackbench_pth.c which uses pthread only and gave the possibility to change behaviour at run time. Hackbench have since then gone through some more rewriting to improve error handling and proper tracking of fork()ed children, to avoid leaving zombies on the system if hackbench stops unexpectedly. rt-tests/src/hackbench/Makefile0000600001204700120500000000016212252162340017204 0ustar williamswilliamshackbench: hackbench.c $(CC) $(CFLAGS) -o hackbench hackbench.c -g -Wall -O2 -lpthread clean : rm -f hackbench rt-tests/src/lib/0000700001204700120500000000000012252162340014403 5ustar williamswilliamsrt-tests/src/lib/rt-get_cpu.c0000600001204700120500000000105712252162340016625 0ustar williamswilliams/* * Copyright (C) 2009 John Kacur */ #include "rt-get_cpu.h" #ifdef __NR_getcpu #elif defined(__GLIBC__) && defined(__GLIBC_MINOR__) \ && __GLIBC__>=2 && __GLIBC_MINOR__>=6 #else int (*get_cpu)(void); int (*get_cpu_vdsop)(unsigned int *, unsigned int *, void *); int get_cpu_setup(void) { void *handle = dlopen("linux-vdso.so.1", RTLD_LAZY); get_cpu_vdsop = NULL; if (handle) { get_cpu_vdsop = dlsym(handle, "getcpu"); dlclose(handle); if (get_cpu_vdsop) { get_cpu = getcpu_vdso; return 0; } } return -1; } #endif rt-tests/src/lib/error.c0000600001204700120500000000262712252162340015711 0ustar williamswilliams/* * Copyright (C) 2009 John Kacur * * error routines, similar to those found in * Advanced Programming in the UNIX Environment 2nd ed. */ #include "error.h" /* Print an error message, plus a message for err and exit with error err */ void err_exit(int err, char *fmt, ...) { va_list ap; va_start(ap, fmt); err_doit(err, fmt, ap); va_end(ap); exit(err); } /* print an error message and return */ void err_msg(char *fmt, ...) { va_list ap; va_start(ap, fmt); err_doit(0, fmt, ap); va_end(ap); return; } /* Print an error message, plus a message for err, and return */ void err_msg_n(int err, char *fmt, ...) { va_list ap; va_start(ap, fmt); err_doit(err, fmt, ap); va_end(ap); return; } /* print an error message and quit */ void err_quit(char *fmt, ...) { va_list ap; va_start(ap, fmt); err_doit(0, fmt, ap); va_end(ap); exit(1); } void info(char *fmt, ...) { va_list ap; va_start(ap, fmt); fputs("INFO: ", stderr); err_doit(0, fmt, ap); va_end(ap); } void warn(char *fmt, ...) { va_list ap; va_start(ap, fmt); fputs("WARN: ", stderr); err_doit(0, fmt, ap); va_end(ap); } void fatal(char *fmt, ...) { va_list ap; va_start(ap, fmt); fputs("FATAL: ", stderr); err_doit(0, fmt, ap); va_end(ap); exit(EXIT_FAILURE); } void err_doit(int err, const char *fmt, va_list ap) { if (err) fprintf(stderr, "%s\n", strerror(err)); vfprintf(stderr, fmt, ap); return; } rt-tests/src/lib/rt-utils.c0000600001204700120500000001377712252162340016353 0ustar williamswilliams/* * Copyright (C) 2009 Carsten Emde * Copyright (C) 2010 Clark Williams * * based on functions from cyclictest that has * (C) 2008-2009 Clark Williams * (C) 2005-2007 Thomas Gleixner */ #include #include #include #include #include #include #include #include #include #include #include "rt-utils.h" #include "error.h" static char debugfileprefix[MAX_PATH]; /* * Finds the tracing directory in a mounted debugfs */ char *get_debugfileprefix(void) { char type[100]; FILE *fp; int size; int found = 0; struct stat s; if (debugfileprefix[0] != '\0') goto out; /* look in the "standard" mount point first */ if ((stat("/sys/kernel/debug/tracing", &s) == 0) && S_ISDIR(s.st_mode)) { strcpy(debugfileprefix, "/sys/kernel/debug/tracing/"); goto out; } /* now look in the "other standard" place */ if ((stat("/debug/tracing", &s) == 0) && S_ISDIR(s.st_mode)) { strcpy(debugfileprefix, "/debug/tracing/"); goto out; } /* oh well, parse /proc/mounts and see if it's there */ if ((fp = fopen("/proc/mounts","r")) == NULL) goto out; while (fscanf(fp, "%*s %" STR(MAX_PATH) "s %99s %*s %*d %*d\n", debugfileprefix, type) == 2) { if (strcmp(type, "debugfs") == 0) { found = 1; break; } /* stupid check for systemd-style autofs mount */ if ((strcmp(debugfileprefix, "/sys/kernel/debug") == 0) && (strcmp(type, "systemd") == 0)) { found = 1; break; } } fclose(fp); if (!found) { debugfileprefix[0] = '\0'; goto out; } size = sizeof(debugfileprefix) - strlen(debugfileprefix); strncat(debugfileprefix, "/tracing/", size); out: return debugfileprefix; } int mount_debugfs(char *path) { char *mountpoint = path; char cmd[MAX_PATH]; char *prefix; int ret; /* if it's already mounted just return */ prefix = get_debugfileprefix(); if (strlen(prefix) != 0) { info("debugfs mountpoint: %s\n", prefix); return 0; } if (!mountpoint) mountpoint = "/sys/kernel/debug"; sprintf(cmd, "mount -t debugfs debugfs %s", mountpoint); ret = system(cmd); if (ret != 0) { fprintf(stderr, "Error mounting debugfs at %s: %s\n", mountpoint, strerror(errno)); return -1; } return 0; } static char **tracer_list; static char *tracer_buffer; static int num_tracers; #define CHUNKSZ 1024 /* * return a list of the tracers configured into the running kernel */ int get_tracers(char ***list) { int ret; FILE *fp; char buffer[CHUNKSZ]; char *prefix = get_debugfileprefix(); char *tmpbuf = NULL; char *ptr; int tmpsz = 0; /* if we've already parse it, return what we have */ if (tracer_list) { *list = tracer_list; return num_tracers; } /* open the tracing file available_tracers */ sprintf(buffer, "%savailable_tracers", prefix); if ((fp = fopen(buffer, "r")) == NULL) fatal ("Can't open %s for reading\n", buffer); /* allocate initial buffer */ ptr = tmpbuf = malloc(CHUNKSZ); if (ptr == NULL) fatal("error allocating initial space for tracer list\n"); /* read in the list of available tracers */ while((ret = fread(buffer, sizeof(char), CHUNKSZ, fp))) { if ((ptr+ret+1) > (tmpbuf+tmpsz)) { tmpbuf = realloc(tmpbuf, tmpsz + CHUNKSZ); if (tmpbuf == NULL) fatal("error allocating space for list of valid tracers\n"); tmpsz += CHUNKSZ; } strncpy(ptr, buffer, ret); ptr += ret; } fclose(fp); if (tmpsz == 0) fatal("error reading available tracers\n"); tracer_buffer = tmpbuf; /* get a buffer for the pointers to tracers */ if (!(tracer_list = malloc(sizeof(char *)))) fatal ("error allocatinging tracer list buffer\n"); /* parse the buffer */ ptr = strtok(tmpbuf, " \t\n\r"); do { tracer_list[num_tracers++] = ptr; tracer_list = realloc(tracer_list, sizeof(char*)*(num_tracers+1)); tracer_list[num_tracers] = NULL; } while ((ptr = strtok(NULL, " \t\n\r")) != NULL); /* return the list and number of tracers */ *list = tracer_list; return num_tracers; } /* * return zero if tracername is not a valid tracer, non-zero if it is */ int valid_tracer(char *tracername) { char **list; int ntracers; int i; ntracers = get_tracers(&list); if (ntracers == 0 || tracername == NULL) return 0; for (i = 0; i < ntracers; i++) if (strncmp(list[i], tracername, strlen(list[i])) == 0) return 1; return 0; } /* * enable event tracepoint */ int setevent(char *event, char *val) { char *prefix = get_debugfileprefix(); char buffer[MAX_PATH]; int fd; int ret; sprintf(buffer, "%s%s", prefix, event); if ((fd = open(buffer, O_WRONLY)) < 0) { warn("unable to open %s\n", buffer); return -1; } if ((ret = write(fd, val, strlen(val))) < 0) { warn("unable to write %s to %s\n", val, buffer); close(fd); return -1; } close(fd); return 0; } int event_enable_all(void) { return setevent("events/enable", "1"); } int event_disable_all(void) { return setevent("events/enable", "0"); } int event_enable(char *event) { char path[MAX_PATH]; sprintf(path, "events/%s/enable", event); return setevent(path, "1"); } int event_disable(char *event) { char path[MAX_PATH]; sprintf(path, "events/%s/enable", event); return setevent(path, "0"); } int check_privs(void) { int policy = sched_getscheduler(0); struct sched_param param, old_param; /* if we're already running a realtime scheduler * then we *should* be able to change things later */ if (policy == SCHED_FIFO || policy == SCHED_RR) return 0; /* first get the current parameters */ if (sched_getparam(0, &old_param)) { fprintf(stderr, "unable to get scheduler parameters\n"); return 1; } param = old_param; /* try to change to SCHED_FIFO */ param.sched_priority = 1; if (sched_setscheduler(0, SCHED_FIFO, ¶m)) { fprintf(stderr, "Unable to change scheduling policy!\n"); fprintf(stderr, "either run as root or join realtime group\n"); return 1; } /* we're good; change back and return success */ return sched_setscheduler(0, policy, &old_param); } rt-tests/src/rt-migrate-test/0000700001204700120500000000000012252162340016665 5ustar williamswilliamsrt-tests/src/rt-migrate-test/rt-migrate-test.c0000600001204700120500000003220012252162340022060 0ustar williamswilliams/* * rt-migrate-test.c * * Copyright (C) 2007-2009 Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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; version 2 of the License (not later!) * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #ifndef __USE_XOPEN2K # define __USE_XOPEN2K #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define gettid() syscall(__NR_gettid) #ifndef VERSION_STRING #define VERSION_STRING 0.3 #endif int nr_tasks; int lfd; static int mark_fd = -1; static __thread char buff[BUFSIZ+1]; static void setup_ftrace_marker(void) { struct stat st; char *files[] = { "/sys/kernel/debug/tracing/trace_marker", "/debug/tracing/trace_marker", "/debugfs/tracing/trace_marker", }; int ret; int i; for (i = 0; i < (sizeof(files) / sizeof(char *)); i++) { ret = stat(files[i], &st); if (ret >= 0) goto found; } /* todo, check mounts system */ return; found: mark_fd = open(files[i], O_WRONLY); } static void ftrace_write(const char *fmt, ...) { va_list ap; int n; if (mark_fd < 0) return; va_start(ap, fmt); n = vsnprintf(buff, BUFSIZ, fmt, ap); va_end(ap); write(mark_fd, buff, n); } #define nano2sec(nan) (nan / 1000000000ULL) #define nano2ms(nan) (nan / 1000000ULL) #define nano2usec(nan) (nan / 1000ULL) #define usec2nano(sec) (sec * 1000ULL) #define ms2nano(ms) (ms * 1000000ULL) #define sec2nano(sec) (sec * 1000000000ULL) #define INTERVAL ms2nano(100ULL) #define RUN_INTERVAL ms2nano(20ULL) #define NR_RUNS 50 #define PRIO_START 2 /* 1 millisec off */ #define MAX_ERR usec2nano(1000) #define PROGRESS_CHARS 70 static unsigned long long interval = INTERVAL; static unsigned long long run_interval = RUN_INTERVAL; static unsigned long long max_err = MAX_ERR; static int nr_runs = NR_RUNS; static int prio_start = PRIO_START; static int check; static int stop; static unsigned long long now; static int done; static int loop; static pthread_barrier_t start_barrier; static pthread_barrier_t end_barrier; static unsigned long long **intervals; static unsigned long long **intervals_length; static unsigned long **intervals_loops; static long *thread_pids; static char buffer[BUFSIZ]; static void perr(char *fmt, ...) { va_list ap; va_start(ap, fmt); vsnprintf(buffer, BUFSIZ, fmt, ap); va_end(ap); perror(buffer); fflush(stderr); exit(-1); } static void print_progress_bar(int percent) { int i; int p; if (percent > 100) percent = 100; /* Use stderr, so we don't capture it */ putc('\r', stderr); putc('|', stderr); for (i=0; i < PROGRESS_CHARS; i++) putc(' ', stderr); putc('|', stderr); putc('\r', stderr); putc('|', stderr); p = PROGRESS_CHARS * percent / 100; for (i=0; i < p; i++) putc('-', stderr); fflush(stderr); } static void usage(char **argv) { char *arg = argv[0]; char *p = arg+strlen(arg); while (p >= arg && *p != '/') p--; p++; printf("%s %1.2f\n", p, VERSION_STRING); printf("Usage:\n" "%s nr_tasks\n\n" "-p prio --prio prio base priority to start RT tasks with (2) \n" "-r time --run-time time Run time (ms) to busy loop the threads (20)\n" "-s time --sleep-time time Sleep time (ms) between intervals (100)\n" "-m time --maxerr time Max allowed error (microsecs)\n" "-l loops --loops loops Number of iterations to run (50)\n" "-c --check Stop if lower prio task is quick than higher (off)\n" " () above are defaults \n", p); exit(0); } static void parse_options (int argc, char *argv[]) { for (;;) { int option_index = 0; /** Options for getopt */ static struct option long_options[] = { {"prio", required_argument, NULL, 'p'}, {"run-time", required_argument, NULL, 'r'}, {"sleep-time", required_argument, NULL, 's'}, {"maxerr", required_argument, NULL, 'm'}, {"loops", required_argument, NULL, 'l'}, {"check", no_argument, NULL, 'c'}, {"help", no_argument, NULL, '?'}, {NULL, 0, NULL, 0} }; int c = getopt_long (argc, argv, "p:r:s:m:l:ch", long_options, &option_index); if (c == -1) break; switch (c) { case 'p': prio_start = atoi(optarg); break; case 'r': run_interval = atoi(optarg); break; case 's': interval = atoi(optarg); break; case 'l': nr_runs = atoi(optarg); break; case 'm': max_err = usec2nano(atoi(optarg)); break; case 'c': check = 1; break; case '?': case 'h': usage(argv); break; } } } static unsigned long long get_time(void) { struct timeval tv; unsigned long long time; gettimeofday(&tv, NULL); time = sec2nano(tv.tv_sec); time += usec2nano(tv.tv_usec); return time; } static void record_time(int id, unsigned long long time, unsigned long l) { unsigned long long ltime; if (loop >= nr_runs) return; time -= now; ltime = get_time(); ltime -= now; intervals[loop][id] = time; intervals_length[loop][id] = ltime; intervals_loops[loop][id] = l; } static void print_results(void) { int i; int t; unsigned long long tasks_max[nr_tasks]; unsigned long long tasks_min[nr_tasks]; unsigned long long tasks_avg[nr_tasks]; memset(tasks_max, 0, sizeof(tasks_max[0])*nr_tasks); memset(tasks_min, 0xff, sizeof(tasks_min[0])*nr_tasks); memset(tasks_avg, 0, sizeof(tasks_avg[0])*nr_tasks); printf("Iter: "); for (t=0; t < nr_tasks; t++) printf("%6d ", t); printf("\n"); for (i=0; i < nr_runs; i++) { printf("%4d: ", i); for (t=0; t < nr_tasks; t++) { unsigned long long itv = intervals[i][t]; if (tasks_max[t] < itv) tasks_max[t] = itv; if (tasks_min[t] > itv) tasks_min[t] = itv; tasks_avg[t] += itv; printf("%6lld ", nano2usec(itv)); } printf("\n"); printf(" len: "); for (t=0; t < nr_tasks; t++) { unsigned long long len = intervals_length[i][t]; printf("%6lld ", nano2usec(len)); } printf("\n"); printf(" loops: "); for (t=0; t < nr_tasks; t++) { unsigned long loops = intervals_loops[i][t]; printf("%6ld ", loops); } printf("\n"); printf("\n"); } printf("Parent pid: %d\n", getpid()); for (t=0; t < nr_tasks; t++) { printf(" Task %d (prio %d) (pid %ld):\n", t, t + prio_start, thread_pids[t]); printf(" Max: %lld us\n", nano2usec(tasks_max[t])); printf(" Min: %lld us\n", nano2usec(tasks_min[t])); printf(" Tot: %lld us\n", nano2usec(tasks_avg[t])); printf(" Avg: %lld us\n", nano2usec(tasks_avg[t] / nr_runs)); printf("\n"); } if (check) { if (check < 0) printf(" Failed!\n"); else printf(" Passed!\n"); } } static unsigned long busy_loop(unsigned long long start_time) { unsigned long long time; unsigned long l = 0; do { l++; time = get_time(); } while ((time - start_time) < RUN_INTERVAL); return l; } void *start_task(void *data) { long id = (long)data; unsigned long long start_time; struct sched_param param = { .sched_priority = id + prio_start, }; int ret; int high = 0; cpu_set_t cpumask; cpu_set_t save_cpumask; int cpu = 0; unsigned long l; long pid; ret = sched_getaffinity(0, sizeof(save_cpumask), &save_cpumask); if (ret < 0) perr("getting affinity"); pid = gettid(); /* Check if we are the highest prio task */ if (id == nr_tasks-1) high = 1; ret = sched_setscheduler(0, SCHED_FIFO, ¶m); if (ret < 0 && !id) fprintf(stderr, "Warning, can't set priorities\n"); while (!done) { if (high) { /* rotate around the CPUS */ if (!CPU_ISSET(cpu, &save_cpumask)) cpu = 0; CPU_ZERO(&cpumask); CPU_SET(cpu, &cpumask); cpu++; sched_setaffinity(0, sizeof(cpumask), &cpumask); } pthread_barrier_wait(&start_barrier); start_time = get_time(); ftrace_write("Thread %d: started %lld diff %lld\n", pid, start_time, start_time - now); l = busy_loop(start_time); record_time(id, start_time, l); pthread_barrier_wait(&end_barrier); } return (void*)pid; } static int check_times(int l) { int i; unsigned long long last; unsigned long long last_loops; unsigned long long last_length; for (i=0; i < nr_tasks; i++) { if (i && last < intervals[l][i] && ((intervals[l][i] - last) > max_err)) { /* * May be a false positive. * Make sure that we did more loops * our start is before the end * and the end should be tested. */ if (intervals_loops[l][i] < last_loops || intervals[l][i] > last_length || (intervals_length[l][i] > last_length && intervals_length[l][i] - last_length > max_err)) { check = -1; return 1; } } last = intervals[l][i]; last_loops = intervals_loops[l][i]; last_length = intervals_length[l][i]; } return 0; } static void stop_log(int sig) { stop = 1; } static int count_cpus(void) { FILE *fp; char buf[1024]; int cpus = 0; char *pbuf; size_t *pn; size_t n; int r; n = 1024; pn = &n; pbuf = buf; fp = fopen("/proc/cpuinfo", "r"); if (!fp) perr("Can not read cpuinfo"); while ((r = getline(&pbuf, pn, fp)) >= 0) { char *p; if (strncmp(buf, "processor", 9) != 0) continue; for (p = buf+9; isspace(*p); p++) ; if (*p == ':') cpus++; } fclose(fp); return cpus; } int main (int argc, char **argv) { pthread_t *threads; long i; int ret; struct timespec intv; struct sched_param param; parse_options(argc, argv); signal(SIGINT, stop_log); if (argc >= (optind + 1)) nr_tasks = atoi(argv[optind]); else nr_tasks = count_cpus() + 1; threads = malloc(sizeof(*threads) * nr_tasks); if (!threads) perr("malloc"); memset(threads, 0, sizeof(*threads) * nr_tasks); ret = pthread_barrier_init(&start_barrier, NULL, nr_tasks + 1); ret = pthread_barrier_init(&end_barrier, NULL, nr_tasks + 1); if (ret < 0) perr("pthread_barrier_init"); intervals = malloc(sizeof(void*) * nr_runs); if (!intervals) perr("malloc intervals array"); intervals_length = malloc(sizeof(void*) * nr_runs); if (!intervals_length) perr("malloc intervals length array"); intervals_loops = malloc(sizeof(void*) * nr_runs); if (!intervals_loops) perr("malloc intervals loops array"); thread_pids = malloc(sizeof(long) * nr_tasks); if (!thread_pids) perr("malloc thread_pids"); for (i=0; i < nr_runs; i++) { intervals[i] = malloc(sizeof(unsigned long long)*nr_tasks); if (!intervals[i]) perr("malloc intervals"); memset(intervals[i], 0, sizeof(unsigned long long)*nr_tasks); intervals_length[i] = malloc(sizeof(unsigned long long)*nr_tasks); if (!intervals_length[i]) perr("malloc length intervals"); memset(intervals_length[i], 0, sizeof(unsigned long long)*nr_tasks); intervals_loops[i] = malloc(sizeof(unsigned long)*nr_tasks); if (!intervals_loops[i]) perr("malloc loops intervals"); memset(intervals_loops[i], 0, sizeof(unsigned long)*nr_tasks); } for (i=0; i < nr_tasks; i++) { if (pthread_create(&threads[i], NULL, start_task, (void *)i)) perr("pthread_create"); } /* * Progress bar uses stderr to let users see it when * redirecting output. So we convert stderr to use line * buffering so the progress bar doesn't flicker. */ setlinebuf(stderr); /* up our prio above all tasks */ memset(¶m, 0, sizeof(param)); param.sched_priority = nr_tasks + prio_start; if (sched_setscheduler(0, SCHED_FIFO, ¶m)) fprintf(stderr, "Warning, can't set priority of main thread!\n"); intv.tv_sec = nano2sec(INTERVAL); intv.tv_nsec = INTERVAL % sec2nano(1); print_progress_bar(0); setup_ftrace_marker(); for (loop=0; loop < nr_runs; loop++) { unsigned long long end; now = get_time(); ftrace_write("Loop %d now=%lld\n", loop, now); pthread_barrier_wait(&start_barrier); ftrace_write("All running!!!\n"); nanosleep(&intv, NULL); print_progress_bar((loop * 100)/ nr_runs); end = get_time(); ftrace_write("Loop %d end now=%lld diff=%lld\n", loop, end, end - now); pthread_barrier_wait(&end_barrier); if (stop || (check && check_times(loop))) { loop++; nr_runs = loop; break; } } putc('\n', stderr); pthread_barrier_wait(&start_barrier); done = 1; pthread_barrier_wait(&end_barrier); for (i=0; i < nr_tasks; i++) pthread_join(threads[i], (void*)&thread_pids[i]); print_results(); if (stop) { /* * We use this test in bash while loops * So if we hit Ctrl-C then let the while * loop know to break. */ if (check < 0) exit(-1); else exit(1); } if (check < 0) exit(-1); else exit(0); return 0; } rt-tests/src/ptsematest/0000700001204700120500000000000012252162340016026 5ustar williamswilliamsrt-tests/src/ptsematest/ptsematest.c0000600001204700120500000002671112252162340020374 0ustar williamswilliams/* * ptsematest.c * * Copyright (C) 2009 Carsten Emde * * 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 program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, * USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rt-utils.h" #include "rt-get_cpu.h" #include "error.h" #include #define gettid() syscall(__NR_gettid) #define USEC_PER_SEC 1000000 enum { AFFINITY_UNSPECIFIED, AFFINITY_SPECIFIED, AFFINITY_USEALL }; static pthread_mutex_t *testmutex; static pthread_mutex_t *syncmutex; struct params { int num; int cpu; int priority; int affinity; int sender; int samples; int max_cycles; int tracelimit; int tid; int shutdown; int stopped; struct timespec delay; unsigned int mindiff, maxdiff; double sumdiff; struct timeval unblocked, received, diff; pthread_t threadid; struct params *neighbor; char error[MAX_PATH * 2]; }; void *semathread(void *param) { int mustgetcpu = 0; int first = 1; struct params *par = param; cpu_set_t mask; int policy = SCHED_FIFO; struct sched_param schedp; memset(&schedp, 0, sizeof(schedp)); schedp.sched_priority = par->priority; sched_setscheduler(0, policy, &schedp); if (par->cpu != -1) { CPU_ZERO(&mask); CPU_SET(par->cpu, &mask); if(sched_setaffinity(0, sizeof(mask), &mask) == -1) fprintf(stderr, "WARNING: Could not set CPU affinity " "to CPU #%d\n", par->cpu); } else mustgetcpu = 1; par->tid = gettid(); while (!par->shutdown) { if (par->sender) { pthread_mutex_lock(&syncmutex[par->num]); /* Release lock: Start of latency measurement ... */ gettimeofday(&par->unblocked, NULL); pthread_mutex_unlock(&testmutex[par->num]); par->samples++; if(par->max_cycles && par->samples >= par->max_cycles) par->shutdown = 1; if (mustgetcpu) par->cpu = get_cpu(); } else { /* Receiver */ if (!first) { pthread_mutex_lock(&syncmutex[par->num]); first = 1; } pthread_mutex_lock(&testmutex[par->num]); /* ... Got the lock: End of latency measurement */ gettimeofday(&par->received, NULL); par->samples++; timersub(&par->received, &par->neighbor->unblocked, &par->diff); if (par->diff.tv_usec < par->mindiff) par->mindiff = par->diff.tv_usec; if (par->diff.tv_usec > par->maxdiff) par->maxdiff = par->diff.tv_usec; par->sumdiff += (double) par->diff.tv_usec; if (par->tracelimit && par->maxdiff > par->tracelimit) { char tracing_enabled_file[MAX_PATH]; strcpy(tracing_enabled_file, get_debugfileprefix()); strcat(tracing_enabled_file, "tracing_enabled"); int tracing_enabled = open(tracing_enabled_file, O_WRONLY); if (tracing_enabled >= 0) { write(tracing_enabled, "0", 1); close(tracing_enabled); } else snprintf(par->error, sizeof(par->error), "Could not access %s\n", tracing_enabled_file); par->shutdown = 1; par->neighbor->shutdown = 1; } if (par->max_cycles && par->samples >= par->max_cycles) par->shutdown = 1; if (mustgetcpu) par->cpu = get_cpu(); nanosleep(&par->delay, NULL); pthread_mutex_unlock(&syncmutex[par->num]); } } par->stopped = 1; return NULL; } static void display_help(void) { printf("ptsematest V %1.2f\n", VERSION_STRING); puts("Usage: ptsematest "); puts("Function: test POSIX threads mutex latency"); puts( "Options:\n" "-a [NUM] --affinity run thread #N on processor #N, if possible\n" " with NUM pin all threads to the processor NUM\n" "-b USEC --breaktrace=USEC send break trace command when latency > USEC\n" "-d DIST --distance=DIST distance of thread intervals in us default=500\n" "-i INTV --interval=INTV base interval of thread in us default=1000\n" "-l LOOPS --loops=LOOPS number of loops: default=0(endless)\n" "-p PRIO --prio=PRIO priority\n" "-S --smp SMP testing: options -a -t and same priority\n" " of all threads\n" "-t --threads one thread per available processor\n" "-t [NUM] --threads=NUM number of threads:\n" " without NUM, threads = max_cpus\n" " without -t default = 1\n"); exit(1); } static int setaffinity = AFFINITY_UNSPECIFIED; static int affinity; static int tracelimit; static int priority; static int num_threads = 1; static int max_cycles; static int interval = 1000; static int distance = 500; static int smp; static int sameprio; static void process_options (int argc, char *argv[]) { int error = 0; int max_cpus = sysconf(_SC_NPROCESSORS_CONF); for (;;) { int option_index = 0; /** Options for getopt */ static struct option long_options[] = { {"affinity", optional_argument, NULL, 'a'}, {"breaktrace", required_argument, NULL, 'b'}, {"distance", required_argument, NULL, 'd'}, {"interval", required_argument, NULL, 'i'}, {"loops", required_argument, NULL, 'l'}, {"priority", required_argument, NULL, 'p'}, {"smp", no_argument, NULL, 'S'}, {"threads", optional_argument, NULL, 't'}, {"help", no_argument, NULL, '?'}, {NULL, 0, NULL, 0} }; int c = getopt_long (argc, argv, "a::b:d:i:l:p:St::", long_options, &option_index); if (c == -1) break; switch (c) { case 'a': if (smp) { warn("-a ignored due to --smp\n"); break; } if (optarg != NULL) { affinity = atoi(optarg); setaffinity = AFFINITY_SPECIFIED; } else if (optind= max_cpus) { fprintf(stderr, "ERROR: CPU #%d not found, only %d CPUs available\n", affinity, max_cpus); error = 1; } } if (num_threads < 0 || num_threads > 255) error = 1; if (priority < 0 || priority > 99) error = 1; if (num_threads < 1) error = 1; if (priority && smp) sameprio = 1; if (error) display_help (); } static int volatile shutdown; static void sighand(int sig) { shutdown = 1; } int main(int argc, char *argv[]) { int i; int max_cpus = sysconf(_SC_NPROCESSORS_CONF); int oldsamples = 1; struct params *receiver = NULL; struct params *sender = NULL; sigset_t sigset; struct timespec maindelay; process_options(argc, argv); if (check_privs()) return 1; if (mlockall(MCL_CURRENT|MCL_FUTURE) == -1) { perror("mlockall"); return 1; } signal(SIGINT, sighand); signal(SIGTERM, sighand); receiver = calloc(num_threads, sizeof(struct params)); sender = calloc(num_threads, sizeof(struct params)); if (receiver == NULL || sender == NULL) goto nomem; testmutex = (pthread_mutex_t *) calloc(num_threads, sizeof(pthread_mutex_t)); syncmutex = (pthread_mutex_t *) calloc(num_threads, sizeof(pthread_mutex_t)); if (testmutex == NULL || syncmutex == NULL) goto nomem; for (i = 0; i < num_threads; i++) { receiver[i].mindiff = UINT_MAX; receiver[i].maxdiff = 0; receiver[i].sumdiff = 0.0; pthread_mutex_init(&testmutex[i], NULL); pthread_mutex_init(&syncmutex[i], NULL); /* Wait on first attempt */ pthread_mutex_lock(&testmutex[i]); receiver[i].num = i; receiver[i].cpu = i; switch (setaffinity) { case AFFINITY_UNSPECIFIED: receiver[i].cpu = -1; break; case AFFINITY_SPECIFIED: receiver[i].cpu = affinity; break; case AFFINITY_USEALL: receiver[i].cpu = i % max_cpus; break; } receiver[i].priority = priority; receiver[i].tracelimit = tracelimit; if (priority > 1 && !sameprio) priority--; receiver[i].delay.tv_sec = interval / USEC_PER_SEC; receiver[i].delay.tv_nsec = (interval % USEC_PER_SEC) * 1000; interval += distance; receiver[i].max_cycles = max_cycles; receiver[i].sender = 0; receiver[i].neighbor = &sender[i]; pthread_create(&receiver[i].threadid, NULL, semathread, &receiver[i]); memcpy(&sender[i], &receiver[i], sizeof(receiver[0])); sender[i].sender = 1; sender[i].neighbor = &receiver[i]; pthread_create(&sender[i].threadid, NULL, semathread, &sender[i]); } maindelay.tv_sec = 0; maindelay.tv_nsec = 50000000; /* 50 ms */ while (!shutdown) { int printed; int errorlines = 0; for (i = 0; i < num_threads; i++) shutdown |= receiver[i].shutdown | sender[i].shutdown; if (receiver[0].samples > oldsamples || shutdown) { for (i = 0; i < num_threads; i++) { printf("#%1d: ID%d, P%d, CPU%d, I%ld; #%1d: ID%d, P%d, CPU%d, Cycles %d\n", i*2, receiver[i].tid, receiver[i].priority, receiver[i].cpu, receiver[i].delay.tv_nsec / 1000, i*2+1, sender[i].tid, sender[i].priority, sender[i].cpu, sender[i].samples); } for (i = 0; i < num_threads; i++) { printf("#%d -> #%d, Min %4d, Cur %4d, Avg %4d, Max %4d\n", i*2+1, i*2, receiver[i].mindiff, (int) receiver[i].diff.tv_usec, (int) ((receiver[i].sumdiff / receiver[i].samples) + 0.5), receiver[i].maxdiff); if (receiver[i].error[0] != '\0') { printf("%s", receiver[i].error); errorlines++; receiver[i].error[0] = '\0'; } if (sender[i].error[0] != '\0') { printf("%s", sender[i].error); errorlines++; receiver[i].error[0] = '\0'; } } printed = 1; } else printed = 0; sigemptyset(&sigset); sigaddset(&sigset, SIGTERM); sigaddset(&sigset, SIGINT); pthread_sigmask(SIG_SETMASK, &sigset, NULL); nanosleep(&maindelay, NULL); sigemptyset(&sigset); pthread_sigmask(SIG_SETMASK, &sigset, NULL); if (printed && !shutdown) printf("\033[%dA", num_threads*2 + errorlines); } for (i = 0; i < num_threads; i++) { receiver[i].shutdown = 1; sender[i].shutdown = 1; pthread_mutex_unlock(&testmutex[i]); pthread_mutex_unlock(&syncmutex[i]); } nanosleep(&receiver[0].delay, NULL); for (i = 0; i < num_threads; i++) { if (!receiver[i].stopped) pthread_kill(receiver[i].threadid, SIGTERM); if (!sender[i].stopped) pthread_kill(sender[i].threadid, SIGTERM); } for (i = 0; i < num_threads; i++) { pthread_mutex_destroy(&testmutex[i]); pthread_mutex_destroy(&syncmutex[i]); } nomem: return 0; } rt-tests/src/ptsematest/ptsematest.80000600001204700120500000000501512252162340020313 0ustar williamswilliams.TH "ptsematest" "8" "0.1" "" "" .SH "NAME" .LP \fBptsematest\fR \- Start two threads and measure the latency of interprocess communication with POSIX mutex. .SH "SYNTAX" .LP ptsematest [-a|-a PROC] [-b USEC] [-d DIST] [-i INTV] [-l loops] [-p PRIO] [-t|-t NUM] .br .SH "DESCRIPTION" .LP The program \fBptsematest\fR starts two threads that are synchronized via pthread_mutex_unlock()/pthread_mutex_lock() and measures the latency between releasing and getting the lock. .SH "OPTIONS" .TP .B \-a, \-\-affinity[=PROC] Run on procesor number PROC. If PROC is not specified, run on current processor. .TP .B \-b, \-\-breaktrace=USEC Send break trace command when latency > USEC. This is a debugging option to control the latency tracer in the realtime preemption patch. It is useful to track down unexpected large latencies of a system. .TP .B \-d, \-\-distance=DIST Set the distance of thread intervals in microseconds (default is 500 us). When cylictest is called with the -t option and more than one thread is created, then this distance value is added to the interval of the threads: Interval(thread N) = Interval(thread N-1) + DIST .TP .B \-i, \-\-interval=INTV Set the base interval of the thread(s) in microseconds (default is 1000 us). This sets the interval of the first thread. See also -d. .TP .B \-l, \-\-loops=LOOPS Set the number of loops. The default is 0 (endless). This option is useful for automated tests with a given number of test cycles. ptsematest is stopped once the number of timer intervals has been reached. .TP .B \-p, \-\-prio=PRIO Set the priority of the process. .TP .B \-t, \-\-threads[=NUM] Set the number of test threads (default is 1, if this option is not given). If NUM is specified, create NUM test threads. If NUM is not specifed, NUM is set to the number of available CPUs. .SH "EXAMPLES" The following example was running on a 4-way processor: .LP .nf # ptsematest -a -t -p99 -i100 -d25 -l1000000 #0: ID8672, P99, CPU0, I100; #1: ID8673, P99, CPU0, Cycles 1000000 #2: ID8674, P98, CPU1, I125; #3: ID8675, P98, CPU1, Cycles 811035 #4: ID8676, P97, CPU2, I150; #5: ID8677, P97, CPU2, Cycles 668130 #6: ID8678, P96, CPU3, I175; #7: ID8679, P96, CPU3, Cycles 589423 #1 -> #0, Min 1, Cur 1, Avg 2, Max 11 #3 -> #2, Min 1, Cur 2, Avg 2, Max 13 #5 -> #4, Min 1, Cur 4, Avg 3, Max 12 #7 -> #6, Min 1, Cur 4, Avg 2, Max 12 .fi .SH "AUTHORS" .LP Carsten Emde .SH "SEE ALSO" .LP pthread_mutex_lock(3p), pthread_mutex_unlock(3p) rt-tests/src/ptsematest/Makefile0000600001204700120500000000033712252162340017473 0ustar williamswilliamsCFLAGS += -Wall -O2 LDFLAGS += -lpthread all: ptsematest @echo Done ptsematest.o: ptsematest.c ptsematest: clean: @rm -f *.o tar: clean @rm -f ptsematest $(shell bn=`basename $$PWD`; cd ..; tar -zcf $$bn.tgz $$bn) rt-tests/src/hwlatdetect/0000700001204700120500000000000012252162340016145 5ustar williamswilliamsrt-tests/src/hwlatdetect/hwlatdetect.80000600001204700120500000001022012252162340020543 0ustar williamswilliams.\" Hey, EMACS: -*- nroff -*- .TH HWLATDETECT 8 "May 12, 2009" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME hwlatdetect \- program to control the kernel hardware latency detection module .SH SYNOPSIS .B hwlatdetect .RI "[ \-\-duration=