/**************************************************************************** * * * 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: dtaio.c * Author: Robin T. Miller * Date: August 26, 1993 * * Description: * Functions to handle POSIX Asynchronous I/O requests for 'dt' program. */ #if defined(AIO) #include "dt.h" #include #include #include #if !defined(AIO_PRIO_DFL) # define AIO_PRIO_DFL 0 /* Default scheduling priority. */ #endif /* !defined(AIO_PRIO_DFL) */ /* * Modification History: * * October 21st, 2004 by Robin Miller. * For variable record lengths, ensure we prime the first size * to ensure it meets device size alignment requirements. * * February 13th, 2004 by Robin Miller. * Factor in the file position when doing reverse I/O, to avoid * writing/reading into that area (which is now deemed protected). * * 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. * * January 17th, 2003 by Robin Miller. * On HP-UX, accept ENXIO for I/O's pass EOF. * * November 14th, 2002 by Robin Miller. * On HP-UX, initialize aiocb->aio_sigevent field, or else * EINVAL is returned. aio_sigevent.sigev_notify = SIGEV_NONE; * * June 25th, 2001 by Robin Miller. * Restructured code associated with Tru64 Unix EEI, so we obtain * the EEI status for all tape errors, not just EIO errors. * * January 26th, 2001 by Robin Miller. * Added support for reverse reading and writing. * * January 24th, 2001 by Robin Miller. * Add support for variable I/O requests sizes. * * January 14th, 2001 by Robin Miller. * Added support for multiple volumes option. * Fixed multi-volume write w/lbdata option problem. * * October 4th, 2000 by Robin Miller. * Update is_Eof() to accept ENXIO for AIO reads @ EOM on * SCO UnixWare systems. All other systems return ENOSPC or zero. * * May 9th, 2000 by Robin Miller. * Ensure the closing flag gets reset in dtaio_close_file() before * calling close_file(), or the file descriptor won't get closed! * * May 8th, 2000 by Robin Miller. * Honor the di_closing flag, to avoid a race condition with the * close function being called again while still closing, from the * terminate() routine called by the runtime= alarm, or signals. * * February 17th, 2000 by Robin Miller. * Adding better support for multi-volume tape testing. Mainly, * make it work with writing multiple tape files, rather than one file. * * January 6th, 2000 by Robin Miller. * Added support for multi-volume media. * Added a couple missing aio_return() calls. * * December 30th, 1999 by Robin Miller. * Modify call to do_random() to pass the transfer size. * * November 10th, 1999 by Robin Miller. * If aio_return() fails, report device information. * * August 7th, 1999 by Robin Miller. * Minor mods to support AIO on SCO UnixWare 7.1. * * July 29, 1999 by Robin Miller. * Merge in changes made to compile on FreeBSD. * * July 22nd, 1999 by Robin Miller. * o Added support for IOT (DJ's) test pattern. * o Fixed problem writing wrong starting lba, when lbdata * and random I/O options were enabled. * * July 5th, 1999 by Robin Miller. * Cleanup of compilation warnings on Linux. * * May 27, 1999 by Robin Miller. * Added support for micro-second delays. * * March 1, 1999 by Robin Miller. * For tapes when Debug is enabled, report the file number. * * January 13, 1998 by Robin Miller. * Add support for restarting I/O's after EEI reset recovery. * Modified dtaio_waitall() to optionally adjust data/file counts. * * December 21, 1998 by Robin Miller. * Updates necessary to match tape API changes. * * November 16, 1998 by Robin Miller. * Added pointer to current AIO control block for error reporting. * * October 26, 1998 by Robin Miller. * o Fix incorrect record number displayed when Debug is enabled. * o Don't exit read/write loops when processing partial records. * o Fix problem in write function, where short write processing, * caused us not to write sufficent data bytes (actually, the * file loop in write_file() caused dtaio_write_data() to be * called again, and we'd actually end up writing too much! * o When random I/O and lbdata options are both enabled, use the * file offset seeked to as the starting lbdata address. * * March 20, 1998 by Robin Miller. * Update counts in dtaio_waitall() for accurate statistics. * * January 28, 1998 by Robin Miller. * Add dtaio_close() function, to wait for queued I/O's when we're * aborting, to avoid kernel I/O rundown problem, which panic's * the system if we close the CAM disk driver device descriptor, * prior to AIO's completing (fixed in steelos by Anton Verhulst). * * January 9, 1998 by Robin Miller. * Don't initialize data buffer being written for "disable=compare" * which yields better performance. * * April 3, 1997 by Robin Miller. * Removed use of undocumented AIO_SEEK_CUR in aio_offset. * Also fixed bug where random I/O offset was clobbered, thus * resulting in sequential I/O. * * February 28, 1996 by Robin Miller. * Added support for copying and verifying device/files. * Modified logic so read errors honor users' error limit. * [ NOTE: Copy and verify operations are done sequentially. ] * * November 11, 1995 by Robin Miller. * Fix bug with init'ing and performing pad byte verification. * This caused variable length reads with small increment values * to report an (invalid) pad byte data compare error. e.g.: * * % dt of=/dev/rmt0h min=10k max=64k incr=1 pattern=incr * * July 17, 1995 by Robin Miller. * Conditionalize aio_suspend() via "#if defined(POSIX_4D11)" for * earlier POSIX drafts so only one copy of this file is necessary. * [ NOTE: This is accomplished via -DPOSIX_4D11 in our Makefile. ] * * July 15, 1995 by Robin Miller. * Fix end of media error handling (ENOSPC), and cleanup code. * * July 14, 1995 by Robin Miller. * Add logic to allow rotating through 1st ROTATE_SIZE byes of buffers. * [ This option was being silently ignored before, and nobody knew. ] * * April 15, 1994 by Wayne Casagrande. * Update aiosuspend() interface, which now takes different arguments * due to POSIX standard changing. * * January 20, 1994 by Robin Miller. * When initializing the data buffer, don't do the entire buffer since * init'ing large buffer (e.g. 100m) using min, max, and incr options cause * excessive paging and VERY poor performance. * * October 15, 1993 by Robin Miller. * Sorry folks, major screw up on my part. I forgot to redefine the * test function (tf_write_data) field to point a the dtaio_write_data() * function, so... synchronous writes were still being done (damn!!!). * Also fixed bug when writing to stop looping when end of file reached. */ /* * Forward References: */ #if 0 static void dtaio_checkdevice(struct dinfo *dip); #endif static int dtaio_wait(struct dinfo *dip, struct aiocb *acbp); static int dtaio_waitall(struct dinfo *dip, bool canceling); static int dtaio_wait_reads(struct dinfo *dip); static int dtaio_wait_writes(struct dinfo *dip); static int dtaio_process_read(struct dinfo *dip, struct aiocb *acbp); static int dtaio_process_write(struct dinfo *dip, struct aiocb *acbp); #define AIO_BUFS 8 /* Default number of AIO buffers. */ #define AIO_NotQed -1 /* AIO request not queued flag. */ int aio_bufs = AIO_BUFS; /* The number of AIO buffers. */ int aio_index; /* Index to AIO control block. */ volatile off_t aio_offset; /* AIO offset (we maintain). */ v_large aio_data_bytes; /* Total data bytes per pass. */ v_large aio_file_bytes; /* # of tape bytes processed. */ vu_long aio_record_count; /* # of records to processed. */ u_int32 aio_lba; /* AIO logical block address. */ /* * The following variables are meant to be used with tape devices to * backup unprocessed files and/or records due to read-ahead, to be * repositioned prior to the next test or before closing the tape. */ u_long aio_data_adjust; /* # of data bytes to adjust. */ u_long aio_file_adjust; /* # of tape files to adjust. */ u_long aio_record_adjust; /* # of tape record to adjust. */ struct aiocb *acbs; /* Pointer to AIO control blocks. */ u_char **aiobufs; /* Pointer to base buffer addrs. */ struct aiocb *current_acb; /* Current acb for error reports. */ /* * Declare the POSIX Asynchronous I/O test functions. */ struct dtfuncs aio_funcs = { /* tf_open, tf_close, tf_initialize, */ open_file, dtaio_close_file, dtaio_initialize, /* tf_start_test, tf_end_test, */ init_file, nofunc, /* tf_read_file, tf_read_data, tf_cancel_reads, */ read_file, dtaio_read_data, dtaio_cancel_reads, /* tf_write_file, tf_write_data, tf_cancel_writes, */ write_file, dtaio_write_data, nofunc, /* tf_flush_data, tf_verify_data, tf_reopen_file, */ flush_file, verify_data, reopen_file, /* tf_startup, tf_cleanup, tf_validate_opts */ nofunc, nofunc, validate_opts }; /************************************************************************ * * * dtaio_close_file() - Close an open file descriptor. * * * * Description: * * This function does the AIO file descriptor close processing. * * * * Inputs: dip = The device information pointer. * * * * Return Value: * * Returns 0 / -1 = SUCCESS / FAILURE. * * * ************************************************************************/ int dtaio_close_file (struct dinfo *dip) { int status = SUCCESS; if (dip->di_closing || (dip->di_fd == NoFd)) { return (status); /* Closing or not open. */ } /* * Avoid cancel'ing I/O more than once using the closing flag. * We can get called again by alarm expiring or signal handler. */ dip->di_closing = TRUE; (void) dtaio_cancel (dip); status = dtaio_waitall (dip, FALSE); dip->di_closing = FALSE; return (close_file (dip)); } /* * Allocate and initialize AIO data structures. */ int dtaio_initialize (struct dinfo *dip) { struct aiocb *acbp; size_t size = (sizeof(struct aiocb) * aio_bufs); int index; int status = SUCCESS; #if 0 /* * This check isn't being done, since when linked with libaio.a, * AIO is mimiced via multiple threads to any device, not just * character devices as with libaioraw.a */ dtaio_checkdevice (dip); #endif if ( (dip->di_dtype->dt_dtype == DT_TAPE) && raw_flag && (aio_bufs > 1) ) { Printf("Sorry, tapes are limited to 1 AIO with raw option!\n"); aio_bufs = 1; size = (sizeof(struct aiocb) * aio_bufs); } aio_index = 0; aio_offset = (off_t) 0; if (acbs == NULL) { acbs = (struct aiocb *) Malloc (size); bzero ((char *) acbs, size); if (rotate_flag) { size_t psize = (aio_bufs * sizeof(u_char *)); aiobufs = (u_char **) Malloc (psize); bzero ((char *) aiobufs, psize); } } for (index = 0, acbp = acbs; index < aio_bufs; index++, acbp++) { if (acbp->aio_buf == NULL) { acbp->aio_buf = myalloc (data_size, align_offset); if (rotate_flag) { aiobufs[index] = (u_char *) acbp->aio_buf; } } acbp->aio_fildes = AIO_NotQed; acbp->aio_offset = (off_t) 0; acbp->aio_nbytes = block_size; acbp->aio_reqprio = AIO_PRIO_DFL; /* Set default priority. */ #if defined(SCO) || defined(HP_UX) /* * Note: The AIO manual recommends setting AIO_RAW, but when * this is set, EINVAL is returned by aio_read/aio_write! */ # if defined(SCO) acbp->aio_flags = 0; /* Must be zero to work! */ # endif /* defined(SCO) */ acbp->aio_sigevent.sigev_notify = SIGEV_NONE; #if 0 acbp->aio_flags = 0; /*AIO_RAW;*/ /* Required on SVR4.2(?) */ /* * This signaling method did not exist with the first release * of POSIX AIO. Perhaps I'll add this completion method in * a future release. Note: Tru64 Unix now supports this too! */ acbp->aio_sigevent.sigev_signo = /* use with SIGEV_SIGNAL */; acbp->aio_sigevent.sigev_notify = SIGEV_CALLBACK; acbp->aio_sigevent.sigev_func = my_aio_completion_function; acbp->aio_sigevent.sigev_value = acbp; #endif #endif /* defined(SCO) || defined(HP_UX) */ /* * Use first buffer allocated for initial skip reads, etc. */ if (index == 0) data_buffer = (u_char *) acbp->aio_buf; } return (status); } #if 0 static void dtaio_checkdevice (struct dinfo *dip) { struct stat sb; /* * Asynchronous I/O is for character devices *only*. * [ Is this true for all operating systems? ] */ if (fstat (dip->di_fd, &sb) == FAILURE) { report_error("fstat", FALSE); exit (FATAL_ERROR); } if (!S_ISCHR(sb.st_mode) ) { LogMsg (efp, logLevelCrit, 0, "'%s' is NOT a character device, cannot use asynchronous I/O.\n", dip->di_dname); exit (FATAL_ERROR); } return; } #endif /* 0 */ /* * Cancel outstanding I/O on the specified file descriptor. */ int dtaio_cancel (struct dinfo *dip) { int status; /* * This is not a very useful operation on DEC OSF/1 at this time, * since the drivers do *not* contain a cancel entry point. * So... you cannot actually cancel outstanding I/O requests. */ if ((status = aio_cancel (dip->di_fd, NULL)) == FAILURE) { /* * aio_cancel() returns EBADF if we never issued any AIO! */ if (errno != EBADF) { report_error ("aio_cancel", TRUE); } return (status); } if (debug_flag) { switch (status) { case AIO_ALLDONE: Printf ("All requests completed before cancel...\n"); break; case AIO_CANCELED: Printf ("Outstanding requests were canceled...\n"); break; case AIO_NOTCANCELED: Fprintf ("Outstanding (active?) request NOT canceled...\n"); break; default: Fprintf ("Unexpected status of %d from aio_cancel()...\n", status); break; } } return (status); } int dtaio_cancel_reads (struct dinfo *dip) { int status; struct dtype *dtp = dip->di_dtype; aio_data_adjust = aio_file_adjust = aio_record_adjust = 0L; (void) dtaio_cancel (dip); status = dtaio_waitall (dip, TRUE); if (aio_file_adjust && (dtp->dt_dtype == DT_TAPE) ) { daddr_t count = (daddr_t)aio_file_adjust; /* * Tapes are tricky... we must backup prior to the * last file(s) we processed, then forward space over * its' file mark to be properly positioned (yuck!!!). */ if (end_of_file) count++; status = DoBackwardSpaceFile (dip, count); if (status == SUCCESS) { status = DoForwardSpaceFile (dip, (daddr_t) 1); } } else if (aio_record_adjust && (dtp->dt_dtype == DT_TAPE) ) { /* * If we've read partially into the next file, backup. */ status = DoBackwardSpaceFile (dip, (daddr_t) 1); if (status == SUCCESS) { status = DoForwardSpaceFile (dip, (daddr_t) 1); } } return (status); } #if defined(EEI) static int dtaio_restart(struct dinfo *dip, struct aiocb *first_acbp) { struct aiocb *acbp = first_acbp; size_t bsize; ssize_t count, adjust; int index, error, status = SUCCESS; /* * Find starting index of this AIO request. */ for (index = 0; index < aio_bufs; index++) { if (first_acbp == &acbs[index]) break; } if (index == aio_bufs) abort(); /* Should NEVER happen! */ /* * Now, wait for and restart all previously active I/O's. */ do { /* * Assumes the first request was already waited for! */ if (Debug_flag) { Printf ("Restarting request for acbp at %#lx...\n", acbp); } if (dip->di_mode == READ_MODE) { if ( (error = aio_read (acbp)) == FAILURE) { acbp->aio_fildes = AIO_NotQed; report_error ("aio_read", TRUE); return (error); } } else { if ( (error = aio_write (acbp)) == FAILURE) { acbp->aio_fildes = AIO_NotQed; report_error ("aio_write", TRUE); return (error); } } if (++index == aio_bufs) index = 0; if (index == aio_index) break; acbp = &acbs[index]; if (acbp->aio_fildes == AIO_NotQed) abort(); error = dtaio_wait (dip, acbp); (void) aio_return (acbp); } while (TRUE); return (status); } #endif /* defined(EEI) */ static int dtaio_wait (struct dinfo *dip, struct aiocb *acbp) { int error, status; if (Debug_flag) { Printf ("Waiting for acbp at %#lx to complete...\n", acbp); } /* * Loop waiting for an I/O request to complete. */ while ((error = aio_error (acbp)) == EINPROGRESS) { #if defined(POSIX_4D11) if ((status = aio_suspend (1, (const struct aiocb **)&acbp)) == FAILURE) { #else /* Beyond draft 11... */ if ((status = aio_suspend ((const struct aiocb **)&acbp,1,NULL)) == FAILURE) { #endif /* defined(POSIX_4D11) */ if (errno != EINTR) { report_error ("aio_suspend", TRUE); return (status); } } } if ( (error == FAILURE) && !terminating_flag) { report_error ("aio_error", TRUE); } return (error); } static int dtaio_waitall(struct dinfo *dip, bool canceling) { struct aiocb *acbp; register size_t bsize; register ssize_t count; ssize_t adjust; int index, error, status = SUCCESS; /* * During EEI reset handling, don't touch the active requests, * since dtaio_restart() needs this state to restart reqeusts. */ if (dip->di_proc_eei) return (status); /* * Loop waiting for all I/O requests to complete. */ for (index = 0; index < aio_bufs; index++) { acbp = &acbs[aio_index]; if (++aio_index == aio_bufs) aio_index = 0; if (acbp->aio_fildes == AIO_NotQed) continue; if ( (error = dtaio_wait (dip, acbp))) { status = error; if (status == FAILURE) { acbp->aio_fildes = AIO_NotQed; continue; /* aio_error() failed! */ } } count = aio_return (acbp); acbp->aio_fildes = AIO_NotQed; errno = error; if ( (count == FAILURE) && !dip->di_closing && !terminating_flag) { /* * End of media is handled below. */ #if defined(SCO) || defined(HP_UX) if ( (error != ENOSPC) && (error != ENXIO) ) { #else /* !defined(SCO) && !defined(HP_UX) */ if (error != ENOSPC) { #endif /* defined(SCO) || defined(HP_UX) */ current_acb = acbp; report_error ("aio_return", TRUE); ReportDeviceInfo (dip, acbp->aio_nbytes, 0, (errno == EIO)); status = FAILURE; /* adjust counts below */ } } else if (error) { count = FAILURE; } bsize = acbp->aio_nbytes; /* * Adjust for short records or no data transferred. */ if (count == FAILURE) { aio_data_bytes -= bsize; aio_file_bytes -= bsize; } else if (adjust = (bsize - count)) { if (debug_flag) { Printf("Adjusting byte counts by %d bytes...\n", adjust); } aio_data_bytes -= adjust; aio_file_bytes -= adjust; } /* * Count files or records to adjust after I/O's complete. */ if ( is_Eof (dip, count, (int *) 0) ) { if (!dip->di_end_of_media) aio_file_adjust++; } else if (count > (ssize_t) 0) { aio_record_adjust++; /* * Adjust counts for total statistics. */ if (!canceling) { if (dip->di_mode == READ_MODE) { dip->di_dbytes_read += count; dip->di_fbytes_read += count; } else { dip->di_dbytes_written += count; dip->di_fbytes_written += count; } aio_data_adjust += count; if (count == bsize) { records_processed++; } else { partial_records++; } } } } return (status); } /* * Function to wait for and process read requests. */ static int dtaio_wait_reads (struct dinfo *dip) { struct aiocb *acbp; int index, error, status = SUCCESS; /* * Loop waiting for all I/O requests to complete. */ for (index = 0; index < aio_bufs; index++) { acbp = &acbs[aio_index]; if (++aio_index == aio_bufs) aio_index = 0; if (acbp->aio_fildes == AIO_NotQed) continue; if ( (error = dtaio_process_read (dip, acbp)) == FAILURE) { status = error; } if ( end_of_file || (dip->di_records_read >= record_limit) || (dip->di_fbytes_read >= data_limit) ) { break; } } return (status); } /* * Function to wait for and process write requests. */ static int dtaio_wait_writes (struct dinfo *dip) { struct aiocb *acbp; int index, error, status = SUCCESS; /* * Loop waiting for all I/O requests to complete. */ for (index = 0; index < aio_bufs; index++) { acbp = &acbs[aio_index]; if (++aio_index == aio_bufs) aio_index = 0; if (acbp->aio_fildes == AIO_NotQed) continue; if ( (error = dtaio_process_write (dip, acbp)) == FAILURE) { status = error; if (error_count >= error_limit) break; } } return (status); } /************************************************************************ * * * dtaio_read_data() - Read and optionally verify data read. * * * * Inputs: dip = The device information pointer. * * * * Outputs: Returns SUCCESS/FAILURE = Ok/Error. * * * ************************************************************************/ int dtaio_read_data (struct dinfo *dip) { register struct aiocb *acbp; int error, status = SUCCESS; register size_t bsize, dsize; if (dip->di_random_access) { if (io_dir == REVERSE) { (void)set_position(dip, (off_t)rdata_limit); } aio_lba = get_lba(dip); aio_offset = get_position(dip); } else { aio_offset = dip->di_offset; aio_lba = make_lbdata (dip, aio_offset); } aio_data_bytes = aio_file_bytes = aio_record_count = 0; /* * For variable length records, initialize to minimum record size. */ if (min_size) { if (variable_flag) { dsize = get_variable (dip); } else { dsize = min_size; } } else { dsize = block_size; } /* * Now read and optionally verify the input records. */ while ( (error_count < error_limit) && (dip->di_fbytes_read < data_limit) && (dip->di_records_read < record_limit) ) { if (volumes_flag && (multi_volume >= volume_limit) && (dip->di_volume_records >= volume_records)) { dip->di_volume_records = volume_records; break; } /* * Two loops are used with AIO. The inner loop queues requests up * to the requested amount, and the outer loop checks the actual * data processed. This is done mainly for tapes to handle short * reads & to efficiently handle multiple tape files. */ while ( (error_count < error_limit) && (aio_record_count < record_limit) && (aio_file_bytes < data_limit) ) { if (volumes_flag && (multi_volume >= volume_limit) && (dip->di_volume_records >= volume_records)) { break; } if (rdelay_count) { /* Optional read delay. */ mySleep (rdelay_count); } /* * If data limit was specified, ensure we don't exceed it. */ if ( (aio_file_bytes + dsize) > data_limit) { bsize = (data_limit - aio_file_bytes); } else { bsize = dsize; } acbp = &acbs[aio_index]; /* * If requested, rotate the data buffer through ROTATE_SIZE bytes * to force various unaligned buffer accesses. */ if (rotate_flag) { data_buffer = aiobufs[aio_index]; data_buffer += (rotate_offset++ % ROTATE_SIZE); acbp->aio_buf = data_buffer; } else { data_buffer = (u_char *) acbp->aio_buf; } acbp->aio_fildes = dip->di_fd; if (io_dir == REVERSE) { /*debug*/ if (!aio_offset) abort(); /*debug*/ bsize = MIN((aio_offset-file_position), bsize); aio_offset = (off_t)(aio_offset - bsize); } if (debug_flag && (bsize != dsize) && !variable_flag) { Printf ("Record #%lu, Reading a partial record of %lu bytes...\n", (aio_record_count + 1), bsize); } acbp->aio_nbytes = bsize; if (io_type == RANDOM_IO) { acbp->aio_offset = do_random (dip, FALSE, bsize); } else { acbp->aio_offset = aio_offset; } /* * If we'll be doing a data compare after the read, then * fill the data buffer with the inverted pattern to ensure * the buffer actually gets written into (driver debug mostly). */ if ((io_mode == TEST_MODE) && compare_flag) { init_buffer (data_buffer, bsize, ~pattern); init_padbytes (data_buffer, bsize, ~pattern); } if (Debug_flag) { u_int32 lba = NO_LBA; if (dip->di_random_access || lbdata_flag || iot_pattern) { lba = make_lbdata(dip, (dip->di_volume_bytes + acbp->aio_offset)); } report_record(dip, (dip->di_files_read + 1), (aio_record_count + 1), lba, READ_MODE, (void *)acbp->aio_buf, acbp->aio_nbytes); } if ( (error = aio_read (acbp)) == FAILURE) { acbp->aio_fildes = AIO_NotQed; report_error ("aio_read", TRUE); return (error); } /* * Must adjust record/data counts here to avoid writing * too much data, even though the writes are incomplete. */ aio_data_bytes += bsize; aio_file_bytes += bsize; aio_record_count++; if (io_dir == FORWARD) { aio_offset += bsize; } if (step_offset) { if (io_dir == FORWARD) { aio_offset += step_offset; } else if ((aio_offset -= step_offset) <= (off_t) file_position) { aio_offset = (off_t) file_position; } } /* * For variable length records, adjust the next record size. */ if (min_size) { if (variable_flag) { dsize = get_variable (dip); } else { dsize += incr_count; if (dsize > max_size) dsize = min_size; } } /* * Always ensure the next control block has completed. */ if (++aio_index == aio_bufs) aio_index = 0; if ( (io_dir == REVERSE) && (aio_offset == (off_t) file_position) ) { break; } acbp = &acbs[aio_index]; if (acbp->aio_fildes == AIO_NotQed) continue; /* Never Q'ed. */ if ( (status = dtaio_process_read (dip, acbp)) == FAILURE) { return (status); } if ( end_of_file ) return (status); } /* * We get to this point after we've Q'ed enough requests to * fulfill the requested record and/or data limit. We now * wait for these Q'ed requests to complete, adjusting the * global transfer statistics appropriately which reflects * the actual data processed. */ status = dtaio_wait_reads(dip); if ( end_of_file ) break; /* Stop reading at end of file. */ } return (status); } /************************************************************************ * * * dtaio_process_read() - Process AIO reads & optionally verify data. * * * * Description: * * This function does waits for the requested AIO read request, * * checks the completion status, and optionally verifies the data read. * * * * Inputs: dip = The device information pointer. * * acbp = The AIO control block. * * * * Outputs: Returns SUCCESS/FAILURE/WARNING = Ok/Error/Warning. * * * ************************************************************************/ static int dtaio_process_read (struct dinfo *dip, struct aiocb *acbp) { struct dtfuncs *dtf = dip->di_funcs; register size_t bsize, dsize; register ssize_t count; ssize_t adjust; int error, status = SUCCESS; #if defined(EEI) retry: #endif current_acb = acbp; error = dtaio_wait (dip, acbp); count = aio_return (acbp); #if defined(EEI) if (dip->di_dtype->dt_dtype == DT_TAPE) { if ( (errno == EIO) && eei_resets) { if ( HandleTapeResets(dip) ) { int error = dtaio_restart(dip, acbp); if (error) return (error); goto retry; } } else if (eei_flag) { (void) get_eei_status(dip->di_fd, dip->di_mt); } } #endif /* defined(EEI) */ acbp->aio_fildes = AIO_NotQed; errno = error; if (volumes_flag && (multi_volume >= volume_limit) && (dip->di_volume_records == volume_records)) { return (SUCCESS); } if (count == FAILURE) { /* * End of media is handled below. */ #if defined(SCO) || defined(HP_UX) if ( (error != ENOSPC) && (error != ENXIO) ) { #else /* !defined(SCO) && !defined(HP_UX) */ if (error != ENOSPC) { #endif /* defined(SCO) || defined(HP_UX) */ report_error ("aio_return", TRUE); ReportDeviceInfo (dip, acbp->aio_nbytes, 0, (errno == EIO)); return (FAILURE); } } else if (error) { count = FAILURE; } bsize = acbp->aio_nbytes; data_buffer = (u_char *)acbp->aio_buf; if (min_size) { dsize = bsize; } else { dsize = block_size; } /* * Adjust for short records or no data transferred. */ if (count == FAILURE) { aio_data_bytes -= bsize; aio_file_bytes -= bsize; } else if (adjust = (bsize - count)) { if (debug_flag) { Printf("Adjusting byte counts by %d bytes...\n", adjust); } aio_data_bytes -= adjust; aio_file_bytes -= adjust; } /* * Process end of file/media conditions and handle multi-volume. */ if ( is_Eof (dip, count, &status) ) { if (multi_flag) { if ( (dip->di_dtype->dt_dtype == DT_TAPE) && !dip->di_end_of_logical ) { return (status); /* Expect two file marks @ EOM. */ } status = HandleMultiVolume (dip); aio_record_count = dip->di_records_read; /*aio_file_bytes = dip->di_dbytes_read;*/ aio_offset = (off_t) 0; } return (status); } else { dip->di_end_of_file = FALSE; /* Reset saved end of file state. */ if (count > (ssize_t) 0) { dip->di_dbytes_read += count; dip->di_fbytes_read += count; dip->di_vbytes_read += count; if (count == dsize) { records_processed++; } else { partial_records++; } dip->di_offset = (acbp->aio_offset + count); } if ((status = check_read (dip, count, bsize)) == FAILURE) { if (error_count >= error_limit) return (status); } else if (io_mode == COPY_MODE) { status = copy_record (output_dinfo, data_buffer, count); if ( (error_count >= error_limit) || end_of_file) return (status); } else if (io_mode == VERIFY_MODE) { status = verify_record (output_dinfo, data_buffer, count); if ( (error_count >= error_limit) || end_of_file) return (status); } } /* * Verify the data (unless disabled). */ if ( (status != FAILURE) && compare_flag && (io_mode == TEST_MODE)) { ssize_t vsize = count; if (lbdata_flag || iot_pattern) { aio_lba = make_lbdata(dip, (dip->di_volume_bytes + acbp->aio_offset)); if (iot_pattern) { aio_lba = init_iotdata (vsize, aio_lba, lbdata_size); } } status = (*dtf->tf_verify_data)(dip, data_buffer, vsize, pattern, &aio_lba); /* * Verify the pad bytes (if enabled). */ if ( (status == SUCCESS) && pad_check) { (void) verify_padbytes (dip, data_buffer, vsize, ~pattern, bsize); } } dip->di_records_read++; dip->di_volume_records++; if ( ((io_dir == REVERSE) && (acbp->aio_offset == (off_t) file_position)) || (step_offset && ((acbp->aio_offset - step_offset) <= (off_t) file_position)) ) { set_Eof(dip); } return (status); } /************************************************************************ * * * dtaio_write_data() - Write specified data to the output file. * * * * Inputs: dip = The device information pointer. * * * * Outputs: Returns SUCCESS/FAILURE = Ok/Error. * * * ************************************************************************/ int dtaio_write_data (struct dinfo *dip) { register struct aiocb *acbp; int error, status = SUCCESS; register size_t bsize, dsize; u_int32 lba = lbdata_addr; if (dip->di_random_access) { if (io_dir == REVERSE) { (void)set_position(dip, (off_t)rdata_limit); } aio_lba = lba = get_lba(dip); aio_offset = get_position(dip); } else { aio_offset = dip->di_offset; aio_lba = lba = make_lbdata (dip, aio_offset); } aio_data_bytes = aio_file_bytes = aio_record_count = 0; /* * For variable length records, initialize to minimum record size. */ if (min_size) { if (variable_flag) { dsize = get_variable (dip); } else { dsize = min_size; } } else { dsize = block_size; } /* * Now write the specifed number of records. */ while ( (error_count < error_limit) && (dip->di_fbytes_written < data_limit) && (dip->di_records_written < record_limit) ) { if (volumes_flag && (multi_volume >= volume_limit) && (dip->di_volume_records >= volume_records)) { dip->di_volume_records = volume_records; break; } /* * Two loops are used with AIO. The inner loop queues requests up * tto the requested amount, and the outer loop checks the actual * data processed. This is done to handle short reads, which can * happen frequently with random I/O and large block sizes. */ while ( (error_count < error_limit) && (aio_record_count < record_limit) && (aio_file_bytes < data_limit) ) { if (volumes_flag && (multi_volume >= volume_limit) && (dip->di_volume_records >= volume_records)) { break; } if (wdelay_count) { /* Optional write delay */ mySleep (wdelay_count); } /* * If data limit was specified, ensure we don't exceed it. */ if ( (aio_file_bytes + dsize) > data_limit) { bsize = (data_limit - aio_file_bytes); } else { bsize = dsize; } acbp = &acbs[aio_index]; /* * If requested, rotate the data buffer through ROTATE_SIZE bytes * to force various unaligned buffer accesses. */ if (rotate_flag) { data_buffer = aiobufs[aio_index]; data_buffer += (rotate_offset++ % ROTATE_SIZE); acbp->aio_buf = data_buffer; } else { data_buffer = (u_char *) acbp->aio_buf; } acbp->aio_fildes = dip->di_fd; if (io_dir == REVERSE) { /*debug*/ if (!aio_offset) abort(); /*debug*/ bsize = MIN((aio_offset-file_position), bsize); aio_offset = (off_t)(aio_offset - bsize); } acbp->aio_nbytes = bsize; if (debug_flag && (bsize != dsize) && !variable_flag) { Printf ("Record #%lu, Writing a partial record of %d bytes...\n", (aio_record_count + 1), bsize); } if (io_type == RANDOM_IO) { acbp->aio_offset = do_random (dip, FALSE, bsize); } else { acbp->aio_offset = aio_offset; } if (iot_pattern || lbdata_flag) { lba = make_lbdata (dip, (dip->di_volume_bytes + acbp->aio_offset)); } /* * Initialize the data buffer with a pattern. */ if ((io_mode == TEST_MODE) && compare_flag) { if (iot_pattern) { lba = init_iotdata(bsize, lba, lbdata_size); } fill_buffer (data_buffer, bsize, pattern); } /* * Initialize the logical block data (if enabled). */ if (lbdata_flag && lbdata_size && !iot_pattern) { lba = winit_lbdata (dip, (dip->di_volume_bytes + acbp->aio_offset), data_buffer, bsize, lba, lbdata_size); } if (Debug_flag) { u_int32 lba = NO_LBA; if (dip->di_random_access || lbdata_flag || iot_pattern) { lba = make_lbdata(dip, (dip->di_volume_bytes + acbp->aio_offset)); } report_record(dip, (dip->di_files_written + 1), (aio_record_count + 1), lba, WRITE_MODE, (void *)acbp->aio_buf, acbp->aio_nbytes); } if ( (error = aio_write (acbp)) == FAILURE) { acbp->aio_fildes = AIO_NotQed; report_error ("aio_write", TRUE); return (error); } /* * Must adjust record/data counts here to avoid writing * too much data, even though the writes are incomplete. */ aio_data_bytes += bsize; aio_file_bytes += bsize; aio_record_count++; if (io_dir == FORWARD) { aio_offset += bsize; } if (step_offset) { if (io_dir == FORWARD) { aio_offset += step_offset; } else if ((aio_offset -= step_offset) <= (off_t) file_position) { aio_offset = (off_t) file_position; } } /* * For variable length records, adjust the next record size. */ if (min_size) { if (variable_flag) { dsize = get_variable (dip); } else { dsize += incr_count; if (dsize > max_size) dsize = min_size; } } /* * Always ensure the next control block has completed. */ if (++aio_index == aio_bufs) aio_index = 0; if ( (io_dir == REVERSE) && (aio_offset == (off_t) file_position) ) { break; } acbp = &acbs[aio_index]; if (acbp->aio_fildes == AIO_NotQed) continue; /* Never Q'ed. */ if ( (status = dtaio_process_write (dip, acbp)) == FAILURE) { return (status); } if (end_of_file) break; } /* * We get to this point after we've Q'ed enough requests to * fulfill the requested record and/or data limit. We now * wait for these Q'ed requests to complete, adjusting the * global transfer statistics appropriately which reflects * the actual data processed. */ status = dtaio_wait_writes(dip); if (end_of_file) break; } return (status); } /************************************************************************ * * * dtaio_process_write() - Process AIO write requests. * * * * Description: * * This function does waits for the requested AIO write request * * and checks the completion status. * * * * Inputs: dip = The device info pointer. * * acbp = The AIO control block. * * * * Outputs: Returns SUCCESS/FAILURE/WARNING = Ok/Error/Partial. * * * ************************************************************************/ static int dtaio_process_write (struct dinfo *dip, struct aiocb *acbp) { register size_t bsize, dsize; register ssize_t count; ssize_t adjust; int error, status = SUCCESS; #if defined(EEI) retry: #endif current_acb = acbp; error = dtaio_wait (dip, acbp); count = aio_return (acbp); #if defined(EEI) if (dip->di_dtype->dt_dtype == DT_TAPE) { if ( (errno == EIO) && eei_resets) { if ( HandleTapeResets(dip) ) { dtaio_restart(dip, acbp); goto retry; } } else if (eei_flag) { (void) get_eei_status(dip->di_fd, dip->di_mt); } } #endif /* defined(EEI) */ acbp->aio_fildes = AIO_NotQed; errno = error; if (volumes_flag && (multi_volume >= volume_limit) && (dip->di_volume_records == volume_records)) { return (SUCCESS); } if (count == FAILURE) { #if defined(SCO) || defined(HP_UX) if ( (error != ENOSPC) && (error != ENXIO) ) { #else /* !defined(SCO) && !defined(HP_UX) */ if (error != ENOSPC) { #endif /* defined(SCO) || defined(HP_UX) */ report_error ("aio_return", TRUE); ReportDeviceInfo (dip, acbp->aio_nbytes, 0, (errno == EIO)); return (FAILURE); } } else if (error) { count = FAILURE; } bsize = acbp->aio_nbytes; if (min_size) { dsize = bsize; /* Can lead to wrong partial record count :-) */ } else { dsize = block_size; } /* * Adjust for short records or no data transferred. */ if (count == FAILURE) { aio_data_bytes -= bsize; aio_file_bytes -= bsize; } else if (adjust = (bsize - count)) { aio_data_bytes -= adjust; aio_file_bytes -= adjust; } if (count > (ssize_t) 0) { dip->di_dbytes_written += count; dip->di_fbytes_written += count; dip->di_vbytes_written += count; } /* * Process end of file/media conditions and handle multi-volume. */ if ( is_Eof (dip, count, &status) ) { if (multi_flag) { status = HandleMultiVolume (dip); aio_record_count = dip->di_records_written; /*aio_file_bytes = dip->di_dbytes_written;*/ aio_offset = (off_t) 0; } return (status); } if (count > (ssize_t) 0) { if (count == dsize) { records_processed++; } else { partial_records++; } dip->di_offset = (acbp->aio_offset + count); } if ((status = check_write (dip, count, bsize)) == FAILURE) { if (error_count >= error_limit) return (status); } if ( (status != FAILURE) && raw_flag) { status = write_verify(dip, (u_char *)acbp->aio_buf, count, dsize, acbp->aio_offset); if ( (status == FAILURE) && (error_count >= error_limit) ) { return (status); } } dip->di_records_written++; dip->di_volume_records++; if ( ((io_dir == REVERSE) && (acbp->aio_offset == (off_t) file_position)) || (step_offset && ((acbp->aio_offset - step_offset) <= (off_t) file_position)) ) { set_Eof(dip); } return (status); } #endif /* defined(AIO) */