]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[scsi] Wait for a successful TEST UNIT READY command
authorMichael Brown <mcb30@ipxe.org>
Sat, 16 Oct 2010 12:53:38 +0000 (13:53 +0100)
committerMichael Brown <mcb30@ipxe.org>
Tue, 19 Oct 2010 18:10:02 +0000 (19:10 +0100)
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>
src/drivers/block/scsi.c
src/include/ipxe/scsi.h

index 1abd2a5f317de096b08142bfb27cb6236de9f7a7..d14165134cdd7fa49641d1a22be6986172401039 100644 (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 );
index 5539b61d0fff2cc8df6c120b0e37e969935f3cb7..6dfb7f1ea5e91b102c32b9e421eb9e15535604be 100644 (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];
 };