Public Member Functions | Protected Member Functions | Private Member Functions | Private Attributes

scsi_LnxSG Class Reference
[SCSI Library]

Implementation of scsi_IO interface using Linux SCSI Generic (sg) driver. More...

#include <scsi_lnxsg.h>

Inheritance diagram for scsi_LnxSG:
Inheritance graph
[legend]
Collaboration diagram for scsi_LnxSG:
Collaboration graph
[legend]

List of all members.

Public Member Functions

 scsi_LnxSG (const string &a_deviceID)
virtual ~scsi_LnxSG ()
virtual bool IsOpen ()
virtual void Close ()

Protected Member Functions

virtual void OpenByDeviceName ()
 Open SCSI device by its (platform specific!) name.
virtual void OpenBySCSIID ()
 Open SCSI device by its (current) SCSI ID.
virtual void OpenBySerialNumber ()
 Open SCSI device by its unique identification.
virtual void InqSCSIID ()
 Get SCSI ID of the currently opened device.
int DoIOCtl (const scsi_CDB &a_cdb, UInt32_t a_timeout)
 Function to actually execute CDB commands.
virtual scsi_Status_e GetCDBStatus ()
virtual void CheckDriverStatus (UInt8_t a_cdbCode)
 Checks the status of the driver after the execution of CDB.
virtual void CheckHostStatus (UInt8_t a_cdbCode)
 Checks the status of the host (if available) after the execution of CDB.
virtual bool IsSenseValid ()

Private Member Functions

void SGOpen (bool a_readOnly=false, bool a_excl=false)
bool Reset (int what)

Private Attributes

 log_CLASSID_m
int m_handle
sg_io_hdr_t m_ioHdr

Detailed Description

Implementation of scsi_IO interface using Linux SCSI Generic (sg) driver.

Author:
Matej Kenda

Definition at line 42 of file scsi_lnxsg.h.


Constructor & Destructor Documentation

scsi_LnxSG::scsi_LnxSG ( const string &  a_deviceID  ) 

Definition at line 57 of file scsi_lnxsg.cpp.

References log_FUNC_m.

    : scsi_IO(a_DeviceID),
      m_handle(-1) {

    log_FUNC_m(scsi_LnxSG);
}

scsi_LnxSG::~scsi_LnxSG (  )  [virtual]

Definition at line 68 of file scsi_lnxsg.cpp.

References Close(), IsOpen(), log_FUNC_m, log_WRN_m, and scsi_IO::m_deviceName.

                        {
    log_FUNC_m(~scsi_LnxSG);

    if (IsOpen()) {
        log_WRN_m("Device not yet closed! Closing device " << m_deviceName);
        Close();
    }
}

Here is the call graph for this function:


Member Function Documentation

void scsi_LnxSG::CheckDriverStatus ( UInt8_t  a_cdbCode  )  [protected, virtual]

Checks the status of the driver after the execution of CDB.

Reimplemented from scsi_IO.

Definition at line 254 of file scsi_lnxsg.cpp.

References dbg_NORM, DRIVER_MASK, evt_ERROR, ie_SCSI_DRIVER, ivd_Error, log_DBG_m, log_FUNC_m, log_WriteEvent(), m_ioHdr, scsi_IO::m_strSCSIID, scsi_CommandSize(), scsi_GetOpcodeText(), SG_ERR_DRIVER_OK, and SG_ERR_DRIVER_SENSE.

                                                    {
    if ( m_ioHdr.driver_status                  == SG_ERR_DRIVER_OK   ||
        (m_ioHdr.driver_status & DRIVER_MASK)   == SG_ERR_DRIVER_SENSE) {
        return;
    }
    log_FUNC_m(CheckDriverStatus);

    ostringstream sstr;
    sstr
        << "Driver status code: "
        << hex << m_ioHdr.driver_status << dec
        << " CDB: " << scsi_GetOpcodeText(a_cdbCode);

    // Some Linux drivers report that they don't support large CDBs in
    // driver status.
    if (scsi_CommandSize(a_cdbCode) >= 16) {
        log_DBG_m(dbg_NORM, "Large CDB: " << sstr.str());
    }
    else {
        log_WriteEvent(evt_ERROR, sstr.str(), "SCSI", 0, m_strSCSIID);
    }

    log_MARKLINE_m;
    throw ivd_Error(ie_SCSI_DRIVER, sstr.str());
}

