/* * Copyright (c) International Business Machines Corp., 2001 * * 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 */ /* * FUNCTIONS: Scheduler Test Suite */ /*---------------------------------------------------------------------+ | sched_tc6 | | ==================================================================== | | | | Description: Creates short-term disk I/O bound process | | | | Creates a situation where a real time process is | | waiting on a file lock owned by a user process. The | | scheduler should elevate the user process priority to | | the same level as the real time process to avoid | | deadlock situations and to decrease wait time | | required of the real time process. | | | | Algorithm: o Set process priority | | o Continuously multiply matrices together until | | interrupted. | | | | To compile: cc -o sched_tc6 sched_tc6.c -L. -lpsc | | | | Usage: sched_tc6 [-t priority_type] [-p priority] | | [-l log] [-f] [-v] [-d] | | | | Last update: Ver. 1.3, 4/10/94 23:05:03 | | | | Change Activity | | | | Version Date Name Reason | | 0.1 050689 CTU Initial version | | 0.2 010402 Manoj Iyer Ported to Linux | | | +---------------------------------------------------------------------*/ #include #include #include #include #include #include "sched.h" /* * Defines: * * USAGE: usage statement * * DEFAULT_PRIORITY_TYPE: default priority * * BLOCK_SIZE: block size (in bytes) for raw I/O * * TIMES: number of times to read raw I/O device (~25MB) * */ #define DEFAULT_PRIORITY_TYPE "variable" #define DEFAULT_LOGFILE "sched_tc6.log" #define BLOCK_SIZE 512 #define TIMES 10 #define MAX_TRIES 20 #define NAPTIME 1 #define REAL_TIME "1" #define NO_FORK "0" #define USAGE "Usage: %s [-l log] [-t type] [-p priority] [-f] [-v] [-d]\n" \ " -l log log file \n" \ " -t type priority type 'variable' or 'fixed' \n" \ " -p priority priority value \n" \ " -f fork child \n" \ " -v verbose \n" \ " -d enable debugging messages \n" /* * Function prototypes: * * process_file: reads data file * * parse_args: parse command line arguments */ void parse_args(int, char **); int fork_realtime(char **); int read_file(int, char *); int lock_file(int, short, char *); int unlock_file(int, char *); int lock_error(int, char *); /* * Global variables: * * verbose: enable normal messages * * debug: enable debugging messages * * priority: process type (fixed priority, variable priority) */ int verbose = 0; int debug = 0; int fork_flag = 0; int priority = DEFAULT_PRIORITY; char *logfile = DEFAULT_LOGFILE; char *priority_type = DEFAULT_PRIORITY_TYPE; struct flock flock_struct; struct flock *flock_ptr = &flock_struct; int open_file(char *, int); /*---------------------------------------------------------------------+ | main | | ==================================================================== | | | | Function: ... | | | +---------------------------------------------------------------------*/ int main(int argc, char **argv) { char *filename = NULL; FILE *statfile; pid_t pid = 0; int fd; int rc; clock_t start_time; /* start & stop times */ clock_t stop_time; float elapsed_time; #ifdef __linux__ time_t timer_info; #else struct tms timer_info; /* time accounting info */ #endif if ((filename = getenv("KERNEL")) == NULL) { errno = ENODATA; sys_error("environment variable KERNEL not set", __FILE__, __LINE__); } /* Process command line arguments... */ parse_args(argc, argv); if (verbose) printf("%s: Scheduler TestSuite program\n\n", *argv); if (debug) { printf("\tpriority type: %s\n", priority_type); printf("\tpriority: %d\n", priority); printf("\tlogfile: %s\n", logfile); } /* Adjust the priority of this process if the real time flag is set */ if (!strcmp(priority_type, "fixed")) { #ifndef __linux__ if (setpri(0, DEFAULT_PRIORITY) < 0) sys_error("setpri failed", __FILE__, __LINE__); #else if (setpriority(PRIO_PROCESS, 0, 0) < 0) sys_error("setpri failed", __FILE__, __LINE__); #endif } else { if (nice((priority - 50) - (nice(0) + 20)) < 0 && errno != 0) sys_error("nice failed", __FILE__, __LINE__); } /* Read from raw I/O device and record elapsed time... */ start_time = time(&timer_info); /* Open and lock file file... */ fd = open_file(filename, O_RDWR); if (!lock_file(fd, F_WRLCK, filename)) /* set exclusive lock */ error("lock_file failed", __FILE__, __LINE__); /* If fork flag set, fork a real process */ if (fork_flag) pid = fork_realtime(argv); /* Read file */ if (debug) { printf("\tprocess id %d successfully locked %s\n", getpid(), filename); printf("\tprocess id %d starting to read %s\n", getpid(), filename); } if (!read_file(fd, filename)) error("read_file failed", __FILE__, __LINE__); /* Stop the timer and calculate the elapsed time */ stop_time = time(&timer_info); elapsed_time = (float)(stop_time - start_time) / 100.0; /* Write the elapsed time to the temporary file... */ if ((statfile = fopen(logfile, "w")) == NULL) sys_error("fopen failed", __FILE__, __LINE__); fprintf(statfile, "%f\n", elapsed_time); if (debug) printf("\n\telapsed time: %f\n", elapsed_time); if (fclose(statfile) < 0) sys_error("fclose failed", __FILE__, __LINE__); /* Unlock file at latest possible time to prevent real time child from * writing throughput results before user process parent */ unlock_file(fd, filename); close(fd); if (debug) printf("\tprocess id %d completed read and unlocked file\n", getpid()); /* The parent waits for child process to complete before exiting * so the driver will not read the throughput results file before * child writes to the file */ if (pid != 0) { /* if parent process ... *//* wait for child process */ if (debug) printf ("parent waiting on child process %d to complete\n", pid); while ((rc = wait(NULL)) != pid) if (rc == -1) sys_error("wait failed", __FILE__, __LINE__); /* DARA: which one to use 1st -- hangs 2nd -- ERROR message while (wait((void *) 0) != pid) ; while ((rc=wait ((void *) 0)) != pid) if (rc == -1) sys_error ("wait failed", __FILE__, __LINE__); */ } /* Exit with success! */ if (verbose) printf("\nsuccessful!\n"); return (0); } /*---------------------------------------------------------------------+ | open_file () | | ==================================================================== | | | | Function: ... | | | +---------------------------------------------------------------------*/ int open_file(char *file, int open_mode) { int file_desc; if ((file_desc = open(file, open_mode)) < 0) sys_error("open failed", __FILE__, __LINE__); return (file_desc); } /*---------------------------------------------------------------------+ | fork_realtime () | | ==================================================================== | | | | Function: ... | | | +---------------------------------------------------------------------*/ int fork_realtime(char **args) { int pid; char *results_file = args[2]; char *priority = args[3]; /* fork process then determine if process is parent or child */ pid = fork(); switch (pid) { /* fork failed */ case -1: sys_error("fork failed", __FILE__, __LINE__); /* child process */ case 0: if (execl(*args, *args, REAL_TIME, results_file, priority, NO_FORK, NULL) < 0) sys_error("execl failed", __FILE__, __LINE__); /* parent process */ default: #ifdef DEBUG printf("\tparent process id = %d\n", getpid()); printf("\tchild process id = %d\n\n", pid); #endif break; } return (pid); } /*---------------------------------------------------------------------+ | read_file () | | ==================================================================== | | | | Function: ... | | | +---------------------------------------------------------------------*/ int read_file(int fd, char *filename) { int bytes_read; int loop_count; long total_bytes; off_t lseek(); off_t file_offset = 0; int whence = 0; char buf[BLOCK_SIZE]; /* read file for "TIMES" number of times */ total_bytes = 0; if (debug) printf("\n"); for (loop_count = 1; loop_count <= TIMES; loop_count++) { while ((bytes_read = read(fd, buf, BLOCK_SIZE)) > 0) { if (bytes_read == -1) { sys_error("read failed", __FILE__, __LINE__); } else total_bytes = total_bytes + bytes_read; } if (lseek(fd, file_offset, whence) < 0) sys_error("lseek failed", __FILE__, __LINE__); if (debug) { printf("\r\ttotal bytes read = %ld", total_bytes); fflush(stdout); } total_bytes = 0; } if (debug) printf("\n"); return 1; } /*---------------------------------------------------------------------+ | lock_file () | | ==================================================================== | | | | Function: ... | | | +---------------------------------------------------------------------*/ int lock_file(int fd, short lock_type, char *file) { int lock_attempt = 1; int lock_mode; #ifdef DEBUG lock_mode = F_SETLK; /* return if lock fails */ #else lock_mode = F_SETLKW; /* set lock and use system wait */ #endif /* file segment locking set data type flock - information * passed to system by user -- * l_whence: starting point of relative offset of file * l_start: defines relative offset in bytes from l_whence * l_len: number of consecutive bytes to be locked */ flock_ptr->l_whence = 0; flock_ptr->l_start = 0; flock_ptr->l_len = 0; flock_ptr->l_type = lock_type; while (fcntl(fd, lock_mode, flock_ptr) == -1) { if (lock_error(fd, file)) { sleep(NAPTIME); if (++lock_attempt > MAX_TRIES) { printf ("ERROR: max lock attempts of %d reached\n", MAX_TRIES); return (0); } } else return (0); } return (1); } /*---------------------------------------------------------------------+ | unlock_file () | | ==================================================================== | | | | Function: ... | | | +---------------------------------------------------------------------*/ int unlock_file(int fd, char *file) { flock_ptr->l_type = F_UNLCK; if (fcntl(fd, F_SETLK, flock_ptr) < 0) sys_error("fcntl failed", __FILE__, __LINE__); return 1; } /*---------------------------------------------------------------------+ | main | | ==================================================================== | | | | Function: ... | | | +---------------------------------------------------------------------*/ int lock_error(int fd, char *file) { int ret = 1; printf("ERROR: lock failed: %s\n", file); switch (errno) { case EACCES: /* access not allowed */ fcntl(fd, F_GETLK, flock_ptr); #ifndef __linux__ printf("ERROR: lock exists - nid: %lX pid: %ld\n", flock_ptr->l_sysid, flock_ptr->l_pid); #else printf("ERROR: lock exists - nid:\n"); #endif break; /* * This was a DS error code, and DS does not exist V3.1 */ #ifndef __linux__ case EDIST: /* DS file server blocking requests */ printf("ERROR: errno == EDIST\n"); printf("The server has blocked new inbound requests\n"); printf("or outbound requests are currently blocked.\n"); break; #endif case EAGAIN: /* server too busy */ printf("ERROR: Server too busy to accept the request\n"); break; case EDEADLK: /* only when F_SETLKW cmd is used */ printf("ERROR: Putting the calling process to sleep\n"); printf("would cause a dead lock\n"); ret = 0; break; case ENOLCK: /* out of locks */ printf("ERROR: No more file locks available\n"); ret = 0; break; case ENOMEM: /* out of memory */ printf("ERROR: The server or client does not have enough\n"); printf("memory to service the request.\n"); ret = 0; break; default: /* miscellaneous errors */ printf("ERROR: Unknown lock error\n"); perror("reason"); ret = 0; break; } printf("errno = %d\n", errno); /* log the errno value */ sleep(10); return (ret); } /*---------------------------------------------------------------------+ | parse_args () | | ==================================================================== | | | | Function: Parse the command line arguments & initialize global | | variables. | | | | Updates: (command line options) | | | | [-t] type: priority type "fixed" or "variable" | | [-p] priority: priority value | | [-l] logfile: log file name | | [-v] verbose | | [-d] enable debugging messages | | | +---------------------------------------------------------------------*/ void parse_args(int argc, char **argv) { int opt; int lflg = 0, pflg = 0, tflg = 0; int errflag = 0; char *program_name = *argv; extern char *optarg; /* Command line option */ /* * Parse command line options. */ while ((opt = getopt(argc, argv, "fl:t:p:vd")) != EOF) { switch (opt) { case 'f': /* fork flag */ fork_flag++; break; case 'l': /* log file */ lflg++; logfile = optarg; break; case 't': /* process type */ tflg++; priority_type = optarg; break; case 'p': /* process priority */ pflg++; priority = atoi(optarg); break; case 'v': /* verbose */ verbose++; break; case 'd': /* enable debugging messages */ verbose++; debug++; break; default: errflag++; break; } } /* * Check percentage and process slots... */ if (tflg) { if (strcmp(priority_type, "fixed") && strcmp(priority_type, "variable")) { errflag++; fprintf(stderr, "Error: priority type must be: " "\'fixed\' or \'variable\'\n"); } } if (pflg) { if (priority < 50 || priority > 100) { errflag++; fprintf(stderr, "Error: priority range [50..100]\n"); } } if (errflag) { fprintf(stderr, USAGE, program_name); exit(2); } }