diff --git a/src/core/sanboot.c b/src/core/sanboot.c index 42a30839..85d0bc7f 100644 --- a/src/core/sanboot.c +++ b/src/core/sanboot.c @@ -64,6 +64,16 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ #define SAN_COMMAND_TIMEOUT ( 15 * TICKS_PER_SEC ) +/** + * Number of times to retry commands + * + * We may need to retry commands. For example, the underlying + * connection may be closed by the SAN target due to an inactivity + * timeout, or the SAN target may return pointless "error" messages + * such as "SCSI power-on occurred". + */ +#define SAN_COMMAND_MAX_RETRIES 10 + /** List of SAN devices */ LIST_HEAD ( san_devices ); @@ -331,36 +341,42 @@ sandev_command ( struct san_device *sandev, int ( * command ) ( struct san_device *sandev, const union san_command_params *params ), const union san_command_params *params ) { + unsigned int retries; int rc; /* Sanity check */ assert ( ! timer_running ( &sandev->timer ) ); - /* Reopen block device if applicable */ - if ( sandev_needs_reopen ( sandev ) && - ( ( rc = sandev_reopen ( sandev ) ) != 0 ) ) { - goto err_reopen; + /* (Re)try command */ + for ( retries = 0 ; retries < SAN_COMMAND_MAX_RETRIES ; retries++ ) { + + /* Reopen block device if applicable */ + if ( sandev_needs_reopen ( sandev ) && + ( ( rc = sandev_reopen ( sandev ) ) != 0 ) ) { + continue; + } + + /* Start expiry timer */ + start_timer_fixed ( &sandev->timer, SAN_COMMAND_TIMEOUT ); + + /* Initiate command */ + if ( ( rc = command ( sandev, params ) ) != 0 ) { + stop_timer ( &sandev->timer ); + continue; + } + + /* Wait for command to complete */ + while ( timer_running ( &sandev->timer ) ) + step(); + + /* Exit on success */ + if ( ( rc = sandev->command_rc ) == 0 ) + return 0; } - /* Start expiry timer */ - start_timer_fixed ( &sandev->timer, SAN_COMMAND_TIMEOUT ); + /* Sanity check */ + assert ( ! timer_running ( &sandev->timer ) ); - /* Initiate command */ - if ( ( rc = command ( sandev, params ) ) != 0 ) - goto err_op; - - /* Wait for command to complete */ - while ( timer_running ( &sandev->timer ) ) - step(); - - /* Collect return status */ - rc = sandev->command_rc; - - return rc; - - err_op: - stop_timer ( &sandev->timer ); - err_reopen: return rc; } diff --git a/src/drivers/block/scsi.c b/src/drivers/block/scsi.c index fd5f82b9..847e0d46 100644 --- a/src/drivers/block/scsi.c +++ b/src/drivers/block/scsi.c @@ -40,9 +40,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ -/** Maximum number of command retries */ -#define SCSICMD_MAX_RETRIES 10 - /* Error numbers generated by SCSI sense data */ #define EIO_NO_SENSE __einfo_error ( EINFO_EIO_NO_SENSE ) #define EINFO_EIO_NO_SENSE \ @@ -283,9 +280,6 @@ struct scsi_command { /** Command tag */ uint32_t tag; - /** Retry count */ - unsigned int retries; - /** Private data */ uint8_t priv[0]; }; @@ -449,28 +443,11 @@ static int scsicmd_command ( struct scsi_command *scsicmd ) { * @v rc Reason for close */ static void scsicmd_done ( struct scsi_command *scsicmd, int rc ) { - struct scsi_device *scsidev = scsicmd->scsidev; /* Restart SCSI interface */ intf_restart ( &scsicmd->scsi, rc ); - /* SCSI targets have an annoying habit of returning occasional - * pointless "error" messages such as "power-on occurred", so - * we have to be prepared to retry commands. - */ - if ( ( rc != 0 ) && ( scsicmd->retries++ < SCSICMD_MAX_RETRIES ) ) { - /* Retry command */ - DBGC ( scsidev, "SCSI %p tag %08x failed: %s\n", - scsidev, scsicmd->tag, strerror ( rc ) ); - DBGC ( scsidev, "SCSI %p tag %08x retrying (retry %d)\n", - scsidev, scsicmd->tag, scsicmd->retries ); - if ( ( rc = scsicmd_command ( scsicmd ) ) == 0 ) - return; - } - - /* If we didn't (successfully) reissue the command, hand over - * to the command completion handler. - */ + /* Hand over to the command completion handler */ scsicmd->type->done ( scsicmd, rc ); }