From 5203ad441310a4c2abd4fb79015a6bdadc2a5a4f Mon Sep 17 00:00:00 2001 From: Matthew Dharm Date: Mon, 6 Jun 2005 17:19:29 -0700 Subject: [PATCH] USB Storage: endpoint toggles and reset delays This patch does two things to help reset recovery. It started life as as496 and was rediffed by me. First, the patch checks the result of a CLEAR_HALT request and doesn't reset the endpoint's data toggle unless the request succeeded. Second, it reduces the timeout for a device reset from 20 seconds to 5 seconds. If all goes well, then I've finally figured quilt out and this patch should apply cleanly. Signed-off-by: Alan Stern Signed-off-by: Matthew Dharm Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/transport.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'drivers/usb/storage') diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 9743e289cd3..419afb2216b 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -266,8 +266,9 @@ int usb_stor_clear_halt(struct us_data *us, unsigned int pipe) NULL, 0, 3*HZ); /* reset the endpoint toggle */ - usb_settoggle(us->pusb_dev, usb_pipeendpoint(pipe), - usb_pipeout(pipe), 0); + if (result >= 0) + usb_settoggle(us->pusb_dev, usb_pipeendpoint(pipe), + usb_pipeout(pipe), 0); US_DEBUGP("%s: result = %d\n", __FUNCTION__, result); return result; @@ -1124,7 +1125,7 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us) * It's handy that every transport mechanism uses the control endpoint for * resets. * - * Basically, we send a reset with a 20-second timeout, so we don't get + * Basically, we send a reset with a 5-second timeout, so we don't get * jammed attempting to do the reset. */ static int usb_stor_reset_common(struct us_data *us, @@ -1145,13 +1146,9 @@ static int usb_stor_reset_common(struct us_data *us, clear_bit(US_FLIDX_ABORTING, &us->flags); scsi_unlock(us_to_host(us)); - /* A 20-second timeout may seem rather long, but a LaCie - * StudioDrive USB2 device takes 16+ seconds to get going - * following a powerup or USB attach event. - */ result = usb_stor_control_msg(us, us->send_ctrl_pipe, request, requesttype, value, index, data, size, - 20*HZ); + 5*HZ); if (result < 0) { US_DEBUGP("Soft reset failed: %d\n", result); goto Done; @@ -1173,8 +1170,10 @@ static int usb_stor_reset_common(struct us_data *us, US_DEBUGP("Soft reset: clearing bulk-out endpoint halt\n"); result2 = usb_stor_clear_halt(us, us->send_bulk_pipe); - /* return a result code based on the result of the control message */ - if (result < 0 || result2 < 0) { + /* return a result code based on the result of the clear-halts */ + if (result >= 0) + result = result2; + if (result < 0) { US_DEBUGP("Soft reset failed\n"); goto Done; } -- cgit v1.2.3 From 4d07ef762fc8d6d35ecc1511a3b953a733a61a5f Mon Sep 17 00:00:00 2001 From: Matthew Dharm Date: Mon, 6 Jun 2005 17:21:41 -0700 Subject: [PATCH] USB Storage: port reset on transport error This patch causes a port reset whenever there's a transport error or abort. If that fails it reverts back to doing a mass-storage device reset. It started life as as497 and was rediffed by me. This makes error recovery a lot quicker and more reliable. Signed-off-by: Alan Stern Signed-off-by: Matthew Dharm Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/scsiglue.c | 45 ++++++------------- drivers/usb/storage/scsiglue.h | 1 + drivers/usb/storage/transport.c | 99 ++++++++++++++++++++++++++++------------- drivers/usb/storage/transport.h | 1 + 4 files changed, 82 insertions(+), 64 deletions(-) (limited to 'drivers/usb/storage') diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index e43eddc3d44..da2bfa944b9 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -255,50 +255,23 @@ static int device_reset(struct scsi_cmnd *srb) /* lock the device pointers and do the reset */ down(&(us->dev_semaphore)); - if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) { - result = FAILED; - US_DEBUGP("No reset during disconnect\n"); - } else - result = us->transport_reset(us); + result = us->transport_reset(us); up(&(us->dev_semaphore)); - return result; + return result < 0 ? FAILED : SUCCESS; } -/* This resets the device's USB port. */ -/* It refuses to work if there's more than one interface in - * the device, so that other users are not affected. */ +/* Simulate a SCSI bus reset by resetting the device's USB port. */ /* This is always called with scsi_lock(host) held */ static int bus_reset(struct scsi_cmnd *srb) { struct us_data *us = host_to_us(srb->device->host); - int result, rc; + int result; US_DEBUGP("%s called\n", __FUNCTION__); - /* The USB subsystem doesn't handle synchronisation between - * a device's several drivers. Therefore we reset only devices - * with just one interface, which we of course own. */ - down(&(us->dev_semaphore)); - if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) { - result = -EIO; - US_DEBUGP("No reset during disconnect\n"); - } else if (us->pusb_dev->actconfig->desc.bNumInterfaces != 1) { - result = -EBUSY; - US_DEBUGP("Refusing to reset a multi-interface device\n"); - } else { - rc = usb_lock_device_for_reset(us->pusb_dev, us->pusb_intf); - if (rc < 0) { - US_DEBUGP("unable to lock device for reset: %d\n", rc); - result = rc; - } else { - result = usb_reset_device(us->pusb_dev); - if (rc) - usb_unlock_device(us->pusb_dev); - US_DEBUGP("usb_reset_device returns %d\n", result); - } - } + result = usb_stor_port_reset(us); up(&(us->dev_semaphore)); /* lock the host for the return */ @@ -320,6 +293,14 @@ void usb_stor_report_device_reset(struct us_data *us) } } +/* Report a driver-initiated bus reset to the SCSI layer. + * Calling this for a SCSI-initiated reset is unnecessary but harmless. + * The caller must own the SCSI host lock. */ +void usb_stor_report_bus_reset(struct us_data *us) +{ + scsi_report_bus_reset(us_to_host(us), 0); +} + /*********************************************************************** * /proc/scsi/ functions ***********************************************************************/ diff --git a/drivers/usb/storage/scsiglue.h b/drivers/usb/storage/scsiglue.h index d0a49af026c..737e4fa6045 100644 --- a/drivers/usb/storage/scsiglue.h +++ b/drivers/usb/storage/scsiglue.h @@ -42,6 +42,7 @@ #define _SCSIGLUE_H_ extern void usb_stor_report_device_reset(struct us_data *us); +extern void usb_stor_report_bus_reset(struct us_data *us); extern unsigned char usb_stor_sense_invalidCDB[18]; extern struct scsi_host_template usb_stor_host_template; diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 419afb2216b..e6b1c6cf07f 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -541,15 +541,15 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) */ if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) { US_DEBUGP("-- command was aborted\n"); - goto Handle_Abort; + srb->result = DID_ABORT << 16; + goto Handle_Errors; } /* if there is a transport error, reset and don't auto-sense */ if (result == USB_STOR_TRANSPORT_ERROR) { US_DEBUGP("-- transport indicates error, resetting\n"); - us->transport_reset(us); srb->result = DID_ERROR << 16; - return; + goto Handle_Errors; } /* if the transport provided its own sense data, don't auto-sense */ @@ -669,7 +669,8 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) { US_DEBUGP("-- auto-sense aborted\n"); - goto Handle_Abort; + srb->result = DID_ABORT << 16; + goto Handle_Errors; } if (temp_result != USB_STOR_TRANSPORT_GOOD) { US_DEBUGP("-- auto-sense failure\n"); @@ -678,9 +679,9 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) * multi-target device, since failure of an * auto-sense is perfectly valid */ - if (!(us->flags & US_FL_SCM_MULT_TARG)) - us->transport_reset(us); srb->result = DID_ERROR << 16; + if (!(us->flags & US_FL_SCM_MULT_TARG)) + goto Handle_Errors; return; } @@ -721,12 +722,28 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) return; - /* abort processing: the bulk-only transport requires a reset - * following an abort */ - Handle_Abort: - srb->result = DID_ABORT << 16; - if (us->protocol == US_PR_BULK) + /* Error and abort processing: try to resynchronize with the device + * by issuing a port reset. If that fails, try a class-specific + * device reset. */ + Handle_Errors: + + /* Let the SCSI layer know we are doing a reset, set the + * RESETTING bit, and clear the ABORTING bit so that the reset + * may proceed. */ + scsi_lock(us_to_host(us)); + usb_stor_report_bus_reset(us); + set_bit(US_FLIDX_RESETTING, &us->flags); + clear_bit(US_FLIDX_ABORTING, &us->flags); + scsi_unlock(us_to_host(us)); + + result = usb_stor_port_reset(us); + if (result < 0) { + scsi_lock(us_to_host(us)); + usb_stor_report_device_reset(us); + scsi_unlock(us_to_host(us)); us->transport_reset(us); + } + clear_bit(US_FLIDX_RESETTING, &us->flags); } /* Stop the current URB transfer */ @@ -1134,24 +1151,18 @@ static int usb_stor_reset_common(struct us_data *us, { int result; int result2; - int rc = FAILED; - /* Let the SCSI layer know we are doing a reset, set the - * RESETTING bit, and clear the ABORTING bit so that the reset - * may proceed. - */ - scsi_lock(us_to_host(us)); - usb_stor_report_device_reset(us); - set_bit(US_FLIDX_RESETTING, &us->flags); - clear_bit(US_FLIDX_ABORTING, &us->flags); - scsi_unlock(us_to_host(us)); + if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) { + US_DEBUGP("No reset during disconnect\n"); + return -EIO; + } result = usb_stor_control_msg(us, us->send_ctrl_pipe, request, requesttype, value, index, data, size, 5*HZ); if (result < 0) { US_DEBUGP("Soft reset failed: %d\n", result); - goto Done; + return result; } /* Give the device some time to recover from the reset, @@ -1161,7 +1172,7 @@ static int usb_stor_reset_common(struct us_data *us, HZ*6); if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) { US_DEBUGP("Reset interrupted by disconnect\n"); - goto Done; + return -EIO; } US_DEBUGP("Soft reset: clearing bulk-in endpoint halt\n"); @@ -1173,16 +1184,11 @@ static int usb_stor_reset_common(struct us_data *us, /* return a result code based on the result of the clear-halts */ if (result >= 0) result = result2; - if (result < 0) { + if (result < 0) US_DEBUGP("Soft reset failed\n"); - goto Done; - } - US_DEBUGP("Soft reset done\n"); - rc = SUCCESS; - - Done: - clear_bit(US_FLIDX_RESETTING, &us->flags); - return rc; + else + US_DEBUGP("Soft reset done\n"); + return result; } /* This issues a CB[I] Reset to the device in question @@ -1212,3 +1218,32 @@ int usb_stor_Bulk_reset(struct us_data *us) USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, us->ifnum, NULL, 0); } + +/* Issue a USB port reset to the device. But don't do anything if + * there's more than one interface in the device, so that other users + * are not affected. */ +int usb_stor_port_reset(struct us_data *us) +{ + int result, rc; + + if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) { + result = -EIO; + US_DEBUGP("No reset during disconnect\n"); + } else if (us->pusb_dev->actconfig->desc.bNumInterfaces != 1) { + result = -EBUSY; + US_DEBUGP("Refusing to reset a multi-interface device\n"); + } else { + result = rc = + usb_lock_device_for_reset(us->pusb_dev, us->pusb_intf); + if (result < 0) { + US_DEBUGP("unable to lock device for reset: %d\n", + result); + } else { + result = usb_reset_device(us->pusb_dev); + if (rc) + usb_unlock_device(us->pusb_dev); + US_DEBUGP("usb_reset_device returns %d\n", result); + } + } + return result; +} diff --git a/drivers/usb/storage/transport.h b/drivers/usb/storage/transport.h index e25f8d8fc74..8d9e0663f8f 100644 --- a/drivers/usb/storage/transport.h +++ b/drivers/usb/storage/transport.h @@ -171,4 +171,5 @@ extern int usb_stor_bulk_transfer_buf(struct us_data *us, unsigned int pipe, extern int usb_stor_bulk_transfer_sg(struct us_data *us, unsigned int pipe, void *buf, unsigned int length, int use_sg, int *residual); +extern int usb_stor_port_reset(struct us_data *us); #endif -- cgit v1.2.3 From 86dbde9cbdfe8bc2c2dfe5d33027d3acc55e0470 Mon Sep 17 00:00:00 2001 From: Matthew Dharm Date: Mon, 6 Jun 2005 17:22:42 -0700 Subject: [PATCH] USB Storage: retry hard errors This patch started life as as527, and was rediffed by me. Since the IDE interface doesn't convey much information about types of errors, many USB-IDE adapters report all low-level errors with SK = 0x04, which is supposed to be used only for non-recoverable errors. As a result the SCSI midlayer doesn't retry the command. But quite often a retry would succeed, whereas an unnecessary retry doesn't really hurt anything. This patch uses a recently-implemented flag to tell the SCSI midlayer that such hardware errors should be retried. Signed-off-by: Alan Stern Signed-off-by: Matthew Dharm Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/scsiglue.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/usb/storage') diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index da2bfa944b9..af294bb68c3 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -155,6 +155,15 @@ static int slave_configure(struct scsi_device *sdev) * If this device makes that mistake, tell the sd driver. */ if (us->flags & US_FL_FIX_CAPACITY) sdev->fix_capacity = 1; + + /* USB-IDE bridges tend to report SK = 0x04 (Non-recoverable + * Hardware Error) when any low-level error occurs, + * recoverable or not. Setting this flag tells the SCSI + * midlayer to retry such commands, which frequently will + * succeed and fix the error. The worst this can lead to + * is an occasional series of retries that will all fail. */ + sdev->retry_hwerror = 1; + } else { /* Non-disk-type devices don't need to blacklist any pages -- cgit v1.2.3