You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
314 lines
8.5 KiB
314 lines
8.5 KiB
/******************************************************************************
|
|
*
|
|
* 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
|
|
* testpi-7.c
|
|
*
|
|
* DESCRIPTION
|
|
* measure the latency involved with PI boosting.
|
|
*
|
|
*
|
|
* USAGE:
|
|
* Use run_auto.sh script in current directory to build and run test.
|
|
*
|
|
* AUTHOR
|
|
* Darren Hart <dvhltc@us.ibm.com>
|
|
*
|
|
* HISTORY
|
|
* 2006-May-3: Initial version by Darren Hart <dvhltc@us.ibm.com>
|
|
* 2006-May-4: Timing fixes reported by Vernon Mauery <vernux@us.ibm.com>
|
|
* 2006-May-4: Made the med prio threads RT by Darren Hart <dvhltc@us.ibm.com>
|
|
* 2006-May-5: Modified to use flagging by Vernon Mauery <vernux@us.ibm.com>
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
#include <librttest.h>
|
|
|
|
#define HIGH_PRIO 15
|
|
#define MED_PRIO 10
|
|
#define LOW_PRIO 5
|
|
|
|
#define ITERATIONS 100
|
|
|
|
#define MED_WORK_MS 20
|
|
#define NS_PER_MS 1000000
|
|
|
|
static int use_flag_mutex;
|
|
static int max_delay_us;
|
|
static int max_drop2grab_us;
|
|
|
|
static pthread_mutex_t pi_mutex;
|
|
|
|
// flagging details
|
|
typedef enum {
|
|
LOW_START_CYCLE = 1, // 1
|
|
MED_START_WORK, // 2
|
|
HIGH_GRAB_MUTEX, // 3
|
|
LOW_DROP_MUTEX, // 4
|
|
END_OF_CYCLE, // 5
|
|
END_OF_GAME // 6
|
|
} phase_t;
|
|
|
|
static volatile phase_t phase_flag = END_OF_CYCLE;
|
|
|
|
static pthread_mutex_t flag_mutex;
|
|
|
|
int med_threads = 0;
|
|
long iterations = ITERATIONS;
|
|
|
|
void usage(void)
|
|
{
|
|
rt_help();
|
|
printf("testpi-7 specific options:\n");
|
|
printf(" -i# #: number of iterations\n");
|
|
printf(" -f #: Use flag mutex\n");
|
|
printf(" -x# #:number of mid priority threads\n");
|
|
}
|
|
|
|
int parse_args(int c, char *v)
|
|
{
|
|
int handled = 1;
|
|
switch (c) {
|
|
case 'f':
|
|
use_flag_mutex = 0;
|
|
break;
|
|
case 'h':
|
|
usage();
|
|
exit(0);
|
|
case 'i':
|
|
iterations = atoi(v);
|
|
break;
|
|
case 'x':
|
|
med_threads = atoi(v);
|
|
break;
|
|
default:
|
|
handled = 0;
|
|
break;
|
|
}
|
|
return handled;
|
|
}
|
|
|
|
phase_t _read_flag(const char *s, int l)
|
|
{
|
|
phase_t ret;
|
|
if (use_flag_mutex)
|
|
pthread_mutex_lock(&flag_mutex);
|
|
ret = phase_flag;
|
|
debug(DBG_DEBUG, "%s:%d: read_flag = %s (%d)\n", s, l,
|
|
(ret == LOW_START_CYCLE ? "LOW_START_CYCLE" : ret ==
|
|
MED_START_WORK ? "MED_START_WORK" : ret ==
|
|
HIGH_GRAB_MUTEX ? "HIGH_GRAB_MUTEX" : ret ==
|
|
LOW_DROP_MUTEX ? "LOW_DROP_MUTEX" : ret ==
|
|
END_OF_CYCLE ? "END_OF_CYCLE" : "ERROR"), ret);
|
|
//debug(DBG_DEBUG, "%s:%d: read_flag = %d\n", s, l, ret);
|
|
if (use_flag_mutex)
|
|
pthread_mutex_unlock(&flag_mutex);
|
|
return ret;
|
|
}
|
|
|
|
void _write_flag(const char *s, int l, phase_t new_flag)
|
|
{
|
|
if (use_flag_mutex)
|
|
pthread_mutex_lock(&flag_mutex);
|
|
if (phase_flag != END_OF_GAME) {
|
|
if (new_flag != phase_flag && new_flag != (phase_flag + 1)
|
|
&& !(new_flag == LOW_START_CYCLE
|
|
&& phase_flag == END_OF_CYCLE))
|
|
printf("YOU'RE HOSED: new_flag=%d, phase_flag=%d\n",
|
|
new_flag, phase_flag);
|
|
phase_flag = new_flag;
|
|
debug(DBG_DEBUG, "phase_flag: %s set it to %d\n", s,
|
|
phase_flag);
|
|
debug(DBG_DEBUG, "%s:%d: write_flag = %s (%d)\n", s, l,
|
|
(new_flag ==
|
|
LOW_START_CYCLE ? "LOW_START_CYCLE" : new_flag ==
|
|
MED_START_WORK ? "MED_START_WORK" : new_flag ==
|
|
HIGH_GRAB_MUTEX ? "HIGH_GRAB_MUTEX" : new_flag ==
|
|
LOW_DROP_MUTEX ? "LOW_DROP_MUTEX" : new_flag ==
|
|
END_OF_CYCLE ? "END_OF_CYCLE" : "ERROR"), new_flag);
|
|
//debug(DBG_DEBUG, "%s:%d: write_flag = %d\n", s, l, new_flag);
|
|
}
|
|
if (use_flag_mutex)
|
|
pthread_mutex_unlock(&flag_mutex);
|
|
}
|
|
|
|
#define read_flag(A) _read_flag(__FUNCTION__,__LINE__)
|
|
#define write_flag(A) _write_flag(__FUNCTION__,__LINE__,A)
|
|
|
|
#define while_not_flag(A,B) while (read_flag() != (A) && !thread_quit(B))
|
|
|
|
static nsec_t low_drop_time;
|
|
void *low_prio_rt_thread(void *arg)
|
|
{
|
|
struct thread *t = (struct thread *)arg;
|
|
while (!thread_quit(t)) {
|
|
while_not_flag(LOW_START_CYCLE, t)
|
|
rt_nanosleep(1 * NS_PER_MS);
|
|
debug(DBG_INFO, "low try mutex\n");
|
|
pthread_mutex_lock(&pi_mutex);
|
|
debug(DBG_INFO, "low grab mutex\n");
|
|
write_flag(MED_START_WORK);
|
|
rt_nanosleep(1 * NS_PER_MS);
|
|
while_not_flag(LOW_DROP_MUTEX, t) {
|
|
//printf("!"); fflush(NULL);
|
|
rt_nanosleep(1);
|
|
}
|
|
debug(DBG_INFO, "low drop mutex\n");
|
|
low_drop_time = rt_gettime();
|
|
pthread_mutex_unlock(&pi_mutex);
|
|
while_not_flag(END_OF_CYCLE, t) {
|
|
//printf("@"); fflush(NULL);
|
|
rt_nanosleep(1 * NS_PER_MS);
|
|
}
|
|
}
|
|
debug(DBG_INFO, "low prio thread finished (flags=%#x)\n", t->flags);
|
|
return NULL;
|
|
}
|
|
|
|
void *med_prio_thread(void *arg)
|
|
{
|
|
static atomic_t m_flag = { 0 };
|
|
struct thread *t = (struct thread *)arg;
|
|
#define MP "\t\t\t"
|
|
while (!thread_quit(t)) {
|
|
int i_am_the_one;
|
|
phase_t f;
|
|
while_not_flag(MED_START_WORK, t) {
|
|
//printf("."); fflush(NULL);
|
|
rt_nanosleep(1 * NS_PER_MS);
|
|
}
|
|
if ((i_am_the_one = atomic_inc(&m_flag)) == 1) {
|
|
debug(DBG_INFO, MP "thread %d writing flag\n", t->id);
|
|
write_flag(HIGH_GRAB_MUTEX);
|
|
}
|
|
|
|
debug(DBG_DEBUG, MP "ready to start work\n");
|
|
write_flag(HIGH_GRAB_MUTEX);
|
|
while (((f = read_flag()) == HIGH_GRAB_MUTEX
|
|
|| f == LOW_DROP_MUTEX) && !thread_quit(t)) {
|
|
busy_work_ms(MED_WORK_MS);
|
|
//printf("-"); fflush(NULL);
|
|
}
|
|
debug(DBG_DEBUG, MP "done working -- time to sleep\n");
|
|
if (i_am_the_one == 1) {
|
|
debug(DBG_INFO, MP "thread %d resetting m_flag\n",
|
|
t->id);
|
|
atomic_set(0, &m_flag);
|
|
}
|
|
}
|
|
debug(DBG_INFO, "med prio thread finished\n");
|
|
return NULL;
|
|
#undef MP
|
|
}
|
|
|
|
void *high_prio_rt_thread(void *arg)
|
|
{
|
|
int delta_us;
|
|
int i;
|
|
nsec_t start, now;
|
|
struct thread *t = (struct thread *)arg;
|
|
long iterations = (long)t->arg;
|
|
|
|
#define HP "\t\t\t\t\t"
|
|
for (i = 0; i < iterations; i++) {
|
|
debug(DBG_INFO, "Staring iteration %d\n", i);
|
|
write_flag(LOW_START_CYCLE);
|
|
while_not_flag(HIGH_GRAB_MUTEX, t) {
|
|
//printf("a"); fflush(NULL);
|
|
rt_nanosleep(10 * NS_PER_MS);
|
|
}
|
|
debug(DBG_INFO, HP "high try mutex\n");
|
|
write_flag(LOW_DROP_MUTEX);
|
|
start = rt_gettime();
|
|
pthread_mutex_lock(&pi_mutex);
|
|
now = rt_gettime();
|
|
debug(DBG_INFO, HP "high grab mutex\n");
|
|
write_flag(END_OF_CYCLE);
|
|
debug(DBG_INFO, HP "high drop mutex\n");
|
|
delta_us = (now - start) / NS_PER_US;
|
|
if (delta_us > max_delay_us)
|
|
max_delay_us = delta_us;
|
|
debug(DBG_WARN, "high prio delay time: %d us\n", delta_us);
|
|
delta_us = (now - low_drop_time) / NS_PER_US;
|
|
if (delta_us > max_drop2grab_us)
|
|
max_drop2grab_us = delta_us;
|
|
debug(DBG_WARN, "low drop to high grab time: %d us\n",
|
|
delta_us);
|
|
pthread_mutex_unlock(&pi_mutex);
|
|
rt_nanosleep(10 * NS_PER_MS);
|
|
}
|
|
all_threads_quit();
|
|
write_flag(END_OF_GAME);
|
|
debug(DBG_INFO, HP "high prio done\n");
|
|
#undef HP
|
|
return NULL;
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int i, numcpus;
|
|
setup();
|
|
|
|
rt_init("hfi:x:", parse_args, argc, argv);
|
|
|
|
if (!med_threads) {
|
|
printf
|
|
("This test requires that at least NRCPUS medium priority threads run\n");
|
|
printf
|
|
("If it is run bound to a single CPU, you can specify -x 1\n");
|
|
printf("No User input , using default value for NRCPUS");
|
|
numcpus = sysconf(_SC_NPROCESSORS_ONLN);
|
|
med_threads = numcpus;
|
|
}
|
|
printf(" flag mutex: %s\n", use_flag_mutex ? "enabled" : "disabled");
|
|
printf(" iterations: %ld\n", iterations);
|
|
printf("med threads: %d\n", med_threads);
|
|
|
|
signal(SIGINT, cleanup);
|
|
signal(SIGQUIT, cleanup);
|
|
signal(SIGTERM, cleanup);
|
|
|
|
max_delay_us = 0;
|
|
max_drop2grab_us = 0;
|
|
|
|
init_pi_mutex(&pi_mutex);
|
|
|
|
create_fifo_thread(low_prio_rt_thread, NULL, LOW_PRIO);
|
|
create_fifo_thread(high_prio_rt_thread, (void *)iterations, HIGH_PRIO);
|
|
for (i = 0; i < med_threads; i++) {
|
|
create_fifo_thread(med_prio_thread, NULL, MED_PRIO);
|
|
}
|
|
|
|
while (phase_flag != END_OF_GAME)
|
|
usleep(100);
|
|
join_threads();
|
|
cleanup(0);
|
|
|
|
printf("High priority lock aquisition maximum delay: %dus\n",
|
|
max_delay_us);
|
|
printf
|
|
("Low priority lock drop to high priority acqusistion time: %dus\n",
|
|
max_drop2grab_us);
|
|
printf("\n");
|
|
|
|
return 0;
|
|
}
|