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

xhci: Add a global command queue

Create a list to store command structures, add a structure to it every time
a command is submitted, and remove it from the list once we get a
command completion event matching the command.

Callers that wait for completion will free their command structures themselves.
The other command structures are freed in the command completion event handler.

Also add a check that prevents queuing commands if host is dying
Signed-off-by: default avatarMathias Nyman <mathias.nyman@linux.intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent ddba5cd0
...@@ -1821,6 +1821,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) ...@@ -1821,6 +1821,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
list_del(&cur_cd->cancel_cmd_list); list_del(&cur_cd->cancel_cmd_list);
kfree(cur_cd); kfree(cur_cd);
} }
xhci_cleanup_command_queue(xhci);
for (i = 1; i < MAX_HC_SLOTS; ++i) for (i = 1; i < MAX_HC_SLOTS; ++i)
xhci_free_virt_device(xhci, i); xhci_free_virt_device(xhci, i);
...@@ -2324,6 +2325,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) ...@@ -2324,6 +2325,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
int i; int i;
INIT_LIST_HEAD(&xhci->cancel_cmd_list); INIT_LIST_HEAD(&xhci->cancel_cmd_list);
INIT_LIST_HEAD(&xhci->cmd_list);
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,
......
...@@ -1520,6 +1520,20 @@ static void xhci_handle_cmd_nec_get_fw(struct xhci_hcd *xhci, ...@@ -1520,6 +1520,20 @@ static void xhci_handle_cmd_nec_get_fw(struct xhci_hcd *xhci,
NEC_FW_MINOR(le32_to_cpu(event->status))); NEC_FW_MINOR(le32_to_cpu(event->status)));
} }
static void xhci_del_and_free_cmd(struct xhci_command *cmd)
{
list_del(&cmd->cmd_list);
if (!cmd->completion)
kfree(cmd);
}
void xhci_cleanup_command_queue(struct xhci_hcd *xhci)
{
struct xhci_command *cur_cmd, *tmp_cmd;
list_for_each_entry_safe(cur_cmd, tmp_cmd, &xhci->cmd_list, cmd_list)
xhci_del_and_free_cmd(cur_cmd);
}
static void handle_cmd_completion(struct xhci_hcd *xhci, static void handle_cmd_completion(struct xhci_hcd *xhci,
struct xhci_event_cmd *event) struct xhci_event_cmd *event)
{ {
...@@ -1528,6 +1542,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, ...@@ -1528,6 +1542,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
dma_addr_t cmd_dequeue_dma; dma_addr_t cmd_dequeue_dma;
u32 cmd_comp_code; u32 cmd_comp_code;
union xhci_trb *cmd_trb; union xhci_trb *cmd_trb;
struct xhci_command *cmd;
u32 cmd_type; u32 cmd_type;
cmd_dma = le64_to_cpu(event->cmd_trb); cmd_dma = le64_to_cpu(event->cmd_trb);
...@@ -1545,6 +1560,13 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, ...@@ -1545,6 +1560,13 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
return; return;
} }
cmd = list_entry(xhci->cmd_list.next, struct xhci_command, cmd_list);
if (cmd->command_trb != xhci->cmd_ring->dequeue) {
xhci_err(xhci,
"Command completion event does not match command\n");
return;
}
trace_xhci_cmd_completion(cmd_trb, (struct xhci_generic_trb *) event); trace_xhci_cmd_completion(cmd_trb, (struct xhci_generic_trb *) event);
cmd_comp_code = GET_COMP_CODE(le32_to_cpu(event->status)); cmd_comp_code = GET_COMP_CODE(le32_to_cpu(event->status));
...@@ -1614,6 +1636,9 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, ...@@ -1614,6 +1636,9 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
xhci->error_bitmask |= 1 << 6; xhci->error_bitmask |= 1 << 6;
break; break;
} }
xhci_del_and_free_cmd(cmd);
inc_deq(xhci, xhci->cmd_ring); inc_deq(xhci, xhci->cmd_ring);
} }
...@@ -3998,6 +4023,8 @@ static int queue_command(struct xhci_hcd *xhci, struct xhci_command *cmd, ...@@ -3998,6 +4023,8 @@ static int queue_command(struct xhci_hcd *xhci, struct xhci_command *cmd,
{ {
int reserved_trbs = xhci->cmd_ring_reserved_trbs; int reserved_trbs = xhci->cmd_ring_reserved_trbs;
int ret; int ret;
if (xhci->xhc_state & XHCI_STATE_DYING)
return -ESHUTDOWN;
if (!command_must_succeed) if (!command_must_succeed)
reserved_trbs++; reserved_trbs++;
...@@ -4011,10 +4038,9 @@ static int queue_command(struct xhci_hcd *xhci, struct xhci_command *cmd, ...@@ -4011,10 +4038,9 @@ static int queue_command(struct xhci_hcd *xhci, struct xhci_command *cmd,
"unfailable commands failed.\n"); "unfailable commands failed.\n");
return ret; return ret;
} }
if (cmd->completion)
cmd->command_trb = xhci->cmd_ring->enqueue; cmd->command_trb = xhci->cmd_ring->enqueue;
else list_add_tail(&cmd->cmd_list, &xhci->cmd_list);
kfree(cmd);
queue_trb(xhci, xhci->cmd_ring, false, field1, field2, field3, queue_trb(xhci, xhci->cmd_ring, false, field1, field2, field3,
field4 | xhci->cmd_ring->cycle_state); field4 | xhci->cmd_ring->cycle_state);
......
...@@ -3732,7 +3732,6 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev) ...@@ -3732,7 +3732,6 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
timeleft == 0 ? "Timeout" : "Signal"); timeleft == 0 ? "Timeout" : "Signal");
/* cancel the enable slot request */ /* cancel the enable slot request */
ret = xhci_cancel_cmd(xhci, NULL, command->command_trb); ret = xhci_cancel_cmd(xhci, NULL, command->command_trb);
kfree(command);
return ret; return ret;
} }
...@@ -3891,7 +3890,6 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, ...@@ -3891,7 +3890,6 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
timeleft == 0 ? "Timeout" : "Signal", act); timeleft == 0 ? "Timeout" : "Signal", act);
/* cancel the address device command */ /* cancel the address device command */
ret = xhci_cancel_cmd(xhci, NULL, command->command_trb); ret = xhci_cancel_cmd(xhci, NULL, command->command_trb);
kfree(command);
if (ret < 0) if (ret < 0)
return ret; return ret;
return -ETIME; return -ETIME;
......
...@@ -1484,6 +1484,7 @@ struct xhci_hcd { ...@@ -1484,6 +1484,7 @@ struct xhci_hcd {
#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 cancel_cmd_list;
struct list_head cmd_list;
unsigned int cmd_ring_reserved_trbs; unsigned int cmd_ring_reserved_trbs;
struct xhci_ring *event_ring; struct xhci_ring *event_ring;
struct xhci_erst erst; struct xhci_erst erst;
...@@ -1851,6 +1852,7 @@ int xhci_cancel_cmd(struct xhci_hcd *xhci, struct xhci_command *command, ...@@ -1851,6 +1852,7 @@ int xhci_cancel_cmd(struct xhci_hcd *xhci, struct xhci_command *command,
union xhci_trb *cmd_trb); 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);
/* xHCI roothub code */ /* xHCI roothub code */
void xhci_set_link_state(struct xhci_hcd *xhci, __le32 __iomem **port_array, void xhci_set_link_state(struct xhci_hcd *xhci, __le32 __iomem **port_array,
......
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