/****************************************************************************
 *									    *
 *			  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:	dtmmap.c
 * Author:	Robin T. Miller
 * Date:	September 4, 1993
 *
 * Description:
 *	Functions to do memory mapped I/O for 'dt' program.
 *
 * 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.
 *
 * 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 24th, 2001 by Robin Miller.
 *	Add support for variable I/O requests sizes.
 *
 * April 25th, 2000 by Robin Miller.
 *	Broke the MMAP write function badly during some cleanup (sorry!).
 *
 * July 29, 1999 by Robin Miller.
 *	Merge in changes made to compile on FreeBSD.
 *
 * July 22nd, 1999 by Robin Miller.
 *	Added support for IOT (DJ's) test pattern.
 * 
 * May 27, 1999 by Robin Miller.
 *	Added support for micro-second delays.
 *
 * January 9, 1998 by Robin.
 *	Don't initialize data buffer being written for "disable=compare"
 * which yields better performance.  Also correct a problem referencing
 * data due to compiler optimization (reference_data() use static volatile).
 * [ Changes requested by Marcus Barrow, thanks! ]
 *
 * November 19, 1995 by Robin Miller.
 *	Removed old Sun/OS #ifdef'ed out code, and added support for
 *	logical block data option (yes, valid for use w/data files).
 */
#if defined(MMAP)

#include "dt.h"
#include <limits.h>
#include <sys/mman.h>
#include <sys/stat.h>

#if !defined(MAP_FILE)
#  define MAP_FILE	0
#endif /* !defined(MAP_FILE) */

/*
 * Forward References:
 */
void reference_data (u_char *buffer, size_t count);

/*
 * Declare the memory mapped test functions.
 */
struct dtfuncs mmap_funcs = {
    /*	tf_open,		tf_close,		tf_initialize,	  */
	open_file,		close_file,		nofunc,
    /*  tf_start_test,		tf_end_test,				  */
	mmap_file,		nofunc,
    /*	tf_read_file,		tf_read_data,		tf_cancel_reads,  */
	read_file,		mmap_read_data,		nofunc,
    /*	tf_write_file,		tf_write_data,		tf_cancel_writes, */
	write_file,		mmap_write_data,	nofunc,
    /*	tf_flush_data,		tf_verify_data,		tf_reopen_file,   */
	mmap_flush,		verify_data,		mmap_reopen_file,
    /*	tf_startup,		tf_cleanup,		tf_validate_opts  */
	nofunc,			nofunc,			mmap_validate_opts
};

/************************************************************************
 *									*
 * mmap_file()	Memory map the input or output file.			*
 *									*
 * Inputs:	dip = The device information pointer.			*
 *									*
 * Return Value:							*
 *		Returns 0 / -1 = SUCESS / FAILURE.			*
 *									*
 ************************************************************************/
int
mmap_file (struct dinfo *dip)
{
	int fd = dip->di_fd;
	int status = SUCCESS;

	/*
	 * For memory mapped I/O, map the file to a buffer.
	 */
	if (dip->di_mode == READ_MODE) {
	    mmap_buffer = (u_char *) mmap ((char *) 0, data_limit,
		PROT_READ, (MAP_FILE | MAP_PRIVATE), fd, (off_t) 0);
	} else { /* Output file */
	    /*
	     * Set the output file to the specified limit before
	     * memory mapping the file.
	     */
	    if (ftruncate (fd, data_limit) < 0) {
		report_error ("ftruncate", TRUE);
		exit (exit_status);
	    }
	    mmap_buffer = (u_char *) mmap ((caddr_t) 0, data_limit,
	(PROT_READ | PROT_WRITE), (MAP_FILE | MAP_SHARED), fd, (off_t) 0);
	}
	if (mmap_buffer == (u_char *) -1) {
	    report_error ("mmap", TRUE);
	    exit (exit_status);
	}
	mmap_bufptr = mmap_buffer;

	/*
	 * File positioning options currently ignored... maybe later.
	 */

	return (status);
}

/************************************************************************
 *									*
 * mmap_flush()	Flush memory map file data to permanent storage.	*
 *									*
 * Inputs:	dip = The device information pointer.			*
 *									*
 * Return Value:							*
 *		Returns 0 / -1 = SUCESS / FAILURE.			*
 *									*
 ************************************************************************/
