blob: 971adf9e54fee8fa255d31e888e1311efb48a161 [file] [log] [blame]
/*
* 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);
}
}