2
0
mirror of https://github.com/xcat2/xNBA.git synced 2025-04-02 16:50:38 +00:00

[scsi] Wait for a successful TEST UNIT READY command

Some SCSI targets (observed with an EMC CLARiiON Fibre Channel target)
will not respond to commands correctly until a TEST UNIT READY has
been issued.  In particular, a READ CAPACITY (10) command will return
with a success status, but no capacity data.

Fix by issuing a TEST UNIT READY command automatically, and delaying
further SCSI commands until the TEST UNIT READY has succeeded.

Reported-by: Hadar Hen Zion <hadarh@mellanox.co.il>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Michael Brown 2010-10-16 13:53:38 +01:00
parent b0e434280e
commit 57bab0ae4a
2 changed files with 144 additions and 3 deletions

View File

@ -24,6 +24,8 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <byteswap.h>
#include <errno.h>
#include <ipxe/list.h>
#include <ipxe/process.h>
#include <ipxe/xfer.h>
#include <ipxe/blockdev.h>
#include <ipxe/scsi.h>
@ -202,11 +204,24 @@ struct scsi_device {
/** SCSI LUN */
struct scsi_lun lun;
/** Flags */
unsigned int flags;
/** TEST UNIT READY interface */
struct interface ready;
/** TEST UNIT READY process */
struct process process;
/** List of commands */
struct list_head cmds;
};
/** SCSI device flags */
enum scsi_device_flags {
/** Unit is ready */
SCSIDEV_UNIT_READY = 0x0001,
};
/** A SCSI command */
struct scsi_command {
/** Reference count */
@ -624,6 +639,26 @@ static struct scsi_command_type scsicmd_read_capacity = {
.done = scsicmd_read_capacity_done,
};
/**
* Construct SCSI TEST UNIT READY command
*
* @v scsicmd SCSI command
* @v command SCSI command IU
*/
static void scsicmd_test_unit_ready_cmd ( struct scsi_command *scsicmd __unused,
struct scsi_cmd *command ) {
struct scsi_cdb_test_unit_ready *testready = &command->cdb.testready;
testready->opcode = SCSI_OPCODE_TEST_UNIT_READY;
}
/** SCSI TEST UNIT READY command type */
static struct scsi_command_type scsicmd_test_unit_ready = {
.name = "TEST UNIT READY",
.cmd = scsicmd_test_unit_ready_cmd,
.done = scsicmd_close,
};
/** SCSI command block interface operations */
static struct interface_operation scsicmd_block_op[] = {
INTF_OP ( intf_close, struct scsi_command *, scsicmd_close ),
@ -751,6 +786,34 @@ static int scsidev_read_capacity ( struct scsi_device *scsidev,
0, 0, UNULL, 0 );
}
/**
* Test to see if SCSI device is ready
*
* @v scsidev SCSI device
* @v block Block data interface
* @ret rc Return status code
*/
static int scsidev_test_unit_ready ( struct scsi_device *scsidev,
struct interface *block ) {
return scsidev_command ( scsidev, block, &scsicmd_test_unit_ready,
0, 0, UNULL, 0 );
}
/**
* Check SCSI device flow-control window
*
* @v scsidev SCSI device
* @ret len Length of window
*/
static size_t scsidev_window ( struct scsi_device *scsidev ) {
/* Refuse commands until unit is confirmed ready */
if ( ! ( scsidev->flags & SCSIDEV_UNIT_READY ) )
return 0;
return xfer_window ( &scsidev->scsi );
}
/**
* Close SCSI device
*
@ -761,9 +824,13 @@ static void scsidev_close ( struct scsi_device *scsidev, int rc ) {
struct scsi_command *scsicmd;
struct scsi_command *tmp;
/* Stop process */
process_del ( &scsidev->process );
/* Shut down interfaces */
intf_shutdown ( &scsidev->block, rc );
intf_shutdown ( &scsidev->scsi, rc );
intf_shutdown ( &scsidev->ready, rc );
/* Shut down any remaining commands */
list_for_each_entry_safe ( scsicmd, tmp, &scsidev->cmds, list ) {
@ -775,6 +842,7 @@ static void scsidev_close ( struct scsi_device *scsidev, int rc ) {
/** SCSI device block interface operations */
static struct interface_operation scsidev_block_op[] = {
INTF_OP ( xfer_window, struct scsi_device *, scsidev_window ),
INTF_OP ( block_read, struct scsi_device *, scsidev_read ),
INTF_OP ( block_write, struct scsi_device *, scsidev_write ),
INTF_OP ( block_read_capacity, struct scsi_device *,
@ -787,6 +855,67 @@ static struct interface_descriptor scsidev_block_desc =
INTF_DESC_PASSTHRU ( struct scsi_device, block,
scsidev_block_op, scsi );
/**
* Handle SCSI TEST UNIT READY response
*
* @v scsidev SCSI device
* @v rc Reason for close
*/
static void scsidev_ready ( struct scsi_device *scsidev, int rc ) {
/* Shut down interface */
intf_shutdown ( &scsidev->ready, rc );
/* Close device on failure */
if ( rc != 0 ) {
DBGC ( scsidev, "SCSI %p not ready: %s\n",
scsidev, strerror ( rc ) );
scsidev_close ( scsidev, rc );
return;
}
/* Mark device as ready */
scsidev->flags |= SCSIDEV_UNIT_READY;
xfer_window_changed ( &scsidev->block );
DBGC ( scsidev, "SCSI %p unit is ready\n", scsidev );
}
/** SCSI device TEST UNIT READY interface operations */
static struct interface_operation scsidev_ready_op[] = {
INTF_OP ( intf_close, struct scsi_device *, scsidev_ready ),
};
/** SCSI device TEST UNIT READY interface descriptor */
static struct interface_descriptor scsidev_ready_desc =
INTF_DESC ( struct scsi_device, ready, scsidev_ready_op );
/**
* SCSI TEST UNIT READY process
*
* @v process Process
*/
static void scsidev_step ( struct process *process ) {
struct scsi_device *scsidev =
container_of ( process, struct scsi_device, process );
int rc;
/* Wait until underlying SCSI device is ready */
if ( xfer_window ( &scsidev->scsi ) == 0 )
return;
/* Stop process */
process_del ( &scsidev->process );
DBGC ( scsidev, "SCSI %p waiting for unit to become ready\n",
scsidev );
/* Issue TEST UNIT READY command */
if ( ( rc = scsidev_test_unit_ready ( scsidev, &scsidev->ready )) !=0){
scsidev_close ( scsidev, rc );
return;
}
}
/** SCSI device SCSI interface operations */
static struct interface_operation scsidev_scsi_op[] = {
INTF_OP ( intf_close, struct scsi_device *, scsidev_close ),
@ -816,14 +945,14 @@ int scsi_open ( struct interface *block, struct interface *scsi,
ref_init ( &scsidev->refcnt, NULL );
intf_init ( &scsidev->block, &scsidev_block_desc, &scsidev->refcnt );
intf_init ( &scsidev->scsi, &scsidev_scsi_desc, &scsidev->refcnt );
intf_init ( &scsidev->ready, &scsidev_ready_desc, &scsidev->refcnt );
process_init ( &scsidev->process, scsidev_step, &scsidev->refcnt );
INIT_LIST_HEAD ( &scsidev->cmds );
memcpy ( &scsidev->lun, lun, sizeof ( scsidev->lun ) );
DBGC ( scsidev, "SCSI %p created for LUN " SCSI_LUN_FORMAT "\n",
scsidev, SCSI_LUN_DATA ( scsidev->lun ) );
/* Attach to SCSI and parent and interfaces, mortalise self,
* and return
*/
/* Attach to SCSI and parent interfaces, mortalise self, and return */
intf_plug_plug ( &scsidev->scsi, scsi );
intf_plug_plug ( &scsidev->block, block );
ref_put ( &scsidev->refcnt );

View File

@ -28,6 +28,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define SCSI_OPCODE_READ_CAPACITY_10 0x25 /**< READ CAPACITY (10) */
#define SCSI_OPCODE_SERVICE_ACTION_IN 0x9e /**< SERVICE ACTION IN */
#define SCSI_SERVICE_ACTION_READ_CAPACITY_16 0x10 /**< READ CAPACITY (16) */
#define SCSI_OPCODE_TEST_UNIT_READY 0x00 /**< TEST UNIT READY */
/** @} */
@ -192,6 +193,16 @@ struct scsi_capacity_16 {
uint8_t reserved[20];
} __attribute__ (( packed ));
/** A SCSI "TEST UNIT READY" CDB */
struct scsi_cdb_test_unit_ready {
/** Opcode (0x00) */
uint8_t opcode;
/** Reserved */
uint8_t reserved[4];
/** Control byte */
uint8_t control;
} __attribute__ (( packed ));
/** A SCSI Command Data Block */
union scsi_cdb {
struct scsi_cdb_read_10 read10;
@ -200,6 +211,7 @@ union scsi_cdb {
struct scsi_cdb_write_16 write16;
struct scsi_cdb_read_capacity_10 readcap10;
struct scsi_cdb_read_capacity_16 readcap16;
struct scsi_cdb_test_unit_ready testready;
unsigned char bytes[16];
};