/****************************************************************************** * * 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; }