Commit c311e391 authored by Mathias Nyman's avatar Mathias Nyman Committed by Greg Kroah-Hartman

xhci: rework command timeout and cancellation,

Use one timer to control command timeout.

start/kick the timer every time a command is completed and a
new command is waiting, or a new command is added to a empty list.

If the timer runs out, then tag the current command as "aborted", and
start the xhci command abortion process.

Previously each function that submitted a command had its own timer.
If that command timed out, a new command structure for the
command was created and it was put on a cancel_cmd_list list,
then a pci write to abort the command ring was issued.

when the ring was aborted, it checked if the current command
was the one to be canceled, later when the ring was stopped the
driver got ownership of the TRBs in the command ring,
compared then to the TRBs in the cancel_cmd_list,
and turned them into No-ops.

Now, instead, at timeout we tag the status of the command in the
command queue to be aborted, and start the ring abortion.
Ring abortion stops the command ring and gives control of the
commands to us.
All the aborted commands are now turned into No-ops.

If the ring is already stopped when the command times outs its not possible
to start the ring abortion, in this case the command is turnd to No-op
right away.

All these changes allows us to remove the entire cancel_cmd_list code.

The functions waiting for a command to finish no longer have their own timeouts.
They will wait either until the command completes normally,
or until the whole command abortion is done.
Signed-off-by: default avatarMathias Nyman <mathias.nyman@linux.intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 9ea1833e
...@@ -271,7 +271,6 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend) ...@@ -271,7 +271,6 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend)
struct xhci_virt_device *virt_dev; struct xhci_virt_device *virt_dev;
struct xhci_command *cmd; struct xhci_command *cmd;
unsigned long flags; unsigned long flags;
int timeleft;
int ret; int ret;
int i; int i;
...@@ -304,12 +303,10 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend) ...@@ -304,12 +303,10 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend)
spin_unlock_irqrestore(&xhci->lock, flags); spin_unlock_irqrestore(&xhci->lock, flags);
/* Wait for last stop endpoint command to finish */ /* Wait for last stop endpoint command to finish */
timeleft = wait_for_completion_interruptible_timeout( wait_for_completion(cmd->completion);
cmd->completion,
XHCI_CMD_DEFAULT_TIMEOUT); if (cmd->status == COMP_CMD_ABORT || cmd->status == COMP_CMD_STOP) {
if (timeleft <= 0) { xhci_warn(xhci, "Timeout while waiting for stop endpoint command\n");
xhci_warn(xhci, "%s while waiting for stop endpoint command\n",
timeleft == 0 ? "Timeout" : "Signal");
ret = -ETIME; ret = -ETIME;
} }
xhci_free_command(xhci, cmd); xhci_free_command(xhci, cmd);
......
...@@ -1793,10 +1793,11 @@ void xhci_free_command(struct xhci_hcd *xhci, ...@@ -1793,10 +1793,11 @@ void xhci_free_command(struct xhci_hcd *xhci,
void xhci_mem_cleanup(struct xhci_hcd *xhci) void xhci_mem_cleanup(struct xhci_hcd *xhci)
{ {
struct device *dev = xhci_to_hcd(xhci)->self.controller; struct device *dev = xhci_to_hcd(xhci)->self.controller;
struct xhci_cd *cur_cd, *next_cd;
int size; int size;
int i, j, num_ports; int i, j, num_ports;
del_timer_sync(&xhci->cmd_timer);
/* Free the Event Ring Segment Table and the actual Event Ring */ /* Free the Event Ring Segment Table and the actual Event Ring */
size = sizeof(struct xhci_erst_entry)*(xhci->erst.num_entries); size = sizeof(struct xhci_erst_entry)*(xhci->erst.num_entries);
if (xhci->erst.entries) if (xhci->erst.entries)
...@@ -1815,11 +1816,6 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) ...@@ -1815,11 +1816,6 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
xhci_ring_free(xhci, xhci->cmd_ring); xhci_ring_free(xhci, xhci->cmd_ring);
xhci->cmd_ring = NULL; xhci->cmd_ring = NULL;
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed command ring"); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed command ring");
list_for_each_entry_safe(cur_cd, next_cd,
&xhci->cancel_cmd_list, cancel_cmd_list) {
list_del(&cur_cd->cancel_cmd_list);
kfree(cur_cd);
}
xhci_cleanup_command_queue(xhci); xhci_cleanup_command_queue(xhci);
for (i = 1; i < MAX_HC_SLOTS; ++i) for (i = 1; i < MAX_HC_SLOTS; ++i)
...@@ -2323,7 +2319,6 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) ...@@ -2323,7 +2319,6 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
u32 page_size, temp; u32 page_size, temp;
int i; int i;
INIT_LIST_HEAD(&xhci->cancel_cmd_list);
INIT_LIST_HEAD(&xhci->cmd_list); INIT_LIST_HEAD(&xhci->cmd_list);
page_size = readl(&xhci->op_regs->page_size); page_size = readl(&xhci->op_regs->page_size);
...@@ -2510,6 +2505,11 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) ...@@ -2510,6 +2505,11 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
"Wrote ERST address to ir_set 0."); "Wrote ERST address to ir_set 0.");
xhci_print_ir_set(xhci, 0); xhci_print_ir_set(xhci, 0);
/* init command timeout timer */
init_timer(&xhci->cmd_timer);
xhci->cmd_timer.data = (unsigned long) xhci;
xhci->cmd_timer.function = xhci_handle_command_timeout;
/* /*
* XXX: Might need to set the Interrupter Moderation Register to * XXX: Might need to set the Interrupter Moderation Register to
* something other than the default (~1ms minimum between interrupts). * something other than the default (~1ms minimum between interrupts).
......
This diff is collapsed.
...@@ -1820,6 +1820,11 @@ static int xhci_configure_endpoint_result(struct xhci_hcd *xhci, ...@@ -1820,6 +1820,11 @@ static int xhci_configure_endpoint_result(struct xhci_hcd *xhci,
int ret; int ret;
switch (*cmd_status) { switch (*cmd_status) {
case COMP_CMD_ABORT:
case COMP_CMD_STOP:
xhci_warn(xhci, "Timeout while waiting for configure endpoint command\n");
ret = -ETIME;
break;
case COMP_ENOMEM: case COMP_ENOMEM:
dev_warn(&udev->dev, "Not enough host controller resources " dev_warn(&udev->dev, "Not enough host controller resources "
"for new device state.\n"); "for new device state.\n");
...@@ -1866,6 +1871,11 @@ static int xhci_evaluate_context_result(struct xhci_hcd *xhci, ...@@ -1866,6 +1871,11 @@ static int xhci_evaluate_context_result(struct xhci_hcd *xhci,
struct xhci_virt_device *virt_dev = xhci->devs[udev->slot_id]; struct xhci_virt_device *virt_dev = xhci->devs[udev->slot_id];
switch (*cmd_status) { switch (*cmd_status) {
case COMP_CMD_ABORT:
case COMP_CMD_STOP:
xhci_warn(xhci, "Timeout while waiting for evaluate context command\n");
ret = -ETIME;
break;
case COMP_EINVAL: case COMP_EINVAL:
dev_warn(&udev->dev, "WARN: xHCI driver setup invalid evaluate " dev_warn(&udev->dev, "WARN: xHCI driver setup invalid evaluate "
"context command.\n"); "context command.\n");
...@@ -2590,7 +2600,6 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci, ...@@ -2590,7 +2600,6 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
bool ctx_change, bool must_succeed) bool ctx_change, bool must_succeed)
{ {
int ret; int ret;
int timeleft;
unsigned long flags; unsigned long flags;
struct xhci_input_control_ctx *ctrl_ctx; struct xhci_input_control_ctx *ctrl_ctx;
struct xhci_virt_device *virt_dev; struct xhci_virt_device *virt_dev;
...@@ -2646,21 +2655,7 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci, ...@@ -2646,21 +2655,7 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
spin_unlock_irqrestore(&xhci->lock, flags); spin_unlock_irqrestore(&xhci->lock, flags);
/* Wait for the configure endpoint command to complete */ /* Wait for the configure endpoint command to complete */
timeleft = wait_for_completion_interruptible_timeout( wait_for_completion(command->completion);
command->completion,
XHCI_CMD_DEFAULT_TIMEOUT);
if (timeleft <= 0) {
xhci_warn(xhci, "%s while waiting for %s command\n",
timeleft == 0 ? "Timeout" : "Signal",
ctx_change == 0 ?
"configure endpoint" :
"evaluate context");
/* cancel the configure endpoint command */
ret = xhci_cancel_cmd(xhci, command, command->command_trb);
if (ret < 0)
return ret;
return -ETIME;
}
if (!ctx_change) if (!ctx_change)
ret = xhci_configure_endpoint_result(xhci, udev, ret = xhci_configure_endpoint_result(xhci, udev,
...@@ -3438,7 +3433,6 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev) ...@@ -3438,7 +3433,6 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev)
unsigned int slot_id; unsigned int slot_id;
struct xhci_virt_device *virt_dev; struct xhci_virt_device *virt_dev;
struct xhci_command *reset_device_cmd; struct xhci_command *reset_device_cmd;
int timeleft;
int last_freed_endpoint; int last_freed_endpoint;
struct xhci_slot_ctx *slot_ctx; struct xhci_slot_ctx *slot_ctx;
int old_active_eps = 0; int old_active_eps = 0;
...@@ -3506,15 +3500,7 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev) ...@@ -3506,15 +3500,7 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev)
spin_unlock_irqrestore(&xhci->lock, flags); spin_unlock_irqrestore(&xhci->lock, flags);
/* Wait for the Reset Device command to finish */ /* Wait for the Reset Device command to finish */
timeleft = wait_for_completion_interruptible_timeout( wait_for_completion(reset_device_cmd->completion);
reset_device_cmd->completion,
XHCI_CMD_DEFAULT_TIMEOUT);
if (timeleft <= 0) {
xhci_warn(xhci, "%s while waiting for reset device command\n",
timeleft == 0 ? "Timeout" : "Signal");
ret = -ETIME;
goto command_cleanup;
}
/* The Reset Device command can't fail, according to the 0.95/0.96 spec, /* The Reset Device command can't fail, according to the 0.95/0.96 spec,
* unless we tried to reset a slot ID that wasn't enabled, * unless we tried to reset a slot ID that wasn't enabled,
...@@ -3522,6 +3508,11 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev) ...@@ -3522,6 +3508,11 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev)
*/ */
ret = reset_device_cmd->status; ret = reset_device_cmd->status;
switch (ret) { switch (ret) {
case COMP_CMD_ABORT:
case COMP_CMD_STOP:
xhci_warn(xhci, "Timeout waiting for reset device command\n");
ret = -ETIME;
goto command_cleanup;
case COMP_EBADSLT: /* 0.95 completion code for bad slot ID */ case COMP_EBADSLT: /* 0.95 completion code for bad slot ID */
case COMP_CTX_STATE: /* 0.96 completion code for same thing */ case COMP_CTX_STATE: /* 0.96 completion code for same thing */
xhci_dbg(xhci, "Can't reset device (slot ID %u) in %s state\n", xhci_dbg(xhci, "Can't reset device (slot ID %u) in %s state\n",
...@@ -3691,7 +3682,6 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev) ...@@ -3691,7 +3682,6 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
{ {
struct xhci_hcd *xhci = hcd_to_xhci(hcd); struct xhci_hcd *xhci = hcd_to_xhci(hcd);
unsigned long flags; unsigned long flags;
int timeleft;
int ret; int ret;
struct xhci_command *command; struct xhci_command *command;
...@@ -3711,19 +3701,9 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev) ...@@ -3711,19 +3701,9 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
xhci_ring_cmd_db(xhci); xhci_ring_cmd_db(xhci);
spin_unlock_irqrestore(&xhci->lock, flags); spin_unlock_irqrestore(&xhci->lock, flags);
/* XXX: how much time for xHC slot assignment? */ wait_for_completion(command->completion);
timeleft = wait_for_completion_interruptible_timeout(
command->completion,
XHCI_CMD_DEFAULT_TIMEOUT);
if (timeleft <= 0) {
xhci_warn(xhci, "%s while waiting for a slot\n",
timeleft == 0 ? "Timeout" : "Signal");
/* cancel the enable slot request */
ret = xhci_cancel_cmd(xhci, NULL, command->command_trb);
return ret;
}
if (!xhci->slot_id) { if (!xhci->slot_id || command->status != COMP_SUCCESS) {
xhci_err(xhci, "Error while assigning device slot ID\n"); xhci_err(xhci, "Error while assigning device slot ID\n");
xhci_err(xhci, "Max number of devices this xHCI host supports is %u.\n", xhci_err(xhci, "Max number of devices this xHCI host supports is %u.\n",
HCS_MAX_SLOTS( HCS_MAX_SLOTS(
...@@ -3792,7 +3772,6 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, ...@@ -3792,7 +3772,6 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
{ {
const char *act = setup == SETUP_CONTEXT_ONLY ? "context" : "address"; const char *act = setup == SETUP_CONTEXT_ONLY ? "context" : "address";
unsigned long flags; unsigned long flags;
int timeleft;
struct xhci_virt_device *virt_dev; struct xhci_virt_device *virt_dev;
int ret = 0; int ret = 0;
struct xhci_hcd *xhci = hcd_to_xhci(hcd); struct xhci_hcd *xhci = hcd_to_xhci(hcd);
...@@ -3867,23 +3846,18 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, ...@@ -3867,23 +3846,18 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
spin_unlock_irqrestore(&xhci->lock, flags); spin_unlock_irqrestore(&xhci->lock, flags);
/* ctrl tx can take up to 5 sec; XXX: need more time for xHC? */ /* ctrl tx can take up to 5 sec; XXX: need more time for xHC? */
timeleft = wait_for_completion_interruptible_timeout( wait_for_completion(command->completion);
command->completion, XHCI_CMD_DEFAULT_TIMEOUT);
/* FIXME: From section 4.3.4: "Software shall be responsible for timing /* FIXME: From section 4.3.4: "Software shall be responsible for timing
* the SetAddress() "recovery interval" required by USB and aborting the * the SetAddress() "recovery interval" required by USB and aborting the
* command on a timeout. * command on a timeout.
*/ */
if (timeleft <= 0) {
xhci_warn(xhci, "%s while waiting for setup %s command\n",
timeleft == 0 ? "Timeout" : "Signal", act);
/* cancel the address device command */
ret = xhci_cancel_cmd(xhci, NULL, command->command_trb);
if (ret < 0)
return ret;
return -ETIME;
}
switch (command->status) { switch (command->status) {
case COMP_CMD_ABORT:
case COMP_CMD_STOP:
xhci_warn(xhci, "Timeout while waiting for setup device command\n");
ret = -ETIME;
break;
case COMP_CTX_STATE: case COMP_CTX_STATE:
case COMP_EBADSLT: case COMP_EBADSLT:
xhci_err(xhci, "Setup ERROR: setup %s command for slot %d.\n", xhci_err(xhci, "Setup ERROR: setup %s command for slot %d.\n",
......
...@@ -1295,7 +1295,6 @@ struct xhci_td { ...@@ -1295,7 +1295,6 @@ struct xhci_td {
/* command descriptor */ /* command descriptor */
struct xhci_cd { struct xhci_cd {
struct list_head cancel_cmd_list;
struct xhci_command *command; struct xhci_command *command;
union xhci_trb *cmd_trb; union xhci_trb *cmd_trb;
}; };
...@@ -1480,9 +1479,10 @@ struct xhci_hcd { ...@@ -1480,9 +1479,10 @@ struct xhci_hcd {
#define CMD_RING_STATE_RUNNING (1 << 0) #define CMD_RING_STATE_RUNNING (1 << 0)
#define CMD_RING_STATE_ABORTED (1 << 1) #define CMD_RING_STATE_ABORTED (1 << 1)
#define CMD_RING_STATE_STOPPED (1 << 2) #define CMD_RING_STATE_STOPPED (1 << 2)
struct list_head cancel_cmd_list;
struct list_head cmd_list; struct list_head cmd_list;
unsigned int cmd_ring_reserved_trbs; unsigned int cmd_ring_reserved_trbs;
struct timer_list cmd_timer;
struct xhci_command *current_cmd;
struct xhci_ring *event_ring; struct xhci_ring *event_ring;
struct xhci_erst erst; struct xhci_erst erst;
/* Scratchpad */ /* Scratchpad */
...@@ -1845,8 +1845,8 @@ void xhci_queue_config_ep_quirk(struct xhci_hcd *xhci, ...@@ -1845,8 +1845,8 @@ void xhci_queue_config_ep_quirk(struct xhci_hcd *xhci,
unsigned int slot_id, unsigned int ep_index, unsigned int slot_id, unsigned int ep_index,
struct xhci_dequeue_state *deq_state); struct xhci_dequeue_state *deq_state);
void xhci_stop_endpoint_command_watchdog(unsigned long arg); void xhci_stop_endpoint_command_watchdog(unsigned long arg);
int xhci_cancel_cmd(struct xhci_hcd *xhci, struct xhci_command *command, void xhci_handle_command_timeout(unsigned long data);
union xhci_trb *cmd_trb);
void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id, void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id,
unsigned int ep_index, unsigned int stream_id); unsigned int ep_index, unsigned int stream_id);
void xhci_cleanup_command_queue(struct xhci_hcd *xhci); void xhci_cleanup_command_queue(struct xhci_hcd *xhci);
......
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