int
mmap_flush (struct dinfo *dip)
{
	int status = SUCCESS;

	/*
	 * Sync out modified pages & invalid the address range to
	 * force them to be obtained from the file system during the
	 * read pass.
	 */
	if (dip->di_mode == WRITE_MODE) {
	    if (msync ((caddr_t)mmap_buffer, dip->di_dbytes_written,
					 MS_INVALIDATE) == FAILURE) {
		report_error ("msync", TRUE);
		terminate (errno);
	    }
	}
	return (status);
}

/************************************************************************
 *									*
 * mmap_reopen_file() - Reopen memory mapped input or output file.	*
 *									*
 * Inputs:	dip = The device information pointer.			*
 *		oflags = The device/file open flags.			*
 *									*
 * Return Value:							*
 *		Returns 0 / -1 = SUCESS / FAILURE.			*
 *									*
 ************************************************************************/
int
mmap_reopen_file (struct dinfo *dip, int oflags)
{
    /*
     * For memory mapped files, remove the mappings before closing
     * the file.
     */
    if (mmap_flag) {
	if (munmap ((caddr_t)mmap_buffer, data_limit) == FAILURE) {
	    report_error ("munmap", TRUE);
	    terminate (errno);
	}
	mmap_bufptr = mmap_buffer = (u_char *) 0;
    }

    return (reopen_file (dip, oflags));
}

/************************************************************************
 *									*
 * mmap_validate_opts() - Validate Memory Mapped Test Options.		*
 *									*
 * Description:								*
 *	This function verifies the options specified for memory mapped	*
 * file testing are valid.						*
 *									*
 * Inputs:	dip = The device information pointer.			*
 *									*
 * Return Value:							*
 *		Returns SUCESS / FAILURE = Valid / Invalid Options.	*
 *									*
 ************************************************************************/
int
mmap_validate_opts (struct dinfo *dip)
{
    int status = SUCCESS;

    /*
     * For memory mapped I/O, ensure the user specified a limit, and
     * that the block size is a multiple of the page size (a MUST!).
     */
    if (mmap_flag) {
	if (data_limit == INFINITY) {
	    Fprintf ("You must specify a data limit for memory mapped I/O.\n");
	    status = FAILURE;
	} else if (block_size % page_size) {
	    Fprintf (
	"Please specify a block size modulo of the page size (%d).\n", page_size);
	    status = FAILURE;
	} else if (aio_flag) {
	    Fprintf ("Cannot enable async I/O with memory mapped I/O.\n");
	    status = FAILURE;
	} else {
	    status = validate_opts (dip);
	}
    }
    return (status);
}

/************************************************************************
 *									*
 * mmap_read_data() - Read and optionally verify memory mapped data.	*
 *									*
 * Inputs:	dip = The device information pointer.			*
 *									*
 * Outputs:	Returns SUCCESS/FAILURE = Ok/Error.			*
 *									*
 ************************************************************************/
int
mmap_read_data (struct dinfo *dip)
{
	ssize_t count;
	size_t bsize, dsize;
	int status = SUCCESS;
	u_int32 lba = lbdata_addr;
	struct dtfuncs *dtf = dip->di_funcs;

	/*
	 * 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 (rdelay_count) {			/* Optional read delay.	*/
		mySleep (rdelay_count);
	    }

	    /*
	     * If data limit was specified, ensure we don't exceed it.
	     */
	    if ( (dip->di_fbytes_read + dsize) > data_limit) {
		bsize = (data_limit - dip->di_fbytes_read);
		if (debug_flag) {
		    Printf ("Reading partial record of %d bytes...\n", bsize);
		}
	    } else {
		bsize = dsize;
	    }

	    count = bsize;			/* Paged in by system.	*/

	    if ((io_mode == TEST_MODE) && compare_flag) {
		if (iot_pattern) {
		    lba = init_iotdata (count, lba, lbdata_size);
		}
	    }

	    /*
	     * Stop reading when end of file is reached.
	     */
	    if (count == (ssize_t) 0) {		/* Pseudo end of file. */
		if (debug_flag) {
		    Printf ("End of memory mapped file detected...\n");
		}
		end_of_file = TRUE;
		exit_status = END_OF_FILE;
		break;
	    } else {
		dip->di_dbytes_read += count;
		dip->di_fbytes_read += count;
	        if ((status = check_read (dip, count, bsize)) == FAILURE) {
		    break;
		}
	    }

	    if (count == dsize) {
		records_processed++;
	    } else {
		partial_records++;
	    }

	    /*
	     * Verify the data (unless disabled).
	     */
	    if (compare_flag) {
		status = (*dtf->tf_verify_data)(dip, mmap_bufptr, count, pattern, &lba);
	    } else {
		/*
		 * Must reference the data to get it paged in.
		 */
		reference_data (mmap_bufptr, count);
	    }
	    mmap_bufptr += count;

	    /*
	     * 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;
		}
	    }

	    if ( (dip->di_fbytes_read >= data_limit) ||
		 (++dip->di_records_read >= record_limit) ) {
		break;
	    }

#ifdef notdef
	/*
	 * Can't do this right now... if it's not mapped via mmap(), you'll
	 * get a "Segmentation Fault" and core dump.  Need more logic...
	 */
	    if (step_offset) mmap_bufptr += step_offset;
