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:
parent
b0e434280e
commit
57bab0ae4a
@ -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 );
|
||||
|
@ -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];
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user