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