Commit 08c85f44 authored by Matthew Dharm's avatar Matthew Dharm Committed by Greg Kroah-Hartman

[PATCH] USB: usb-storage: timeouts and aborts

This patch adds timeouts to usb_stor_control_msg().  Now we will no longer
have to use the usb_control_msg() routine in the usb core, so all our
control messages can be interrupted by scsi aborts or disconnects.

This also includes the new serial-number for auto-REQUEST-SENSE change.
The new serial number has one bit toggled from the old, guaranteeing that
it is unique.

Following a suggestion of David Brownell, this makes the transport-reset
function attempt to clear a halt condition on both bulk pipes even if one
of them fails.
parent 812be635
...@@ -75,7 +75,7 @@ ...@@ -75,7 +75,7 @@
* below, which atomically tests-and-clears the URB_ACTIVE bit in us->flags * below, which atomically tests-and-clears the URB_ACTIVE bit in us->flags
* to see if the current_urb needs to be stopped. Likewise, the SG_ACTIVE * to see if the current_urb needs to be stopped. Likewise, the SG_ACTIVE
* bit is tested to see if the current_sg scatter-gather request needs to be * bit is tested to see if the current_sg scatter-gather request needs to be
* stopped. * stopped. The timeout callback routine does much the same thing.
* *
* When a disconnect occurs, the DISCONNECTING bit in us->flags is set to * When a disconnect occurs, the DISCONNECTING bit in us->flags is set to
* prevent new URBs from being submitted, and usb_stor_stop_transport() is * prevent new URBs from being submitted, and usb_stor_stop_transport() is
...@@ -109,6 +109,19 @@ static void usb_stor_blocking_completion(struct urb *urb, struct pt_regs *regs) ...@@ -109,6 +109,19 @@ static void usb_stor_blocking_completion(struct urb *urb, struct pt_regs *regs)
complete(urb_done_ptr); complete(urb_done_ptr);
} }
/* This is the timeout handler which will cancel an URB when its timeout
* expires.
*/
static void timeout_handler(unsigned long us_)
{
struct us_data *us = (struct us_data *) us_;
if (test_and_clear_bit(US_FLIDX_URB_ACTIVE, &us->flags)) {
US_DEBUGP("Timeout -- cancelling URB\n");
usb_unlink_urb(us->current_urb);
}
}
/* This is the common part of the URB message submission code /* This is the common part of the URB message submission code
* *
...@@ -116,9 +129,10 @@ static void usb_stor_blocking_completion(struct urb *urb, struct pt_regs *regs) ...@@ -116,9 +129,10 @@ static void usb_stor_blocking_completion(struct urb *urb, struct pt_regs *regs)
* command _must_ pass through this function (or something like it) for the * command _must_ pass through this function (or something like it) for the
* abort mechanisms to work properly. * abort mechanisms to work properly.
*/ */
static int usb_stor_msg_common(struct us_data *us) static int usb_stor_msg_common(struct us_data *us, int timeout)
{ {
struct completion urb_done; struct completion urb_done;
struct timer_list to_timer;
int status; int status;
/* don't submit URBS during abort/disconnect processing */ /* don't submit URBS during abort/disconnect processing */
...@@ -155,24 +169,42 @@ static int usb_stor_msg_common(struct us_data *us) ...@@ -155,24 +169,42 @@ static int usb_stor_msg_common(struct us_data *us)
usb_unlink_urb(us->current_urb); usb_unlink_urb(us->current_urb);
} }
} }
/* submit the timeout timer, if a timeout was requested */
if (timeout > 0) {
init_timer(&to_timer);
to_timer.expires = jiffies + timeout;
to_timer.function = timeout_handler;
to_timer.data = (unsigned long) us;
add_timer(&to_timer);
}
/* wait for the completion of the URB */ /* wait for the completion of the URB */
wait_for_completion(&urb_done); wait_for_completion(&urb_done);
clear_bit(US_FLIDX_URB_ACTIVE, &us->flags); clear_bit(US_FLIDX_URB_ACTIVE, &us->flags);
/* clean up the timeout timer */
if (timeout > 0)
del_timer_sync(&to_timer);
/* return the URB status */ /* return the URB status */
return us->current_urb->status; return us->current_urb->status;
} }
/* This is our function to emulate usb_control_msg() with enough control /*
* to make aborts/resets/timeouts work * Transfer one control message, with timeouts, and allowing early
* termination. Return codes are usual -Exxx, *not* USB_STOR_XFER_xxx.
*/ */
int usb_stor_control_msg(struct us_data *us, unsigned int pipe, int usb_stor_control_msg(struct us_data *us, unsigned int pipe,
u8 request, u8 requesttype, u16 value, u16 index, u8 request, u8 requesttype, u16 value, u16 index,
void *data, u16 size) void *data, u16 size, int timeout)
{ {
int status; int status;
US_DEBUGP("%s: rq=%02x rqtype=%02x value=%04x index=%02x len=%u\n",
__FUNCTION__, request, requesttype,
value, index, size);
/* fill in the devrequest structure */ /* fill in the devrequest structure */
us->dr->bRequestType = requesttype; us->dr->bRequestType = requesttype;
us->dr->bRequest = request; us->dr->bRequest = request;
...@@ -184,7 +216,7 @@ int usb_stor_control_msg(struct us_data *us, unsigned int pipe, ...@@ -184,7 +216,7 @@ int usb_stor_control_msg(struct us_data *us, unsigned int pipe,
usb_fill_control_urb(us->current_urb, us->pusb_dev, pipe, usb_fill_control_urb(us->current_urb, us->pusb_dev, pipe,
(unsigned char*) us->dr, data, size, (unsigned char*) us->dr, data, size,
usb_stor_blocking_completion, NULL); usb_stor_blocking_completion, NULL);
status = usb_stor_msg_common(us); status = usb_stor_msg_common(us, timeout);
/* return the actual length of the data transferred if no error */ /* return the actual length of the data transferred if no error */
if (status == 0) if (status == 0)
...@@ -192,9 +224,9 @@ int usb_stor_control_msg(struct us_data *us, unsigned int pipe, ...@@ -192,9 +224,9 @@ int usb_stor_control_msg(struct us_data *us, unsigned int pipe,
return status; return status;
} }
/* This is a version of usb_clear_halt() that doesn't read the status from /* This is a version of usb_clear_halt() that allows early termination and
* the device -- this is because some devices crash their internal firmware * doesn't read the status from the device -- this is because some devices
* when the status is requested after a halt. * crash their internal firmware when the status is requested after a halt.
* *
* A definitive list of these 'bad' devices is too difficult to maintain or * A definitive list of these 'bad' devices is too difficult to maintain or
* make complete enough to be useful. This problem was first observed on the * make complete enough to be useful. This problem was first observed on the
...@@ -214,12 +246,7 @@ int usb_stor_clear_halt(struct us_data *us, unsigned int pipe) ...@@ -214,12 +246,7 @@ int usb_stor_clear_halt(struct us_data *us, unsigned int pipe)
result = usb_stor_control_msg(us, us->send_ctrl_pipe, result = usb_stor_control_msg(us, us->send_ctrl_pipe,
USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, 0, USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, 0,
endp, NULL, 0); /* note: no 3*HZ timeout */ endp, NULL, 0, 3*HZ);
US_DEBUGP("usb_stor_clear_halt: result=%d\n", result);
/* this is a failure case */
if (result < 0)
return result;
/* reset the toggles and endpoint flags */ /* reset the toggles and endpoint flags */
usb_endpoint_running(us->pusb_dev, usb_pipeendpoint(pipe), usb_endpoint_running(us->pusb_dev, usb_pipeendpoint(pipe),
...@@ -227,7 +254,8 @@ int usb_stor_clear_halt(struct us_data *us, unsigned int pipe) ...@@ -227,7 +254,8 @@ int usb_stor_clear_halt(struct us_data *us, unsigned int pipe)
usb_settoggle(us->pusb_dev, usb_pipeendpoint(pipe), usb_settoggle(us->pusb_dev, usb_pipeendpoint(pipe),
usb_pipeout(pipe), 0); usb_pipeout(pipe), 0);
return 0; US_DEBUGP("%s: result = %d\n", __FUNCTION__, result);
return result;
} }
...@@ -317,7 +345,7 @@ int usb_stor_ctrl_transfer(struct us_data *us, unsigned int pipe, ...@@ -317,7 +345,7 @@ int usb_stor_ctrl_transfer(struct us_data *us, unsigned int pipe,
usb_fill_control_urb(us->current_urb, us->pusb_dev, pipe, usb_fill_control_urb(us->current_urb, us->pusb_dev, pipe,
(unsigned char*) us->dr, data, size, (unsigned char*) us->dr, data, size,
usb_stor_blocking_completion, NULL); usb_stor_blocking_completion, NULL);
result = usb_stor_msg_common(us); result = usb_stor_msg_common(us, 0);
return interpret_urb_result(us, pipe, size, result, return interpret_urb_result(us, pipe, size, result,
us->current_urb->actual_length); us->current_urb->actual_length);
...@@ -347,7 +375,7 @@ int usb_stor_intr_transfer(struct us_data *us, void *buf, unsigned int length) ...@@ -347,7 +375,7 @@ int usb_stor_intr_transfer(struct us_data *us, void *buf, unsigned int length)
usb_fill_int_urb(us->current_urb, us->pusb_dev, pipe, buf, usb_fill_int_urb(us->current_urb, us->pusb_dev, pipe, buf,
maxp, usb_stor_blocking_completion, NULL, maxp, usb_stor_blocking_completion, NULL,
us->ep_bInterval); us->ep_bInterval);
result = usb_stor_msg_common(us); result = usb_stor_msg_common(us, 0);
return interpret_urb_result(us, pipe, length, result, return interpret_urb_result(us, pipe, length, result,
us->current_urb->actual_length); us->current_urb->actual_length);
...@@ -368,7 +396,7 @@ int usb_stor_bulk_transfer_buf(struct us_data *us, unsigned int pipe, ...@@ -368,7 +396,7 @@ int usb_stor_bulk_transfer_buf(struct us_data *us, unsigned int pipe,
/* fill and submit the URB */ /* fill and submit the URB */
usb_fill_bulk_urb(us->current_urb, us->pusb_dev, pipe, buf, length, usb_fill_bulk_urb(us->current_urb, us->pusb_dev, pipe, buf, length,
usb_stor_blocking_completion, NULL); usb_stor_blocking_completion, NULL);
result = usb_stor_msg_common(us); result = usb_stor_msg_common(us, 0);
/* store the actual length of the data transferred */ /* store the actual length of the data transferred */
if (act_len) if (act_len)
...@@ -490,7 +518,6 @@ void usb_stor_invoke_transport(Scsi_Cmnd *srb, struct us_data *us) ...@@ -490,7 +518,6 @@ void usb_stor_invoke_transport(Scsi_Cmnd *srb, struct us_data *us)
} }
/* if there is a transport error, reset and don't auto-sense */ /* if there is a transport error, reset and don't auto-sense */
/* What if we want to abort during the reset? */
if (result == USB_STOR_TRANSPORT_ERROR) { if (result == USB_STOR_TRANSPORT_ERROR) {
US_DEBUGP("-- transport indicates error, resetting\n"); US_DEBUGP("-- transport indicates error, resetting\n");
us->transport_reset(us); us->transport_reset(us);
...@@ -559,6 +586,7 @@ void usb_stor_invoke_transport(Scsi_Cmnd *srb, struct us_data *us) ...@@ -559,6 +586,7 @@ void usb_stor_invoke_transport(Scsi_Cmnd *srb, struct us_data *us)
unsigned char old_sc_data_direction; unsigned char old_sc_data_direction;
unsigned char old_cmd_len; unsigned char old_cmd_len;
unsigned char old_cmnd[MAX_COMMAND_SIZE]; unsigned char old_cmnd[MAX_COMMAND_SIZE];
unsigned long old_serial_number;
US_DEBUGP("Issuing auto-REQUEST_SENSE\n"); US_DEBUGP("Issuing auto-REQUEST_SENSE\n");
...@@ -594,6 +622,10 @@ void usb_stor_invoke_transport(Scsi_Cmnd *srb, struct us_data *us) ...@@ -594,6 +622,10 @@ void usb_stor_invoke_transport(Scsi_Cmnd *srb, struct us_data *us)
old_sg = srb->use_sg; old_sg = srb->use_sg;
srb->use_sg = 0; srb->use_sg = 0;
/* change the serial number -- toggle the high bit*/
old_serial_number = srb->serial_number;
srb->serial_number ^= 0x80000000;
/* issue the auto-sense command */ /* issue the auto-sense command */
temp_result = us->transport(us->srb, us); temp_result = us->transport(us->srb, us);
...@@ -601,6 +633,7 @@ void usb_stor_invoke_transport(Scsi_Cmnd *srb, struct us_data *us) ...@@ -601,6 +633,7 @@ void usb_stor_invoke_transport(Scsi_Cmnd *srb, struct us_data *us)
srb->request_buffer = old_request_buffer; srb->request_buffer = old_request_buffer;
srb->request_bufflen = old_request_bufflen; srb->request_bufflen = old_request_bufflen;
srb->use_sg = old_sg; srb->use_sg = old_sg;
srb->serial_number = old_serial_number;
srb->sc_data_direction = old_sc_data_direction; srb->sc_data_direction = old_sc_data_direction;
srb->cmd_len = old_cmd_len; srb->cmd_len = old_cmd_len;
memcpy(srb->cmnd, old_cmnd, MAX_COMMAND_SIZE); memcpy(srb->cmnd, old_cmnd, MAX_COMMAND_SIZE);
...@@ -616,10 +649,8 @@ void usb_stor_invoke_transport(Scsi_Cmnd *srb, struct us_data *us) ...@@ -616,10 +649,8 @@ void usb_stor_invoke_transport(Scsi_Cmnd *srb, struct us_data *us)
* multi-target device, since failure of an * multi-target device, since failure of an
* auto-sense is perfectly valid * auto-sense is perfectly valid
*/ */
if (!(us->flags & US_FL_SCM_MULT_TARG)) { if (!(us->flags & US_FL_SCM_MULT_TARG))
/* What if we try to abort during the reset? */
us->transport_reset(us); us->transport_reset(us);
}
srb->result = DID_ERROR << 16; srb->result = DID_ERROR << 16;
return; return;
} }
...@@ -982,43 +1013,38 @@ static int usb_stor_reset_common(struct us_data *us, ...@@ -982,43 +1013,38 @@ static int usb_stor_reset_common(struct us_data *us,
u16 value, u16 index, void *data, u16 size) u16 value, u16 index, void *data, u16 size)
{ {
int result; int result;
int result2;
/* A 20-second timeout may seem rather long, but a LaCie /* A 20-second timeout may seem rather long, but a LaCie
* StudioDrive USB2 device takes 16+ seconds to get going * StudioDrive USB2 device takes 16+ seconds to get going
* following a powerup or USB attach event. */ * following a powerup or USB attach event. */
/* Use usb_control_msg() because this is not a queued-command */ result = usb_stor_control_msg(us, us->send_ctrl_pipe,
result = usb_control_msg(us->pusb_dev, us->send_ctrl_pipe,
request, requesttype, value, index, data, size, request, requesttype, value, index, data, size,
20*HZ); 20*HZ);
if (result < 0) if (result < 0) {
goto Done; US_DEBUGP("Soft reset failed: %d\n", result);
return FAILED;
}
/* long wait for reset */ /* long wait for reset */
set_current_state(TASK_UNINTERRUPTIBLE); set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(HZ*6); schedule_timeout(HZ*6);
set_current_state(TASK_RUNNING); set_current_state(TASK_RUNNING);
/* Use usb_clear_halt() because this is not a queued-command */
US_DEBUGP("Soft reset: clearing bulk-in endpoint halt\n"); US_DEBUGP("Soft reset: clearing bulk-in endpoint halt\n");
result = usb_clear_halt(us->pusb_dev, us->recv_bulk_pipe); result = usb_stor_clear_halt(us, us->recv_bulk_pipe);
if (result < 0)
goto Done;
US_DEBUGP("Soft reset: clearing bulk-out endpoint halt\n"); US_DEBUGP("Soft reset: clearing bulk-out endpoint halt\n");
result = usb_clear_halt(us->pusb_dev, us->send_bulk_pipe); result2 = usb_stor_clear_halt(us, us->send_bulk_pipe);
Done:
/* return a result code based on the result of the control message */ /* return a result code based on the result of the control message */
if (result < 0) { if (result < 0 || result2 < 0) {
US_DEBUGP("Soft reset failed: %d\n", result); US_DEBUGP("Soft reset failed\n");
result = FAILED; return FAILED;
} else {
US_DEBUGP("Soft reset done\n");
result = SUCCESS;
} }
return result; US_DEBUGP("Soft reset done\n");
return SUCCESS;
} }
/* This issues a CB[I] Reset to the device in question /* This issues a CB[I] Reset to the device in question
......
...@@ -160,9 +160,9 @@ extern void usb_stor_stop_transport(struct us_data*); ...@@ -160,9 +160,9 @@ extern void usb_stor_stop_transport(struct us_data*);
extern int usb_stor_control_msg(struct us_data *us, unsigned int pipe, extern int usb_stor_control_msg(struct us_data *us, unsigned int pipe,
u8 request, u8 requesttype, u16 value, u16 index, u8 request, u8 requesttype, u16 value, u16 index,
void *data, u16 size); void *data, u16 size, int timeout);
extern int usb_stor_clear_halt(struct us_data *us, unsigned int pipe);
extern int usb_stor_clear_halt(struct us_data*, unsigned int pipe);
extern int usb_stor_ctrl_transfer(struct us_data *us, unsigned int pipe, extern int usb_stor_ctrl_transfer(struct us_data *us, unsigned int pipe,
u8 request, u8 requesttype, u16 value, u16 index, u8 request, u8 requesttype, u16 value, u16 index,
void *data, u16 size); void *data, u16 size);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment