| /* |
| * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 <fcntl.h> |
| #include <stdlib.h> |
| #include <sys/time.h> |
| #include <time.h> |
| #include <sys/resource.h> |
| #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 ((void *) 0)) != 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, (char *) 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); |
| } |
| } |