summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUwe Kleine-König <u.kleine-koenig@pengutronix.de>2011-08-20 14:49:25 +0200
committerUwe Kleine-König <u.kleine-koenig@pengutronix.de>2011-08-20 14:49:25 +0200
commit579bae293907bc8ed4e9036e0594e44d4e77b709 (patch)
tree4624efd97efde1f4ba7923c482d6ff832c9126ca
parent23305f2d44021c5d75d975376b2493f1263efe2d (diff)
parent3e55619c86c2604638c06c31c87881084f643274 (diff)
downloadrt-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--Makefile2
-rw-r--r--rt-tests.spec-in12
-rw-r--r--src/backfire/Makefile.module6
-rw-r--r--src/backfire/sendme.c12
-rw-r--r--src/cyclictest/cyclictest.c135
-rw-r--r--src/include/rt-tests.h124
-rw-r--r--src/pi_tests/Makefile71
-rw-r--r--src/pi_tests/classic_pi.c613
8 files changed, 930 insertions, 45 deletions
diff --git a/Makefile b/Makefile
index 48bef8a..59438e9 100644
--- a/Makefile
+++ b/Makefile
@@ -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), &current_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, &current_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);
+ }
+}