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