Commit adfd407e authored by Victor Kotlyar's avatar Victor Kotlyar
Browse files

Ported commit 01b32f6ffc3d28ec7681eff02973ba91191fcc60 castor/master

CASTOR-5042: The SCSI layer of the tape server overlooks SCSI errors
not coming from the target

Fixed.

Add output for all errors available for SCSI call.
The logic implemented is to go through all errors in the following
order: status, host_status, driver_status. In case of particular
error an appropriate exception generated.
parent 1311a2bf
......@@ -323,6 +323,97 @@ std::string castor::tape::SCSI::statusToString(unsigned char status)
}
}
//------------------------------------------------------------------------------
// hostStatusToString
//------------------------------------------------------------------------------
std::string castor::tape::SCSI::hostStatusToString
(const unsigned short int hostStatus) {
switch (hostStatus) {
case castor::tape::SCSI::HostStatus::OK:
return "OK";
case castor::tape::SCSI::HostStatus::NO_CONNECT:
return "NO CONNECT";
case castor::tape::SCSI::HostStatus::BUS_BUSY:
return "BUS BUSY";
case castor::tape::SCSI::HostStatus::TIME_OUT:
return "TIME OUT";
case castor::tape::SCSI::HostStatus::BAD_TARGET:
return "BAD TARGET";
case castor::tape::SCSI::HostStatus::ABORT:
return "ABORT";
case castor::tape::SCSI::HostStatus::PARITY:
return "PARITY";
case castor::tape::SCSI::HostStatus::ERROR:
return "ERROR";
case castor::tape::SCSI::HostStatus::RESET:
return "RESET";
case castor::tape::SCSI::HostStatus::BAD_INTR:
return "BAD INTR";
case castor::tape::SCSI::HostStatus::PASSTHROUGH:
return "PASSTHROUGH";
case castor::tape::SCSI::HostStatus::SOFT_ERROR:
return "SOFT ERROR";
default:
std::stringstream ret;
ret << "Unknown host status code: "
<< std::hex << std::nouppercase << std::showbase
<< hostStatus;
return ret.str();
}
}
//------------------------------------------------------------------------------
// driverStatusToString
//------------------------------------------------------------------------------
std::string castor::tape::SCSI::driverStatusToString
(const unsigned short int driverStatus) {
switch (driverStatus & castor::tape::SCSI::DriverStatus::MASK) {
case castor::tape::SCSI::DriverStatus::OK:
return "OK";
case castor::tape::SCSI::DriverStatus::BUSY:
return "BUSY";
case castor::tape::SCSI::DriverStatus::SOFT:
return "SOFT";
case castor::tape::SCSI::DriverStatus::MEDIA:
return "MEDIA";
case castor::tape::SCSI::DriverStatus::ERROR:
return "ERROR";
case castor::tape::SCSI::DriverStatus::INVALID:
return "INVALID";
case castor::tape::SCSI::DriverStatus::TIMEOUT:
return "TIMEOUT";
case castor::tape::SCSI::DriverStatus::HARD:
return "HARD";
case castor::tape::SCSI::DriverStatus::SENSE:
return "SENSE";
default:
std::stringstream ret;
ret << "Unknown driver status code: "
<< std::hex << std::nouppercase << std::showbase
<< driverStatus;
return ret.str();
}
}
//------------------------------------------------------------------------------
// driverStatusSuggestionsToString
//------------------------------------------------------------------------------
std::string castor::tape::SCSI::driverStatusSuggestionsToString
(const unsigned short int driverStatus) {
std::stringstream ret;
if (driverStatus & castor::tape::SCSI::DriverStatusSuggest::RETRY)
ret << " RETRY";
if (driverStatus & castor::tape::SCSI::DriverStatusSuggest::ABORT)
ret << " ABORT";
if (driverStatus & castor::tape::SCSI::DriverStatusSuggest::REMAP)
ret << " REMAP";
if (driverStatus & castor::tape::SCSI::DriverStatusSuggest::DIE)
ret << " DIE";
if (driverStatus & castor::tape::SCSI::DriverStatusSuggest::SENSE)
ret << " SENSE";
return ret.str();
}
const castor::tape::SCSI::senseConstants::error_info castor::tape::SCSI::senseConstants::ascStrings[] =
{
{0x0000, "No additional sense information"},
......@@ -1119,4 +1210,4 @@ std::string castor::tape::SCSI::LBPMethodToString(const unsigned char LBPMethod)
default:
return "Unknown";
}
}
\ No newline at end of file
}
......@@ -229,12 +229,96 @@ namespace SCSI {
TASK_ABORTED = 0x40
} Status_t;
};
class HostStatus {
public:
enum {
OK = 0x00, /* NO error */
NO_CONNECT = 0x01, /* Couldn't connect before timeout period */
BUS_BUSY = 0x02, /* BUS stayed busy through time out period */
TIME_OUT = 0x03, /* TIMED OUT for other reason (often this an
* unexpected device selection timeout) */
BAD_TARGET = 0x04, /* BAD target, device not responding */
ABORT = 0x05, /* Told to abort for some other reason. From lk
* 2.4.15 the SCSI subsystem supports 16 byte commands
* however few adapter drivers do. Those HBA drivers
* that don't support 16 byte commands will yield this
* error code if a 16 byte command is passed to a SCSI
* device they control. */
PARITY = 0x06, /* Parity error. Older SCSI parallel buses have a
* parity bit for error detection. This probably
* indicates a cable or termination problem. */
ERROR = 0x07, /* Internal error detected in the host adapter. This
* may not be fatal (and the command may have
* succeeded). The aic7xxx and sym53c8xx adapter
* drivers sometimes report this for data underruns or
* overruns */
RESET = 0x08, /* The SCSI bus (or this device) has been reset. Any
* SCSI device on a SCSI bus is capable of instigating
* a reset. */
BAD_INTR = 0x09, /* Received an unexpected interrupt */
PASSTHROUGH = 0x0a, /* Force command past mid-level */
SOFT_ERROR = 0x0b, /* The low-level driver wants a retry */
} HostStatus_t;
};
class DriverStatus {
public:
enum {
OK = 0x00, /* Typically no suggestion */
BUSY = 0x01,
SOFT = 0x02,
MEDIA = 0x03,
ERROR = 0x04,
INVALID = 0x05,
TIMEOUT = 0x06, /* Adapter driver is unable to control the SCSI bus
* to its is setting its devices offline
* (and giving up) */
HARD = 0x07,
SENSE = 0x08, /* Implies sense_buffer output */
MASK = 0x0f /* The mask for driver status codes */
} DriverStatus_t;
};
class DriverStatusSuggest {
public:
enum {
RETRY = 0x10,
ABORT = 0x20,
REMAP = 0x30,
DIE = 0x40,
SENSE = 0x80,
MASK = 0xf0 /* The mask for driver status suggestion codes */
} DriverStatusSuggest_t;
};
/**
* Helper function turning SCSI status to string
*/
std::string statusToString(unsigned char status);
/**
* Turn a SCSI host_status code into a string
* @param The host status
* @return The string representation for the host status
*/
std::string hostStatusToString(const unsigned short int hostStatus);
/**
* Turn a SCSI driver_status code into a string
* @param The driver status
* @return The string representation for the driver status
*/
std::string driverStatusToString(const unsigned short int driverStatus);
/**
* Turn a SCSI driver_status code suggestions into a string
* @param The driver status
* @return The string representation for the driver status code suggestions
*/
std::string driverStatusSuggestionsToString(
const unsigned short int driverStatus);
class logSensePages {
public:
enum {
......
......@@ -23,7 +23,29 @@
#include "Exception.hpp"
void castor::tape::SCSI::ExceptionLauncher(const SCSI::Structures::LinuxSGIO_t & sgio, std::string context) {
//------------------------------------------------------------------------------
// ExceptionLauncher
//------------------------------------------------------------------------------
void castor::tape::SCSI::ExceptionLauncher(const SCSI::Structures::LinuxSGIO_t &
sgio, std::string context) {
std::stringstream contextWithStatuses;
contextWithStatuses << context
<< std::hex << std::nouppercase << std::showbase
<< " status=" << static_cast<unsigned int>(sgio.status)
<< " host_status=" << sgio.host_status
<< " driver_status=" << sgio.driver_status
<< ":";
checkAndThrowSgStatus(sgio, contextWithStatuses.str());
checkAndThrowSgHostStatus(sgio, contextWithStatuses.str());
checkAndThrowSgDriverStatus(sgio, contextWithStatuses.str());
}
//------------------------------------------------------------------------------
// checkAndThrowSgStatus
//------------------------------------------------------------------------------
void castor::tape::SCSI::checkAndThrowSgStatus(
const SCSI::Structures::LinuxSGIO_t & sgio, const std::string context) {
if (SCSI::Status::GOOD != sgio.status) {
if (SCSI::Status::CHECK_CONDITION == sgio.status) {
unsigned char senseKey;
......@@ -52,3 +74,101 @@ void castor::tape::SCSI::ExceptionLauncher(const SCSI::Structures::LinuxSGIO_t &
}
}
}
//------------------------------------------------------------------------------
// checkAndThrowSgHostStatus
//------------------------------------------------------------------------------
void castor::tape::SCSI::checkAndThrowSgHostStatus(
const SCSI::Structures::LinuxSGIO_t & sgio, const std::string context) {
if (SCSI::HostStatus::OK != sgio.host_status) {
throw HostException(sgio.host_status, context);
}
}
//------------------------------------------------------------------------------
// checkAndThrowSgDriverStatus
//------------------------------------------------------------------------------
void castor::tape::SCSI::checkAndThrowSgDriverStatus(
const SCSI::Structures::LinuxSGIO_t & sgio, const std::string context) {
if (SCSI::DriverStatus::OK != sgio.driver_status) {
throw DriverException(sgio.driver_status,
(SCSI::Structures::senseData_t<255> *)sgio.sbp, context);
}
}
//------------------------------------------------------------------------------
// Exception
//------------------------------------------------------------------------------
castor::tape::SCSI::Exception::Exception(
unsigned char status, castor::tape::SCSI::Structures::senseData_t<255>* sense,
const std::string& context): cta::exception::Exception("") {
std::stringstream w;
w << context << (context.size()?" ":"")
<< "SCSI command failed with status "
<< SCSI::statusToString(status);
if (SCSI::Status::CHECK_CONDITION == status) {
w << ": Sense Information";
try {
w << ": " << sense->getSenseKeyString();
} catch (Exception &ex) {
w << ": In addition, failed to get Sense Key string: "
<< ex.getMessage();
}
try {
w << ": " << sense->getACSString();
} catch (Exception &ex) {
w << ": In addition, failed to get ACS string: "
<< ex.getMessage();
}
}
setWhat(w.str());
}
//------------------------------------------------------------------------------
// HostException
//------------------------------------------------------------------------------
castor::tape::SCSI::HostException::HostException(
const unsigned short int host_status, const std::string& context):
cta::exception::Exception("") {
std::stringstream w;
w << context << (context.size()?" ":"")
<< "SCSI command failed with host_status: "
<< SCSI::hostStatusToString(host_status);
setWhat(w.str());
}
//------------------------------------------------------------------------------
// DriverException
//------------------------------------------------------------------------------
castor::tape::SCSI::DriverException::DriverException(
const unsigned short int driver_status,
castor::tape::SCSI::Structures::senseData_t<255>* sense,
const std::string& context) : cta::exception::Exception("") {
std::stringstream w;
const std::string driverSuggestions =
SCSI::driverStatusSuggestionsToString(driver_status);
w << context << (context.size() ? " " : "")
<< "SCSI command failed with driver_status: "
<< SCSI::driverStatusToString(driver_status)
<< (driverSuggestions.size() ? ": Driver suggestions:" : "")
<< driverSuggestions;
if (SCSI::DriverStatus::SENSE & driver_status) {
w << ": Sense Information";
try {
w << ": " << sense->getSenseKeyString();
} catch (Exception &ex) {
w << ": In addition, failed to get Sense Key string: "
<< ex.getMessage();
}
try {
w << ": " << sense->getACSString();
} catch (Exception &ex) {
w << ": In addition, failed to get ACS string: "
<< ex.getMessage();
}
}
setWhat(w.str());
}
......@@ -36,29 +36,7 @@ namespace SCSI {
class Exception: public cta::exception::Exception {
public:
Exception(unsigned char status, castor::tape::SCSI::Structures::senseData_t<255> * sense,
const std::string & context = ""):
cta::exception::Exception("") {
std::stringstream w;
w << context << (context.size()?" ":"")
<< "SCSI command failed with status "
<< SCSI::statusToString(status);
if (SCSI::Status::CHECK_CONDITION == status) {
w << ": Sense Information";
try {
w << ": " << sense->getSenseKeyString();
} catch (Exception &ex) {
w << ": In addition, failed to get Sense Key string: "
<< ex.getMessage().str();
}
try {
w << ": " << sense->getACSString();
} catch (Exception &ex) {
w << ": In addition, failed to get ACS string: "
<< ex.getMessage().str();
}
}
setWhat(w.str());
}
const std::string & context = "");
virtual ~Exception() throw () {}
}; // class Exception
......@@ -89,13 +67,64 @@ namespace SCSI {
const std::string & context = ""):
Exception(status, sense, context) {};
}; // class UnitAttentionException
/**
* Failed with host error.
*/
class HostException: public cta::exception::Exception {
public:
HostException(const unsigned short int host_status,
const std::string & context = "");
virtual ~HostException() throw () {}
}; // class HostException
/**
* Failed with driver error.
*/
class DriverException: public cta::exception::Exception {
public:
DriverException(const unsigned short int driver_status,
castor::tape::SCSI::Structures::senseData_t<255> * sense,
const std::string & context = "");
virtual ~DriverException() throw () {}
}; // class DriverException
/**
* Automated exception launcher in case of SCSI command error. Does nothing
* in the absence of errors.
* @param sgio the sgio struct.
*/
void ExceptionLauncher (const SCSI::Structures::LinuxSGIO_t & sgio, std::string context = "");
/**
* Check and throw exception in case of SCSI command error with bad status.
* Does nothing in the absence of errors.
* @param sgio the sgio struct.
* @param context The string to be used as the beginning for the exception
* message.
*/
void checkAndThrowSgStatus(const SCSI::Structures::LinuxSGIO_t & sgio,
const std::string context);
/**
* Check and throw exception in case of SCSI command error with bad host
* status. Does nothing in the absence of errors.
* @param sgio the sgio struct.
* @param context The string to be used as the beginning for the exception
* message.
*/
void checkAndThrowSgHostStatus(const SCSI::Structures::LinuxSGIO_t & sgio,
const std::string context);
/**
* Check and throw exception in case of SCSI command error with bad driver
* status. Does nothing in the absence of errors.
* @param sgio the sgio struct.
* @param context The string to be used as the beginning for the exception
* message.
*/
void checkAndThrowSgDriverStatus(const SCSI::Structures::LinuxSGIO_t & sgio,
const std::string context);
} // namespace SCSI
} // namespace tape
} // namespace castor
......@@ -684,7 +684,147 @@ namespace unitTests {
ASSERT_NE(std::string::npos, what.find("In exception validation: "));
}
}
TEST(castor_tape_SCSI_Structures, HostException) {
castor::tape::SCSI::Structures::LinuxSGIO_t sgio;
sgio.status = castor::tape::SCSI::Status::GOOD;
sgio.host_status = castor::tape::SCSI::HostStatus::OK;
ASSERT_NO_THROW(castor::tape::SCSI::ExceptionLauncher(sgio));
sgio.host_status = castor::tape::SCSI::HostStatus::NO_CONNECT;
ASSERT_THROW(castor::tape::SCSI::ExceptionLauncher(sgio),
castor::tape::SCSI::HostException);
sgio.host_status = castor::tape::SCSI::HostStatus::BUS_BUSY;
ASSERT_THROW(castor::tape::SCSI::ExceptionLauncher(sgio),
castor::tape::SCSI::HostException);
sgio.host_status = castor::tape::SCSI::HostStatus::TIME_OUT;
ASSERT_THROW(castor::tape::SCSI::ExceptionLauncher(sgio),
castor::tape::SCSI::HostException);
sgio.host_status = castor::tape::SCSI::HostStatus::BAD_TARGET;
ASSERT_THROW(castor::tape::SCSI::ExceptionLauncher(sgio),
castor::tape::SCSI::HostException);
sgio.host_status = castor::tape::SCSI::HostStatus::ABORT;
ASSERT_THROW(castor::tape::SCSI::ExceptionLauncher(sgio),
castor::tape::SCSI::HostException);
sgio.host_status = castor::tape::SCSI::HostStatus::PARITY;
ASSERT_THROW(castor::tape::SCSI::ExceptionLauncher(sgio),
castor::tape::SCSI::HostException);
sgio.host_status = castor::tape::SCSI::HostStatus::ERROR;
ASSERT_THROW(castor::tape::SCSI::ExceptionLauncher(sgio),
castor::tape::SCSI::HostException);
sgio.host_status = castor::tape::SCSI::HostStatus::RESET;
ASSERT_THROW(castor::tape::SCSI::ExceptionLauncher(sgio),
castor::tape::SCSI::HostException);
sgio.host_status = castor::tape::SCSI::HostStatus::BAD_INTR;
ASSERT_THROW(castor::tape::SCSI::ExceptionLauncher(sgio),
castor::tape::SCSI::HostException);
sgio.host_status = castor::tape::SCSI::HostStatus::PASSTHROUGH;
ASSERT_THROW(castor::tape::SCSI::ExceptionLauncher(sgio),
castor::tape::SCSI::HostException);
sgio.host_status = castor::tape::SCSI::HostStatus::SOFT_ERROR;
ASSERT_THROW(castor::tape::SCSI::ExceptionLauncher(sgio),
castor::tape::SCSI::HostException);
try {
castor::tape::SCSI::ExceptionLauncher(sgio, "In exception validation:");
ASSERT_TRUE(false);
}
catch (castor::tape::SCSI::HostException & ex) {
std::string what(ex.getMessageValue());
ASSERT_NE(std::string::npos, what.find("SOFT ERROR"));
/* We check here that the formatting is also done correctly (space added
* when context not empty */
ASSERT_NE(std::string::npos, what.find("In exception validation: "));
}
}
TEST(castor_tape_SCSI_Structures, DriverException) {
castor::tape::SCSI::Structures::LinuxSGIO_t sgio;
sgio.status = castor::tape::SCSI::Status::GOOD;
sgio.driver_status = castor::tape::SCSI::DriverStatus::OK;
ASSERT_NO_THROW(castor::tape::SCSI::ExceptionLauncher(sgio));
sgio.driver_status = castor::tape::SCSI::DriverStatus::BUSY;
ASSERT_THROW(castor::tape::SCSI::ExceptionLauncher(sgio),
castor::tape::SCSI::DriverException);
sgio.driver_status = castor::tape::SCSI::DriverStatus::SOFT;
ASSERT_THROW(castor::tape::SCSI::ExceptionLauncher(sgio),
castor::tape::SCSI::DriverException);
sgio.driver_status = castor::tape::SCSI::DriverStatus::MEDIA;
ASSERT_THROW(castor::tape::SCSI::ExceptionLauncher(sgio),
castor::tape::SCSI::DriverException);
sgio.driver_status = castor::tape::SCSI::DriverStatus::ERROR;
ASSERT_THROW(castor::tape::SCSI::ExceptionLauncher(sgio),
castor::tape::SCSI::DriverException);
sgio.driver_status = castor::tape::SCSI::DriverStatus::INVALID;
ASSERT_THROW(castor::tape::SCSI::ExceptionLauncher(sgio),
castor::tape::SCSI::DriverException);
sgio.driver_status = castor::tape::SCSI::DriverStatus::TIMEOUT;
ASSERT_THROW(castor::tape::SCSI::ExceptionLauncher(sgio),
castor::tape::SCSI::DriverException);
sgio.driver_status = castor::tape::SCSI::DriverStatus::HARD;
ASSERT_THROW(castor::tape::SCSI::ExceptionLauncher(sgio),
castor::tape::SCSI::DriverException);
/* sense is a special case*/
castor::tape::SCSI::Structures::senseData_t<255> sense;
sgio.setSenseBuffer(&sense);
/* fill up the ASC part of the */
sense.responseCode = 0x70;
sense.fixedFormat.ASC = 0x14;
sense.fixedFormat.ASCQ = 0x04;
sgio.driver_status = castor::tape::SCSI::DriverStatus::SENSE;
ASSERT_THROW(castor::tape::SCSI::ExceptionLauncher(sgio),
castor::tape::SCSI::DriverException);
/* add suggestions*/
sgio.driver_status |= castor::tape::SCSI::DriverStatusSuggest::RETRY;
ASSERT_THROW(castor::tape::SCSI::ExceptionLauncher(sgio),
castor::tape::SCSI::DriverException);
sgio.driver_status |= castor::tape::SCSI::DriverStatusSuggest::ABORT;
ASSERT_THROW(castor::tape::SCSI::ExceptionLauncher(sgio),
castor::tape::SCSI::DriverException);
sgio.driver_status |= castor::tape::SCSI::DriverStatusSuggest::REMAP;
ASSERT_THROW(castor::tape::SCSI::ExceptionLauncher(sgio),
castor::tape::SCSI::DriverException);
sgio.driver_status |= castor::tape::SCSI::DriverStatusSuggest::DIE;
ASSERT_THROW(castor::tape::SCSI::ExceptionLauncher(sgio),
castor::tape::SCSI::DriverException);
sgio.driver_status |= castor::tape::SCSI::DriverStatusSuggest::SENSE;
ASSERT_THROW(castor::tape::SCSI::ExceptionLauncher(sgio),
castor::tape::SCSI::DriverException);
try {
castor::tape::SCSI::ExceptionLauncher(sgio, "In exception validation:");
ASSERT_TRUE(false);
}
catch (castor::tape::SCSI::DriverException & ex) {
std::string what(ex.getMessageValue());
ASSERT_NE(std::string::npos, what.find(": SENSE"));
ASSERT_NE(std::string::npos, what.find("Driver suggestions:"));
ASSERT_NE(std::string::npos, what.find(" RETRY ABORT REMAP DIE SENSE"));
ASSERT_NE(std::string::npos, what.find("Block sequence error"));
/* We check here that the formatting is also done correctly (space added
* when context not empty */
ASSERT_NE(std::string::npos, what.find("In exception validation: "));
}
try {
sgio.driver_status = castor::tape::SCSI::DriverStatus::TIMEOUT;
castor::tape::SCSI::ExceptionLauncher(sgio, "In exception validation:");
ASSERT_TRUE(false);
}
catch (castor::tape::SCSI::DriverException & ex) {
std::string what(ex.getMessageValue());
ASSERT_NE(std::string::npos, what.find(": TIMEOUT"));
ASSERT_EQ(std::string::npos, what.find("Driver suggestions:"));
ASSERT_EQ(std::string::npos, what.find(" RETRY ABORT REMAP DIE SENSE"));
ASSERT_EQ(std::string::npos, what.find("Block sequence error"));
/* We check here that the formatting is also done correctly (space added
* when context not empty */
ASSERT_NE(std::string::npos, what.find("In exception validation: "));
}