xhci: Use delayed_work instead of timer for command timeout
This is preparation to fix abort operation race (See "xhci: Fix race related to abort operation"). To make timeout sleepable, use delayed_work instead of timer. [change a newly added pending timer fix to pending work -Mathias] Signed-off-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
4dea70778c
commit
cb4d5ce588
@ -1829,7 +1829,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
|
|||||||
int size;
|
int size;
|
||||||
int i, j, num_ports;
|
int i, j, num_ports;
|
||||||
|
|
||||||
del_timer_sync(&xhci->cmd_timer);
|
cancel_delayed_work_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);
|
||||||
@ -2376,9 +2376,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
|
|||||||
|
|
||||||
INIT_LIST_HEAD(&xhci->cmd_list);
|
INIT_LIST_HEAD(&xhci->cmd_list);
|
||||||
|
|
||||||
/* init command timeout timer */
|
/* init command timeout work */
|
||||||
setup_timer(&xhci->cmd_timer, xhci_handle_command_timeout,
|
INIT_DELAYED_WORK(&xhci->cmd_timer, xhci_handle_command_timeout);
|
||||||
(unsigned long)xhci);
|
|
||||||
|
|
||||||
page_size = readl(&xhci->op_regs->page_size);
|
page_size = readl(&xhci->op_regs->page_size);
|
||||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||||
|
@ -279,6 +279,11 @@ void xhci_ring_cmd_db(struct xhci_hcd *xhci)
|
|||||||
readl(&xhci->dba->doorbell[0]);
|
readl(&xhci->dba->doorbell[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool xhci_mod_cmd_timer(struct xhci_hcd *xhci, unsigned long delay)
|
||||||
|
{
|
||||||
|
return mod_delayed_work(system_wq, &xhci->cmd_timer, delay);
|
||||||
|
}
|
||||||
|
|
||||||
static int xhci_abort_cmd_ring(struct xhci_hcd *xhci)
|
static int xhci_abort_cmd_ring(struct xhci_hcd *xhci)
|
||||||
{
|
{
|
||||||
u64 temp_64;
|
u64 temp_64;
|
||||||
@ -295,7 +300,7 @@ static int xhci_abort_cmd_ring(struct xhci_hcd *xhci)
|
|||||||
* but the completion event in never sent. Use the cmd timeout timer to
|
* but the completion event in never sent. Use the cmd timeout timer to
|
||||||
* handle those cases. Use twice the time to cover the bit polling retry
|
* handle those cases. Use twice the time to cover the bit polling retry
|
||||||
*/
|
*/
|
||||||
mod_timer(&xhci->cmd_timer, jiffies + (2 * XHCI_CMD_DEFAULT_TIMEOUT));
|
xhci_mod_cmd_timer(xhci, 2 * XHCI_CMD_DEFAULT_TIMEOUT);
|
||||||
xhci_write_64(xhci, temp_64 | CMD_RING_ABORT,
|
xhci_write_64(xhci, temp_64 | CMD_RING_ABORT,
|
||||||
&xhci->op_regs->cmd_ring);
|
&xhci->op_regs->cmd_ring);
|
||||||
|
|
||||||
@ -320,7 +325,7 @@ static int xhci_abort_cmd_ring(struct xhci_hcd *xhci)
|
|||||||
|
|
||||||
xhci_err(xhci, "Stopped the command ring failed, "
|
xhci_err(xhci, "Stopped the command ring failed, "
|
||||||
"maybe the host is dead\n");
|
"maybe the host is dead\n");
|
||||||
del_timer(&xhci->cmd_timer);
|
cancel_delayed_work(&xhci->cmd_timer);
|
||||||
xhci->xhc_state |= XHCI_STATE_DYING;
|
xhci->xhc_state |= XHCI_STATE_DYING;
|
||||||
xhci_halt(xhci);
|
xhci_halt(xhci);
|
||||||
return -ESHUTDOWN;
|
return -ESHUTDOWN;
|
||||||
@ -1251,21 +1256,22 @@ static void xhci_handle_stopped_cmd_ring(struct xhci_hcd *xhci,
|
|||||||
if ((xhci->cmd_ring->dequeue != xhci->cmd_ring->enqueue) &&
|
if ((xhci->cmd_ring->dequeue != xhci->cmd_ring->enqueue) &&
|
||||||
!(xhci->xhc_state & XHCI_STATE_DYING)) {
|
!(xhci->xhc_state & XHCI_STATE_DYING)) {
|
||||||
xhci->current_cmd = cur_cmd;
|
xhci->current_cmd = cur_cmd;
|
||||||
mod_timer(&xhci->cmd_timer, jiffies + XHCI_CMD_DEFAULT_TIMEOUT);
|
xhci_mod_cmd_timer(xhci, XHCI_CMD_DEFAULT_TIMEOUT);
|
||||||
xhci_ring_cmd_db(xhci);
|
xhci_ring_cmd_db(xhci);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void xhci_handle_command_timeout(unsigned long data)
|
void xhci_handle_command_timeout(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct xhci_hcd *xhci;
|
struct xhci_hcd *xhci;
|
||||||
int ret;
|
int ret;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
u64 hw_ring_state;
|
u64 hw_ring_state;
|
||||||
bool second_timeout = false;
|
bool second_timeout = false;
|
||||||
xhci = (struct xhci_hcd *) data;
|
|
||||||
|
xhci = container_of(to_delayed_work(work), struct xhci_hcd, cmd_timer);
|
||||||
|
|
||||||
spin_lock_irqsave(&xhci->lock, flags);
|
spin_lock_irqsave(&xhci->lock, flags);
|
||||||
|
|
||||||
@ -1273,7 +1279,7 @@ void xhci_handle_command_timeout(unsigned long data)
|
|||||||
* If timeout work is pending, or current_cmd is NULL, it means we
|
* If timeout work is pending, or current_cmd is NULL, it means we
|
||||||
* raced with command completion. Command is handled so just return.
|
* raced with command completion. Command is handled so just return.
|
||||||
*/
|
*/
|
||||||
if (!xhci->current_cmd || timer_pending(&xhci->cmd_timer)) {
|
if (!xhci->current_cmd || delayed_work_pending(&xhci->cmd_timer)) {
|
||||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1346,7 +1352,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
|
|||||||
|
|
||||||
cmd = list_entry(xhci->cmd_list.next, struct xhci_command, cmd_list);
|
cmd = list_entry(xhci->cmd_list.next, struct xhci_command, cmd_list);
|
||||||
|
|
||||||
del_timer(&xhci->cmd_timer);
|
cancel_delayed_work(&xhci->cmd_timer);
|
||||||
|
|
||||||
trace_xhci_cmd_completion(cmd_trb, (struct xhci_generic_trb *) event);
|
trace_xhci_cmd_completion(cmd_trb, (struct xhci_generic_trb *) event);
|
||||||
|
|
||||||
@ -1437,7 +1443,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
|
|||||||
if (cmd->cmd_list.next != &xhci->cmd_list) {
|
if (cmd->cmd_list.next != &xhci->cmd_list) {
|
||||||
xhci->current_cmd = list_entry(cmd->cmd_list.next,
|
xhci->current_cmd = list_entry(cmd->cmd_list.next,
|
||||||
struct xhci_command, cmd_list);
|
struct xhci_command, cmd_list);
|
||||||
mod_timer(&xhci->cmd_timer, jiffies + XHCI_CMD_DEFAULT_TIMEOUT);
|
xhci_mod_cmd_timer(xhci, XHCI_CMD_DEFAULT_TIMEOUT);
|
||||||
} else if (xhci->current_cmd == cmd) {
|
} else if (xhci->current_cmd == cmd) {
|
||||||
xhci->current_cmd = NULL;
|
xhci->current_cmd = NULL;
|
||||||
}
|
}
|
||||||
@ -3808,9 +3814,9 @@ static int queue_command(struct xhci_hcd *xhci, struct xhci_command *cmd,
|
|||||||
|
|
||||||
/* if there are no other commands queued we start the timeout timer */
|
/* if there are no other commands queued we start the timeout timer */
|
||||||
if (xhci->cmd_list.next == &cmd->cmd_list &&
|
if (xhci->cmd_list.next == &cmd->cmd_list &&
|
||||||
!timer_pending(&xhci->cmd_timer)) {
|
!delayed_work_pending(&xhci->cmd_timer)) {
|
||||||
xhci->current_cmd = cmd;
|
xhci->current_cmd = cmd;
|
||||||
mod_timer(&xhci->cmd_timer, jiffies + XHCI_CMD_DEFAULT_TIMEOUT);
|
xhci_mod_cmd_timer(xhci, XHCI_CMD_DEFAULT_TIMEOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
queue_trb(xhci, xhci->cmd_ring, false, field1, field2, field3,
|
queue_trb(xhci, xhci->cmd_ring, false, field1, field2, field3,
|
||||||
|
@ -1568,7 +1568,7 @@ struct xhci_hcd {
|
|||||||
#define CMD_RING_STATE_STOPPED (1 << 2)
|
#define CMD_RING_STATE_STOPPED (1 << 2)
|
||||||
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 delayed_work cmd_timer;
|
||||||
struct xhci_command *current_cmd;
|
struct xhci_command *current_cmd;
|
||||||
struct xhci_ring *event_ring;
|
struct xhci_ring *event_ring;
|
||||||
struct xhci_erst erst;
|
struct xhci_erst erst;
|
||||||
@ -1934,7 +1934,7 @@ 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);
|
||||||
void xhci_handle_command_timeout(unsigned long data);
|
void xhci_handle_command_timeout(struct work_struct *work);
|
||||||
|
|
||||||
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);
|
||||||
|
Loading…
Reference in New Issue
Block a user