Here is the call graph for this function:

void scsi_LnxSG::CheckHostStatus ( UInt8_t  a_cdbCode  )  [protected, virtual]

Checks the status of the host (if available) after the execution of CDB.

Reimplemented from scsi_IO.

Definition at line 280 of file scsi_lnxsg.cpp.

References dbg_NORM, DID_TARGET_FAILURE, evt_ERROR, ie_SCSI_HOST, ivd_Error, log_DBG_m, log_FUNC_m, log_WriteEvent(), m_ioHdr, scsi_IO::m_strSCSIID, scsi_CommandSize(), scsi_GetOpcodeText(), and SG_ERR_DID_OK.

                                                  {
    if (m_ioHdr.host_status == SG_ERR_DID_OK) {
        return;
    }
    log_FUNC_m(CheckHostStatus);

#ifdef DID_TARGET_FAILURE
    if (m_ioHdr.host_status == DID_TARGET_FAILURE) {
        // starting at the RHEL6.2 kernel 2.6.32-220-el6 and at 2.6.38 vanilla 
        // kernels DID_TARGET_FAILURE is returned in case of a check condition.
        // This one is checked later. so we ignore it.
        log_DBG_m(dbg_NORM, "DID_TARGET_FAILURE detected in m_ioHdr.host_status");
        return;
    }
#endif

    ostringstream sstr;
    sstr
        << "SCSI host status code: "
        << hex << m_ioHdr.host_status << dec
        << " CDB: " << scsi_GetOpcodeText(a_cdbCode);

    // Some Linux drivers report that they don't support large CDBs in
    // host status.
    if (scsi_CommandSize(a_cdbCode) >= 16) {
        log_DBG_m(dbg_NORM, "Large CDB: " << sstr.str());
    }
    else {
        log_WriteEvent(evt_ERROR, sstr.str(), "SCSI", 0, m_strSCSIID);
    }

    log_MARKLINE_m;
    throw ivd_Error(ie_SCSI_HOST, sstr.str());
}

Here is the call graph for this function:

void scsi_LnxSG::Close (  )  [virtual]

Implements scsi_IO.

Definition at line 187 of file scsi_lnxsg.cpp.

References dbg_LOW, ie_SCSI_CLOSE, ivd_Error, log_DBG_m, log_FUNC_m, scsi_IO::m_deviceID, and m_handle.

Referenced by ~scsi_LnxSG().

                      {
    log_FUNC_m(Close);

    log_DBG_m(dbg_LOW, "Closing device ID: " << m_deviceID);

    if (m_handle < 0) {
        log_MARKLINE_m;
        throw ivd_Error(ie_SCSI_CLOSE);
    }
    close(m_handle);
    m_handle = -1;
}

Here is the caller graph for this function:

int scsi_LnxSG::DoIOCtl ( const scsi_CDB a_cdb,
UInt32_t  a_timeout 
) [protected, virtual]

Function to actually execute CDB commands.

Returns:
result code of platform specific ioctl command.

Implements scsi_IO.

Definition at line 200 of file scsi_lnxsg.cpp.

