diff options
author | Uwe Kleine-König <u.kleine-koenig@pengutronix.de> | 2011-08-20 14:49:25 +0200 |
---|---|---|
committer | Uwe Kleine-König <u.kleine-koenig@pengutronix.de> | 2011-08-20 14:49:25 +0200 |
commit | 579bae293907bc8ed4e9036e0594e44d4e77b709 (patch) | |
tree | 4624efd97efde1f4ba7923c482d6ff832c9126ca | |
parent | 23305f2d44021c5d75d975376b2493f1263efe2d (diff) | |
parent | 3e55619c86c2604638c06c31c87881084f643274 (diff) | |
download | rt-tests-579bae293907bc8ed4e9036e0594e44d4e77b709.tar.gz |
Merge tag 'v0.74' of git://git.kernel.org/pub/scm/linux/kernel/git/clrkwllms/rt-tests
Notice: this object is not reachable from any branch.
Notice: this object is not reachable from any branch.
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | rt-tests.spec-in | 12 | ||||
-rw-r--r-- | src/backfire/Makefile.module | 6 | ||||
-rw-r--r-- | src/backfire/sendme.c | 12 | ||||
-rw-r--r-- | src/cyclictest/cyclictest.c | 135 | ||||
-rw-r--r-- | src/include/rt-tests.h | 124 | ||||
-rw-r--r-- | src/pi_tests/Makefile | 71 | ||||
-rw-r--r-- | src/pi_tests/classic_pi.c | 613 |
8 files changed, 930 insertions, 45 deletions
@@ -1,4 +1,4 @@ -VERSION_STRING = 0.73 +VERSION_STRING = 0.74 sources = cyclictest.c signaltest.c pi_stress.c rt-migrate-test.c \ ptsematest.c sigwaittest.c svsematest.c pmqtest.c sendme.c \ diff --git a/rt-tests.spec-in b/rt-tests.spec-in index e7400e0..bbd164e 100644 --- a/rt-tests.spec-in +++ b/rt-tests.spec-in @@ -10,7 +10,7 @@ URL: git://git.kernel.org/pub/scm/linux/kernel/git/tglx/rt-tests.git Source0: %{name}-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root Obsoletes: cyclictest signaltest pi_tests -BuildRequires: numactl-devel +BuildRequires: numactl-devel python %description rt-tests is a set of programs that test and measure various components of @@ -62,6 +62,16 @@ rm -rf $RPM_BUILD_ROOT /usr/share/man/man8/hackbench.8.gz %changelog +* Thu Aug 18 2011 Clark Williams <williams@redhat.com> - 0.74-1 +- changes to deal with 3.0 kernel +- fixed buildrequires in specfile for Python +- fixed spelling error in printf in cyclictest +- from John Kacur <jkacur@redhat.com> + - Make the function header style consistent with the rest of cyclictest. + - Spelling clean-ups +- from Uwe Kleine-König <u.kleine-koenig@pengutronix.de> + - fix possible buffer overflow in string handling + * Mon May 9 2011 Clark Williams <williams@redhat.com> - 0.73-1 - fixed signal handling in hackbench (avoid thousands of zombies) - from Geunsik Lim <geunsik.lim@samsung.com> diff --git a/src/backfire/Makefile.module b/src/backfire/Makefile.module new file mode 100644 index 0000000..30011b8 --- /dev/null +++ b/src/backfire/Makefile.module @@ -0,0 +1,6 @@ +obj-m := backfire.o +KDIR := /lib/modules/$(shell uname -r)/build +PWD := $(shell pwd) + +default: + $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules diff --git a/src/backfire/sendme.c b/src/backfire/sendme.c index 27ea077..8c169dd 100644 --- a/src/backfire/sendme.c +++ b/src/backfire/sendme.c @@ -28,6 +28,7 @@ #include <sched.h> #include <string.h> #include <time.h> +#include <errno.h> #include "rt-utils.h" #include "rt-get_cpu.h" @@ -63,9 +64,16 @@ static int kernvar(int mode, const char *name, char *value, size_t sizeofvalue) 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); - strncpy(filename, fileprefix, sizeof(filename)); - strncat(filename, name, sizeof(filename) - strlen(fileprefix)); path = open(filename, mode); if (path >= 0) { if (mode == O_RDONLY) { diff --git a/src/cyclictest/cyclictest.c b/src/cyclictest/cyclictest.c index 29d0fe5..6be5521 100644 --- a/src/cyclictest/cyclictest.c +++ b/src/cyclictest/cyclictest.c @@ -1,7 +1,7 @@ /* * High resolution timer test software * - * (C) 2008-2010 Clark Williams <williams@redhat.com> + * (C) 2008-2011 Clark Williams <williams@redhat.com> * (C) 2005-2007 Thomas Gleixner <tglx@linutronix.de> * * This program is free software; you can redistribute it and/or @@ -187,10 +187,11 @@ static int traceopt_count; static int traceopt_size; enum kernelversion { - KV_NOT_26, + KV_NOT_SUPPORTED, KV_26_LT18, KV_26_LT24, - KV_26_CURR + KV_26_33, + KV_30 }; enum { @@ -201,14 +202,24 @@ enum { 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); - strncpy(filename, fileprefix, sizeof(filename)); - strncat(filename, name, sizeof(filename) - strlen(fileprefix)); path = open(filename, mode); if (path >= 0) { if (mode == O_RDONLY) { @@ -231,7 +242,7 @@ static void setkernvar(const char *name, char *value) int i; char oldvalue[KVALUELEN]; - if (kernelversion != KV_26_CURR) { + if (kernelversion < KV_26_33) { if (kernvar(O_RDONLY, name, oldvalue, sizeof(oldvalue))) fprintf(stderr, "could not retrieve %s\n", name); else { @@ -313,9 +324,7 @@ void traceopt(char *option) traceptr[traceopt_count++] = ptr; } - -static int -trace_file_exists(char *name) +static int trace_file_exists(char *name) { struct stat sbuf; char *tracing_prefix = get_debugfileprefix(); @@ -324,30 +333,34 @@ trace_file_exists(char *name) return stat(path, &sbuf) ? 0 : 1; } +static void tracemark(char *comment) +{ + /* bail out if we're not tracing */ + /* or if the kernel doesn't support trace_mark */ + if (!tracelimit || kernelversion < KV_26_33 || tracemark_fd < 0) + return; + write(tracemark_fd, comment, strlen(comment)); +} + 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_CURR: - if (trace_file_exists("tracing_on")) - setkernvar("tracing_on", "1"); - else - setkernvar("tracing_enabled", "1"); + 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_CURR: - if (trace_file_exists("tracing_on")) - setkernvar("tracing_on", "0"); - else - setkernvar("tracing_enabled", "0"); + case KV_26_33: + case KV_30: + write(trace_fd, "0", 1); break; default: break; } @@ -401,24 +414,25 @@ static void setup_tracer(void) if (!tracelimit) return; - if (kernelversion == KV_26_CURR) { + if (kernelversion >= KV_26_33) { char testname[MAX_PATH]; fileprefix = get_debugfileprefix(); - strcpy(testname, fileprefix); - strcat(testname, "tracing_enabled"); - if (access(testname, R_OK)) - warn("%s not found\n" + 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_CURR) { + if (kernelversion >= KV_26_33) { char buffer[32]; int ret; - setkernvar("tracing_enabled", "1"); + if (trace_file_exists("tracing_enabled") && + !trace_file_exists("tracing_on")) + setkernvar("tracing_enabled", "1"); sprintf(buffer, "%d", tracelimit); setkernvar("tracing_thresh", buffer); @@ -448,9 +462,9 @@ static void setup_tracer(void) ret = settracer("preemptirqsoff"); break; case EVENTS: - ret = settracer("events"); - if (ftrace) - ret = settracer(functiontracer); + /* per rostedt: use nop tracer with event tracing */ + setkernvar("events/enable", "1"); + ret = settracer("nop"); break; case CTXTSWITCH: ret = settracer("sched_switch"); @@ -494,7 +508,28 @@ static void setup_tracer(void) setkernvar(traceroptions, traceptr[i]); } setkernvar("tracing_max_latency", "0"); - setkernvar("latency_hist/wakeup/reset", "1"); + 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"); @@ -637,6 +672,8 @@ void *timerthread(void *param) stat->threadstarted++; + tracemark("entering time loop"); + while (!shutdown) { uint64_t diff; @@ -679,8 +716,11 @@ void *timerthread(void *param) tsnorm(&next); break; } + clock_gettime(par->clock, &now); + tracemark("out of critical loop, calculating"); + if (use_nsecs) diff = calcdiff_ns(now, next); else @@ -699,6 +739,7 @@ void *timerthread(void *param) if (!stopped && tracelimit && (diff > tracelimit)) { stopped++; + tracemark("hit latency threshold"); tracing(0); shutdown++; pthread_mutex_lock(&break_thread_id_lock); @@ -731,6 +772,7 @@ void *timerthread(void *param) if (par->max_cycles && par->max_cycles == stat->cycles) break; + tracemark("heading back to sleep"); } out: @@ -1030,7 +1072,7 @@ static void process_options (int argc, char *argv[]) setaffinity = AFFINITY_USEALL; use_nanosleep = MODE_CLOCK_NANOSLEEP; #else - warn("cyclicteset was not built with the numa option\n"); + warn("cyclictest was not built with the numa option\n"); warn("ignoring --numa or -U\n"); #endif break; @@ -1101,7 +1143,7 @@ static int check_kernel(void) if (ret) { fprintf(stderr, "uname failed: %s. Assuming not 2.6\n", strerror(errno)); - return KV_NOT_26; + return KV_NOT_SUPPORTED; } sscanf(kname.release, "%d.%d.%d", &maj, &min, &sub); if (maj == 2 && min == 6) { @@ -1110,16 +1152,21 @@ static int check_kernel(void) else if (sub < 24) kv = KV_26_LT24; else if (sub < 28) { - kv = KV_26_CURR; + kv = KV_26_33; strcpy(functiontracer, "ftrace"); strcpy(traceroptions, "iter_ctrl"); } else { - kv = KV_26_CURR; + 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_26; + kv = KV_NOT_SUPPORTED; return kv; } @@ -1187,11 +1234,11 @@ static void print_hist(struct thread_param *par[], int nthreads) if (histofall && nthreads > 1) printf(" %09llu", log_entries[nthreads]); printf("\n"); - printf("# Min Latencys:"); + printf("# Min Latencies:"); for (j = 0; j < nthreads; j++) printf(" %05lu", par[j]->stats->min); printf("\n"); - printf("# Avg Latencys:"); + 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); @@ -1284,8 +1331,8 @@ int main(int argc, char **argv) kernelversion = check_kernel(); - if (kernelversion == KV_NOT_26) - warn("Most functions require kernel 2.6\n"); + if (kernelversion == KV_NOT_SUPPORTED) + warn("Running on unknown kernel version...YMMV\n"); setup_tracer(); @@ -1508,12 +1555,18 @@ int main(int argc, char **argv) if (tracelimit) tracing(0); + /* close any tracer file descriptors */ + if (tracemark_fd >= 0) + close(tracemark_fd); + if (trace_fd >= 0) + close(trace_fd); + /* unlock everything */ if (lockall) munlockall(); /* Be a nice program, cleanup */ - if (kernelversion != KV_26_CURR) + if (kernelversion < KV_26_33) restorekernvars(); exit(ret); diff --git a/src/include/rt-tests.h b/src/include/rt-tests.h new file mode 100644 index 0000000..d7eefd9 --- /dev/null +++ b/src/include/rt-tests.h @@ -0,0 +1,124 @@ +#ifndef __RT_TESTS_H__ +#define __RT_TESTS_H__ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#define __USE_GNU +#include <fcntl.h> +#include <getopt.h> +#include <pthread.h> +#include <signal.h> +#include <sched.h> +#include <string.h> +#include <time.h> +#include <errno.h> + +#include <linux/unistd.h> + +#include <sys/prctl.h> +#include <sys/stat.h> +#include <sys/sysinfo.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/utsname.h> +#include <sys/mman.h> + +#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 diff --git a/src/pi_tests/Makefile b/src/pi_tests/Makefile new file mode 100644 index 0000000..6b9b595 --- /dev/null +++ b/src/pi_tests/Makefile @@ -0,0 +1,71 @@ +PACKAGE := 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) diff --git a/src/pi_tests/classic_pi.c b/src/pi_tests/classic_pi.c new file mode 100644 index 0000000..59e907b --- /dev/null +++ b/src/pi_tests/classic_pi.c @@ -0,0 +1,613 @@ +/* + classic_pi - Classic Priority Inversion deadlock test case + + Copyright (C) 2006, 2007 Clark Williams <williams@redhat.com> + + 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 <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <stdarg.h> +#include <pthread.h> +#include <sched.h> +#include <unistd.h> +#include <string.h> +#include <signal.h> +#include <getopt.h> + +/* 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 <n> |--inversions=<n>\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); + } +} |