mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 19:00:25 +00:00
USB: whci-hcd: make endpoint_reset method async
usb_hcd_endpoint_reset() may be called in atomic context and must not sleep. So make whci-hcd's endpoint_reset() asynchronous. URBs submitted while the reset is in progress will be queued (on the std list) and transfers will resume once the reset is complete. Signed-off-by: David Vrabel <david.vrabel@csr.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
586dfc8caf
commit
831baa4915
5 changed files with 31 additions and 6 deletions
|
@ -227,11 +227,21 @@ void scan_async_work(struct work_struct *work)
|
|||
/*
|
||||
* Now that the ASL is updated, complete the removal of any
|
||||
* removed qsets.
|
||||
*
|
||||
* If the qset was to be reset, do so and reinsert it into the
|
||||
* ASL if it has pending transfers.
|
||||
*/
|
||||
spin_lock_irq(&whc->lock);
|
||||
|
||||
list_for_each_entry_safe(qset, t, &whc->async_removed_list, list_node) {
|
||||
qset_remove_complete(whc, qset);
|
||||
if (qset->reset) {
|
||||
qset_reset(whc, qset);
|
||||
if (!list_empty(&qset->stds)) {
|
||||
asl_qset_insert_begin(whc, qset);
|
||||
queue_work(whc->workqueue, &whc->async_work);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irq(&whc->lock);
|
||||
|
@ -267,7 +277,7 @@ int asl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags)
|
|||
else
|
||||
err = qset_add_urb(whc, qset, urb, GFP_ATOMIC);
|
||||
if (!err) {
|
||||
if (!qset->in_sw_list)
|
||||
if (!qset->in_sw_list && !qset->remove)
|
||||
asl_qset_insert_begin(whc, qset);
|
||||
} else
|
||||
usb_hcd_unlink_urb_from_ep(&whc->wusbhc.usb_hcd, urb);
|
||||
|
|
|
@ -192,19 +192,23 @@ static void whc_endpoint_reset(struct usb_hcd *usb_hcd,
|
|||
struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
|
||||
struct whc *whc = wusbhc_to_whc(wusbhc);
|
||||
struct whc_qset *qset;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&whc->lock, flags);
|
||||
|
||||
qset = ep->hcpriv;
|
||||
if (qset) {
|
||||
qset->remove = 1;
|
||||
qset->reset = 1;
|
||||
|
||||
if (usb_endpoint_xfer_bulk(&ep->desc)
|
||||
|| usb_endpoint_xfer_control(&ep->desc))
|
||||
queue_work(whc->workqueue, &whc->async_work);
|
||||
else
|
||||
queue_work(whc->workqueue, &whc->periodic_work);
|
||||
|
||||
qset_reset(whc, qset);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&whc->lock, flags);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -255,11 +255,21 @@ void scan_periodic_work(struct work_struct *work)
|
|||
/*
|
||||
* Now that the PZL is updated, complete the removal of any
|
||||
* removed qsets.
|
||||
*
|
||||
* If the qset was to be reset, do so and reinsert it into the
|
||||
* PZL if it has pending transfers.
|
||||
*/
|
||||
spin_lock_irq(&whc->lock);
|
||||
|
||||
list_for_each_entry_safe(qset, t, &whc->periodic_removed_list, list_node) {
|
||||
qset_remove_complete(whc, qset);
|
||||
if (qset->reset) {
|
||||
qset_reset(whc, qset);
|
||||
if (!list_empty(&qset->stds)) {
|
||||
qset_insert_in_sw_list(whc, qset);
|
||||
queue_work(whc->workqueue, &whc->periodic_work);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irq(&whc->lock);
|
||||
|
@ -295,7 +305,7 @@ int pzl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags)
|
|||
else
|
||||
err = qset_add_urb(whc, qset, urb, GFP_ATOMIC);
|
||||
if (!err) {
|
||||
if (!qset->in_sw_list)
|
||||
if (!qset->in_sw_list && !qset->remove)
|
||||
qset_insert_in_sw_list(whc, qset);
|
||||
} else
|
||||
usb_hcd_unlink_urb_from_ep(&whc->wusbhc.usb_hcd, urb);
|
||||
|
|
|
@ -103,7 +103,6 @@ static void qset_fill_qh(struct whc_qset *qset, struct urb *urb)
|
|||
void qset_clear(struct whc *whc, struct whc_qset *qset)
|
||||
{
|
||||
qset->td_start = qset->td_end = qset->ntds = 0;
|
||||
qset->remove = 0;
|
||||
|
||||
qset->qh.link = cpu_to_le32(QH_LINK_NTDS(8) | QH_LINK_T);
|
||||
qset->qh.status = qset->qh.status & QH_STATUS_SEQ_MASK;
|
||||
|
@ -125,7 +124,7 @@ void qset_clear(struct whc *whc, struct whc_qset *qset)
|
|||
*/
|
||||
void qset_reset(struct whc *whc, struct whc_qset *qset)
|
||||
{
|
||||
wait_for_completion(&qset->remove_complete);
|
||||
qset->reset = 0;
|
||||
|
||||
qset->qh.status &= ~QH_STATUS_SEQ_MASK;
|
||||
qset->qh.cur_window = cpu_to_le32((1 << qset->max_burst) - 1);
|
||||
|
@ -156,6 +155,7 @@ struct whc_qset *get_qset(struct whc *whc, struct urb *urb,
|
|||
|
||||
void qset_remove_complete(struct whc *whc, struct whc_qset *qset)
|
||||
{
|
||||
qset->remove = 0;
|
||||
list_del_init(&qset->list_node);
|
||||
complete(&qset->remove_complete);
|
||||
}
|
||||
|
|
|
@ -264,6 +264,7 @@ struct whc_qset {
|
|||
unsigned in_sw_list:1;
|
||||
unsigned in_hw_list:1;
|
||||
unsigned remove:1;
|
||||
unsigned reset:1;
|
||||
struct urb *pause_after_urb;
|
||||
struct completion remove_complete;
|
||||
int max_burst;
|
||||
|
|
Loading…
Reference in a new issue