#endif
	}
	return (status);
}

/************************************************************************
 *									*
 * reference_data() - Reference Data of Memory Mapped File.		*
 *									*
 * Description:								*
 *	This function simply references each data byte to force pages	*
 * to be mapped in by the system (memory mapped file I/O).		*
 *									*
 * Inputs:	buffer = Data buffer to reference.			*
 *		count = Number of bytes to reference.			*
 *									*
 * Return Value:							*
 *		Void.							*
 *									*
 ************************************************************************/
void
reference_data (u_char *buffer, size_t count)
{
	size_t i = (size_t) 0;
	u_char *bptr = buffer;
	static volatile u_char data;

	while (i++ < count) {
		data = *bptr++;
	}
}

/************************************************************************
 *									*
 * mman_write_data() - Write data to memory mapped output file.		*
 *									*
 * Inputs:	dip = The device information pointer.			*
 *									*
 * Outputs:	Returns SUCCESS/FAILURE = Ok/Error.			*
 *									*
 ************************************************************************/
int
mmap_write_data (struct dinfo *dip)
{
	ssize_t count;
	size_t bsize, dsize;
	int status = SUCCESS;
	u_int32 lba = lbdata_addr;

	/*
	 * For variable length records, initialize to minimum record size.
	 */
	if (min_size) {
	    dsize = min_size;
	} else {
	    dsize = block_size;
	}

	/*
	 * Now write the specifed number of records.
	 */
	while ( (dip->di_fbytes_written < data_limit) &&
		(dip->di_records_written < record_limit) ) {

	    if (wdelay_count) {			/* Optional write delay	*/
		mySleep (wdelay_count);
	    }

	    /*
	     * If data limit was specified, ensure we don't exceed it.
	     */
	    if ( (dip->di_fbytes_written + dsize) > data_limit) {
		bsize = (data_limit - dip->di_fbytes_written);
		if (debug_flag) {
		    Printf ("Writing partial record of %d bytes...\n",
								bsize);
		}
	    } else {
		bsize = dsize;
	    }

	    count = bsize;
	    if ((io_mode == TEST_MODE) && compare_flag) {
	        if (iot_pattern) {
		    lba = init_iotdata(count, lba, lbdata_size);
		}
		fill_buffer (mmap_bufptr, count, pattern);
	    }

	    /*
	     * Initialize the logical block data (if enabled).
	     */
	    if (lbdata_flag && lbdata_size && !iot_pattern) {
		lba = init_lbdata (mmap_bufptr, count, lba, lbdata_size);
	    }

	    mmap_bufptr += count;
	    dip->di_dbytes_written += count;
	    dip->di_fbytes_written += count;

	    /*
	     * Stop writing when end of file is reached.
	     */
	    if (count == (ssize_t) 0) {		/* Pseudo end of file. */
		if (debug_flag) {
		    Printf ("End of memory mapped file reached...\n");
		}
		end_of_file = TRUE;
		exit_status = END_OF_FILE;
		break;
	    }

	    if ((status = check_write (dip, count, bsize)) == FAILURE) {
		break;
	    } else {
		if (count == dsize) {
		    records_processed++;
		} else {
		    partial_records++;
		}
	    }

	    /*
	     * For variable length records, adjust the next record size.
	     */
	    if (min_size) {
		dsize += incr_count;
		if (dsize > max_size) dsize = min_size;
	    }

	    ++dip->di_records_written;

#ifdef notdef
	/*
	 * Can't do this right now... if it's not mapped via mmap(), you'll
	 * get a "Segmentation Fault" and core dump.  Need more logic...
	 */
	    if (step_offset) mmap_bufptr += step_offset;
#endif
	}
	return (status);
}

#endif /* defined(MMAP) */


syntax highlighted by Code2HTML, v. 0.9.1