/**************************************************************************** * * * COPYRIGHT (c) 1990 - 2004 * * This Software Provided * * By * * Robin's Nest Software Inc. * * * * Permission to use, copy, modify, distribute and sell this software and * * its documentation for any purpose and without fee is hereby granted, * * provided that the above copyright notice appear in all copies and that * * both that copyright notice and this permission notice appear in the * * supporting documentation, and that the name of the author not be used * * in advertising or publicity pertaining to distribution of the software * * without specific, written prior permission. * * * * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN * * NO EVENT SHALL HE BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL * * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF * * THIS SOFTWARE. * * * ****************************************************************************/ /* * Module: dtprocs.c * Author: Robin T. Miller * Date: August 7, 1993 * * Description: * Functions to handle multiple processes for 'dt' program. * * Modification History: * * November 17th, 2003 by Robin Miller. * Breakup output to stdout or stderr, rather than writing * all output to stderr. If output file is stdout ('-') or a log * file is specified, then all output reverts to stderr. * * March 14th, 2003 by Robin Miller. * Added support for testing an individual slice. * * May 31st, 2001 by Robin Miller. * Update abort_procs() to use max_procs, valid for both multiple * processes and slices, and loop through entire process table since * some processes may have finished already (use to break out early). * When starting slices with multiple processes, prime the data * pattern to ensure each process uses the same pattern in a slice. * * February 6th, 2001 by Robin Miller. * If doing multiple slices in reverse direction, ensure the data * limit gets tot he slice length, or we'll overflow into previous slice. * * February 1st, 2001 by Robin Miller. * Fix dumb problem starting multiple procs with multiple slices. * * January 28th, 2001 by Robin Miller. * When aborting processes, send SIGINT instead of SIGTERM, so * statistics gets reported (important on long running commands :-). * Added support for multiple slices option. This sets up each * process exercising a different range of blocks (slice) on the disk. * * January 2nd, 2001 by Robin Miller. * Make changes to build using MKS/NuTCracker product. * * March 28th, 2000 by Robin Miller. * When creating multiple processes, do a better job determining * when a unique device name should be constructed. Was broken for * named piped (FIFO's). * * May 3, 1999 by Robin Miller. * Allocate more space for unique file names, since the size * of pid_t is now 31 bits in Steel. * * April 8, 1999 by Robin Miller. * Merge in Jeff Detjen's changes for current process count. * * April 29, 1998 by Robin Miller. * Add support for an alternate device directory. * * July 17, 1995 by Robin Miller. * Apply a severity priority to child exit status. * Added flag to ensure process abortion occurs only once. * * July 5, 1995 by Robin Miller. * Properly check for child process exiting as result of a signal. * * March 28, 1995 by Robin Miller. * Report specific error for "no processes started", and exit with * error status if the system process limit has been exceeded. * * November 4, 1994 by Robin Miller. * Don't set SIGCHLD signal to SIG_IGN (set to SIG_DFL) or else * waitpid() won't detect any child processes (OSF R1.3 and QNX). * [ Unfortunately, the POSIX standard states "The specification * of the effects of SIG_IGN on SIGCHLD as implementation defined * and permits, but does NOT require, the System V effect of * causing terminating children to be ignored by wait(). Yuck!!! ] */ #include "dt.h" #include #include #include #define PROC_ALLOC (sizeof(pid_t) * 3) /* Extra allocation for PID. */ /* * Structure to track multiple processes. */ struct dt_procs { pid_t dt_pid; /* The child process ID. */ int dt_status; /* The child exit status. */ bool dt_active; /* The process active flag. */ }; /* * Slice Range Definition: */ typedef struct slice_info { int slice; /* Slice number. */ large_t slice_position; /* Starting slice position. */ large_t slice_length; /* The slice data length. */ } slice_info_t; /* * Forward References: */ static void init_slice_info(struct dinfo *dip, slice_info_t *sip, large_t *data_resid); static void setup_slice(struct dinfo *dip, slice_info_t *sip); struct dt_procs *ptable; /* Multiple 'dt' procs table. */ int num_procs = 0; /* Number of procs to create. */ int cur_proc = 0; /* Current count of processes. */ int max_procs = 0; /* Maximum processes started. */ int procs_active = 0; /* Number of active processes. */ int num_slices = 0; /* Number of slices to create. */ int slice_num = 0; /* Slice number to operate on. */ /* * abort_procs - Abort processes started by the parent. */ void abort_procs(void) { static int aborted_processes = FALSE; struct dt_procs *dtp; int procs; pid_t pid; if ((ptable == NULL) || aborted_processes) return; /* * Force all processes to terminate. */ for (dtp = ptable, procs=0; procs < max_procs; procs++, dtp++) { if ((pid = dtp->dt_pid) == (pid_t) 0) continue; if (debug_flag) { Printf("Aborting child process %d via a SIGINT (%d)...\n", pid, SIGINT); } if (dtp->dt_active) (void) kill (pid, SIGINT); } aborted_processes = TRUE; } void await_procs(void) { pid_t wpid; struct dt_procs *dtp; int procs, status; if (debug_flag) { Printf ("Waiting for %d child processes to complete...\n", procs_active); } while (1) { if ((wpid = waitpid ((pid_t) -1, &child_status, 0)) == FAILURE) { if (errno == ECHILD) { if (procs_active) abort(); /* Programming error... */ break; /* No more children... */ } else if (errno == EINTR) { abort_procs(); continue; } else { report_error ("waitpid", FALSE); exit (FATAL_ERROR); } } /* * Examine the child process status. */ if ( WIFSTOPPED(child_status) ) { Printf ("Child process %d, stopped by signal %d.\n", wpid, WSTOPSIG(child_status)); continue; /* Maybe attached from debugger... */ } else if ( WIFSIGNALED(child_status) ) { status = WTERMSIG(child_status); Fprintf ("Child process %d, exiting because of signal %d\n", wpid, status); } else { /* Process must be exiting... */ status = WEXITSTATUS (child_status); if (debug_flag) { Printf ("Child process %d, exiting with status %d\n", wpid, status); } } if ( (exit_status == SUCCESS) && (status != SUCCESS) ) { if ( (oncerr_action == ABORT) && (status != WARNING) && (status != END_OF_FILE) ) { abort_procs(); /* Abort procs on error. */ } /* * Save the most sever error for parent exit status. * * Severity Priorities: WARNING (lowest) * END_OF_FILE * Signal Number * FATAL_ERROR (highest) */ if ( ((exit_status == SUCCESS) || (status == FATAL_ERROR)) || ((exit_status == WARNING) && (status > WARNING)) || ((exit_status == END_OF_FILE) && (status > WARNING)) ) { exit_status = status; /* Set error code for exit. */ } } /* * House keeping... (mostly sanity check, not really necessary). */ for (dtp = ptable, procs = 0; procs < max_procs; procs++, dtp++) { if (dtp->dt_pid == wpid) { dtp->dt_active = FALSE; dtp->dt_status = status; procs_active--; } } } /* End of while(1)... */ } pid_t fork_process(void) { pid_t pid; if ((pid = fork()) == (pid_t) -1) { if (errno == EAGAIN) { if (procs_active == 0) { LogMsg (efp, logLevelCrit, 0, "ERROR: could NOT start any processes, please check your system...\n"); exit (FATAL_ERROR); } else { Printf ( "WARNING: system imposed process limit reached, only %d procs started...\n", procs_active); } } else { report_error ("fork", FALSE); abort_procs(); } } return (pid); } pid_t start_procs(void) { struct dt_procs *dtp; size_t psize; int procs; max_procs = num_procs; psize = (max_procs * sizeof(*dtp)); if ((ptable = (struct dt_procs *)malloc(psize)) == NULL) { report_error ("No memory for proc table", FALSE); exit (FATAL_ERROR); } bzero((char *)ptable, psize); #if !defined(__MSDOS__) || defined(__NUTC__) (void) signal (SIGCHLD, SIG_DFL); #endif (void) signal (SIGHUP, terminate); (void) signal (SIGINT, terminate); (void) signal (SIGTERM, terminate); cur_proc = 1; procs_active = 0; for (dtp = ptable, procs = 0; procs < max_procs; procs++, dtp++) { if ((child_pid = fork_process()) == (pid_t) -1) { break; } else if (child_pid) { /* Parent process gets the PID. */ cur_proc++; dtp->dt_pid = child_pid; dtp->dt_active = TRUE; procs_active++; if (debug_flag) { Printf ("Started Process %d...\n", child_pid); } } else { /* Child process... */ struct stat sb; bool make_unique = FALSE; int error; if (!output_file) break; error = stat (output_file, &sb); if (!error) { if ( S_ISREG(sb.st_mode) ) { make_unique = TRUE; } /* Leave all other types alone! */ } else if ( (NEL (output_file, DEV_PREFIX, DEV_LEN)) && (NEL (output_file, ADEV_PREFIX, ADEV_LEN)) ) { make_unique = TRUE; /* Ok, not a device directory. */ } /* * Construct unique file name for file system I/O. */ if (make_unique) { char *bp; bp = (char *)malloc(strlen(output_file) + PROC_ALLOC); (void)sprintf(bp, "%s-%d", output_file, getpid()); output_dinfo->di_dname = output_file = bp; } break; /* Child process, continue... */ } } return (child_pid); } pid_t start_slices(void) { struct dinfo *dip = active_dinfo; struct dt_procs *dtp; size_t psize; struct slice_info slice_info; slice_info_t *sip = &slice_info; large_t data_resid; int procs; max_procs = num_slices; psize = (max_procs * sizeof(*dtp)); if ((ptable = (struct dt_procs *)malloc(psize)) == NULL) { report_error ("No memory for proc table", FALSE); exit (FATAL_ERROR); } bzero((char *)ptable, psize); #if !defined(__MSDOS__) || defined(__NUTC__) (void) signal (SIGCHLD, SIG_DFL); #endif (void) signal (SIGHUP, terminate); (void) signal (SIGINT, terminate); (void) signal (SIGTERM, terminate); init_slice_info(dip, sip, &data_resid); cur_proc = 1; procs_active = 0; for (dtp = ptable, procs = 0; procs < max_procs; procs++, dtp++) { sip->slice++; if ((child_pid = fork_process()) == (pid_t) -1) { break; } else if (child_pid) { /* Parent process gets the PID. */ cur_proc++; dtp->dt_pid = child_pid; dtp->dt_active = TRUE; procs_active++; if (debug_flag) { Printf ("Started Slice %d, PID %d...\n", sip->slice, child_pid); } if (procs < max_procs) { sip->slice_position += sip->slice_length; if (procs == max_procs) { sip->slice_length += data_resid; } } } else { /* Child process... */ /* * Initialize the starting data pattern for each slice. */ if (unique_pattern) { pattern = data_patterns[(cur_proc - 1) % npatterns]; } setup_slice(dip, sip); break; /* Child process, continue... */ } } return (child_pid); } void init_slice(struct dinfo *dip, int slice) { struct slice_info slice_info; slice_info_t *sip = &slice_info; large_t data_resid; init_slice_info(dip, sip, &data_resid); sip->slice_position += (sip->slice_length * (slice - 1)); /* * Any residual goes to the last slice. */ if (slice == num_slices) { sip->slice_length += data_resid; } sip->slice = slice; setup_slice(dip, sip); /* * Initialize the starting data pattern for each slice. */ if (unique_pattern) { pattern = data_patterns[(slice - 1) % npatterns]; } return; } static void init_slice_info(struct dinfo *dip, slice_info_t *sip, large_t *data_resid) { large_t slice_length; sip->slice = 0; sip->slice_position = file_position; slice_length = ((dip->di_data_limit - file_position) / num_slices); sip->slice_length = rounddown(slice_length, dip->di_dsize); if (sip->slice_length < dip->di_dsize) { LogMsg (efp, logLevelCrit, 0, "Slice length of " LUF " bytes is too small!\n", sip->slice_length); exit (FATAL_ERROR); } *data_resid = (dip->di_data_limit - (sip->slice_length * num_slices)); *data_resid = rounddown(*data_resid, dip->di_dsize); return; } static void setup_slice(struct dinfo *dip, slice_info_t *sip) { file_position = sip->slice_position; if (dip->di_random_io) { rdata_limit = (file_position + sip->slice_length); } /* * Restrict data limit to slice length or user set limit. */ data_limit = MIN(data_limit, sip->slice_length); if (debug_flag || Debug_flag) { large_t dlimit = (dip->di_random_io) ? rdata_limit : data_limit; Printf("Slice %d Information:\n" "\t\t Start: " FUF " offset (lba %u)\n" "\t\t End: " FUF " offset (lba %u)\n" "\t\tLength: " FUF " bytes (%d blocks)\n" "\t\t Limit: " FUF " bytes (%d blocks)\n", sip->slice, file_position, (u_int32)(file_position / dip->di_dsize), (file_position + sip->slice_length), ((file_position + sip->slice_length) / dip->di_dsize), sip->slice_length, (sip->slice_length / dip->di_dsize), dlimit, (dlimit / dip->di_dsize)); } return; }