References dbg_DETAIL, dbg_EXTAPI, errno, scsi_CDB::GetBufferPointer(), scsi_CDB::GetBufferSize(), scsi_CDB::GetCmdPointer(), scsi_CDB::GetCmdSize(), scsi_IO::GetSense(), scsi_IO::GetSenseSize(), scsi_CDB::GetTransferDirection(), log_DBG_m, log_FUNC_m, m_handle, m_ioHdr, scsi_IO::m_pdDirectMode, MIN_DIRECT_BUFFER_SIZE, op_READ_10, op_READ_6, op_WRITE_10, and op_WRITE_6.

                                                                 {
    log_FUNC_m(DoIOCtl);

    memset(&m_ioHdr, 0, sizeof(m_ioHdr));
    m_ioHdr.interface_id = static_cast<int>('S');
    m_ioHdr.dxfer_direction = a_cdb.GetTransferDirection();

    m_ioHdr.cmdp = const_cast<unsigned char*>(a_cdb.GetCmdPointer());
    m_ioHdr.cmd_len = a_cdb.GetCmdSize();

    // Don't use scatter-gather, we use direct IO, so set this to 0.
    m_ioHdr.iovec_count = 0;

    m_ioHdr.dxfer_len =
        static_cast<const unsigned int>(a_cdb.GetBufferSize());

    m_ioHdr.dxferp =
        reinterpret_cast<unsigned char *>(a_cdb.GetBufferPointer());

    // Set pointers to sense structure
    m_ioHdr.mx_sb_len = GetSenseSize();
    m_ioHdr.sbp =
        reinterpret_cast<UInt8_t*>(const_cast<data_Sense_t*>(GetSense()));

    UInt8_t cdbCode = a_cdb.GetCmdPointer()[0];

    if (cdbCode == op_READ_6 || cdbCode == op_READ_10 ||
        cdbCode == op_WRITE_6 || cdbCode == op_WRITE_10) {
        //
        // Do not transfer buffers from user space to kernel
        // space for larger blocks of data when doing read/write operations
        // (use direct IO instead).
        //
        // Direct IO can also be disabled via env variable SG_NO_DIO!
        //
        if (m_ioHdr.dxfer_len > MIN_DIRECT_BUFFER_SIZE && m_pdDirectMode) {
            m_ioHdr.flags |= SG_FLAG_DIRECT_IO;
        }
    };

    m_ioHdr.timeout = a_timeout;

    log_DBG_m(dbg_EXTAPI, "ioctl(.., SG_IO, ..)")
    int result = ioctl(m_handle, SG_IO, &m_ioHdr);
    if (result == -1) {
        log_DBG_m(dbg_DETAIL, "Return errno: " << errno)
        return errno;
    }
    else {
        log_DBG_m(dbg_DETAIL , "Return result: " << result)
        return result;
    }
}

Here is the call graph for this function:

virtual scsi_Status_e scsi_LnxSG::GetCDBStatus (  )  [inline, protected, virtual]

Implements scsi_IO.

Definition at line 61 of file scsi_lnxsg.h.

References m_ioHdr.

Referenced by IsSenseValid().

                                         {
        return (scsi_Status_e)(m_ioHdr.status & st_STATUS_MASK);
    }

Here is the caller graph for this function:

void scsi_LnxSG::InqSCSIID (  )  [protected, virtual]

Get SCSI ID of the currently opened device.

Implements scsi_IO.

Definition at line 315 of file scsi_lnxsg.cpp.

References dbg_EXTAPI, errno, scsi_IO::GetSCSIID(), log_DBG_m, log_FUNC_m, scsi_IO::m_channel, m_handle, scsi_IO::m_id, scsi_IO::m_lun, scsi_IO::m_port, and scsi_IO::m_strSCSIID.

Referenced by OpenByDeviceName().

                           {
    log_FUNC_m(InqSCSIID);

    struct sg_scsi_id   scsiID;

    log_DBG_m(dbg_EXTAPI, "ioctl(.., SG_GET_SCSI_ID, ..)")
    int result = ioctl(m_handle, SG_GET_SCSI_ID, (void*)(&scsiID));
    if (result != 0) {
        log_MARKLINE_m;
        throw ivd_SysError(errno,
            "ioctl(.., SG_GET_SCSI_ID, ..) returned error");
    }

    m_port = scsiID.host_no;
    m_channel = scsiID.channel;
    m_id = scsiID.scsi_id;
    m_lun = scsiID.lun;

    m_strSCSIID = GetSCSIID();
}

Here is the call graph for this function:

Here is the caller graph for this function:

bool scsi_LnxSG::IsOpen (  )  [virtual]

Implements scsi_IO.

Definition at line 64 of file scsi_lnxsg.cpp.

References m_handle.

