1/******************************************************************************
2 *
3 *   Copyright © International Business Machines  Corp., 2007, 2008
4 *
5 *   This program is free software;  you can redistribute it and/or modify
6 *   it under the terms of the GNU General Public License as published by
7 *   the Free Software Foundation; either version 2 of the License, or
8 *   (at your option) any later version.
9 *
10 *   This program is distributed in the hope that it will be useful,
11 *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
12 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
13 *   the GNU General Public License for more details.
14 *
15 *   You should have received a copy of the GNU General Public License
16 *   along with this program;  if not, write to the Free Software
17 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 * NAME
20 *      sched_football.c
21 *
22 * DESCRIPTION
23 *      This is a scheduler test that uses a football analogy.
24 *      The premise is that we want to make sure that lower priority threads
25 *      (defensive team). The offense is trying to increment the balls position,
26 *      while the defense is trying to block that from happening.
27 *      And the ref (highest priority thread) will blow the wistle if the
28 *      ball moves. Finally, we have crazy fans (higer prority) that try to
29 *      distract the defense by occasionally running onto the field.
30 *
31 *      Steps:
32 *       - Create a fixed number of offense threads (lower priority)
33 *       - Create a referee thread (highest priority)
34 *       - Once everyone is on the field, the offense thread increments the
35 *	 value of 'the_ball' and yields. The defense thread tries to block
36 *	 the ball by never letting the offense players get the CPU (it just
37 * 	   does a sched_yield).
38 *       - The refree threads wakes up regularly to check if the game is over :)
39 *       - In the end, if the value of 'the_ball' is >0, the test is considered
40 *	 to have failed.
41 *
42 * USAGE:
43 *      Use run_auto.sh script in current directory to build and run test.
44 *
45 * AUTHOR
46 *      John Stultz <johnstul@xxxxxxxxx >
47 *
48 * HISTORY
49 *     2006-03-16 Reduced verbosity, non binary failure reporting, removal of
50 *		crazy_fans thread, added game_length argument by Darren Hart.
51 *     2007-08-01 Remove all thread cleanup in favor of simply exiting.Various
52 *		bugfixes and cleanups. -- Josh Triplett
53 *     2009-06-23 Simplified atomic startup mechanism, avoiding thundering herd
54 *		scheduling at the beginning of the game. -- Darren Hart
55 *
56 *****************************************************************************/
57
58#include <stdio.h>
59#include <stdlib.h>
60#include <signal.h>
61#include <time.h>
62#include <string.h>
63#include <pthread.h>
64#include <sched.h>
65#include <errno.h>
66#include <sys/syscall.h>
67#include <unistd.h>
68#include <sys/time.h>
69#include <librttest.h>
70
71#define DEF_GAME_LENGTH 5
72
73/* Here's the position of the ball */
74volatile int the_ball;
75
76static int players_per_team = 0;
77static int game_length = DEF_GAME_LENGTH;
78static atomic_t players_ready;
79
80void usage(void)
81{
82	rt_help();
83	printf("sched_football specific options:\n");
84	printf("  -nPLAYERS     players per team (defaults to num_cpus)\n");
85	printf("  -lGAME_LENGTH game length in seconds (defaults to %d s)\n",
86	       DEF_GAME_LENGTH);
87}
88
89int parse_args(int c, char *v)
90{
91
92	int handled = 1;
93	switch (c) {
94	case 'h':
95		usage();
96		exit(0);
97	case 'n':
98		players_per_team = atoi(v);
99		break;
100	case 'l':
101		game_length = atoi(v);
102		break;
103	default:
104		handled = 0;
105		break;
106	}
107	return handled;
108}
109
110/* This is the defensive team. They're trying to block the offense */
111void *thread_defense(void *arg)
112{
113	atomic_inc(&players_ready);
114	/*keep the ball from being moved */
115	while (1) {
116		sched_yield();	/* let other defenders run */
117	}
118	return NULL;
119}
120
121/* This is the offensive team. They're trying to move the ball */
122void *thread_offense(void *arg)
123{
124	atomic_inc(&players_ready);
125	while (1) {
126		the_ball++;	/* move the ball ahead one yard */
127		sched_yield();	/* let other offensive players run */
128	}
129	return NULL;
130}
131
132int referee(int game_length)
133{
134	struct timeval start, now;
135	int final_ball;
136
137	printf("Game On (%d seconds)!\n", game_length);
138
139	gettimeofday(&start, NULL);
140	now = start;
141
142	/* Start the game! */
143	the_ball = 0;
144
145	/* Watch the game */
146	while ((now.tv_sec - start.tv_sec) < game_length) {
147		sleep(1);
148		gettimeofday(&now, NULL);
149	}
150	/* Blow the whistle */
151	printf("Game Over!\n");
152	final_ball = the_ball;
153	printf("Final ball position: %d\n", final_ball);
154	return final_ball != 0;
155}
156
157int main(int argc, char *argv[])
158{
159	struct sched_param param;
160	int priority;
161	int i;
162	int result;
163	setup();
164
165	rt_init("n:l:h", parse_args, argc, argv);
166
167	if (players_per_team == 0)
168		players_per_team = sysconf(_SC_NPROCESSORS_ONLN);
169
170	atomic_set(0, &players_ready);
171
172	printf("Running with: players_per_team=%d game_length=%d\n",
173	       players_per_team, game_length);
174
175	/* We're the ref, so set our priority right */
176	param.sched_priority = sched_get_priority_min(SCHED_FIFO) + 80;
177	sched_setscheduler(0, SCHED_FIFO, &param);
178
179	/*
180	 * Start the offense
181	 * They are lower priority than defense, so they must be started first.
182	 */
183	priority = 15;
184	printf("Starting %d offense threads at priority %d\n",
185	       players_per_team, priority);
186	for (i = 0; i < players_per_team; i++)
187		create_fifo_thread(thread_offense, NULL, priority);
188
189	/* Wait for the offense threads to start */
190	while (atomic_get(&players_ready) < players_per_team)
191		usleep(100);
192
193	/* Start the defense */
194	priority = 30;
195	printf("Starting %d defense threads at priority %d\n",
196	       players_per_team, priority);
197	for (i = 0; i < players_per_team; i++)
198		create_fifo_thread(thread_defense, NULL, priority);
199
200	/* Wait for the defense threads to start */
201	while (atomic_get(&players_ready) < players_per_team * 2)
202		usleep(100);
203
204	/* Ok, everyone is on the field, bring out the ref */
205	printf("Starting referee thread\n");
206	result = referee(game_length);
207	printf("Result: %s\n", result ? "FAIL" : "PASS");
208	return result;
209
210}
211