mirror of
https://github.com/SDL-Hercules-390/hyperion.git
synced 2026-04-10 22:24:32 +02:00
1. Move SETMODE calls into hthreads functions. 2. Issue message that identifies source statement that failed.
2161 lines
77 KiB
C
2161 lines
77 KiB
C
/* SCSITAPE.C (c) Copyright "Fish" (David B. Trout), 2005-2012 */
|
|
/* Hercules SCSI tape handling module */
|
|
/* */
|
|
/* Released under "The Q Public License Version 1" */
|
|
/* (http://www.hercules-390.org/herclic.html) as modifications to */
|
|
/* Hercules. */
|
|
|
|
/* Original Author: "Fish" (David B. Trout) */
|
|
/* Prime Maintainer: "Fish" (David B. Trout) */
|
|
/* Secondary Maintainer: Ivan Warren */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* This module contains only the support for SCSI tapes. Please see */
|
|
/* the 'tapedev.c' (and possibly other) source module(s) for infor- */
|
|
/* mation regarding other supported emulated tape/media formats. */
|
|
/*-------------------------------------------------------------------*/
|
|
/*-------------------------------------------------------------------*/
|
|
/* Messages issued by this module are prefixed HHCTA3nn */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
#include "hstdinc.h"
|
|
#include "hercules.h"
|
|
#include "scsitape.h"
|
|
|
|
//#define ENABLE_TRACING_STMTS 1 // (Fish: DEBUGGING)
|
|
//#include "dbgtrace.h" // (Fish: DEBUGGING)
|
|
|
|
#if defined(OPTION_SCSI_TAPE)
|
|
|
|
// (the following is just a [slightly] shorter name for our own internal use)
|
|
#define SLOW_UPDATE_STATUS_TIMEOUT MAX_NORMAL_SCSI_DRIVE_QUERY_RESPONSE_TIMEOUT_USECS
|
|
#define MAX_GSTAT_FREQ_USECS 1000000 // (once per second max)
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Open a SCSI tape device */
|
|
/* */
|
|
/* If successful, the file descriptor is stored in the device block */
|
|
/* and the return value is zero. Otherwise the return value is -1. */
|
|
/* */
|
|
/* Note that not having a tape mounted is a non-fatal error (rc==0)) */
|
|
/* */
|
|
/* If the status indicates the tape is not mounted or a good status */
|
|
/* cannot otherwise be obtained, the file descriptor is CLOSED but */
|
|
/* THE RETURN CODE IS STILL == 0 !!!! */
|
|
/* */
|
|
/* ** WARNING! ** */
|
|
/* */
|
|
/* The caller MUST check for a valid (non-negative) file descriptor */
|
|
/* before trying to use it if this function returns 0 == success!! */
|
|
/* */
|
|
/* A success == 0 return means the device filename CAN be opened, */
|
|
/* but not necessarily that the file can be used! If the file cannot */
|
|
/* be used (i.e. no tape mounted), the file is CLOSED but the return */
|
|
/* code is still == 0. */
|
|
/* */
|
|
/* The return code is -1 ONLY when the filename itself is invalid */
|
|
/* and the device file thus cannot even be opened. */
|
|
/* */
|
|
/*-------------------------------------------------------------------*/
|
|
int open_scsitape (DEVBLK *dev, BYTE *unitstat, BYTE code)
|
|
{
|
|
/* Is an open for this device already in progress? */
|
|
if (dev->stape_mntdrq.link.Flink)
|
|
{
|
|
/* Yes. Device is good but no tape is mounted (yet) */
|
|
build_senseX( TAPE_BSENSE_TAPEUNLOADED, dev, unitstat, code );
|
|
return 0; // (quick exit; in progress == open success)
|
|
}
|
|
|
|
ASSERT( dev->fd < 0 ); // (sanity check)
|
|
dev->fd = -1;
|
|
dev->sstat = GMT_DR_OPEN( -1 );
|
|
|
|
/* Open the SCSI tape device */
|
|
dev->readonly = 0;
|
|
dev->fd = open_tape( dev->filename, O_RDWR | O_BINARY | O_NONBLOCK );
|
|
if (dev->fd < 0 && EROFS == errno )
|
|
{
|
|
dev->readonly = 1;
|
|
dev->fd = open_tape( dev->filename, O_RDONLY | O_BINARY | O_NONBLOCK );
|
|
}
|
|
|
|
/* Check for successful open */
|
|
if (dev->fd < 0)
|
|
{
|
|
WRMSG (HHC00205, "E", SSID_TO_LCSS(dev->ssid), dev->devnum,
|
|
dev->filename, "scsi", "open_tape()", strerror(errno));
|
|
build_senseX( TAPE_BSENSE_ITFERROR, dev, unitstat, code );
|
|
return -1; // (FATAL error; device cannot be opened)
|
|
}
|
|
define_BOT_pos( dev ); // (always after successful open)
|
|
|
|
/* Obtain the initial tape device/media status information */
|
|
/* and start the mount-monitoring thread if option enabled */
|
|
int_scsi_status_update( dev, 0 );
|
|
|
|
/* Asynchronous open now in progress? */
|
|
if (dev->stape_mntdrq.link.Flink)
|
|
{
|
|
/* Yes. Device is good but no tape is mounted (yet) */
|
|
build_senseX( TAPE_BSENSE_TAPEUNLOADED, dev, unitstat, code );
|
|
return 0; // (quick exit; in progress == open success)
|
|
}
|
|
|
|
/* Finish up the open process... */
|
|
if (STS_NOT_MOUNTED( dev ))
|
|
{
|
|
/* Intervention required if no tape is currently mounted.
|
|
Note: we return "success" because the filename is good
|
|
(device CAN be opened) but close the file descriptor
|
|
since there's no tape currently mounted on the drive.*/
|
|
#if !defined( _MSVC_ )
|
|
int fd = dev->fd;
|
|
dev->fd = -1;
|
|
close_tape( fd );
|
|
#endif // !_MSVC_
|
|
build_senseX( TAPE_BSENSE_TAPEUNLOADED, dev, unitstat, code );
|
|
return 0; // (because device file IS valid and CAN be opened)
|
|
}
|
|
|
|
/* Set variable length block processing to complete the open */
|
|
if (finish_scsitape_open( dev, unitstat, code ) != 0)
|
|
{
|
|
/* We cannot use this device; fail the open.
|
|
'finish_scsitape_open' has already issued
|
|
the error message and closed the device. */
|
|
return -1; // (open failure)
|
|
}
|
|
return 0; // (open success)
|
|
|
|
} /* end function open_scsitape */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Finish SCSI Tape open: sets variable length block i/o mode... */
|
|
/* Returns 0 == success, -1 = failure. */
|
|
/*-------------------------------------------------------------------*/
|
|
/* THIS FUNCTION IS AN INTEGRAL PART OF TAPE OPEN PROCESSING! */
|
|
/* If this function fails then the overall tape device open fails! */
|
|
/*-------------------------------------------------------------------*/
|
|
int finish_scsitape_open( DEVBLK *dev, BYTE *unitstat, BYTE code )
|
|
{
|
|
int rc; /* Return code */
|
|
int oflags; /* re-open flags */
|
|
struct mtop opblk; /* Area for MTIOCTOP ioctl */
|
|
|
|
/* Switch drive over to BLOCKING-mode i/o... */
|
|
|
|
close_tape( dev->fd );
|
|
oflags = O_BINARY | (dev->readonly ? O_RDONLY : O_RDWR);
|
|
VERIFY( (dev->fd = open_tape (dev->filename, oflags)) > 0);
|
|
|
|
/* Since a tape was just mounted, reset the blockid back to zero */
|
|
|
|
dev->blockid = 0;
|
|
dev->fenced = 0;
|
|
|
|
/* Set the tape device to process variable length blocks */
|
|
|
|
if (!STS_WR_PROT( dev ))
|
|
{
|
|
opblk.mt_op = MTSETBLK;
|
|
opblk.mt_count = 0;
|
|
|
|
rc = ioctl_tape (dev->fd, MTIOCTOP, (char*)&opblk);
|
|
|
|
if (rc < 0)
|
|
{
|
|
/* Device cannot be used; fail the open */
|
|
int save_errno = errno;
|
|
rc = dev->fd;
|
|
dev->fd = -1;
|
|
close_tape( rc );
|
|
errno = save_errno;
|
|
WRMSG (HHC00205, "E", SSID_TO_LCSS(dev->ssid), dev->devnum,
|
|
dev->filename, "scsi", "ioctl_tape(MTSETBLK)", strerror(errno));
|
|
build_senseX(TAPE_BSENSE_ITFERROR,dev,unitstat,code);
|
|
return -1; /* (fatal error) */
|
|
}
|
|
}
|
|
|
|
#if defined( HAVE_DECL_MTEWARN ) && HAVE_DECL_MTEWARN
|
|
|
|
// Try to request EOM/EOT (end-of-media/tape) early-warning
|
|
|
|
// Note: if it fails, oh well. There's no need to scare the
|
|
// user with a warning message. We'll either get the warning
|
|
// or we won't. Either way there's nothing we can do about it.
|
|
// We did the best we could.
|
|
|
|
if (!STS_WR_PROT( dev ))
|
|
{
|
|
opblk.mt_op = MTEWARN;
|
|
opblk.mt_count = dev->eotmargin;
|
|
|
|
ioctl_tape (dev->fd, MTIOCTOP, (char*)&opblk);
|
|
|
|
// (ignore any error; it either worked or it didn't)
|
|
}
|
|
|
|
#endif // defined( HAVE_DECL_MTEWARN ) && HAVE_DECL_MTEWARN
|
|
|
|
return 0; /* (success) */
|
|
|
|
} /* end function finish_scsitape_open */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Close SCSI tape device file */
|
|
/*-------------------------------------------------------------------*/
|
|
void close_scsitape(DEVBLK *dev)
|
|
{
|
|
int rc = 0;
|
|
|
|
obtain_lock( &sysblk.stape_lock );
|
|
|
|
// Remove drive from SCSIMOUNT thread's work queue...
|
|
|
|
if ( dev->stape_mntdrq.link.Flink) {
|
|
RemoveListEntry( &dev->stape_mntdrq.link );
|
|
InitializeListLink( &dev->stape_mntdrq.link );
|
|
}
|
|
|
|
// Remove drive from the STATUS thread's work queue...
|
|
|
|
if ( dev->stape_statrq.link.Flink) {
|
|
RemoveListEntry( &dev->stape_statrq.link );
|
|
InitializeListLink( &dev->stape_statrq.link );
|
|
}
|
|
|
|
// Close the file if it's open...
|
|
if (dev->fd >= 0)
|
|
{
|
|
if (dev->stape_close_rewinds)
|
|
{
|
|
struct mtop opblk;
|
|
// opblk.mt_op = MTLOAD; // (not sure which is more correct)
|
|
opblk.mt_op = MTREW;
|
|
opblk.mt_count = 1;
|
|
|
|
if ((rc = ioctl_tape ( dev->fd, MTIOCTOP, (char*)&opblk)) != 0)
|
|
{
|
|
WRMSG (HHC00205, "E", SSID_TO_LCSS(dev->ssid), dev->devnum,
|
|
dev->filename, "scsi", "ioctl_tape(MTREW)", strerror(errno));
|
|
}
|
|
}
|
|
|
|
// Close tape drive...
|
|
close_tape( dev->fd );
|
|
|
|
dev->fd = -1;
|
|
dev->blockid = -1;
|
|
dev->curfilen = 0;
|
|
dev->nxtblkpos = 0;
|
|
dev->prvblkpos = -1;
|
|
}
|
|
|
|
dev->sstat = GMT_DR_OPEN(-1); // (forced)
|
|
dev->fenced = (rc >= 0) ? 0 : 1;
|
|
|
|
release_lock( &sysblk.stape_lock );
|
|
|
|
} /* end function close_scsitape */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Read a block from a SCSI tape device */
|
|
/* */
|
|
/* If successful, return value is block length read. */
|
|
/* If a tapemark was read, the return value is zero, and the */
|
|
/* current file number in the device block is incremented. */
|
|
/* If error, return value is -1 and unitstat is set to CE+DE+UC */
|
|
/*-------------------------------------------------------------------*/
|
|
int read_scsitape (DEVBLK *dev, BYTE *buf, BYTE *unitstat,BYTE code)
|
|
{
|
|
int rc;
|
|
/* int save_errno; */
|
|
|
|
rc = read_tape (dev->fd, buf, MAX_BLKLEN);
|
|
|
|
if (rc >= 0)
|
|
{
|
|
dev->blockid++;
|
|
|
|
/* Increment current file number if tapemark was read */
|
|
if (rc == 0)
|
|
dev->curfilen++;
|
|
|
|
/* Return block length or zero if tapemark */
|
|
return rc;
|
|
}
|
|
|
|
/* Handle read error condition */
|
|
|
|
WRMSG (HHC00205, "E", SSID_TO_LCSS(dev->ssid), dev->devnum,
|
|
dev->filename, "scsi", "read_tape()", strerror(errno));
|
|
|
|
if ( STS_NOT_MOUNTED( dev ) )
|
|
build_senseX(TAPE_BSENSE_TAPEUNLOADED,dev,unitstat,code);
|
|
else
|
|
build_senseX(TAPE_BSENSE_READFAIL,dev,unitstat,code);
|
|
|
|
return -1;
|
|
|
|
} /* end function read_scsitape */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Write a block to a SCSI tape device */
|
|
/* */
|
|
/* If successful, return value is zero. */
|
|
/* If error, return value is -1 and unitstat is set to CE+DE+UC */
|
|
/*-------------------------------------------------------------------*/
|
|
int write_scsitape (DEVBLK *dev, BYTE *buf, U32 len,
|
|
BYTE *unitstat, BYTE code)
|
|
{
|
|
int rc;
|
|
int save_errno;
|
|
|
|
/* Write data block to SCSI tape device */
|
|
|
|
rc = write_tape (dev->fd, buf, len);
|
|
|
|
#if defined( _MSVC_ )
|
|
if (errno == ENOSPC)
|
|
dev->eotwarning = 1;
|
|
#endif
|
|
|
|
if (rc >= (int)len)
|
|
{
|
|
dev->blockid++;
|
|
return 0;
|
|
}
|
|
|
|
/* LINUX EOM BEHAVIOUR WHEN WRITING
|
|
|
|
When the end of medium early warning is encountered,
|
|
the current write is finished and the number of bytes
|
|
is returned. The next write returns -1 and errno is
|
|
set to ENOSPC. To enable writing a trailer, the next
|
|
write is allowed to proceed and, if successful, the
|
|
number of bytes is returned. After this, -1 and the
|
|
number of bytes are alternately returned until the
|
|
physical end of medium (or some other error) occurs.
|
|
*/
|
|
|
|
if (errno == ENOSPC)
|
|
{
|
|
int_scsi_status_update( dev, 0 );
|
|
|
|
rc = write_tape (dev->fd, buf, len);
|
|
|
|
if (rc >= (int)len)
|
|
{
|
|
dev->eotwarning = 1;
|
|
dev->blockid++;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* Handle write error condition... */
|
|
|
|
save_errno = errno;
|
|
{
|
|
WRMSG (HHC00205, "E", SSID_TO_LCSS(dev->ssid), dev->devnum,
|
|
dev->filename, "scsi", "write_tape()", strerror(errno));
|
|
|
|
int_scsi_status_update( dev, 0 );
|
|
}
|
|
errno = save_errno;
|
|
|
|
if ( STS_NOT_MOUNTED( dev ) )
|
|
build_senseX(TAPE_BSENSE_TAPEUNLOADED,dev,unitstat,code);
|
|
else
|
|
{
|
|
if (errno == EIO)
|
|
{
|
|
if(STS_EOT(dev))
|
|
build_senseX(TAPE_BSENSE_ENDOFTAPE,dev,unitstat,code);
|
|
else
|
|
build_senseX(TAPE_BSENSE_WRITEFAIL,dev,unitstat,code);
|
|
}
|
|
else
|
|
build_senseX(TAPE_BSENSE_ITFERROR,dev,unitstat,code);
|
|
}
|
|
|
|
return -1;
|
|
|
|
} /* end function write_scsitape */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Write a tapemark to a SCSI tape device */
|
|
/* */
|
|
/* If successful, return value is zero. */
|
|
/* If error, return value is -1 and unitstat is set to CE+DE+UC */
|
|
/*-------------------------------------------------------------------*/
|
|
int write_scsimark (DEVBLK *dev, BYTE *unitstat,BYTE code)
|
|
{
|
|
int rc, save_errno;
|
|
|
|
/* Write tape mark to SCSI tape */
|
|
|
|
rc = int_write_scsimark( dev );
|
|
|
|
#if defined( _MSVC_ )
|
|
if (errno == ENOSPC)
|
|
dev->eotwarning = 1;
|
|
#endif
|
|
|
|
if (rc >= 0)
|
|
return 0;
|
|
|
|
/* LINUX EOM BEHAVIOUR WHEN WRITING
|
|
|
|
When the end of medium early warning is encountered,
|
|
the current write is finished and the number of bytes
|
|
is returned. The next write returns -1 and errno is
|
|
set to ENOSPC. To enable writing a trailer, the next
|
|
write is allowed to proceed and, if successful, the
|
|
number of bytes is returned. After this, -1 and the
|
|
number of bytes are alternately returned until the
|
|
physical end of medium (or some other error) occurs.
|
|
*/
|
|
|
|
if (errno == ENOSPC)
|
|
{
|
|
int_scsi_status_update( dev, 0 );
|
|
|
|
if (int_write_scsimark( dev ) >= 0)
|
|
{
|
|
dev->eotwarning = 1;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* Handle write error condition... */
|
|
|
|
save_errno = errno;
|
|
{
|
|
WRMSG (HHC00205, "E", SSID_TO_LCSS(dev->ssid), dev->devnum,
|
|
dev->filename, "scsi", "write_scsimark()", strerror(errno));
|
|
|
|
int_scsi_status_update( dev, 0 );
|
|
}
|
|
errno = save_errno;
|
|
|
|
if ( STS_NOT_MOUNTED( dev ) )
|
|
{
|
|
build_senseX(TAPE_BSENSE_TAPEUNLOADED,dev,unitstat,code);
|
|
}
|
|
else
|
|
{
|
|
switch(errno)
|
|
{
|
|
case EIO:
|
|
if(STS_EOT(dev))
|
|
build_senseX(TAPE_BSENSE_ENDOFTAPE,dev,unitstat,code);
|
|
else
|
|
build_senseX(TAPE_BSENSE_WRITEFAIL,dev,unitstat,code);
|
|
break;
|
|
case ENOSPC:
|
|
build_senseX(TAPE_BSENSE_ENDOFTAPE,dev,unitstat,code);
|
|
break;
|
|
default:
|
|
build_senseX(TAPE_BSENSE_ITFERROR,dev,unitstat,code);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
|
|
} /* end function write_scsimark */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* (internal 'write_scsimark' helper function) */
|
|
/*-------------------------------------------------------------------*/
|
|
int int_write_scsimark (DEVBLK *dev) // (internal function)
|
|
{
|
|
int rc;
|
|
struct mtop opblk;
|
|
|
|
opblk.mt_op = MTWEOF;
|
|
opblk.mt_count = 1;
|
|
|
|
rc = ioctl_tape (dev->fd, MTIOCTOP, (char*)&opblk);
|
|
|
|
if (rc >= 0)
|
|
{
|
|
/* Increment current file number since tapemark was written */
|
|
/*dev->curfilen++;*/ /* (CCW processor handles this automatically
|
|
so there's no need for us to do it here) */
|
|
|
|
/* (tapemarks count as block identifiers too!) */
|
|
dev->blockid++;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Synchronize a SCSI tape device (i.e. commit its data to tape) */
|
|
/* */
|
|
/* If successful, return value is zero. */
|
|
/* If error, return value is -1 and unitstat is set to CE+DE+UC */
|
|
/*-------------------------------------------------------------------*/
|
|
int sync_scsitape (DEVBLK *dev, BYTE *unitstat,BYTE code)
|
|
{
|
|
int rc;
|
|
int save_errno;
|
|
struct mtop opblk;
|
|
|
|
/*
|
|
GA32-0566-02 ("IBM Tape Device Drivers - Programming
|
|
Reference"):
|
|
|
|
STIOCQRYPOS
|
|
|
|
"[...] A write filemark of count 0 is always issued to
|
|
the drive, which flushes all data from the buffers to
|
|
the tape media. After the write filemark completes, the
|
|
query is issued."
|
|
|
|
Write Tapemark
|
|
|
|
"[...] The WriteTapemark entry point may also be called
|
|
with the dwTapemarkCount parameter set to 0 and the
|
|
bImmediate parameter set to FALSE. This has the effect
|
|
of committing any uncommitted data written by previous
|
|
WriteFile calls ... to the media."
|
|
*/
|
|
|
|
opblk.mt_op = MTWEOF;
|
|
opblk.mt_count = 0; // (zero to force a commit)
|
|
|
|
if ((rc = ioctl_tape (dev->fd, MTIOCTOP, (char*)&opblk)) >= 0)
|
|
{
|
|
#if defined( _MSVC_ )
|
|
if (errno == ENOSPC)
|
|
dev->eotwarning = 1;
|
|
#endif
|
|
return 0; // (success)
|
|
}
|
|
|
|
/* LINUX EOM BEHAVIOUR WHEN WRITING
|
|
|
|
When the end of medium early warning is encountered,
|
|
the current write is finished and the number of bytes
|
|
is returned. The next write returns -1 and errno is
|
|
set to ENOSPC. To enable writing a trailer, the next
|
|
write is allowed to proceed and, if successful, the
|
|
number of bytes is returned. After this, -1 and the
|
|
number of bytes are alternately returned until the
|
|
physical end of medium (or some other error) occurs.
|
|
*/
|
|
|
|
if (errno == ENOSPC)
|
|
{
|
|
int_scsi_status_update( dev, 0 );
|
|
|
|
opblk.mt_op = MTWEOF;
|
|
opblk.mt_count = 0; // (zero to force a commit)
|
|
|
|
if ((rc = ioctl_tape (dev->fd, MTIOCTOP, (char*)&opblk)) >= 0)
|
|
{
|
|
dev->eotwarning = 1;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* Handle write error condition... */
|
|
|
|
save_errno = errno;
|
|
{
|
|
WRMSG (HHC00205, "E", SSID_TO_LCSS(dev->ssid), dev->devnum,
|
|
dev->filename, "scsi", "ioctl_tape(MTWEOF)", strerror(errno));
|
|
|
|
int_scsi_status_update( dev, 0 );
|
|
}
|
|
errno = save_errno;
|
|
|
|
if ( STS_NOT_MOUNTED( dev ) )
|
|
{
|
|
build_senseX(TAPE_BSENSE_TAPEUNLOADED,dev,unitstat,code);
|
|
}
|
|
else
|
|
{
|
|
switch(errno)
|
|
{
|
|
case EIO:
|
|
if(STS_EOT(dev))
|
|
build_senseX(TAPE_BSENSE_ENDOFTAPE,dev,unitstat,code);
|
|
else
|
|
build_senseX(TAPE_BSENSE_WRITEFAIL,dev,unitstat,code);
|
|
break;
|
|
case ENOSPC:
|
|
build_senseX(TAPE_BSENSE_ENDOFTAPE,dev,unitstat,code);
|
|
break;
|
|
default:
|
|
build_senseX(TAPE_BSENSE_ITFERROR,dev,unitstat,code);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
|
|
} /* end function sync_scsitape */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Forward space over next block of SCSI tape device */
|
|
/* */
|
|
/* If successful, return value is +1. */
|
|
/* If the block skipped was a tapemark, the return value is zero, */
|
|
/* and the current file number in the device block is incremented. */
|
|
/* If error, return value is -1 and unitstat is set to CE+DE+UC */
|
|
/*-------------------------------------------------------------------*/
|
|
int fsb_scsitape (DEVBLK *dev, BYTE *unitstat,BYTE code)
|
|
{
|
|
int rc;
|
|
int save_errno;
|
|
struct mtop opblk;
|
|
|
|
/* Forward space block on SCSI tape */
|
|
|
|
opblk.mt_op = MTFSR;
|
|
opblk.mt_count = 1;
|
|
|
|
rc = ioctl_tape (dev->fd, MTIOCTOP, (char*)&opblk);
|
|
|
|
if ( rc >= 0 )
|
|
{
|
|
dev->blockid++;
|
|
/* Return +1 to indicate forward space successful */
|
|
return +1;
|
|
}
|
|
|
|
/* Check for spacing over a tapemark... */
|
|
|
|
save_errno = errno;
|
|
{
|
|
int_scsi_status_update( dev, 0 );
|
|
}
|
|
errno = save_errno;
|
|
|
|
// PROGRAMMING NOTE: please see the "Programming Note" in the
|
|
// 'bsb_scsitape' function regarding usage of the 'EOF' status
|
|
// to detect spacing over tapemarks.
|
|
|
|
if ( EIO == errno && STS_EOF(dev) ) // (fwd-spaced over tapemark?)
|
|
{
|
|
dev->curfilen++;
|
|
dev->blockid++;
|
|
/* Return 0 to indicate tapemark was spaced over */
|
|
return 0;
|
|
}
|
|
|
|
/* Bona fide forward space block error ... */
|
|
|
|
save_errno = errno;
|
|
{
|
|
WRMSG (HHC00205, "E", SSID_TO_LCSS(dev->ssid), dev->devnum,
|
|
dev->filename, "scsi", "ioctl_tape(MTFSR)", strerror(errno));
|
|
}
|
|
errno = save_errno;
|
|
|
|
if ( STS_NOT_MOUNTED( dev ) )
|
|
{
|
|
build_senseX(TAPE_BSENSE_TAPEUNLOADED,dev,unitstat,code);
|
|
}
|
|
else
|
|
{
|
|
switch(errno)
|
|
{
|
|
case EIO:
|
|
if(STS_EOT(dev))
|
|
build_senseX(TAPE_BSENSE_ENDOFTAPE,dev,unitstat,code);
|
|
else
|
|
build_senseX(TAPE_BSENSE_READFAIL,dev,unitstat,code);
|
|
break;
|
|
case ENOSPC:
|
|
build_senseX(TAPE_BSENSE_ENDOFTAPE,dev,unitstat,code);
|
|
break;
|
|
default:
|
|
build_senseX(TAPE_BSENSE_ITFERROR,dev,unitstat,code);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
|
|
} /* end function fsb_scsitape */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Backspace to previous block of SCSI tape device */
|
|
/* */
|
|
/* If successful, return value is +1. */
|
|
/* If the block is a tapemark, the return value is zero, */
|
|
/* and the current file number in the device block is decremented. */
|
|
/* If error, return value is -1 and unitstat is set to CE+DE+UC */
|
|
/*-------------------------------------------------------------------*/
|
|
int bsb_scsitape (DEVBLK *dev, BYTE *unitstat,BYTE code)
|
|
{
|
|
int rc;
|
|
int save_errno;
|
|
struct mtop opblk;
|
|
struct mtget starting_mtget;
|
|
|
|
/* PROGRAMMING NOTE: There is currently no way to distinguish
|
|
** between a "normal" backspace-block error and a "backspaced-
|
|
** into-loadpoint" i/o error, since the only error indication
|
|
** we get [in response to a backspace block attempt] is simply
|
|
** 'EIO'. (Interrogating the status AFTER the fact (to see if
|
|
** we're positioned at loadpoint) doesn't tell us whether we
|
|
** were already positioned at loadpoint *before* the error was
|
|
** was encountered or whether we're only positioned at load-
|
|
** point because we *did* in fact backspace over the very first
|
|
** block on the tape (and are thus now, after the fact, sitting
|
|
** at loadpoint because we *did* backspace over a block but it
|
|
** just got an error for some reason).
|
|
**
|
|
** Thus, we have absolutely no choice here but to retrieve the
|
|
** status BEFORE we attempt the i/o to see if we're ALREADY at
|
|
** loadpoint. If we are, then we immediately return an error
|
|
** ("backspaced-into-loadpoint") *without* even attemting the
|
|
** i/o at all. If we're *not* already sitting at loadpoint how-
|
|
** ever, then we go ahead an attempt the i/o and then check for
|
|
** an error afterwards.
|
|
*/
|
|
|
|
/* Obtain tape status before backward space... */
|
|
int_scsi_status_update( dev, 0 );
|
|
|
|
/* (save the current status before the i/o in case of error) */
|
|
memcpy( &starting_mtget, &dev->mtget, sizeof( struct mtget ) );
|
|
|
|
/* Unit check if already at start of tape */
|
|
if ( STS_BOT( dev ) )
|
|
{
|
|
dev->eotwarning = 0;
|
|
build_senseX(TAPE_BSENSE_LOADPTERR,dev,unitstat,code);
|
|
return -1;
|
|
}
|
|
|
|
/* Attempt the backspace i/o...*/
|
|
opblk.mt_op = MTBSR;
|
|
opblk.mt_count = 1;
|
|
|
|
rc = ioctl_tape (dev->fd, MTIOCTOP, (char*)&opblk);
|
|
|
|
if ( rc >= 0 )
|
|
{
|
|
dev->blockid--;
|
|
/* Return +1 to indicate backspace successful */
|
|
return +1;
|
|
}
|
|
|
|
/* Retrieve new status after the [supposed] i/o error... */
|
|
save_errno = errno;
|
|
{
|
|
int_scsi_status_update( dev, 0 );
|
|
}
|
|
errno = save_errno;
|
|
|
|
/* Check for backspacing over tapemark... */
|
|
|
|
/* PROGRAMMING NOTE: on Windows, our scsi tape driver (w32stape.c)
|
|
** sets 'EOF' status whenever a tapemark is spaced over in EITHER
|
|
** direction (forward OR backward), whereas *nix operating systems
|
|
** do not. They set 'EOF' status only when FORWARD spacing over a
|
|
** tapemark but not when BACKSPACING over one.
|
|
**
|
|
** (Apparently the EOF status was actually meant to mean that the
|
|
** tape is "PHYSICALLY POSITIONED PAST [physical] eof" (i.e. past
|
|
** an "eof marker" (i.e. a tapemark)) and nothing more. That is to
|
|
** say, it is apparently NOT meant to mean a tapemark was passed
|
|
** over, but rather only that you're "POSITIONED PAST" a tapemark.)
|
|
**
|
|
** Therefore since 'EOF' status will thus *NEVER* be set whenever
|
|
** a tapemark is spaced over in the *BACKWARD* direction [on non-
|
|
** Windows operating systems], we need some other means of distin-
|
|
** guishing between true backspace-block i/o errors and ordinary
|
|
** spacing over a tapemark (which is NOT an i/o error but which
|
|
** *is* an "out of the ordinary" (unit exception) type of event).
|
|
**
|
|
** Extensive research on this issue has revealed the *ONLY* semi-
|
|
** reliable means of distinguishing between them is by checking
|
|
** the "file#" and "block#" fields of the status structure after
|
|
** the supposed i/o error. If the file# is one less than it was
|
|
** before and the block# is -1, then a tapemark was simply spaced
|
|
** over. If the file# and block# is anything else however, then
|
|
** the originally reported error was a bona-fide i/o error (i.e.
|
|
** the original backspace-block (MTBSR) actually *failed*).
|
|
**
|
|
** I say "semi-reliable" because comments seem to indicate that
|
|
** the "file#" and "block#" fields of the mtget status structure
|
|
** "are not always used". The best that I can tell however, is
|
|
** most *nix operating systems *do* seem to maintain them. Thus,
|
|
** for now, we're going to rely on their accuracy since without
|
|
** them there's really no way whatsoever to distingish between
|
|
** a normal backspacing over a tapemark unit exception condition
|
|
** and a bona-fide i/o error (other than doing our own SCSI i/o
|
|
** of course (which we don't support (yet))). -- Fish, May 2008
|
|
*/
|
|
if ( EIO == errno )
|
|
{
|
|
#if defined( _MSVC_ )
|
|
|
|
/* Windows always sets 'EOF' status whenever a tapemark is
|
|
spaced over in EITHER direction (forward OR backward) */
|
|
|
|
if ( STS_EOF(dev) ) /* (passed over tapemark?) */
|
|
|
|
#else // !defined( _MSVC_ )
|
|
|
|
/* Unix-type systems unfortunately do NOT set 'EOF' whenever
|
|
backspacing over a tapemark (see PROGRAMMING NOTE above),
|
|
so we need to check the status struct's file# and block#
|
|
fields instead... */
|
|
|
|
/* (passed over tapemark?) */
|
|
if (1
|
|
&& dev->mtget.mt_fileno == (starting_mtget.mt_fileno - 1)
|
|
&& dev->mtget.mt_blkno == -1
|
|
)
|
|
#endif // defined( _MSVC_ )
|
|
{
|
|
dev->curfilen--;
|
|
dev->blockid--;
|
|
/* Return 0 to indicate tapemark was spaced over */
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* Bona fide backspace block i/o error ... */
|
|
save_errno = errno;
|
|
{
|
|
WRMSG (HHC00205, "E", SSID_TO_LCSS(dev->ssid), dev->devnum,
|
|
dev->filename, "scsi", "ioctl_tape(MTBSR)", strerror(errno));
|
|
}
|
|
errno = save_errno;
|
|
|
|
if ( STS_NOT_MOUNTED( dev ) )
|
|
build_senseX(TAPE_BSENSE_TAPEUNLOADED,dev,unitstat,code);
|
|
else
|
|
{
|
|
if ( EIO == errno && STS_BOT(dev) )
|
|
{
|
|
dev->eotwarning = 0;
|
|
build_senseX(TAPE_BSENSE_LOADPTERR,dev,unitstat,code);
|
|
}
|
|
else
|
|
build_senseX(TAPE_BSENSE_LOCATEERR,dev,unitstat,code);
|
|
}
|
|
|
|
return -1;
|
|
|
|
} /* end function bsb_scsitape */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Forward space to next file of SCSI tape device */
|
|
/* */
|
|
/* If successful, the return value is zero, and the current file */
|
|
/* number in the device block is incremented. */
|
|
/* If error, return value is -1 and unitstat is set to CE+DE+UC */
|
|
/*-------------------------------------------------------------------*/
|
|
int fsf_scsitape (DEVBLK *dev, BYTE *unitstat,BYTE code)
|
|
{
|
|
int rc;
|
|
int save_errno;
|
|
struct mtop opblk;
|
|
|
|
/* Forward space file on SCSI tape */
|
|
|
|
opblk.mt_op = MTFSF;
|
|
opblk.mt_count = 1;
|
|
|
|
rc = ioctl_tape (dev->fd, MTIOCTOP, (char*)&opblk);
|
|
|
|
/* Since we have no idea how many blocks we've skipped over
|
|
(as a result of doing the forward-space file), we now have
|
|
no clue as to what the proper current blockid should be.
|
|
*/
|
|
dev->blockid = -1; // (actual position now unknown!)
|
|
|
|
if ( rc >= 0 )
|
|
{
|
|
dev->curfilen++;
|
|
return 0;
|
|
}
|
|
|
|
/* Handle error condition */
|
|
|
|
dev->fenced = 1; // (actual position now unknown!)
|
|
|
|
save_errno = errno;
|
|
{
|
|
WRMSG (HHC00205, "E", SSID_TO_LCSS(dev->ssid), dev->devnum,
|
|
dev->filename, "scsi", "ioctl_tape(MTFSF)", strerror(errno));
|
|
}
|
|
errno = save_errno;
|
|
|
|
if ( STS_NOT_MOUNTED( dev ) )
|
|
{
|
|
build_senseX(TAPE_BSENSE_TAPEUNLOADED,dev,unitstat,code);
|
|
}
|
|
else
|
|
{
|
|
switch(errno)
|
|
{
|
|
case EIO:
|
|
if(STS_EOT(dev))
|
|
build_senseX(TAPE_BSENSE_ENDOFTAPE,dev,unitstat,code);
|
|
else
|
|
build_senseX(TAPE_BSENSE_READFAIL,dev,unitstat,code);
|
|
break;
|
|
case ENOSPC:
|
|
build_senseX(TAPE_BSENSE_ENDOFTAPE,dev,unitstat,code);
|
|
break;
|
|
default:
|
|
build_senseX(TAPE_BSENSE_ITFERROR,dev,unitstat,code);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
|
|
} /* end function fsf_scsitape */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Backspace to previous file of SCSI tape device */
|
|
/* */
|
|
/* If successful, the return value is zero, and the current file */
|
|
/* number in the device block is decremented. */
|
|
/* If error, return value is -1 and unitstat is set to CE+DE+UC */
|
|
/*-------------------------------------------------------------------*/
|
|
int bsf_scsitape (DEVBLK *dev, BYTE *unitstat,BYTE code)
|
|
{
|
|
int rc;
|
|
int save_errno;
|
|
struct mtop opblk;
|
|
|
|
/* PROGRAMMING NOTE: There is currently no way to distinguish
|
|
** between a "normal" backspace-file error and a "backspaced-
|
|
** into-loadpoint" i/o error, since the only error indication
|
|
** we get [in response to a backspace file attempt] is simply
|
|
** 'EIO'. (Interrogating the status AFTER the fact (to see if
|
|
** we're positioned at loadpoint) doesn't tell us whether we
|
|
** were already positioned at loadpoint *before* the error was
|
|
** was encountered or whether we're only positioned ar load-
|
|
** point because we *did* in fact backspace over a BOT tape-
|
|
** mark on the tape (and are thus now, after the fact, sitting
|
|
** at loadpoint because we *did* backspace over a tape-mark
|
|
** but it just got an error for some reason).
|
|
**
|
|
** Thus, we have absolutely no choice here but to retrieve the
|
|
** status BEFORE we attempt the i/o to see if we're ALREADY at
|
|
** loadpoint. If we are, then we immediately return an error
|
|
** ("backspaced-into-loadpoint") *without* even attemting the
|
|
** i/o at all. If we're *not* already sitting at loadpoint how-
|
|
** ever, then we go ahead an attempt the i/o and then check for
|
|
** an error afterwards.
|
|
*/
|
|
|
|
/* Obtain tape status before backward space... (no choice!) */
|
|
int_scsi_status_update( dev, 0 );
|
|
|
|
/* Unit check if already at start of tape */
|
|
if ( STS_BOT( dev ) )
|
|
{
|
|
dev->eotwarning = 0;
|
|
build_senseX(TAPE_BSENSE_LOADPTERR,dev,unitstat,code);
|
|
return -1;
|
|
}
|
|
|
|
/* Attempt the backspace i/o...*/
|
|
|
|
opblk.mt_op = MTBSF;
|
|
opblk.mt_count = 1;
|
|
|
|
rc = ioctl_tape (dev->fd, MTIOCTOP, (char*)&opblk);
|
|
|
|
/* Since we have no idea how many blocks we've skipped over
|
|
(as a result of doing the back-space file), we now have
|
|
no clue as to what the proper current blockid should be.
|
|
*/
|
|
dev->blockid = -1; // (actual position now unknown!)
|
|
|
|
if ( rc >= 0 )
|
|
{
|
|
dev->curfilen--;
|
|
return 0;
|
|
}
|
|
|
|
/* Handle error condition */
|
|
|
|
dev->fenced = 1; // (actual position now unknown!)
|
|
|
|
save_errno = errno;
|
|
{
|
|
WRMSG (HHC00205, "E", SSID_TO_LCSS(dev->ssid), dev->devnum,
|
|
dev->filename, "scsi", "ioctl_tape(MTBSF)", strerror(errno));
|
|
}
|
|
errno = save_errno;
|
|
|
|
if ( STS_NOT_MOUNTED( dev ) )
|
|
build_senseX(TAPE_BSENSE_TAPEUNLOADED,dev,unitstat,code);
|
|
else
|
|
{
|
|
if ( EIO == errno && STS_BOT(dev) )
|
|
{
|
|
dev->eotwarning = 0;
|
|
build_senseX(TAPE_BSENSE_LOADPTERR,dev,unitstat,code);
|
|
}
|
|
else
|
|
build_senseX(TAPE_BSENSE_LOCATEERR,dev,unitstat,code);
|
|
}
|
|
|
|
return -1;
|
|
|
|
} /* end function bsf_scsitape */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Rewind an SCSI tape device */
|
|
/*-------------------------------------------------------------------*/
|
|
int rewind_scsitape(DEVBLK *dev,BYTE *unitstat,BYTE code)
|
|
{
|
|
int rc;
|
|
/* int save_errno; */
|
|
struct mtop opblk;
|
|
|
|
// opblk.mt_op = MTLOAD; // (not sure which is more correct)
|
|
opblk.mt_op = MTREW;
|
|
opblk.mt_count = 1;
|
|
|
|
rc = ioctl_tape (dev->fd, MTIOCTOP, (char*)&opblk);
|
|
|
|
if ( rc >= 0 )
|
|
{
|
|
dev->sstat |= GMT_BOT( -1 ); // (forced)
|
|
dev->blockid = 0;
|
|
dev->curfilen = 0;
|
|
dev->fenced = 0;
|
|
return 0;
|
|
}
|
|
|
|
dev->fenced = 1; // (because the rewind failed)
|
|
dev->blockid = -1; // (because the rewind failed)
|
|
dev->curfilen = -1; // (because the rewind failed)
|
|
|
|
WRMSG (HHC00205, "E", SSID_TO_LCSS(dev->ssid), dev->devnum,
|
|
dev->filename, "scsi", "ioctl_tape(MTREW)", strerror(errno));
|
|
|
|
if ( STS_NOT_MOUNTED( dev ) )
|
|
build_senseX(TAPE_BSENSE_TAPEUNLOADED,dev,unitstat,code);
|
|
else
|
|
build_senseX(TAPE_BSENSE_REWINDFAILED,dev,unitstat,code);
|
|
|
|
return -1;
|
|
|
|
} /* end function rewind_scsitape */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Rewind Unload a SCSI tape device (and CLOSE it too!) */
|
|
/*-------------------------------------------------------------------*/
|
|
void int_scsi_rewind_unload(DEVBLK *dev, BYTE *unitstat, BYTE code )
|
|
{
|
|
int rc;
|
|
struct mtop opblk;
|
|
|
|
// opblk.mt_op = MTUNLOAD; // (not sure which is more correct)
|
|
opblk.mt_op = MTOFFL;
|
|
opblk.mt_count = 1;
|
|
|
|
rc = ioctl_tape (dev->fd, MTIOCTOP, (char*)&opblk);
|
|
|
|
if ( rc >= 0 )
|
|
{
|
|
dev->fenced = 0;
|
|
|
|
if ( dev->ccwtrace || dev->ccwstep )
|
|
WRMSG (HHC00210, "I", SSID_TO_LCSS(dev->ssid), dev->devnum, dev->filename, "scsi");
|
|
|
|
// PR# tape/88: no sense with 'close_scsitape'
|
|
// attempting a rewind if the tape is unloaded!
|
|
|
|
dev->stape_close_rewinds = 0; // (skip rewind attempt)
|
|
|
|
close_scsitape( dev ); // (required for REW UNLD)
|
|
return;
|
|
}
|
|
|
|
dev->fenced = 1; // (because the rewind-unload failed)
|
|
dev->curfilen = -1; // (because the rewind-unload failed)
|
|
dev->blockid = -1; // (because the rewind-unload failed)
|
|
|
|
WRMSG (HHC00205, "E", SSID_TO_LCSS(dev->ssid), dev->devnum,
|
|
dev->filename, "scsi", "ioctl_tape(MTOFFL)", strerror( errno ) );
|
|
|
|
if ( STS_NOT_MOUNTED( dev ) )
|
|
build_senseX(TAPE_BSENSE_TAPEUNLOADED,dev,unitstat,code);
|
|
else
|
|
build_senseX(TAPE_BSENSE_REWINDFAILED,dev,unitstat,code);
|
|
|
|
} /* end function int_scsi_rewind_unload */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Erase Gap */
|
|
/*-------------------------------------------------------------------*/
|
|
int erg_scsitape( DEVBLK *dev, BYTE *unitstat, BYTE code )
|
|
{
|
|
#if defined( OPTION_SCSI_ERASE_GAP )
|
|
int rc;
|
|
|
|
if (!dev->stape_no_erg)
|
|
{
|
|
struct mtop opblk;
|
|
|
|
opblk.mt_op = MTERASE;
|
|
opblk.mt_count = 0; // (zero means "short" erase-gap)
|
|
|
|
rc = ioctl_tape( dev->fd, MTIOCTOP, (char*)&opblk );
|
|
|
|
#if defined( _MSVC_ )
|
|
if (errno == ENOSPC)
|
|
dev->eotwarning = 1;
|
|
#endif
|
|
|
|
if ( rc < 0 )
|
|
{
|
|
/* LINUX EOM BEHAVIOUR WHEN WRITING
|
|
|
|
When the end of medium early warning is encountered,
|
|
the current write is finished and the number of bytes
|
|
is returned. The next write returns -1 and errno is
|
|
set to ENOSPC. To enable writing a trailer, the next
|
|
write is allowed to proceed and, if successful, the
|
|
number of bytes is returned. After this, -1 and the
|
|
number of bytes are alternately returned until the
|
|
physical end of medium (or some other error) occurs.
|
|
*/
|
|
|
|
if (errno == ENOSPC)
|
|
{
|
|
int_scsi_status_update( dev, 0 );
|
|
|
|
opblk.mt_op = MTERASE;
|
|
opblk.mt_count = 0; // (zero means "short" erase-gap)
|
|
|
|
if ( (rc = ioctl_tape( dev->fd, MTIOCTOP, (char*)&opblk )) >= 0 )
|
|
dev->eotwarning = 1;
|
|
}
|
|
|
|
if ( rc < 0)
|
|
{
|
|
WRMSG (HHC00205, "E", SSID_TO_LCSS(dev->ssid), dev->devnum,
|
|
dev->filename, "scsi", "ioctl_tape(MTERASE)", strerror(errno));
|
|
build_senseX(TAPE_BSENSE_WRITEFAIL,dev,unitstat,code);
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0; // (success)
|
|
|
|
#else // !defined( OPTION_SCSI_ERASE_GAP )
|
|
|
|
UNREFERENCED ( dev );
|
|
UNREFERENCED ( code );
|
|
UNREFERENCED ( unitstat );
|
|
|
|
return 0; // (treat as nop)
|
|
|
|
#endif // defined( OPTION_SCSI_ERASE_GAP )
|
|
|
|
} /* end function erg_scsitape */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Data Security Erase */
|
|
/*-------------------------------------------------------------------*/
|
|
int dse_scsitape( DEVBLK *dev, BYTE *unitstat, BYTE code )
|
|
{
|
|
#if defined( OPTION_SCSI_ERASE_TAPE )
|
|
|
|
struct mtop opblk;
|
|
|
|
opblk.mt_op = MTERASE;
|
|
opblk.mt_count = 1; // (one means "long" erase-tape)
|
|
|
|
if ( ioctl_tape( dev->fd, MTIOCTOP, (char*)&opblk ) < 0 )
|
|
{
|
|
WRMSG (HHC00205, "E", SSID_TO_LCSS(dev->ssid), dev->devnum,
|
|
dev->filename, "scsi", "ioctl_tape(MTERASE)", strerror(errno));
|
|
build_senseX(TAPE_BSENSE_WRITEFAIL,dev,unitstat,code);
|
|
return -1;
|
|
}
|
|
|
|
return 0; // (success)
|
|
|
|
#else // !defined( OPTION_SCSI_ERASE_TAPE )
|
|
|
|
UNREFERENCED ( dev );
|
|
UNREFERENCED ( code );
|
|
UNREFERENCED ( unitstat );
|
|
|
|
return 0; // (treat as nop)
|
|
|
|
#endif // defined( OPTION_SCSI_ERASE_TAPE )
|
|
|
|
} /* end function dse_scsitape */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* readblkid_scsitape */
|
|
/*-------------------------------------------------------------------*/
|
|
/* Output values are returned in BIG-ENDIAN guest format... */
|
|
/*-------------------------------------------------------------------*/
|
|
int readblkid_scsitape ( DEVBLK* dev, BYTE* logical, BYTE* physical )
|
|
{
|
|
// ZZ FIXME: The two blockid fields that READ BLOCK ID
|
|
// are returning are the "Channel block ID" and "Device
|
|
// block ID" fields, which correspond directly to the
|
|
// SCSI "First block location" and "Last block location"
|
|
// fields (as returned by a READ POSITION scsi command),
|
|
// so we really SHOULD be doing our own direct scsi i/o
|
|
// for ourselves so we can retrieve BOTH of those values
|
|
// directly from the real/actual physical device itself,
|
|
// but until we can add code to Herc to do that, we must
|
|
// return the same value for each since ioctl(MTIOCPOS)
|
|
// only returns us one value (the logical position) and
|
|
// not both that we really prefer...
|
|
|
|
// (And for the record, we want the "Channel block ID"
|
|
// value, also known as the SCSI "First block location"
|
|
// value, also known as the >>LOGICAL<< value and *NOT*
|
|
// the absolute/physical device-relative value)
|
|
|
|
struct mtpos mtpos;
|
|
BYTE blockid[4];
|
|
|
|
if (ioctl_tape( dev->fd, MTIOCPOS, (char*) &mtpos ) < 0 )
|
|
{
|
|
/* Informative ERROR message if tracing */
|
|
|
|
int save_errno = errno;
|
|
{
|
|
if ( dev->ccwtrace || dev->ccwstep )
|
|
WRMSG(HHC90205, "D"
|
|
,SSID_TO_LCSS(dev->ssid)
|
|
,dev->devnum
|
|
,dev->filename
|
|
,"scsi", "ioctl_tape(MTTELL)"
|
|
,strerror(errno)
|
|
);
|
|
}
|
|
errno = save_errno;
|
|
|
|
return -1; // (errno should already be set)
|
|
}
|
|
|
|
// Convert MTIOCPOS value to guest BIG-ENDIAN format...
|
|
|
|
mtpos.mt_blkno = CSWAP32( mtpos.mt_blkno ); // (guest <- host)
|
|
|
|
// Handle emulated vs. physical tape-device block-id format issue...
|
|
|
|
blockid_actual_to_emulated( dev, (BYTE*)&mtpos.mt_blkno, blockid );
|
|
|
|
// Until we can add code to Herc to do direct SCSI i/o (so that
|
|
// we can retrieve BOTH values directly from the device itself),
|
|
// we have no choice but to return the same value for each since
|
|
// the ioctl(MTIOCPOS) call only returns the logical value and
|
|
// not also the physical value that we wish it would...
|
|
|
|
if (logical) memcpy( logical, &blockid[0], 4 );
|
|
if (physical) memcpy( physical, &blockid[0], 4 );
|
|
|
|
return 0; // (success)
|
|
|
|
} /* end function readblkid_scsitape */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* locateblk_scsitape */
|
|
/*-------------------------------------------------------------------*/
|
|
/* Input value is passed in little-endian host format... */
|
|
/*-------------------------------------------------------------------*/
|
|
int locateblk_scsitape ( DEVBLK* dev, U32 blockid, BYTE *unitstat, BYTE code )
|
|
{
|
|
int rc;
|
|
struct mtop mtop;
|
|
|
|
UNREFERENCED( unitstat ); // (not used)
|
|
UNREFERENCED( code ); // (not used)
|
|
|
|
// Convert the passed host-format blockid value into the proper
|
|
// 32-bit vs. 22-bit guest-format the physical device expects ...
|
|
|
|
blockid = CSWAP32( blockid ); // (guest <- host)
|
|
|
|
blockid_emulated_to_actual( dev, (BYTE*)&blockid, (BYTE*)&mtop.mt_count );
|
|
|
|
mtop.mt_count = CSWAP32( mtop.mt_count ); // (host <- guest)
|
|
mtop.mt_op = MTSEEK;
|
|
|
|
// Ask the actual hardware to do an actual physical locate...
|
|
|
|
if ((rc = ioctl_tape( dev->fd, MTIOCTOP, (char*)&mtop )) < 0)
|
|
{
|
|
int save_errno = errno;
|
|
{
|
|
if ( dev->ccwtrace || dev->ccwstep )
|
|
WRMSG(HHC00205, "W"
|
|
,SSID_TO_LCSS(dev->ssid)
|
|
,dev->devnum
|
|
,dev->filename
|
|
,"scsi", "ioctl_tape(MTSEEK)"
|
|
,strerror(errno)
|
|
);
|
|
}
|
|
errno = save_errno;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*********************************************************************/
|
|
/** **/
|
|
/** BLOCK-ID ADJUSTMENT FUNCTIONS **/
|
|
/** **/
|
|
/*********************************************************************/
|
|
/*
|
|
The following conversion functions compensate for the fact that
|
|
the emulated device type might actually be completely different
|
|
from the actual real [SCSI] device being used for the emulation.
|
|
|
|
That is to say, the actual SCSI device being used may actually
|
|
be a 3590 type device but is defined in Hercules as a 3480 (or
|
|
vice-versa). Thus while the device actually behaves as a 3590,
|
|
we need to emulate 3480 functionality instead (and vice-versa).
|
|
|
|
For 3480/3490 devices, the block ID has the following format:
|
|
|
|
__________ ________________________________________________
|
|
| Bit | Description |
|
|
|__________|________________________________________________|
|
|
| 0 | Direction Bit |
|
|
| | |
|
|
| | 0 Wrap 1 |
|
|
| | 1 Wrap 2 |
|
|
|__________|________________________________________________|
|
|
| 1-7 | Segment Number |
|
|
|__________|________________________________________________|
|
|
| 8-9 | Format Mode |
|
|
| | |
|
|
| | 00 3480 format |
|
|
| | 01 3480-2 XF format |
|
|
| | 10 3480 XF format |
|
|
| | 11 Reserved |
|
|
| | |
|
|
| | Note: The 3480 format does not support IDRC. |
|
|
|__________|________________________________________________|
|
|
| 10-31 | Logical Block Number |
|
|
|__________|________________________________________________|
|
|
|
|
For 3480's and 3490's, first block recorded on the tape has
|
|
a block ID value of X'01000000', whereas for 3590 devices the
|
|
block ID is a full 32 bits and the first block on the tape is
|
|
block ID x'00000000'.
|
|
|
|
For the 32-bit to 22-bit (and vice versa) conversion, we're
|
|
relying on (hoping really!) that an actual 32-bit block-id value
|
|
will never actually exceed 30 bits (1-bit wrap + 7-bit segment#
|
|
+ 22-bit block-id) since we perform the conversion by simply
|
|
splitting the low-order 30 bits of a 32-bit block-id into a sep-
|
|
arate 8-bit (wrap and segment#) and 22-bit (block-id) fields,
|
|
and then shifting them into their appropriate position (and of
|
|
course combining/appending them for the opposite conversion).
|
|
|
|
As such, this of course implies that we are thus treating the
|
|
wrap bit and 7-bit segment number values of a 3480/3490 "22-bit
|
|
format" blockid as simply the high-order 8 bits of an actual
|
|
30-bit physical blockid (which may or may not work properly on
|
|
actual SCSI hardware depending on how[*] it handles inaccurate
|
|
blockid values).
|
|
|
|
|
|
-----------------
|
|
|
|
[*] Most(?) [SCSI] devices treat the blockid value used in a
|
|
Locate CCW as simply an "approximate location" of where the
|
|
block in question actually resides on the physical tape, and
|
|
will, after positioning itself to the *approximate* physical
|
|
location of where the block is *believed* to reside, proceed
|
|
to then perform the final positioning at low-speed based on
|
|
its reading of its actual internally-recorded blockid values.
|
|
|
|
Thus, even when the supplied Locate block-id value is wrong,
|
|
the Locate should still succeed, albeit less efficiently since
|
|
it may be starting at a physical position quite distant from
|
|
where the actual block is actually physically located on the
|
|
actual media.
|
|
*/
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* blockid_emulated_to_actual */
|
|
/*-------------------------------------------------------------------*/
|
|
/* Locate CCW helper: convert guest-supplied 3480 or 3590 blockid */
|
|
/* to the actual SCSI hardware blockid format */
|
|
/* Both I/P AND O/P are presumed to be in BIG-ENDIAN guest format */
|
|
/*-------------------------------------------------------------------*/
|
|
void blockid_emulated_to_actual
|
|
(
|
|
DEVBLK *dev, // ptr to Hercules device
|
|
BYTE *emu_blkid, // ptr to i/p 4-byte block-id in guest storage
|
|
BYTE *act_blkid // ptr to o/p 4-byte block-id for actual SCSI i/o
|
|
)
|
|
{
|
|
if ( TAPEDEVT_SCSITAPE != dev->tapedevt )
|
|
{
|
|
memcpy( act_blkid, emu_blkid, 4 );
|
|
return;
|
|
}
|
|
|
|
#if defined(OPTION_SCSI_TAPE)
|
|
if (0x3590 == dev->devtype)
|
|
{
|
|
// 3590 being emulated; guest block-id is full 32-bits...
|
|
|
|
if (dev->stape_blkid_32)
|
|
{
|
|
// SCSI using full 32-bit block-ids too. Just copy as-is...
|
|
|
|
memcpy( act_blkid, emu_blkid, 4 );
|
|
}
|
|
else
|
|
{
|
|
// SCSI using 22-bit block-ids. Use low-order 30 bits
|
|
// of 32-bit guest-supplied blockid and convert it
|
|
// into a "22-bit format" blockid value for SCSI...
|
|
|
|
blockid_32_to_22 ( emu_blkid, act_blkid );
|
|
}
|
|
}
|
|
else // non-3590 being emulated; guest block-id is 22-bits...
|
|
{
|
|
if (dev->stape_blkid_32)
|
|
{
|
|
// SCSI using full 32-bit block-ids. Extract the wrap,
|
|
// segment# and 22-bit blockid bits from the "22-bit
|
|
// format" guest-supplied blockid value and combine
|
|
// (append) them into a contiguous low-order 30 bits
|
|
// of a 32-bit blockid value for SCSI to use...
|
|
|
|
blockid_22_to_32 ( emu_blkid, act_blkid );
|
|
}
|
|
else
|
|
{
|
|
// SCSI using 22-bit block-ids too. Just copy as-is...
|
|
|
|
memcpy( act_blkid, emu_blkid, 4 );
|
|
}
|
|
}
|
|
#endif /* defined(OPTION_SCSI_TAPE) */
|
|
|
|
} /* end function blockid_emulated_to_actual */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* blockid_actual_to_emulated */
|
|
/*-------------------------------------------------------------------*/
|
|
/* Read Block Id CCW helper: convert an actual SCSI block-id */
|
|
/* to guest emulated 3480/3590 format */
|
|
/* Both i/p and o/p are presumed to be in big-endian guest format */
|
|
/*-------------------------------------------------------------------*/
|
|
void blockid_actual_to_emulated
|
|
(
|
|
DEVBLK *dev, // ptr to Hercules device (for 'devtype')
|
|
BYTE *act_blkid, // ptr to i/p 4-byte block-id from actual SCSI i/o
|
|
BYTE *emu_blkid // ptr to o/p 4-byte block-id in guest storage
|
|
)
|
|
{
|
|
if ( TAPEDEVT_SCSITAPE != dev->tapedevt )
|
|
{
|
|
memcpy( emu_blkid, act_blkid, 4 );
|
|
return;
|
|
}
|
|
|
|
#if defined(OPTION_SCSI_TAPE)
|
|
if (dev->stape_blkid_32)
|
|
{
|
|
// SCSI using full 32-bit block-ids...
|
|
if (0x3590 == dev->devtype)
|
|
{
|
|
// Emulated device is a 3590 too. Just copy as-is...
|
|
memcpy( emu_blkid, act_blkid, 4 );
|
|
}
|
|
else
|
|
{
|
|
// Emulated device using 22-bit format. Convert...
|
|
blockid_32_to_22 ( act_blkid, emu_blkid );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// SCSI using 22-bit format block-ids...
|
|
if (0x3590 == dev->devtype)
|
|
{
|
|
// Emulated device using full 32-bit format. Convert...
|
|
blockid_22_to_32 ( act_blkid, emu_blkid );
|
|
}
|
|
else
|
|
{
|
|
// Emulated device using 22-bit format too. Just copy as-is...
|
|
memcpy( emu_blkid, act_blkid, 4 );
|
|
}
|
|
}
|
|
#endif /* defined(OPTION_SCSI_TAPE) */
|
|
|
|
} /* end function blockid_actual_to_emulated */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* blockid_32_to_22 */
|
|
/*-------------------------------------------------------------------*/
|
|
/* Convert a 3590 32-bit blockid into 3480 "22-bit format" blockid */
|
|
/* Both i/p and o/p are presumed to be in big-endian guest format */
|
|
/*-------------------------------------------------------------------*/
|
|
void blockid_32_to_22 ( BYTE *in_32blkid, BYTE *out_22blkid )
|
|
{
|
|
out_22blkid[0] = ((in_32blkid[0] << 2) & 0xFC) | ((in_32blkid[1] >> 6) & 0x03);
|
|
out_22blkid[1] = in_32blkid[1] & 0x3F;
|
|
out_22blkid[2] = in_32blkid[2];
|
|
out_22blkid[3] = in_32blkid[3];
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* blockid_22_to_32 */
|
|
/*-------------------------------------------------------------------*/
|
|
/* Convert a 3480 "22-bit format" blockid into a 3590 32-bit blockid */
|
|
/* Both i/p and o/p are presumed to be in big-endian guest format */
|
|
/*-------------------------------------------------------------------*/
|
|
void blockid_22_to_32 ( BYTE *in_22blkid, BYTE *out_32blkid )
|
|
{
|
|
out_32blkid[0] = (in_22blkid[0] >> 2) & 0x3F;
|
|
out_32blkid[1] = ((in_22blkid[0] << 6) & 0xC0) | (in_22blkid[1] & 0x3F);
|
|
out_32blkid[2] = in_22blkid[2];
|
|
out_32blkid[3] = in_22blkid[3];
|
|
}
|
|
|
|
/*********************************************************************/
|
|
/** **/
|
|
/** INTERNAL STATUS & AUTOMOUNT FUNCTIONS **/
|
|
/** **/
|
|
/*********************************************************************/
|
|
|
|
/* Forward references... */
|
|
static int int_scsi_status_wait ( DEVBLK* dev, int usecs );
|
|
static void* get_stape_status_thread ( void* notused );
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* get_stape_status_thread */
|
|
/*-------------------------------------------------------------------*/
|
|
static
|
|
void* get_stape_status_thread( void* notused )
|
|
{
|
|
LIST_ENTRY* pListEntry;
|
|
STSTATRQ* req;
|
|
DEVBLK* dev = NULL;
|
|
struct mtget mtget;
|
|
int timeout;
|
|
char buf[64];
|
|
|
|
UNREFERENCED(notused);
|
|
MSGBUF( buf, "SCSI-TAPE status monitor");
|
|
WRMSG( HHC00100, "I", thread_id(), get_thread_priority(0), buf );
|
|
|
|
// PROGRAMMING NOTE: it is EXTREMELY IMPORTANT that the status-
|
|
// retrieval thread (i.e. ourselves) be set to a priority that
|
|
// is AT LEAST one priority slot ABOVE what the device-threads
|
|
// are currently set to in order to prevent their request for
|
|
// new/updated status from erroneously timing out (thereby mis-
|
|
// leading them to mistakenly believe no tape is mounted when
|
|
// in acuality there is!). The issue is, the caller only waits
|
|
// for so long for us to return the status to them so we better
|
|
// ensure we return it to them in a timely fashion else they be
|
|
// mislead to believe there's no tape mounted (since, by virtue
|
|
// of their request having timed out, they presume no tape is
|
|
// mounted since the retrieval took too long (which only occurs
|
|
// whenever (duh!) there's no tape mounted!)). Thus, if there
|
|
// *is* a tape mounted, we better be DARN sure to return them
|
|
// the status as quickly as possible in order to prevent their
|
|
// wait from timing out. We ensure this by setting our own pri-
|
|
// ority HIGHER than theirs.
|
|
|
|
// PROGRAMMING NOTE: currently, it looks like each priority slot
|
|
// differs from each other priority slot by '8' units, which is
|
|
// why we use the value '10' here (to ensure OUR priority gets
|
|
// set to the next higher slot). If this ever changes then the
|
|
// below code will need to be adjusted appropriately. -- Fish
|
|
|
|
set_thread_priority( 0, (sysblk.devprio - 10) );
|
|
|
|
obtain_lock( &sysblk.stape_lock );
|
|
|
|
do
|
|
{
|
|
sysblk.stape_getstat_busy = 1;
|
|
broadcast_condition( &sysblk.stape_getstat_cond );
|
|
|
|
// Process all work items currently in our queue...
|
|
|
|
while (!IsListEmpty( &sysblk.stape_status_link ) && !sysblk.shutdown)
|
|
{
|
|
pListEntry = RemoveListHead( &sysblk.stape_status_link );
|
|
InitializeListLink( pListEntry );
|
|
req = CONTAINING_RECORD( pListEntry, STSTATRQ, link );
|
|
dev = req->dev;
|
|
|
|
// Status queries limited GLOBALLY to one per second,
|
|
// since there's no way of knowing whether a drive is
|
|
// on the same or different bus as the other drive(s).
|
|
|
|
for
|
|
(
|
|
timeout = 0
|
|
;
|
|
1
|
|
&& !sysblk.shutdown
|
|
&& sysblk.stape_query_status_tod.tv_sec
|
|
&& !(timeout = timed_wait_condition_relative_usecs
|
|
(
|
|
&sysblk.stape_getstat_cond,
|
|
&sysblk.stape_lock,
|
|
MAX_GSTAT_FREQ_USECS,
|
|
&sysblk.stape_query_status_tod
|
|
))
|
|
;
|
|
);
|
|
|
|
if (!sysblk.shutdown)
|
|
{
|
|
// Query drive status...
|
|
|
|
// Since this may take quite a while to do if there's no tape
|
|
// mounted, we release the lock before attempting to retrieve
|
|
// the status and then re-acquire it afterwards...
|
|
|
|
release_lock( &sysblk.stape_lock );
|
|
{
|
|
define_BOT_pos( dev ); // (always before MTIOCGET)
|
|
|
|
// NOTE: the following may take up to *>10<* seconds to
|
|
// complete on Windows whenever there's no tape mounted,
|
|
// but apparently only with certain hardware. On a fast
|
|
// quad-cpu Windows 2003 Server system with an Adaptec
|
|
// AHA2944UW SCSI control for example, it completes right
|
|
// away (i.e. IMMEDIATELY), whereas on a medium dual-proc
|
|
// Windows 2000 Server system with TEKRAM SCSI controller
|
|
// it takes *>> 10 <<* seconds!...
|
|
|
|
if (0 == ioctl_tape( dev->fd, MTIOCGET, (char*)&mtget ))
|
|
{
|
|
memcpy( &dev->mtget, &mtget, sizeof( mtget ));
|
|
}
|
|
}
|
|
obtain_lock( &sysblk.stape_lock );
|
|
|
|
broadcast_condition( &dev->stape_sstat_cond );
|
|
gettimeofday( &sysblk.stape_query_status_tod, NULL );
|
|
}
|
|
|
|
} // end while (!IsListEmpty)...
|
|
|
|
if (!sysblk.shutdown)
|
|
{
|
|
// Sleep until more/new work arrives...
|
|
|
|
sysblk.stape_getstat_busy = 0;
|
|
broadcast_condition( &sysblk.stape_getstat_cond );
|
|
wait_condition( &sysblk.stape_getstat_cond,
|
|
&sysblk.stape_lock );
|
|
}
|
|
}
|
|
while (!sysblk.shutdown);
|
|
|
|
// (discard all work items since we're going away)
|
|
|
|
while (!IsListEmpty( &sysblk.stape_status_link ))
|
|
{
|
|
pListEntry = RemoveListHead( &sysblk.stape_status_link );
|
|
InitializeListLink( pListEntry );
|
|
}
|
|
|
|
WRMSG(HHC00101, "I", thread_id(), get_thread_priority(0), buf);
|
|
|
|
sysblk.stape_getstat_busy = 0;
|
|
sysblk.stape_getstat_tid = 0;
|
|
broadcast_condition( &sysblk.stape_getstat_cond );
|
|
release_lock( &sysblk.stape_lock );
|
|
|
|
return NULL;
|
|
|
|
} /* end function get_stape_status_thread */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* int_scsi_status_wait */
|
|
/*-------------------------------------------------------------------*/
|
|
static
|
|
int int_scsi_status_wait( DEVBLK* dev, int usecs )
|
|
{
|
|
int rc;
|
|
|
|
if (unlikely( dev->fd < 0 )) // (has drive been opened yet?)
|
|
return -1; // (cannot proceed until it is)
|
|
|
|
obtain_lock( &sysblk.stape_lock );
|
|
|
|
// Create the status retrieval thread if it hasn't been yet.
|
|
// We do the actual retrieval of the status in a worker thread
|
|
// because retrieving the status from a drive that doesn't have
|
|
// a tape mounted may take a long time (at least on Windows).
|
|
|
|
if (unlikely( !sysblk.stape_getstat_tid ))
|
|
{
|
|
int rc;
|
|
VERIFY
|
|
(
|
|
(rc = create_thread
|
|
(
|
|
&sysblk.stape_getstat_tid,
|
|
JOINABLE,
|
|
get_stape_status_thread,
|
|
NULL,
|
|
"get_stape_status_thread"
|
|
))
|
|
== 0
|
|
);
|
|
if (rc)
|
|
WRMSG( HHC00102, "E", strerror( rc ));
|
|
}
|
|
|
|
// Add our request to its work queue if needed...
|
|
|
|
if (!dev->stape_statrq.link.Flink)
|
|
{
|
|
InsertListTail( &sysblk.stape_status_link, &dev->stape_statrq.link );
|
|
}
|
|
|
|
// Wake up the status retrieval thread (if needed)...
|
|
|
|
if (!sysblk.stape_getstat_busy)
|
|
{
|
|
broadcast_condition( &sysblk.stape_getstat_cond );
|
|
}
|
|
|
|
// Wait only so long for the status to be updated...
|
|
|
|
rc = timed_wait_condition_relative_usecs
|
|
(
|
|
&dev->stape_sstat_cond, // ptr to condition to wait on
|
|
&sysblk.stape_lock, // ptr to controlling lock (must be held!)
|
|
usecs, // max #of microseconds to wait
|
|
NULL // [OPTIONAL] ptr to tod value (may be NULL)
|
|
);
|
|
|
|
release_lock( &sysblk.stape_lock );
|
|
return rc;
|
|
} /* end function int_scsi_status_wait */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Check if a SCSI tape is positioned past the EOT reflector or not */
|
|
/*-------------------------------------------------------------------*/
|
|
int passedeot_scsitape( DEVBLK *dev )
|
|
{
|
|
return dev->eotwarning; // (1==past EOT reflector; 0==not)
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Determine if the tape is Ready (tape drive door status) */
|
|
/* Returns: true/false: 1 = ready, 0 = NOT ready */
|
|
/*-------------------------------------------------------------------*/
|
|
int is_tape_mounted_scsitape( DEVBLK *dev, BYTE *unitstat, BYTE code )
|
|
{
|
|
UNREFERENCED(unitstat);
|
|
UNREFERENCED(code);
|
|
|
|
/* Update tape mounted status */
|
|
int_scsi_status_update( dev, 1 ); // (safe/fast internal call)
|
|
|
|
return ( STS_MOUNTED( dev ) );
|
|
} /* end function driveready_scsitape */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Force a manual status refresh/update (DANGEROUS!) */
|
|
/*-------------------------------------------------------------------*/
|
|
int update_status_scsitape( DEVBLK *dev ) // (external tmh call)
|
|
{
|
|
// * * WARNING! * *
|
|
|
|
// PROGRAMMING NOTE: do NOT call this function indiscriminately,
|
|
// as doing so COULD cause improper functioning of the guest o/s!
|
|
|
|
// How? Simple: if there's already a tape job running on the guest
|
|
// using the tape drive and we just so happen to request a status
|
|
// update at the precise moment a guest i/o encounters a tapemark,
|
|
// it's possible for US to receive the "tapemark" status and thus
|
|
// cause the guest to end up NOT SEEING the tapemark! Therefore,
|
|
// you should ONLY call this function whenever the current status
|
|
// indicates there's no tape mounted. If the current status says
|
|
// there *is* a tape mounted, you must NOT call this function!
|
|
|
|
// If the current status says there's a tape mounted and the user
|
|
// knows this to be untrue (e.g. they manually unloaded it maybe)
|
|
// then to kick off the auto-scsi-mount thread they must manually
|
|
// issue the 'devinit' command themselves. We CANNOT presume that
|
|
// a "mounted" status is bogus. We can ONLY safely presume that a
|
|
// "UNmounted" status may possibly be bogus. Thus we only ask for
|
|
// a status refresh if the current status is "not mounted" but we
|
|
// purposely do NOT force a refresh if the status is "mounted"!!
|
|
|
|
if ( STS_NOT_MOUNTED( dev ) ) // (if no tape mounted)
|
|
int_scsi_status_update( dev, 0 ); // (then probably safe)
|
|
|
|
return 0;
|
|
} /* end function update_status_scsitape */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Update SCSI tape status (and display it if CCW tracing is active) */
|
|
/*-------------------------------------------------------------------*/
|
|
void int_scsi_status_update( DEVBLK* dev, int mountstat_only )
|
|
{
|
|
create_automount_thread( dev ); // (only if needed of course)
|
|
|
|
// PROGRAMMING NOTE: only normal i/o requests (as well as the
|
|
// scsi_tapemountmon_thread thread whenever AUTO_SCSI_MOUNT is
|
|
// enabled and active) ever actually call us with mountstat_only
|
|
// set to zero (in order to update our actual status value).
|
|
//
|
|
// Thus if we're called with a non-zero mountstat_only argument
|
|
// (meaning all the caller is interested in is whether or not
|
|
// there's a tape mounted on the drive (which only the panel
|
|
// and GUI threads normally do (and which they do continuously
|
|
// whenever they do do it!))) then we simply return immediately
|
|
// so as to cause the caller to continue using whatever status
|
|
// happens to already be set for the drive (which should always
|
|
// be accurate).
|
|
//
|
|
// This prevents us from continuously "banging on the drive"
|
|
// asking for the status when in reality the status we already
|
|
// have should already be accurate (since it is updated after
|
|
// every i/o or automatically by the auto-mount thread)
|
|
|
|
if (likely(mountstat_only)) // (if only want mount status)
|
|
return; // (then current should be ok)
|
|
|
|
// Update status...
|
|
|
|
if (likely(STS_MOUNTED( dev )))
|
|
{
|
|
// According to our current status value there is a tape mounted,
|
|
// so we should wait for a full/complete/accurate status update,
|
|
// regardless of however long that may take...
|
|
|
|
int rc;
|
|
while (ETIMEDOUT == (rc = int_scsi_status_wait( dev,
|
|
MAX_GSTAT_FREQ_USECS + (2 * SLOW_UPDATE_STATUS_TIMEOUT) )))
|
|
{
|
|
if ( dev->ccwtrace || dev->ccwstep )
|
|
{
|
|
// "%1d:%04X Tape status retrieval timeout"
|
|
WRMSG( HHC00243, "W", SSID_TO_LCSS( dev->ssid ), dev->devnum );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No tape is mounted (or so we believe). Attempt to retrieve
|
|
// an updated tape status value, but if we cannot do so within
|
|
// a reasonable period of time (SLOW_UPDATE_STATUS_TIMEOUT),
|
|
// then continue using whatever our current tape status is...
|
|
|
|
int_scsi_status_wait( dev, SLOW_UPDATE_STATUS_TIMEOUT );
|
|
}
|
|
|
|
create_automount_thread( dev ); // (in case status changed)
|
|
|
|
/* Display tape status if tracing is active */
|
|
if (unlikely( dev->ccwtrace || dev->ccwstep ))
|
|
{
|
|
WRMSG( HHC00211, "I"
|
|
,SSID_TO_LCSS(dev->ssid)
|
|
,dev->devnum
|
|
,( (dev->filename[0]) ? (dev->filename) : ("(undefined)") )
|
|
,( (dev->fd < 0 ) ? ( "closed" ) : ( "opened" ) )
|
|
,(U32)dev->sstat
|
|
,STS_ONLINE(dev) ? "ON-LINE" : "OFF-LINE"
|
|
,STS_MOUNTED(dev) ? "READY" : "NO-TAPE"
|
|
,STS_TAPEMARK(dev) ? " TAPE-MARK" : ""
|
|
,STS_EOF (dev) ? " END-OF-FILE" : ""
|
|
,STS_BOT (dev) ? " LOAD-POINT" : ""
|
|
,STS_EOT (dev) ? " END-OF-TAPE" : ""
|
|
,STS_EOD (dev) ? " END-OF-DATA" : ""
|
|
,STS_WR_PROT (dev) ? " WRITE-PROTECT" : ""
|
|
);
|
|
|
|
if ( STS_BOT(dev) )
|
|
dev->eotwarning = 0;
|
|
}
|
|
|
|
} /* end function int_scsi_status_update */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* ASYNCHRONOUS TAPE OPEN */
|
|
/* SCSI tape tape-mount monitoring thread (monitors for tape mounts) */
|
|
/* Auto-started by 'int_scsi_status_update' when it notices there is */
|
|
/* no tape mounted on whatever device it's checking the status of, */
|
|
/* or by the ReqAutoMount function for unsatisfied mount requests. */
|
|
/*-------------------------------------------------------------------*/
|
|
void create_automount_thread( DEVBLK* dev )
|
|
{
|
|
// AUTO-SCSI-MOUNT
|
|
//
|
|
// If no tape is currently mounted on this device,
|
|
// kick off the tape mount monitoring thread that
|
|
// will monitor for tape mounts (if it doesn't al-
|
|
// ready still exist)...
|
|
|
|
obtain_lock( &sysblk.stape_lock );
|
|
|
|
// Is scsimount enabled?
|
|
|
|
if (likely( sysblk.auto_scsi_mount_secs ))
|
|
{
|
|
// Create thread if needed...
|
|
|
|
if (unlikely( !sysblk.stape_mountmon_tid ))
|
|
{
|
|
int rc;
|
|
VERIFY
|
|
(
|
|
(rc = create_thread
|
|
(
|
|
&sysblk.stape_mountmon_tid,
|
|
DETACHED,
|
|
scsi_tapemountmon_thread,
|
|
NULL,
|
|
"scsi_tapemountmon_thread"
|
|
))
|
|
== 0
|
|
);
|
|
if (rc)
|
|
WRMSG( HHC00102, "E", strerror( rc ));
|
|
}
|
|
|
|
// Enable it for our drive if needed...
|
|
|
|
if (STS_NOT_MOUNTED( dev ))
|
|
{
|
|
if (!dev->stape_mntdrq.link.Flink)
|
|
{
|
|
InsertListTail( &sysblk.stape_mount_link, &dev->stape_mntdrq.link );
|
|
}
|
|
}
|
|
}
|
|
|
|
release_lock( &sysblk.stape_lock );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* ASYNCHRONOUS TAPE OPEN */
|
|
/*-------------------------------------------------------------------*/
|
|
/* AUTO_SCSI_MOUNT thread... */
|
|
/*-------------------------------------------------------------------*/
|
|
void *scsi_tapemountmon_thread( void *notused )
|
|
{
|
|
struct timeval now;
|
|
int timeout, fd;
|
|
LIST_ENTRY* pListEntry;
|
|
STMNTDRQ* req;
|
|
DEVBLK* dev = NULL;
|
|
char buf[64];
|
|
|
|
UNREFERENCED(notused);
|
|
MSGBUF( buf, "SCSI-TAPE mount monitor");
|
|
WRMSG( HHC00100, "I", thread_id(), get_thread_priority(0), buf );
|
|
|
|
obtain_lock( &sysblk.stape_lock );
|
|
|
|
while (sysblk.auto_scsi_mount_secs && !sysblk.shutdown)
|
|
{
|
|
// Wait for automount interval to expire...
|
|
|
|
gettimeofday( &now, NULL );
|
|
|
|
for
|
|
(
|
|
timeout = 0
|
|
;
|
|
1
|
|
&& !sysblk.shutdown
|
|
&& sysblk.auto_scsi_mount_secs
|
|
&& !(timeout = timed_wait_condition_relative_usecs
|
|
(
|
|
&sysblk.stape_getstat_cond,
|
|
&sysblk.stape_lock,
|
|
sysblk.auto_scsi_mount_secs * 1000000,
|
|
&now
|
|
))
|
|
;
|
|
);
|
|
|
|
if (sysblk.auto_scsi_mount_secs && !sysblk.shutdown)
|
|
{
|
|
// Process all work items...
|
|
|
|
pListEntry = sysblk.stape_mount_link.Flink;
|
|
|
|
while (pListEntry != &sysblk.stape_mount_link)
|
|
{
|
|
req = CONTAINING_RECORD( pListEntry, STMNTDRQ, link );
|
|
dev = req->dev;
|
|
pListEntry = pListEntry->Flink;
|
|
|
|
// Open drive if needed...
|
|
|
|
if ((fd = dev->fd) < 0)
|
|
{
|
|
dev->readonly = 0;
|
|
fd = open_tape( dev->filename, O_RDWR | O_BINARY | O_NONBLOCK );
|
|
if (fd < 0 && EROFS == errno )
|
|
{
|
|
dev->readonly = 1;
|
|
fd = open_tape( dev->filename, O_RDONLY | O_BINARY | O_NONBLOCK );
|
|
}
|
|
|
|
// Check for successful open
|
|
|
|
if (fd < 0)
|
|
{
|
|
WRMSG( HHC00213, "E", SSID_TO_LCSS( dev->ssid ), dev->devnum,
|
|
dev->filename, "scsi", errno, strerror( errno ));
|
|
continue; // (go on to next drive)
|
|
}
|
|
|
|
define_BOT_pos( dev ); // (always after successful open)
|
|
dev->fd = fd; // (so far so good)
|
|
}
|
|
|
|
// Retrieve the current status...
|
|
|
|
// PLEASE NOTE that we must do this WITHOUT holding the stape_lock
|
|
// since the 'int_scsi_status_update' and sub-functions all expect
|
|
// the lock to NOT be held so that THEY can then attempt to acquire
|
|
// it when needed...
|
|
|
|
release_lock( &sysblk.stape_lock );
|
|
{
|
|
int_scsi_status_update( dev, 0 );
|
|
}
|
|
obtain_lock( &sysblk.stape_lock );
|
|
|
|
// (check again for shutdown)
|
|
|
|
if (sysblk.shutdown || !sysblk.auto_scsi_mount_secs)
|
|
break;
|
|
|
|
// Has a tape [finally] been mounted yet??
|
|
|
|
if (STS_NOT_MOUNTED( dev ))
|
|
{
|
|
#if !defined( _MSVC_ )
|
|
dev->fd = -1;
|
|
close_tape( fd );
|
|
#endif
|
|
continue; // (go on to next drive)
|
|
}
|
|
|
|
// Yes, remove completed work item...
|
|
|
|
RemoveListEntry( &dev->stape_mntdrq.link );
|
|
InitializeListLink( &dev->stape_mntdrq.link );
|
|
|
|
// Finish the open drive process (set drive to variable length
|
|
// block processing mode, etc)...
|
|
|
|
// PLEASE NOTE that we must do this WITHOUT holding the stape_lock
|
|
// since the 'finish_scsitape_open' and sub-functions all expect
|
|
// the lock to NOT be held so that THEY can then attempt to acquire
|
|
// it when needed...
|
|
|
|
release_lock( &sysblk.stape_lock );
|
|
{
|
|
if ( finish_scsitape_open( dev, NULL, 0 ) == 0 )
|
|
{
|
|
// Notify the guest that the tape has now been loaded by
|
|
// presenting an unsolicited device attention interrupt...
|
|
|
|
device_attention( dev, CSW_DE );
|
|
}
|
|
}
|
|
obtain_lock( &sysblk.stape_lock );
|
|
|
|
} // end for (all work items)...
|
|
|
|
} // end if (sysblk.auto_scsi_mount_secs && !sysblk.shutdown)
|
|
|
|
} // end while (sysblk.auto_scsi_mount_secs && !sysblk.shutdown)
|
|
|
|
// (discard all work items since we're going away)
|
|
|
|
while (!IsListEmpty( &sysblk.stape_mount_link ))
|
|
{
|
|
pListEntry = RemoveListHead( &sysblk.stape_mount_link );
|
|
InitializeListLink( pListEntry );
|
|
|
|
// (remove from the STATUS thread's work queue too!)
|
|
|
|
req = CONTAINING_RECORD( pListEntry, STMNTDRQ, link );
|
|
dev = req->dev;
|
|
|
|
if ( dev->stape_statrq.link.Flink) {
|
|
RemoveListEntry( &dev->stape_statrq.link );
|
|
InitializeListLink( &dev->stape_statrq.link );
|
|
}
|
|
}
|
|
|
|
WRMSG(HHC00101, "I", thread_id(), get_thread_priority(0), buf);
|
|
|
|
sysblk.stape_mountmon_tid = 0; // (we're going away)
|
|
release_lock( &sysblk.stape_lock );
|
|
return NULL;
|
|
|
|
} /* end function scsi_tapemountmon_thread */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Tell driver (if needed) what a BOT position looks like... */
|
|
/*-------------------------------------------------------------------*/
|
|
void define_BOT_pos( DEVBLK *dev )
|
|
{
|
|
#ifdef _MSVC_
|
|
|
|
// PROGRAMMING NOTE: Need to tell 'w32stape.c' here the
|
|
// information it needs to detect physical BOT (load-point).
|
|
|
|
// This is not normally needed as most drivers determine
|
|
// it for themselves based on the type (manufacturer/model)
|
|
// of tape drive being used, but since I haven't added the
|
|
// code to 'w32stape.c' to do that yet (involves talking
|
|
// directly to the SCSI device itself) we thus, for now,
|
|
// need to pass that information directly to 'w32stape.c'
|
|
// ourselves...
|
|
|
|
U32 msk = 0xFF3FFFFF; // (3480/3490 default)
|
|
U32 bot = 0x01000000; // (3480/3490 default)
|
|
|
|
if ( dev->stape_blkid_32 )
|
|
{
|
|
msk = 0xFFFFFFFF; // (3590 default)
|
|
bot = 0x00000000; // (3590 default)
|
|
}
|
|
|
|
VERIFY( 0 == w32_define_BOT( dev->fd, msk, bot ) );
|
|
|
|
#else
|
|
UNREFERENCED(dev);
|
|
#endif // _MSVC_
|
|
}
|
|
|
|
#endif // defined(OPTION_SCSI_TAPE)
|