Referenced by ~scsi_LnxSG().

                        {
    return (m_handle >= 0);
}

Here is the caller graph for this function:

virtual bool scsi_LnxSG::IsSenseValid (  )  [inline, protected, virtual]

Implements scsi_IO.

Definition at line 67 of file scsi_lnxsg.h.

References GetCDBStatus(), m_ioHdr, SG_ERR_DRIVER_SENSE, st_BUSY, st_CHECK_CONDITION, and st_COMMAND_TERMINATED.

                                {
        scsi_Status_e status    = GetCDBStatus();
        int           drvStatus = m_ioHdr.driver_status;

        if (status == st_CHECK_CONDITION ||
            status == st_COMMAND_TERMINATED ||
            status == st_BUSY ||
            drvStatus & SG_ERR_DRIVER_SENSE) {
            return true;
        }
        else {
            return false;
        }
    }

Here is the call graph for this function:

void scsi_LnxSG::OpenByDeviceName (  )  [protected, virtual]

Open SCSI device by its (platform specific!) name.

Implements scsi_IO.

Definition at line 77 of file scsi_lnxsg.cpp.

References dbg_LOW, InqSCSIID(), scsi_IO::InqSerialNumber(), scsi_IO::InqStandard(), log_DBG_m, log_FUNC_m, scsi_IO::m_deviceName, and SGOpen().

                                 {
    log_FUNC_m(OpenByDeviceName);

    log_DBG_m(dbg_LOW, "Opening device file: " << m_deviceName);

    SGOpen(false, true);

    InqSCSIID();
    InqStandard();
    InqSerialNumber();
}

Here is the call graph for this function:

void scsi_LnxSG::OpenBySCSIID (  )  [protected, virtual]

Open SCSI device by its (current) SCSI ID.

Warning:
Implementations must take care to close the device if an error occurs.

Implements scsi_IO.

Definition at line 89 of file scsi_lnxsg.cpp.

References dbg_LOW, log_DBG_m, log_FUNC_m, scsi_IO::m_channel, scsi_IO::m_id, scsi_IO::m_lun, and scsi_IO::m_port.

                              {
    log_FUNC_m(OpenBySCSIID);

    log_DBG_m(dbg_LOW,
        "Opening SCSI ID: " <<
        m_port << ":" <<
        m_channel << ":" <<
        m_id << ":" <<
        m_lun);

}

void scsi_LnxSG::OpenBySerialNumber (  )  [protected, virtual]

Open SCSI device by its unique identification.

Warning:
Implementations must take care to close the device if an error occurs.

Implements scsi_IO.

Definition at line 101 of file scsi_lnxsg.cpp.

References dbg_LOW, log_DBG_m, log_FUNC_m, scsi_IO::m_productID, scsi_IO::m_productSerialNumber, and scsi_IO::m_vendorID.

                                    {
    log_FUNC_m(OpenBySerialNumber);

    log_DBG_m(dbg_LOW,
        "Opening SCSI Serial #: " <<
        m_vendorID << ":" <<
        m_productID << ":" <<
        m_productSerialNumber);

}

bool scsi_LnxSG::Reset ( int  what  )  [private]

Definition at line 337 of file scsi_lnxsg.cpp.

                               {
/*
    int k;
    int res;

    k = what;
    res = ioctl(m_handle, SG_SCSI_RESET, &k);
    if (res < 0) {
        if (EBUSY == errno)
            printf("sg_reset: BUSY, may be resetting now\n");
        else if (EIO == errno)
            printf("sg_reset: requested type of reset may not be available\n");
        else if (EACCES == errno)
            printf("sg_reset: to do a reset needs root permission\n");
        else
            printf("sg_reset: SG_SCSI_RESET not supported\n");
        return false;
    }
    if (SG_SCSI_RESET_NOTHING == k)
        printf("sg_reset: did nothing, device is normal mode\n");
    else {
        if (SG_SCSI_RESET_DEVICE == k)
            printf("sg_reset: started device reset\n");
        else if (SG_SCSI_RESET_BUS == k)
            printf("sg_reset: started bus reset\n");
        else if (SG_SCSI_RESET_HOST == k)
            printf("sg_reset: started host reset\n");

        printf("waiting for the reset to complete...\n");
        int j = 0;
        k = SG_SCSI_RESET_NOTHING;
        do {
            if (0 != j)
                sleep(1);
            res = ioctl(m_handle, SG_SCSI_RESET, &k);
            ++j;
        } while ((res < 0) && (EBUSY == errno));
        printf("  ... reset seemingly completed\n");
    }
*/
    return true;

}

