333 lines
8.6 KiB
C
333 lines
8.6 KiB
C
/******************************************************************************
|
|
*
|
|
* Copyright © International Business Machines Corp., 2006, 2008
|
|
*
|
|
* 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
|
|
*
|
|
* NAME
|
|
* prio-preempt.c
|
|
*
|
|
* DESCRIPTION
|
|
* Test whether priority pre-emption works fine.
|
|
*
|
|
* The main thread:
|
|
* - Creates a minimum of (N-1) busy threads at priority starting at
|
|
* SCHED_FIFO + 80
|
|
* - Creates 26 FIFO (T1, T2,...,T26) threads with priorities 10, 11,...,36.
|
|
* - Each of these worker threads executes the following piece of code:
|
|
* pthread_mutex_lock(Mi);
|
|
* pthread_cond_wait(CVi);
|
|
* pthread_mutex_unlock(Mi);
|
|
*
|
|
* where Mi is the ith pthread_mutex_t and CVi is the ith conditional
|
|
* variable.So, at the end of this loop, 26 threads are all waiting on
|
|
* seperate condvars and mutexes.
|
|
* - Wakes up thread at priority 10 (T1) by executing:
|
|
* pthread_mutex_lock(M1);
|
|
* pthread_cond_signal(CV1);
|
|
* pthread_mutex_unlock(M1);
|
|
*
|
|
* - Waits for all the worker threads to finish execution.
|
|
* T1 then wakes up T2 by signalling on the condvar CV2 and sets a flag
|
|
* called T1_after_wait to indicate that it is after the wait. It then
|
|
* checks if T2_after_wait has been set or not. If not, the test fails,
|
|
* else the process continues with other threads. The thread T1 expects
|
|
* T2_after_wait to be set as, the moment T1 signals on CV2, T2 is
|
|
* supposed to be scheduled (in accordance with priority preemption).
|
|
*
|
|
* USAGE:
|
|
* Use run_auto.sh script in current directory to build and run test.
|
|
*
|
|
* AUTHOR
|
|
* Dinakar Guniguntala <dino@us.ibm.com>
|
|
*
|
|
* HISTORY
|
|
* 2006-Jun-01: Initial version by Dinakar Guniguntala
|
|
* Changes from John Stultz and Vivek Pallantla
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <signal.h>
|
|
#include <time.h>
|
|
#include <pthread.h>
|
|
#include <sched.h>
|
|
#include <errno.h>
|
|
#include <sys/syscall.h>
|
|
#include <librttest.h>
|
|
|
|
#define NUM_WORKERS 27
|
|
#define CHECK_LIMIT 1
|
|
|
|
volatile int busy_threads = 0;
|
|
volatile int test_over = 0;
|
|
volatile int threads_running = 0;
|
|
static int rt_threads = -1;
|
|
static int int_threads = 0;
|
|
static pthread_mutex_t bmutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
static pthread_mutex_t mutex[NUM_WORKERS + 1];
|
|
static pthread_cond_t cond[NUM_WORKERS + 1];
|
|
static int t_after_wait[NUM_WORKERS];
|
|
|
|
static int ret = 0;
|
|
|
|
pthread_barrier_t barrier;
|
|
|
|
void usage(void)
|
|
{
|
|
rt_help();
|
|
printf("prio-preempt specific options:\n");
|
|
printf(" -i #: enable interrupter threads\n");
|
|
printf(" -n# #: number of busy threads\n");
|
|
}
|
|
|
|
int parse_args(int c, char *v)
|
|
{
|
|
|
|
int handled = 1;
|
|
switch (c) {
|
|
case 'h':
|
|
usage();
|
|
exit(0);
|
|
case 'i':
|
|
int_threads = 1;
|
|
break;
|
|
case 'n':
|
|
rt_threads = atoi(v);
|
|
break;
|
|
default:
|
|
handled = 0;
|
|
break;
|
|
}
|
|
return handled;
|
|
}
|
|
|
|
void *int_thread(void *arg)
|
|
{
|
|
intptr_t a = 0;
|
|
while (!test_over) {
|
|
/* do some busy work */
|
|
if (!(a % 4))
|
|
a = a * 3;
|
|
else if (!(a % 6))
|
|
a = a / 2;
|
|
else
|
|
a++;
|
|
usleep(20);
|
|
}
|
|
return (void *)a;
|
|
}
|
|
|
|
void *busy_thread(void *arg)
|
|
{
|
|
struct sched_param sched_param;
|
|
int policy, mypri = 0, tid;
|
|
tid = (intptr_t) (((struct thread *)arg)->arg);
|
|
|
|
if (pthread_getschedparam(pthread_self(), &policy, &sched_param) != 0) {
|
|
printf("ERR: Couldn't get pthread info \n");
|
|
} else {
|
|
mypri = sched_param.sched_priority;
|
|
}
|
|
|
|
pthread_mutex_lock(&bmutex);
|
|
busy_threads++;
|
|
printf("Busy Thread %d(%d): Running...\n", tid, mypri);
|
|
pthread_mutex_unlock(&bmutex);
|
|
|
|
/* TODO: Add sched set affinity here */
|
|
|
|
/* Busy loop */
|
|
while (!test_over) ;
|
|
|
|
printf("Busy Thread %d(%d): Exiting\n", tid, mypri);
|
|
return NULL;
|
|
}
|
|
|
|
void *worker_thread(void *arg)
|
|
{
|
|
struct sched_param sched_param;
|
|
int policy, rc, mypri = 0, tid, times = 0;
|
|
tid = (intptr_t) (((struct thread *)arg)->arg);
|
|
nsec_t pstart, pend;
|
|
|
|
if (pthread_getschedparam(pthread_self(), &policy, &sched_param) != 0) {
|
|
printf("ERR: Couldn't get pthread info \n");
|
|
} else {
|
|
mypri = sched_param.sched_priority;
|
|
}
|
|
/* check in */
|
|
pthread_mutex_lock(&bmutex);
|
|
threads_running++;
|
|
pthread_mutex_unlock(&bmutex);
|
|
|
|
/* block */
|
|
rc = pthread_mutex_lock(&mutex[tid]);
|
|
if (tid == 0)
|
|
pthread_barrier_wait(&barrier);
|
|
rc = pthread_cond_wait(&cond[tid], &mutex[tid]);
|
|
rc = pthread_mutex_unlock(&mutex[tid]);
|
|
|
|
debug(DBG_INFO, "%llu: Thread %d(%d) wakes up from sleep \n",
|
|
rt_gettime(), tid, mypri);
|
|
|
|
/*check if we're the last thread */
|
|
if (tid == NUM_WORKERS - 1) {
|
|
t_after_wait[tid] = 1;
|
|
pthread_mutex_lock(&bmutex);
|
|
threads_running--;
|
|
pthread_mutex_unlock(&bmutex);
|
|
return NULL;
|
|
}
|
|
|
|
/* Signal next thread */
|
|
rc = pthread_mutex_lock(&mutex[tid + 1]);
|
|
rc = pthread_cond_signal(&cond[tid + 1]);
|
|
debug(DBG_INFO, "%llu: Thread %d(%d): Sent signal (%d) to (%d)\n",
|
|
rt_gettime(), tid, mypri, rc, tid + 1);
|
|
|
|
pstart = pend = rt_gettime();
|
|
rc = pthread_mutex_unlock(&mutex[tid + 1]);
|
|
|
|
debug(DBG_INFO, "%llu: Thread %d(%d) setting it's bit \n", rt_gettime(),
|
|
tid, mypri);
|
|
|
|
t_after_wait[tid] = 1;
|
|
|
|
while (t_after_wait[tid + 1] != 1) {
|
|
pend = rt_gettime();
|
|
times++;
|
|
}
|
|
|
|
if (times >= (int)pass_criteria) {
|
|
printf
|
|
("Thread %d(%d): Non-Preempt limit reached. %llu ns latency\n",
|
|
tid, mypri, pend - pstart);
|
|
ret = 1;
|
|
}
|
|
|
|
/* check out */
|
|
pthread_mutex_lock(&bmutex);
|
|
threads_running--;
|
|
pthread_mutex_unlock(&bmutex);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void *master_thread(void *arg)
|
|
{
|
|
int i, pri_boost;
|
|
|
|
pthread_barrier_init(&barrier, NULL, 2);
|
|
|
|
/* start interrupter thread */
|
|
if (int_threads) {
|
|
pri_boost = 90;
|
|
for (i = 0; i < rt_threads; i++) {
|
|
create_fifo_thread(int_thread, NULL,
|
|
sched_get_priority_min(SCHED_FIFO) +
|
|
pri_boost);
|
|
}
|
|
}
|
|
|
|
/* start the (N-1) busy threads */
|
|
pri_boost = 80;
|
|
for (i = rt_threads; i > 1; i--) {
|
|
create_fifo_thread(busy_thread, (void *)(intptr_t) i,
|
|
sched_get_priority_min(SCHED_FIFO) +
|
|
pri_boost);
|
|
}
|
|
|
|
/* make sure children are started */
|
|
while (busy_threads < (rt_threads - 1))
|
|
usleep(100);
|
|
|
|
printf("Busy threads created!\n");
|
|
|
|
/* start NUM_WORKERS worker threads */
|
|
for (i = 0, pri_boost = 10; i < NUM_WORKERS; i++, pri_boost += 2) {
|
|
pthread_mutex_init(&mutex[i], NULL);
|
|
pthread_cond_init(&cond[i], NULL);
|
|
create_fifo_thread(worker_thread, (void *)(intptr_t) i,
|
|
sched_get_priority_min(SCHED_FIFO) +
|
|
pri_boost);
|
|
}
|
|
|
|
printf("Worker threads created\n");
|
|
/* Let the worker threads wait on the cond vars */
|
|
while (threads_running < NUM_WORKERS)
|
|
usleep(100);
|
|
|
|
/* Ensure the first worker has called cond_wait */
|
|
pthread_barrier_wait(&barrier);
|
|
|
|
printf("Signaling first thread\n");
|
|
pthread_mutex_lock(&mutex[0]);
|
|
pthread_cond_signal(&cond[0]);
|
|
pthread_mutex_unlock(&mutex[0]);
|
|
|
|
while (threads_running)
|
|
usleep(500000); /* this period greatly affects the number of failures! */
|
|
|
|
test_over = 1;
|
|
return NULL;
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int pri_boost, numcpus;
|
|
setup();
|
|
|
|
pass_criteria = CHECK_LIMIT;
|
|
rt_init("hin:", parse_args, argc, argv);
|
|
|
|
numcpus = sysconf(_SC_NPROCESSORS_ONLN);
|
|
|
|
/* Max no. of busy threads should always be less than/equal the no. of cpus
|
|
Otherwise, the box will hang */
|
|
|
|
if (rt_threads == -1 || rt_threads > numcpus) {
|
|
rt_threads = numcpus;
|
|
printf("Maximum busy thread count(%d), "
|
|
"should not exceed number of cpus(%d)\n", rt_threads,
|
|
numcpus);
|
|
printf("Using %d\n", numcpus);
|
|
}
|
|
|
|
/* Test boilder plate: title and parameters */
|
|
printf("\n-------------------\n");
|
|
printf("Priority Preemption\n");
|
|
printf("-------------------\n\n");
|
|
printf("Busy Threads: %d\n", rt_threads);
|
|
printf("Interrupter Threads: %s\n",
|
|
int_threads ? "Enabled" : "Disabled");
|
|
printf("Worker Threads: %d\n\n", NUM_WORKERS);
|
|
|
|
pri_boost = 81;
|
|
create_fifo_thread(master_thread, NULL,
|
|
sched_get_priority_min(SCHED_FIFO) + pri_boost);
|
|
|
|
/* wait for threads to complete */
|
|
join_threads();
|
|
|
|
printf
|
|
("\nCriteria: All threads appropriately preempted within %d loop(s)\n",
|
|
(int)pass_criteria);
|
|
printf("Result: %s\n", ret ? "FAIL" : "PASS");
|
|
return ret;
|
|
}
|