1 /*
2  *   Copyright (c) International Business Machines  Corp., 2001
3  *
4  *   This program is free software;  you can redistribute it and/or modify
5  *   it under the terms of the GNU General Public License as published by
6  *   the Free Software Foundation; either version 2 of the License, or
7  *   (at your option) any later version.
8  *
9  *   This program is distributed in the hope that it will be useful,
10  *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
12  *   the GNU General Public License for more details.
13  *
14  *   You should have received a copy of the GNU General Public License
15  *   along with this program;  if not, write to the Free Software
16  *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 /*
19  * FUNCTIONS: Scheduler Test Suite
20  */
21 
22 /*---------------------------------------------------------------------+
23 |                               sched_tc6                              |
24 | ==================================================================== |
25 |                                                                      |
26 | Description:  Creates short-term disk I/O bound process              |
27 |                                                                      |
28 |		Creates a situation where a real time process is       |
29 |		waiting on a file lock owned by a user process.  The   |
30 |		scheduler should elevate the user process priority to  |
31 |		the same level as the real time process to avoid       |
32 |		deadlock situations and to decrease wait time          |
33 |		required of the real time process.                     |
34 |                                                                      |
35 | Algorithm:    o  Set process priority                                |
36 |               o  Continuously multiply matrices together until       |
37 |                  interrupted.                                        |
38 |                                                                      |
39 | To compile:   cc -o sched_tc6 sched_tc6.c -L. -lpsc                  |
40 |                                                                      |
41 | Usage:        sched_tc6 [-t priority_type] [-p priority]             |
42 |                         [-l log] [-f] [-v] [-d]                      |
43 |                                                                      |
44 | Last update:   Ver. 1.3, 4/10/94 23:05:03                           |
45 |                                                                      |
46 | Change Activity                                                      |
47 |                                                                      |
48 |   Version  Date    Name  Reason                                      |
49 |    0.1     050689  CTU   Initial version                             |
50 |    0.2     010402  Manoj Iyer Ported to Linux			       |
51 |                                                                      |
52 +---------------------------------------------------------------------*/
53 
54 #include <fcntl.h>
55 #include <stdlib.h>
56 #include <sys/time.h>
57 #include <time.h>
58 #include <sys/resource.h>
59 #include "sched.h"
60 
61 /*
62  * Defines:
63  *
64  * USAGE: usage statement
65  *
66  * DEFAULT_PRIORITY_TYPE: default priority
67  *
68  * BLOCK_SIZE: block size (in bytes) for raw I/O
69  *
70  * TIMES: number of times to read raw I/O device (~25MB)
71  *
72  */
73 #define DEFAULT_PRIORITY_TYPE	"variable"
74 #define DEFAULT_LOGFILE		"sched_tc6.log"
75 #define BLOCK_SIZE		512
76 #define TIMES			10
77 #define MAX_TRIES		20
78 #define NAPTIME			1
79 #define REAL_TIME		"1"
80 #define NO_FORK			"0"
81 #define USAGE "Usage:  %s  [-l log] [-t type] [-p priority] [-f] [-v] [-d]\n" \
82               "        -l log      log file                             \n" \
83               "        -t type     priority type 'variable' or 'fixed'  \n" \
84               "        -p priority priority value                       \n" \
85               "        -f          fork child                           \n" \
86               "        -v          verbose                              \n" \
87               "        -d          enable debugging messages            \n"
88 
89 /*
90  * Function prototypes:
91  *
92  * process_file: reads data file
93  *
94  * parse_args: parse command line arguments
95  */
96 void parse_args(int, char **);
97 int fork_realtime(char **);
98 int read_file(int, char *);
99 int lock_file(int, short, char *);
100 int unlock_file(int, char *);
101 int lock_error(int, char *);
102 
103 /*
104  * Global variables:
105  *
106  * verbose: enable normal messages
107  *
108  * debug: enable debugging messages
109  *
110  * priority: process type (fixed priority, variable priority)
111  */
112 int verbose = 0;
113 int debug = 0;
114 int fork_flag = 0;
115 int priority = DEFAULT_PRIORITY;
116 char *logfile = DEFAULT_LOGFILE;
117 char *priority_type = DEFAULT_PRIORITY_TYPE;
118 struct flock flock_struct;
119 struct flock *flock_ptr = &flock_struct;
120 
121 int open_file(char *, int);
122 
123 /*---------------------------------------------------------------------+
124 |                                 main                                 |
125 | ==================================================================== |
126 |                                                                      |
127 | Function:  ...                                                       |
128 |                                                                      |
129 +---------------------------------------------------------------------*/
main(int argc,char ** argv)130 int main(int argc, char **argv)
131 {
132 	char *filename = NULL;
133 	FILE *statfile;
134 	pid_t pid = 0;
135 	int fd;
136 	int rc;
137 	clock_t start_time;	/* start & stop times */
138 	clock_t stop_time;
139 	float elapsed_time;
140 #ifdef __linux__
141 	time_t timer_info;
142 #else
143 	struct tms timer_info;	/* time accounting info */
144 #endif
145 
146 	if ((filename = getenv("KERNEL")) == NULL) {
147 		errno = ENODATA;
148 		sys_error("environment variable KERNEL not set", __FILE__,
149 			  __LINE__);
150 	}
151 
152 	/* Process command line arguments...  */
153 	parse_args(argc, argv);
154 	if (verbose)
155 		printf("%s: Scheduler TestSuite program\n\n", *argv);
156 	if (debug) {
157 		printf("\tpriority type:  %s\n", priority_type);
158 		printf("\tpriority:       %d\n", priority);
159 		printf("\tlogfile:        %s\n", logfile);
160 	}
161 
162 	/* Adjust the priority of this process if the real time flag is set */
163 	if (!strcmp(priority_type, "fixed")) {
164 #ifndef __linux__
165 		if (setpri(0, DEFAULT_PRIORITY) < 0)
166 			sys_error("setpri failed", __FILE__, __LINE__);
167 #else
168 		if (setpriority(PRIO_PROCESS, 0, 0) < 0)
169 			sys_error("setpri failed", __FILE__, __LINE__);
170 #endif
171 	} else {
172 		if (nice((priority - 50) - (nice(0) + 20)) < 0 && errno != 0)
173 			sys_error("nice failed", __FILE__, __LINE__);
174 	}
175 
176 	/* Read from raw I/O device and record elapsed time...  */
177 	start_time = time(&timer_info);
178 
179 	/* Open and lock file file...  */
180 	fd = open_file(filename, O_RDWR);
181 	if (!lock_file(fd, F_WRLCK, filename))	/* set exclusive lock */
182 		error("lock_file failed", __FILE__, __LINE__);
183 
184 	/* If fork flag set, fork a real process */
185 	if (fork_flag)
186 		pid = fork_realtime(argv);
187 
188 	/* Read file */
189 	if (debug) {
190 		printf("\tprocess id %d successfully locked %s\n",
191 		       getpid(), filename);
192 		printf("\tprocess id %d starting to read %s\n",
193 		       getpid(), filename);
194 	}
195 
196 	if (!read_file(fd, filename))
197 		error("read_file failed", __FILE__, __LINE__);
198 
199 	/* Stop the timer and calculate the elapsed time */
200 	stop_time = time(&timer_info);
201 	elapsed_time = (float)(stop_time - start_time) / 100.0;
202 
203 	/* Write the elapsed time to the temporary file...  */
204 	if ((statfile = fopen(logfile, "w")) == NULL)
205 		sys_error("fopen failed", __FILE__, __LINE__);
206 
207 	fprintf(statfile, "%f\n", elapsed_time);
208 	if (debug)
209 		printf("\n\telapsed time: %f\n", elapsed_time);
210 
211 	if (fclose(statfile) < 0)
212 		sys_error("fclose failed", __FILE__, __LINE__);
213 
214 	/* Unlock file at latest possible time to prevent real time child from
215 	 * writing throughput results before user process parent */
216 	unlock_file(fd, filename);
217 	close(fd);
218 
219 	if (debug)
220 		printf("\tprocess id %d completed read and unlocked file\n",
221 		       getpid());
222 
223 	/* The parent waits for child process to complete before exiting
224 	 * so the driver will not read the throughput results file before
225 	 * child writes to the file */
226 	if (pid != 0) {		/* if parent process ... *//* wait for child process */
227 		if (debug)
228 			printf
229 			    ("parent waiting on child process %d to complete\n",
230 			     pid);
231 
232 		while ((rc = wait(NULL)) != pid)
233 			if (rc == -1)
234 				sys_error("wait failed", __FILE__, __LINE__);
235 /*
236 DARA: which one to use
237 1st -- hangs
238 2nd -- ERROR message
239 
240 	    while (wait((void *) 0) != pid) ;
241 	    while ((rc=wait ((void *) 0)) != pid)
242 	       if (rc == -1)
243 	          sys_error ("wait failed", __FILE__, __LINE__);
244 */
245 	}
246 
247 	/* Exit with success! */
248 	if (verbose)
249 		printf("\nsuccessful!\n");
250 	return (0);
251 }
252 
253 /*---------------------------------------------------------------------+
254 |                             open_file ()                             |
255 | ==================================================================== |
256 |                                                                      |
257 | Function:  ...                                                       |
258 |                                                                      |
259 +---------------------------------------------------------------------*/
open_file(char * file,int open_mode)260 int open_file(char *file, int open_mode)
261 {
262 	int file_desc;
263 
264 	if ((file_desc = open(file, open_mode)) < 0)
265 		sys_error("open failed", __FILE__, __LINE__);
266 	return (file_desc);
267 }
268 
269 /*---------------------------------------------------------------------+
270 |                           fork_realtime ()                           |
271 | ==================================================================== |
272 |                                                                      |
273 | Function:  ...                                                       |
274 |                                                                      |
275 +---------------------------------------------------------------------*/
fork_realtime(char ** args)276 int fork_realtime(char **args)
277 {
278 	int pid;
279 	char *results_file = args[2];
280 	char *priority = args[3];
281 
282 	/* fork process then determine if process is parent or child */
283 	pid = fork();
284 	switch (pid) {
285 		/* fork failed  */
286 	case -1:
287 		sys_error("fork failed", __FILE__, __LINE__);
288 
289 		/* child process */
290 	case 0:
291 		if (execl(*args, *args, REAL_TIME, results_file, priority,
292 			  NO_FORK, NULL) < 0)
293 			sys_error("execl failed", __FILE__, __LINE__);
294 
295 		/* parent process */
296 	default:
297 #ifdef DEBUG
298 		printf("\tparent process id = %d\n", getpid());
299 		printf("\tchild process id = %d\n\n", pid);
300 #endif
301 
302 		break;
303 	}
304 	return (pid);
305 }
306 
307 /*---------------------------------------------------------------------+
308 |                             read_file ()                             |
309 | ==================================================================== |
310 |                                                                      |
311 | Function:  ...                                                       |
312 |                                                                      |
313 +---------------------------------------------------------------------*/
read_file(int fd,char * filename)314 int read_file(int fd, char *filename)
315 {
316 	int bytes_read;
317 	int loop_count;
318 	long total_bytes;
319 	off_t lseek();
320 	off_t file_offset = 0;
321 	int whence = 0;
322 
323 	char buf[BLOCK_SIZE];
324 
325 	/* read file for "TIMES" number of times */
326 	total_bytes = 0;
327 	if (debug)
328 		printf("\n");
329 	for (loop_count = 1; loop_count <= TIMES; loop_count++) {
330 		while ((bytes_read = read(fd, buf, BLOCK_SIZE)) > 0) {
331 			if (bytes_read == -1) {
332 				sys_error("read failed", __FILE__, __LINE__);
333 			} else
334 				total_bytes = total_bytes + bytes_read;
335 		}
336 		if (lseek(fd, file_offset, whence) < 0)
337 			sys_error("lseek failed", __FILE__, __LINE__);
338 
339 		if (debug) {
340 			printf("\r\ttotal bytes read = %ld", total_bytes);
341 			fflush(stdout);
342 		}
343 		total_bytes = 0;
344 	}
345 	if (debug)
346 		printf("\n");
347 	return 1;
348 }
349 
350 /*---------------------------------------------------------------------+
351 |                             lock_file ()                             |
352 | ==================================================================== |
353 |                                                                      |
354 | Function:  ...                                                       |
355 |                                                                      |
356 +---------------------------------------------------------------------*/
lock_file(int fd,short lock_type,char * file)357 int lock_file(int fd, short lock_type, char *file)
358 {
359 	int lock_attempt = 1;
360 	int lock_mode;
361 
362 #ifdef DEBUG
363 	lock_mode = F_SETLK;	/* return if lock fails        */
364 #else
365 	lock_mode = F_SETLKW;	/* set lock and use system wait */
366 #endif
367 
368 	/* file segment locking set data type flock - information
369 	 * passed to system by user --
370 	 *   l_whence:  starting point of relative offset of file
371 	 *   l_start:   defines relative offset in bytes from l_whence
372 	 *   l_len:     number of consecutive bytes to be locked
373 	 */
374 	flock_ptr->l_whence = 0;
375 	flock_ptr->l_start = 0;
376 	flock_ptr->l_len = 0;
377 	flock_ptr->l_type = lock_type;
378 
379 	while (fcntl(fd, lock_mode, flock_ptr) == -1) {
380 		if (lock_error(fd, file)) {
381 			sleep(NAPTIME);
382 			if (++lock_attempt > MAX_TRIES) {
383 				printf
384 				    ("ERROR: max lock attempts of %d reached\n",
385 				     MAX_TRIES);
386 				return (0);
387 			}
388 		} else
389 			return (0);
390 	}
391 	return (1);
392 }
393 
394 /*---------------------------------------------------------------------+
395 |                            unlock_file ()                            |
396 | ==================================================================== |
397 |                                                                      |
398 | Function:  ...                                                       |
399 |                                                                      |
400 +---------------------------------------------------------------------*/
unlock_file(int fd,char * file)401 int unlock_file(int fd, char *file)
402 {
403 	flock_ptr->l_type = F_UNLCK;
404 
405 	if (fcntl(fd, F_SETLK, flock_ptr) < 0)
406 		sys_error("fcntl failed", __FILE__, __LINE__);
407 
408 	return 1;
409 }
410 
411 /*---------------------------------------------------------------------+
412 |                                 main                                 |
413 | ==================================================================== |
414 |                                                                      |
415 | Function:  ...                                                       |
416 |                                                                      |
417 +---------------------------------------------------------------------*/
lock_error(int fd,char * file)418 int lock_error(int fd, char *file)
419 {
420 	int ret = 1;
421 
422 	printf("ERROR:  lock failed: %s\n", file);
423 	switch (errno) {
424 	case EACCES:		/* access not allowed */
425 		fcntl(fd, F_GETLK, flock_ptr);
426 #ifndef __linux__
427 		printf("ERROR: lock exists - nid: %lX pid: %ld\n",
428 		       flock_ptr->l_sysid, flock_ptr->l_pid);
429 #else
430 		printf("ERROR: lock exists - nid:\n");
431 #endif
432 		break;
433 
434 		/*
435 		 * This was a DS error code, and DS does not exist V3.1
436 		 */
437 #ifndef __linux__
438 	case EDIST:		/* DS file server blocking requests */
439 		printf("ERROR: errno == EDIST\n");
440 		printf("The server has blocked new inbound requests\n");
441 		printf("or outbound requests are currently blocked.\n");
442 		break;
443 #endif
444 
445 	case EAGAIN:		/* server too busy */
446 		printf("ERROR:  Server too busy to accept the request\n");
447 		break;
448 
449 	case EDEADLK:		/* only when F_SETLKW cmd is used */
450 		printf("ERROR:  Putting the calling process to sleep\n");
451 		printf("would cause a dead lock\n");
452 		ret = 0;
453 		break;
454 
455 	case ENOLCK:		/* out of locks */
456 		printf("ERROR: No more file locks available\n");
457 		ret = 0;
458 		break;
459 
460 	case ENOMEM:		/* out of memory */
461 		printf("ERROR: The server or client does not have enough\n");
462 		printf("memory to service the request.\n");
463 		ret = 0;
464 		break;
465 
466 	default:		/* miscellaneous errors */
467 		printf("ERROR: Unknown lock error\n");
468 		perror("reason");
469 		ret = 0;
470 		break;
471 	}
472 
473 	printf("errno = %d\n", errno);	/* log the errno value */
474 	sleep(10);
475 
476 	return (ret);
477 }
478 
479 /*---------------------------------------------------------------------+
480 |                             parse_args ()                            |
481 | ==================================================================== |
482 |                                                                      |
483 | Function:  Parse the command line arguments & initialize global      |
484 |            variables.                                                |
485 |                                                                      |
486 | Updates:   (command line options)                                    |
487 |                                                                      |
488 |            [-t] type:     priority type "fixed" or "variable"        |
489 |            [-p] priority: priority value                             |
490 |            [-l] logfile:  log file name                              |
491 |            [-v]           verbose                                    |
492 |            [-d]           enable debugging messages                  |
493 |                                                                      |
494 +---------------------------------------------------------------------*/
parse_args(int argc,char ** argv)495 void parse_args(int argc, char **argv)
496 {
497 	int opt;
498 	int lflg = 0, pflg = 0, tflg = 0;
499 	int errflag = 0;
500 	char *program_name = *argv;
501 	extern char *optarg;	/* Command line option */
502 
503 	/*
504 	 * Parse command line options.
505 	 */
506 	while ((opt = getopt(argc, argv, "fl:t:p:vd")) != EOF) {
507 		switch (opt) {
508 		case 'f':	/* fork flag */
509 			fork_flag++;
510 			break;
511 		case 'l':	/* log file */
512 			lflg++;
513 			logfile = optarg;
514 			break;
515 		case 't':	/* process type */
516 			tflg++;
517 			priority_type = optarg;
518 			break;
519 		case 'p':	/* process priority */
520 			pflg++;
521 			priority = atoi(optarg);
522 			break;
523 		case 'v':	/* verbose */
524 			verbose++;
525 			break;
526 		case 'd':	/* enable debugging messages */
527 			verbose++;
528 			debug++;
529 			break;
530 		default:
531 			errflag++;
532 			break;
533 		}
534 	}
535 
536 	/*
537 	 * Check percentage and process slots...
538 	 */
539 	if (tflg) {
540 		if (strcmp(priority_type, "fixed") &&
541 		    strcmp(priority_type, "variable")) {
542 			errflag++;
543 			fprintf(stderr, "Error: priority type must be: "
544 				"\'fixed\' or \'variable\'\n");
545 		}
546 	}
547 	if (pflg) {
548 		if (priority < 50 || priority > 100) {
549 			errflag++;
550 			fprintf(stderr, "Error: priority range [50..100]\n");
551 		}
552 	}
553 	if (errflag) {
554 		fprintf(stderr, USAGE, program_name);
555 		exit(2);
556 	}
557 }
558