void scsi_LnxSG::SGOpen ( bool  a_readOnly = false,
bool  a_excl = false 
) [private]

Definition at line 112 of file scsi_lnxsg.cpp.

References dbg_DETAIL, dbg_EXTAPI, dbg_LOW, errno, ie_SCSI_OPEN, ivd_Error, ivd_Sleep, log_DBG_m, log_FUNC_m, scsi_IO::m_deviceName, and m_handle.

Referenced by OpenByDeviceName().

                                                   {
    log_FUNC_m(SGOpen);

    if (m_handle >= 0) {
        //device already opened
        log_MARKLINE_m;
        throw ivd_Error(ie_SCSI_OPEN);
    }

    // Retry open 5 times
    for (int i = 5; i > 0; i--) {
        log_DBG_m(dbg_EXTAPI, "open(" << m_deviceName << ")");

        m_handle = open(
            m_deviceName.c_str(),
              (a_readOnly ? O_RDONLY : O_RDWR)
            | (a_excl ? O_EXCL : 0)
            | O_NONBLOCK);

        if (m_handle < 0) {
            if (errno == EBUSY || errno == EINTR /*|| errno == ENXIO*/) {
                if (i == 1) {
                    log_MARKLINE_m;
                    throw ivd_SysError(errno, "open(" + m_deviceName + "...)");
                }
                log_DBG_m(dbg_DETAIL,
                    "open() failed. Retrying. Error: " << strerror(errno));
                ivd_Sleep(1);
                continue;
            }
            else {
                log_MARKLINE_m;
                throw ivd_SysError(errno, "open(" + m_deviceName + "...)");
            }
        }
        // open() successful. Break the loop.
        break;
    }
    log_DBG_m(dbg_LOW, "SCSI device handle: " << m_handle);

    // It seems that the kernel needs some patch to support SCSI reset.
/*
    Reset(SG_SCSI_RESET_DEVICE);
    Reset(SG_SCSI_RESET_BUS);
    Reset(SG_SCSI_RESET_HOST);
*/

    log_DBG_m(dbg_DETAIL,
        "Will try to read any garbage from previous usage of the device.");

    {
        // defining local struct
        struct sg_rep {
            struct sg_header    hd;
            unsigned char       rbuf[100];
        } sg_rep;

        int flags;

        flags = fcntl(m_handle, F_GETFL); /* Be very proper about this */
        fcntl(m_handle, F_SETFL, flags|O_NONBLOCK);
        // TODO: Do additional checks of return value and errno!!

        memset(&sg_rep, 0, sizeof(struct sg_header));
        sg_rep.hd.reply_len = sizeof(struct sg_header);

        while (read(m_handle, &sg_rep, sizeof(sg_rep)) >= 0 ||
               errno != EAGAIN) {
            // NULL BODY
        };

        fcntl(m_handle, F_SETFL, flags);
    }
}

Here is the caller graph for this function:


Member Data Documentation

Reimplemented from scsi_IO.

Definition at line 83 of file scsi_lnxsg.h.

int scsi_LnxSG::m_handle [private]

Definition at line 85 of file scsi_lnxsg.h.

Referenced by Close(), DoIOCtl(), InqSCSIID(), IsOpen(), and SGOpen().

sg_io_hdr_t scsi_LnxSG::m_ioHdr [private]

Definition at line 87 of file scsi_lnxsg.h.

Referenced by CheckDriverStatus(), CheckHostStatus(), DoIOCtl(), GetCDBStatus(), and IsSenseValid().


The documentation for this class was generated from the following files: