/****************************************************************************
* *
* 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: dtinfo.c
* Author: Robin T. Miller
* Date: September 8, 1993
*
* Description:
* Setup device and/or system information for 'dt' program.
*/
#include "dt.h"
#if !defined(_QNX_SOURCE)
# if !defined(sun)
# include <sys/ioctl.h>
# endif /* !defined(sun) */
#endif /* !defined(_QNX_SOURCE) */
#include <sys/stat.h>
/*
* Modification History:
*
* October 20th, 2004 by Robin Miller.
* In SetupRegularFile() set user capacity to the max of the
* data limit or the file size, so repeated random I/O with same
* random seed, provides the same results when file is re-read.
*
* July 7th, 2004 by Robin Miller.
* For HP-UX, allow setting the queue depth.
*
* March 30th, 2004 by Robin Miller.
* Add os_system_device_info() for Linux to try to obtain the
* sector size and partition capacity. If this can be obtained for
* random I/O, then do_random() can be avoided, which is a good thing
* since it's broken in 2.4 kernels (can't lseek past end of partition).
*
* February 27th, 2003 by Robin Miller.
* A little more tweaking of the fsync() flag... only enable
* fsync for block or regular file, not for character (raw) disks.
*
* December 5th, 2003 by Robin Miller.
* Conditionalize to exclude tty code.
*
* 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.
*
* June 9th, 2003 by Robin Miller.
* Fix problem setting fsync_flag when dtype=disk/block/regular
* is specified (should do fsync() after writes, but it is *not*).
*
* March 13th, 2003 by Robin Miller.
* Add support for DIOC_DESCRIBE IOCTL to determine device type,
* capacity, and block size.
*
* March 16th, 2002 by Robin Miller.
* In dec_system_device_info() open device with O_NDELAY, incase
* this is a tape device, we don't want a looong delay during the open.
*
* October 4th, 2001 by Robin Miller.
* Fix problem with idtype/odtype options were being overridden by
* dec_system_device_info(). Assume the user is right :-)
*
* August 31st, 2001 by Robin Miller.
* Add casts to values being used to calculate user capacity, which
* is in bytes (64 bits), to ensure 32-bit truncation does not occur.
*
* June 2nd, 2001 by Robin Miller.
* After setting up a "regular" device type, ensure the device
* defaults also get setup. Was not setting min/max/incr defaults.
*
* April 13th, 2001 by Robin Miller.
* Removed old PTOS conditionalization (Tru64 Unix V4.0+ only now).
* If supported, use DEVGETINFO instead of DEVIOCGET for longer
* device names, and more information. If max_capacity, set by the user
* or random I/O and partition 'c' selected, set max capacity returned
* from the device driver. For random I/O, prevents lseek/read logic.
*
* April 12th, 2001 by Robin Miller.
* Delay setting device defaults, until later in setup_device_info().
* When random I/O is selected, setup device values for a disk device.
*
* April 11th, 2001 by Robin Miller.
* For Tru64 Unix, if the user specified a device type, don't ovrride
* it by calling dec_system_device_info(). If the user selected random I/O
* options, enable random access mode. Hey, the worse that can happen is
* they'll get an lseek() error! This handles special disk devices (LSM).
*
* February 6th, 2001 by Robin Miller.
* For Cygnus, map //./ and \\.\ directory prefixes to /dev. This
* is how physical (raw) devices *must* be mounted to work properly.
* If physical devices aren't mounted, Unix style EOM isn't emulated!
* Ensure same processing is performed on existing files and newly
* created files. This includes setting the fsync flag appropriately,
* and setting up the user capacity for random I/O as necessary.
*
* February 1st, 2001 by Robin Miller.
* Make changes to allow multiple slices to an existing file.
*
* January 28th, 2001 by Robin Miller.
* Allow dec_system_device_info() to be called prior to the file
* being opened. We need device info early on for new slices option.
*
* January 25th, 2001 by Robin Miller.
* Flag disk devices, via di_random_access boolean flag. This
* includes regular files, so seek and report functions work on them.
*
* January 24th, 2001 by Robin Miller.
* Setup the default device block size from device_size variable,
* so the user can override this using the "dsize=value" option. Also,
* various functions no longer have to check for this field being zero,
* so perhaps this will help performance too.
*
* January 2nd, 2001 by Robin Miller.
* Make changes to build using MKS/NuTCracker product.
*
* August 22nd, 2000 by Robin Miller.
* Update setup_device_defaults() to better set incr and min values,
* since this function may get called twice on Tru64 Unix systems.
*
* July 14th, 2000 by Robin Miller.
* Added logic to set file sync flag, if a block device or regular
* file is used. Tapes, FIFO's, and other devices do NOT like fsync()!
*
* April 2nd, 2000 by Robin Miller.
* Only set the random data limit when stat'ing the file,
* if it's a regular file, since the st_size might not be bytes.
* On QNX, st_size is the drive capacity, so setup user_capacity
* to avoid seek/read algorithm in FindCapacity() for random I/O.
*
* August 1, 1999 by Robin Miller.
* Do a better job in setup_device_defaults() setting up the
* defaults for min and increment counts. For disks, this value
* needs to be modulo the block size, since most disk drivers do
* not permit non-modulo requests (Tru64 UNIX CAM is exception).
*
* December 21, 1998 by Robin Miller.
* - for DUNIX, allocate mtget structure for tapes.
* - changes for Malloc(), which now clears memory.
*
* December 19, 1998 by Robin Miller.
* For Steel DUNIX, strip trailing spaces from device names.
*
* October 29, 1998 by Robin Miller.
* Implement a random I/O data limit, instead of using the normal
* data limit variable (not good to dual purpose this value).
*
* April 29, 1998 by Robin Miller.
* Add support for an alternate device directory.
*
* October 31, 1993 by Robin Miller.
* Enhance device type setup and honor user specified device type.
*
* October 29, 1993 by Robin Miller.
* Added more checks in setup_device_type() to determine detect
* selection of terminal devices (e.g., console device).
*
* September 15, 1993 by Robin Miller.
* Added stat() of file specified to check for FIFO's and set the
* device type appropriately so other checks could be done prior
* to opening the device (O_NONBLOCK needs set before open()).
*
*/
/*
* Forward References:
*/
static void setup_device_defaults(struct dinfo *dip);
static void SetupRegularFile(struct dinfo *dip, struct stat *sbp);
#if defined(__MSDOS__) || defined(__WIN32__) || defined(_NT_SOURCE)
static bool IsDriveLetter(char *bufptr);
#endif /* defined(__MSDOS__) || defined(__WIN32__) || defined(_NT_SOURCE) */
#if defined(ultrix) || defined(DEC)
static int SetupDiskAttributes(struct dinfo *dip, int fd);
#endif /* defined(ultrix) || defined(DEC) */
/************************************************************************
* *
* setup_device_type() - Setup the device type. *
* *
* Description: *
* This function sets up the specific device type to be tested. *
* Since each operating system has a different method of identifying *
* devices, so this table lookup allows the user to specify the device *
* type. Of course, this could be used to force errors. *
* *
* Inputs: str = Pointer to device type string. *
* *
* Return Value: *
* Returns pointer to device type table entry or NULL. *
* *
************************************************************************/
struct dtype dtype_table[] = {
{ "audio", DT_AUDIO },
{ "block", DT_BLOCK },
{ "character", DT_CHARACTER },
{ "comm", DT_COMM },
{ "disk", DT_DISK },
{ "graphics", DT_GRAPHICS },
{ "memory", DT_MEMORY },
{ "mmap", DT_MMAP },
{ "mouse", DT_MOUSE },
{ "network", DT_NETWORK },
{ "fifo", DT_FIFO },
{ "pipe", DT_PIPE },
{ "printer", DT_PRINTER },
{ "processor", DT_PROCESSOR },
{ "regular", DT_REGULAR },
{ "socket", DT_SOCKET },
{ "special", DT_SPECIAL },
{ "streams", DT_STREAMS },
{ "tape", DT_TAPE },
{ "terminal", DT_TERMINAL },
{ "unknown", DT_UNKNOWN }
};
int num_dtypes = sizeof(dtype_table) / sizeof(dtype_table[0]);
struct dtype *
setup_device_type (char *str)
{
int i;
struct dtype *dtp;
for (dtp = dtype_table, i = 0; i < num_dtypes; i++, dtp++) {
if (strcmp(str, dtp->dt_type) == 0) {
return (dtp);
}
}
fprintf (efp, "Device type '%s' is invalid, valid entrys are:\n", str);
for (dtp = dtype_table, i = 0; i < num_dtypes; i++, dtp++) {
if ( (i % 4) == 0) fprintf (efp, "\n");
fprintf (efp, " %-12s", dtp->dt_type);
}
fprintf (efp, "\n");
return ((struct dtype *) 0);
}
/************************************************************************
* *
* setup_device_defaults() - Setup the device defaults. *
* *
* Description: *
* This function sets up the specific device type defaults, for *
* test parameters which were not specified. *
* *
* Inputs: dip = The device information pointer. *
* *
* Return Value: *
* Returns pointer to device type table entry or NULL. *
* *
* Note: This function may get called twice! On Tru64 Unix, after we *
* open the device, the initial device type may get overridden. *
* *
************************************************************************/
static void
setup_device_defaults (struct dinfo *dip)
{
struct dtype *dtp = dip->di_dtype;
if ( (dtp->dt_dtype == DT_BLOCK) ||
(dtp->dt_dtype == DT_DISK) ||
(dtp->dt_dtype == DT_REGULAR) || dip->di_random_io ) {
/*
* Real device size should already been obtained.
*/
if (!device_size) device_size = BLOCK_SIZE;
if (!lbdata_size) lbdata_size = device_size;
if (max_size && !user_min) min_size = device_size;
if (min_size && !user_incr) incr_count = device_size;
/* Ensure min and incr values are non-zero! */
if (max_size && !min_size) min_size = device_size;
if (min_size && !incr_count) incr_count = device_size;
if (dip->di_random_io) {
if (!random_align) random_align = device_size;
}
if (fsync_flag == UNINITIALIZED) {
if ( (dtp->dt_dtype == DT_BLOCK) ||
(dtp->dt_dtype == DT_REGULAR) ) {
fsync_flag = TRUE;
} else if ( (dtp->dt_dtype == DT_DISK) )
/*
* Devices identified as DT_DISK should be the
* raw (character) device. Since some OS's,
* such as AIX don't like fsync() to disks,
* we'll disable it since it really only has
* meaning to block or regular (FS) files.
*/
fsync_flag = FALSE;
}
} else {
if (!device_size) device_size = 1;
if (max_size && !user_min) min_size = 1;
if (min_size && !user_incr) incr_count = 1;
/* Ensure min and incr values are non-zero! */
if (max_size && !min_size) min_size = 1;
if (min_size && !incr_count) incr_count = 1;
}
dip->di_trigger = trigger;
return;
}
/************************************************************************
* *
* os_system_device_info() - Get OS System Device Information. *
* *
* Description: *
* This function attempts to obtain device information necessary *
* for device specific testing, by using system dependent syscalls. *
* Note: This function is called _after_ the device/file is opened. *
* *
* Inputs: dip = The device information pointer. *
* *
* Return Value: *
* None. *
* *
************************************************************************/
#if defined(ultrix) || defined(DEC)
#include <sys/devio.h>
#include <sys/ioctl.h>
#if defined(DEC)
# include <sys/ioctl_compat.h>
# include <io/common/devgetinfo.h>
#endif /* defined(DEC) */
void
os_system_device_info (struct dinfo *dip)
{
struct devget devget, *devgp = NULL;
device_info_t devinfo, *devip = NULL;
int fd = dip->di_fd;
bool temp_fd = FALSE;
short category;
int i, status;
if (fd == NoFd) {
temp_fd = TRUE;
if ( (fd = open (dip->di_dname, (O_RDONLY | O_NDELAY))) < 0) {
return;
}
}
/*
* Attempt to obtain the device information.
*/
bzero ((char *) &devinfo, sizeof(devinfo));
if (ioctl (fd, DEVGETINFO, (char *) &devinfo) == SUCCESS) {
devip = &devinfo;
category = devip->v1.category;
if ( NEL (devip->v1.device, DEV_UNKNOWN, DEV_STRING_SIZE) ) {
dip->di_device = Malloc(DEV_STRING_SIZE + 1);
(void) strncpy (dip->di_device, devip->v1.device, DEV_STRING_SIZE);
} else if ( NEL (devip->v1.dev_name, DEV_UNKNOWN, DEV_STRING_SIZE) ) {
dip->di_device = Malloc(DEV_STRING_SIZE + 1);
(void) strncpy (dip->di_device, devip->v1.dev_name, DEV_STRING_SIZE);
}
if (dip->di_device) {
/*
* In Steel, device names have trailing spaces. grrr!
*/
for (i = (DEV_STRING_SIZE); i--; ) {
if ( isspace(dip->di_device[i]) ) {
dip->di_device[i] = '\0';
} else {
break;
}
}
}
} else { /* Try the old DEVIOCGET IOCTL... */
(void) bzero ((char *) &devget, sizeof(devget));
if (ioctl (fd, DEVIOCGET, (char *) &devget) < 0) {
if (temp_fd) (void)close(fd);
return;
}
devgp = &devget;
category = devgp->category;
if ( NEL (devgp->device, DEV_UNKNOWN, DEV_SIZE) ) {
dip->di_device = Malloc(DEV_SIZE + 1);
(void) strncpy (dip->di_device, devgp->device, DEV_SIZE);
} else if ( NEL (devgp->dev_name, DEV_UNKNOWN, DEV_SIZE) ) {
dip->di_device = Malloc(DEV_SIZE + 1);
(void) strncpy (dip->di_device, devgp->dev_name, DEV_SIZE);
}
if (dip->di_device) {
/*
* In Steel, device names have trailing spaces. grrr!
*/
for (i = (DEV_SIZE); i--; ) {
if ( isspace(dip->di_device[i]) ) {
dip->di_device[i] = '\0';
} else {
break;
}
}
}
}
/*
* Setup the device type based on the category.
*/
switch (category) {
case DEV_TAPE: /* Tape category. */
dip->di_dtype = setup_device_type("tape");
#if defined(EEI)
dip->di_mt = Malloc(sizeof(*dip->di_mt));
if (eei_flag) clear_eei_status(fd, TRUE);
dip->di_eei_sleep = EEI_SLEEP;
dip->di_eei_retries = EEI_RETRIES;
#endif /* defined(EEI) */
break;
case DEV_DISK: { /* Disk category. */
/*
* If using partition 'c', setup to use the whole capacity.
*
* Note: Only setup maximum capacity for random I/O, or else
* we will inhibit End of Media (EOM) testing.
*/
if (dip->di_random_io || num_slices) {
if (dip->di_dname[strlen(dip->di_dname)-1] == 'c') {
if ( !max_capacity && !user_capacity ) {
max_capacity = TRUE;
}
}
}
/****************************************************************
* Attempt to get disk attributes using DEVGETINFO first, since *
* for SCSI disks we get more information, which we plan to use *
* one day, and we also get the real block (sector) size. *
****************************************************************/
if (devip && (devip->version == VERSION_1) ) {
v1_disk_dev_info_t *diskinfo;
diskinfo = &devip->v1.devinfo.disk;
dip->di_dsize = diskinfo->blocksz;
if (!device_size) device_size = dip->di_dsize;
/*
* NOTE: capacity is whole disk, not the open partition,
* so we don't use it unless selected by the user.
*/
if (max_capacity) {
dip->di_capacity = diskinfo->capacity;
user_capacity = (dip->di_capacity * (large_t)dip->di_dsize);
if (debug_flag) {
Printf("DEVGETINFO Capacity: " LUF " blocks.\n", dip->di_capacity);
}
}
if (dip->di_dsize && !user_lbsize && !lbdata_size) {
lbdata_size = dip->di_dsize;
}
} else {
(void)SetupDiskAttributes(dip, fd);
}
dip->di_dtype = setup_device_type("disk");
/*
* TODO: Need to read disklabel to pickup partition sizes,
* and to check for mounted file systems. More work!
*/
break;
}
case DEV_TERMINAL: /* Terminal category. */
dip->di_dtype = setup_device_type("terminal");
break;
case DEV_PRINTER: /* Printer category. */
dip->di_dtype = setup_device_type("printer");
break;
case DEV_SPECIAL: /* Special category. */
/*
* On Tru64 Unix, LSM volumes are really disks!
*/
if (SetupDiskAttributes(dip, fd) != SUCCESS) {
dip->di_dtype = setup_device_type("special");
}
break;
default:
break;
}
if (temp_fd) (void)close(fd);
return;
}
/*
* SetupDiskAttributes() - Setup Disk Attributes using DEVGETGEOM.
*
* Description:
* This function is used for disk devices which don't support
* the newer DEVGETINFO IOCTL, like LSM devices.
*
* Inputs:
* dip = The device information pointer.
* fd = The file descriptor (NoFd == Not open).
*
* Outputs:
* Returns 0 or -1 for Success/Failure.
*/
static int
SetupDiskAttributes (struct dinfo *dip, int fd)
{
int status;
bool temp_fd = FALSE;
DEVGEOMST devgeom;
if (fd == NoFd) {
temp_fd = TRUE;
if ( (fd = open (dip->di_dname, O_RDONLY)) < 0) {
return (FAILURE);
}
}
/*
* If using partition 'c', setup to use the whole capacity.
*
* Note: Only setup maximum capacity for random I/O, or else
* we will inhibit End of Media (EOM) testing.
*/
if (dip->di_random_io || num_slices) {
if ( (dip->di_device && EQ(dip->di_device,"LSM")) ||
(dip->di_dname[strlen(dip->di_dname)-1] == 'c') ) {
if ( !max_capacity && !user_capacity ) {
max_capacity = TRUE;
}
}
}
/*
* Attempt to obtain the disk geometry. Works for LSM, etc.
*
* NOTE: DEVGETGEOM *fails* on read-only devices (shit!).
*/
bzero ((char *) &devgeom, sizeof(devgeom));
if ((status = ioctl (fd, DEVGETGEOM, (char *) &devgeom)) == SUCCESS) {
dip->di_dsize = devgeom.geom_info.sector_size;
if (!device_size) device_size = dip->di_dsize;
/*
* NOTE: dev_size is whole disk, not the open partition,
* so we don't use it unless selected by the user.
*/
if (max_capacity) {
dip->di_capacity = devgeom.geom_info.dev_size;
user_capacity = (dip->di_capacity * (large_t)dip->di_dsize);
if (debug_flag) {
Printf("DEVGETGEOM Capacity: " LUF " blocks.\n", dip->di_capacity);
}
}
if (dip->di_dsize && !user_lbsize && !lbdata_size) {
lbdata_size = dip->di_dsize;
}
dip->di_dtype = setup_device_type("disk");
}
/*
* TODO: Need to read disklabel to pickup partition sizes,
* and to check for mounted file systems. More work!
*/
if (temp_fd) (void)close(fd);
return (status);
}
#endif /* defined(ultrix) || defined(DEC) */
#if defined(HP_UX)
#include <sys/diskio.h>
#include <sys/scsi.h>
static int get_queue_depth(int fd, unsigned int *qdepth);
static int set_queue_depth(int fd, unsigned int qdepth);
void
os_system_device_info (struct dinfo *dip)
{
disk_describe_type disk_type, *disktp = &disk_type;
union inquiry_data inquiry;
int fd = dip->di_fd;
bool temp_fd = FALSE;
short category;
int i;
if (fd == NoFd) {
temp_fd = TRUE;
if ( (fd = open (dip->di_dname, (O_RDONLY | O_NDELAY))) < 0) {
return;
}
}
/*
* Attempt to obtain the device information.
*/
bzero ((char *) disktp, sizeof(*disktp));
if (ioctl (fd, DIOC_DESCRIBE, disktp) == SUCCESS) {
if (disktp->dev_type != UNKNOWN_DEV_TYPE) {
size_t size = sizeof(disktp->model_num);
dip->di_device = Malloc(size + 1);
(void) strncpy (dip->di_device, disktp->model_num, size);
/*
* Strip trailing spaces from the device name.
*/
for (i = size; i--; ) {
if ( isspace(dip->di_device[i]) ) {
dip->di_device[i] = '\0';
} else {
break;
}
}
dip->di_dsize = disktp->lgblksz;
/*
* Only setup capacity for random I/O, since we want to test
* end of file conditions on sequential reads and writes.
*/
if (dip->di_random_io || num_slices) {
if ( !max_capacity && !user_capacity ) {
max_capacity = TRUE;
dip->di_capacity = (disktp->maxsva + 1);
user_capacity = (dip->di_capacity * (large_t)dip->di_dsize);
if (debug_flag) {
Printf("DIOC_DESCRIBE Capacity: " LUF " blocks (%u byte blocks).\n",
dip->di_capacity, dip->di_dsize);
}
}
}
}
switch (disktp->dev_type) {
case CDROM_DEV_TYPE: /* CDROM device */
case DISK_DEV_TYPE: /* Disk device */
case WORM_DEV_TYPE: /* Write once read many optical device */
case MO_DEV_TYPE: /* Magneto Optical device */
dip->di_dtype = setup_device_type("disk");
if (dip->di_qdepth != 0xFFFFFFFF) {
(void)set_queue_depth(fd, dip->di_qdepth);
}
break;
case CTD_DEV_TYPE: /* Cartridge tape device */
dip->di_dtype = setup_device_type("tape");
break;
default:
break;
}
} else if (ioctl (fd, SIOC_INQUIRY, &inquiry) == SUCCESS) {
struct inquiry_2 *inq = (struct inquiry_2 *)&inquiry;
size_t size = sizeof(inq->product_id);
if (debug_flag) {
Printf("SIOC_INQUIRY device type %u\n", inq->dev_type);
}
dip->di_device = Malloc(size + 1);
(void) strncpy (dip->di_device, inq->product_id, size);
for (i = size; i--; ) {
if ( isspace(dip->di_device[i]) ) {
dip->di_device[i] = '\0';
} else {
break;
}
}
switch (inq->dev_type) {
case SCSI_DIRECT_ACCESS:
case SCSI_WORM:
case SCSI_CDROM:
case SCSI_MO:
dip->di_dtype = setup_device_type("disk");
if (dip->di_qdepth != 0xFFFFFFFF) {
(void)set_queue_depth(fd, dip->di_qdepth);
}
break;
case SCSI_SEQUENTIAL_ACCESS:
dip->di_dtype = setup_device_type("tape");
break;
default:
break;
}
}
if (temp_fd) (void)close(fd);
return;
}
static int
get_queue_depth(int fd, unsigned int *qdepth)
{
struct sioc_lun_limits lun_limits;
int status;
(void)memset(&lun_limits, '\0', sizeof(lun_limits));
if ( (status = ioctl(fd, SIOC_GET_LUN_LIMITS, &lun_limits)) < 0) {
if (debug_flag) {
perror("SIOC_SET_LUN_LIMITS failed");
}
} else {
*qdepth = lun_limits.max_q_depth;
}
return (status);
}
static int
set_queue_depth(int fd, unsigned int qdepth)
{
struct sioc_lun_limits lun_limits;
int status;
if (debug_flag) {
unsigned int qd;
if (get_queue_depth (fd, &qd) == 0) {
Printf("Current queue depth is %u\n", qd);
}
}
(void)memset(&lun_limits, '\0', sizeof(lun_limits));
lun_limits.max_q_depth = qdepth;
/*
* For performance testing, allow disabling tags.
*/
if (qdepth == 0) {
#if defined(SCTL_DISABLE_TAGS)
lun_limits.flags = SCTL_DISABLE_TAGS;
#else /* !defined(SCTL_DISABLE_TAGS) */
lun_limits.flags = 0;
#endif /* defined(SCTL_DISABLE_TAGS) */
} else {
lun_limits.flags = SCTL_ENABLE_TAGS;
}
if ( (status = ioctl(fd, SIOC_SET_LUN_LIMITS, &lun_limits)) < 0) {
if (debug_flag) {
perror("SIOC_SET_LUN_LIMITS failed");
}
} else if (debug_flag) {
Printf("Queue depth set to %u\n", qdepth);
}
return (status);
}
#endif /* defined(HP_UX) */
#if defined(__linux__)
/* Ugly stuff to avoid conflict with Linux BLOCK_SIZE definition. */
#undef BLOCK_SIZE
#include <linux/fs.h>
#undef BLOCK_SIZE
#define BLOCK_SIZE 512
void
os_system_device_info (struct dinfo *dip)
{
int fd = dip->di_fd;
bool temp_fd = FALSE;
unsigned long nr_sects;
int sect_size;
if (fd == NoFd) {
temp_fd = TRUE;
if ( (fd = open (dip->di_dname, (O_RDONLY | O_NDELAY))) < 0) {
return;
}
}
/*
* Try to obtain the sector size.
*/
if (ioctl (fd, BLKSSZGET, §_size) == SUCCESS) {
dip->di_dsize = sect_size;
if (debug_flag) {
Printf("BLKSSZGET Sector Size: %d bytes\n", sect_size);
}
}
/*
* If this IOCTL succeeds, we will assume it's a disk device.
*
* Note: The size returned is for the partition (thank-you!).
*/
if (ioctl (fd, BLKGETSIZE, &nr_sects) == SUCCESS) {
if (!dip->di_dsize) dip->di_dsize = BLOCK_SIZE;
/*
* Only setup capacity for random I/O, since we want to test
* end of file conditions on sequential reads and writes.
*/
if (dip->di_random_io || num_slices) {
if ( !max_capacity && !user_capacity ) {
max_capacity = TRUE;
dip->di_capacity = nr_sects;
user_capacity = (dip->di_capacity * (large_t)dip->di_dsize);
if (debug_flag) {
Printf("BLKGETSIZE Capacity: " LUF " blocks (%u byte blocks).\n",
dip->di_capacity, dip->di_dsize);
}
}
}
}
if (temp_fd) (void)close(fd);
return;
}
#endif /* defined(__linux__) */
/*
* Note: This function called after the device is opened!
*/
void
system_device_info (struct dinfo *dip)
{
if (dip->di_dtype == NULL) {
struct stat sb;
/*
* For regular files, set the fsync flag to flush writes.
* Note: This handles processing of *new* output files.
*/
if ( (fstat (dip->di_fd, &sb) == SUCCESS) &&
( S_ISREG(sb.st_mode) ) ) {
SetupRegularFile (dip, &sb);
} else {
dip->di_dtype = setup_device_type("unknown");
}
}
if (fsync_flag == UNINITIALIZED) { fsync_flag = FALSE; }
return;
}
/************************************************************************
* *
* setup_device_info() - Setup Initial Device Information. *
* *
* Description: *
* This function allocates a device information entry, and does *
* the initial setup of certain information based on known options. *
* This function is meant to be called prior to opening the device so *
* test specific functions are known for initial processing. *
* *
* Inputs: dname = The device name. *
* *
* Return Value: *
* Returns pointer to device information entry. *
* *
************************************************************************/
struct dinfo *
setup_device_info (char *dname, struct dtype *dtp)
{
struct dinfo *dip;
struct stat sb;
dip = (struct dinfo *) Malloc (sizeof(*dip));
dip->di_fd = NoFd;
dip->di_dname = dname;
#if defined(HP_UX)
dip->di_qdepth = qdepth;
#endif
dip->di_funcs = &generic_funcs;
if ( (io_dir == REVERSE) || (io_type == RANDOM_IO) ) {
dip->di_random_io = TRUE;
}
#if defined(AIO)
if (aio_flag) {
dip->di_funcs = &aio_funcs;
}
#endif /* defined(AIO) */
#if defined(MMAP)
if (mmap_flag) {
dip->di_funcs = &mmap_funcs;
dtp = setup_device_type("mmap");
}
#endif /* defined(MMAP) */
#if defined(ultrix) || defined(DEC) || defined(HP_UX) || defined(__linux__)
/*
* Must do this early on, to set device type and size.
*/
if (dtp == NULL) {
os_system_device_info (dip);
dtp = dip->di_dtype;
}
#endif /* defined(ultrix) || defined(DEC) || defined(HP_UX) || defined(__linux__) */
/*
* If user specified a device type, don't override it.
*/
if (dtp == NULL) {
/*
* Determine test functions based on device name.
*/
if ( (EQL (dname, DEV_PREFIX, DEV_LEN)) ||
(EQL (dname, ADEV_PREFIX, ADEV_LEN)) ||
(EQL (dname, NDEV_PREFIX, NDEV_LEN)) ) {
char *dentry;
if (EQL (dname, DEV_PREFIX, DEV_LEN)) {
dentry = (dname + DEV_LEN);
} else if (EQL (dname, ADEV_PREFIX, ADEV_LEN)) {
dentry = (dname + ADEV_LEN);
} else {
dentry = (dname + NDEV_LEN);
}
#if defined(__CYGWIN__)
/*
* Map //./ or \\.\ to /dev/ prefix for Cygnus raw mounts.
*/
{ if (NEL (dname, DEV_PREFIX, DEV_LEN)) {
char *cygnus_dname = Malloc(strlen(dname)+1);
(void)strcpy(cygnus_dname, DEV_PREFIX);
(void)strcat(cygnus_dname, dentry);
dip->di_dname = cygnus_dname;
}
}
#endif /* defined(__CYGWIN__) */
if ( (ttyport_flag == TRUE) ||
(EQL (dentry, TTY_NAME, TTY_LEN)) ||
(EQL (dentry, CONSOLE_NAME, CONSOLE_LEN)) ) {
dtp = setup_device_type("terminal");
} else if ( (EQL (dentry, TAPE_NAME, sizeof(TAPE_NAME)-1)) ||
(EQL (dentry, NTAPE_NAME, sizeof(NTAPE_NAME)-1)) ) {
dtp = setup_device_type("tape");
} else if ( (EQL (dentry, DISK_NAME, sizeof(DISK_NAME)-1)) ||
(EQL (dentry, RDISK_NAME, sizeof(RDISK_NAME)-1)) ) {
dtp = setup_device_type("disk");
} else if ( (EQL (dentry, CDROM_NAME, sizeof(CDROM_NAME)-1)) ||
(EQL (dentry, RCDROM_NAME, sizeof(RCDROM_NAME)-1)) ) {
dtp = setup_device_type("disk");
}
#if defined(__MSDOS__) || defined(__WIN32__) || defined(_NT_SOURCE)
if ( (dtp == NULL) && (IsDriveLetter (dentry)) ) {
dtp = setup_device_type("block");
}
#endif /* defined(__MSDOS__) || defined(__WIN32__) || defined(_NT_SOURCE) */
}
#if defined(FIFO)
if ( (dtp == NULL) &&
(stat (dname, &sb) == SUCCESS) ) {
if ( S_ISFIFO(sb.st_mode) ) {
verify_flag = FALSE;
dip->di_funcs = &fifo_funcs;
dtp = setup_device_type("fifo");
}
}
#endif /* defined(FIFO) */
if ( (dtp == NULL) &&
(strlen(dname) == 1) && (*dname == '-') ) {
dtp = setup_device_type("pipe");
}
if ( (dtp == NULL) &&
(stat (dname, &sb) == SUCCESS)) {
if ( S_ISBLK(sb.st_mode) ) {
dtp = setup_device_type("block");
if (fsync_flag == UNINITIALIZED) {
fsync_flag = TRUE;
}
} else if ( S_ISCHR(sb.st_mode) ) {
/*
* Character devices are NOT treated as disks!
*/
#if defined(ultrix) || defined(DEC)
if (SetupDiskAttributes(dip, dip->di_fd) != SUCCESS)
#endif
dtp = setup_device_type("character");
}
}
} /* if (dtp == NULL) */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* End of device type setup. Special setup follows. */
/* - - - - - - - - - - - - - - - - - - - - - - - - - */
/*
* Do special setup for certain device types.
*/
if (dip->di_dtype = dtp) {
#if defined(TTY)
if (dtp->dt_dtype == DT_TERMINAL) {
ttyport_flag = TRUE; /* this should go away... */
dip->di_funcs = &tty_funcs;
} else
#endif /* defined(TTY) */
if ( (dtp->dt_dtype == DT_BLOCK) ||
(dtp->dt_dtype == DT_DISK) || dip->di_random_io ) {
dip->di_random_access = TRUE;
}
setup_device_defaults (dip);
}
/*
* If the device size isn't set, then set it to our default.
*
* Note: This size is used for finding disk capacity, random I/O,
* variable requests, and reporting failing relative block.
*/
if ( !dip->di_dsize ) {
if (!device_size) {
device_size = BLOCK_SIZE;
}
dip->di_dsize = device_size;
}
/*
* Note: This handles *existing* input/output files.
*/
if (stat (dname, &sb) == SUCCESS) {
if ( S_ISREG(sb.st_mode) ) {
SetupRegularFile (dip, &sb);
if ( (dispose_mode == DELETE_FILE) && keep_existing && !num_procs) {
dispose_mode = KEEP_FILE; /* Keep existing files! */
}
}
#if defined(_QNX_SOURCE)
else if ( S_ISBLK(sb.st_mode) ) {
user_capacity = ((large_t)sb.st_size * (large_t)dip->di_dsize);
}
#endif /* defined(_QNX_SOURCE) */
} else if (errno == ENOENT) {
/*
* File doesn't exist, assume a regular file will be created,
*/
if (dtp == NULL) {
SetupRegularFile (dip, NULL);
}
}
return (dip);
}
static void
SetupRegularFile (struct dinfo *dip, struct stat *sbp)
{
/*
* If random I/O was selected, and a data or record limit was
* not specified (i.e. runtime=n), then setup the file size.
* This is necessary to limit random I/O within file size, or
* for newly created files setup capacity based on data limit.
*/
if ( (dip->di_random_io || num_slices) &&
(rdata_limit == (large_t)0) && !user_capacity) {
if (sbp && sbp->st_size) {
/*
* This MAX is done, so random I/O to a file can be
* duplicated when specifying the same random seed.
* If file size is used, and it's less than limit,
* then random limit gets set too low.
*/
if (data_limit != INFINITY) {
user_capacity = MAX(data_limit, (large_t)sbp->st_size);
} else {
user_capacity = (large_t) sbp->st_size;
}
} else if (data_limit != INFINITY) {
user_capacity = data_limit;
}
}
if (fsync_flag == UNINITIALIZED) {
fsync_flag = TRUE; /* Only has meaning when writing. */
}
dip->di_random_access = TRUE;
dip->di_dtype = setup_device_type("regular");
setup_device_defaults (dip);
}
#if defined(__MSDOS__) || defined(__WIN32__) || defined(_NT_SOURCE)
static bool
IsDriveLetter(char *bufptr)
{
/* Check for drive letters "[a-zA-Z]:" */
if ((strlen(bufptr) == 2) && (bufptr[1] == ':') &&
((bufptr[0] >= 'a') && (bufptr[0] <= 'z') ||
(bufptr[0] >= 'A') && (bufptr[0] <= 'Z'))) {
return (TRUE);
}
return (FALSE);
}
#endif /* defined(__MSDOS__) || defined(__WIN32__) || defined(_NT_SOURCE) */
syntax highlighted by Code2HTML, v. 0.9.1