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