Commit 831baa49 authored by David Vrabel's avatar David Vrabel Committed by Greg Kroah-Hartman

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: default avatarDavid Vrabel <david.vrabel@csr.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 586dfc8c
...@@ -227,11 +227,21 @@ void scan_async_work(struct work_struct *work) ...@@ -227,11 +227,21 @@ void scan_async_work(struct work_struct *work)
/* /*
* Now that the ASL is updated, complete the removal of any * Now that the ASL is updated, complete the removal of any
* removed qsets. * 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); spin_lock_irq(&whc->lock);
list_for_each_entry_safe(qset, t, &whc->async_removed_list, list_node) { list_for_each_entry_safe(qset, t, &whc->async_removed_list, list_node) {
qset_remove_complete(whc, qset); 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); spin_unlock_irq(&whc->lock);
...@@ -267,7 +277,7 @@ int asl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags) ...@@ -267,7 +277,7 @@ int asl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags)
else else
err = qset_add_urb(whc, qset, urb, GFP_ATOMIC); err = qset_add_urb(whc, qset, urb, GFP_ATOMIC);
if (!err) { if (!err) {
if (!qset->in_sw_list) if (!qset->in_sw_list && !qset->remove)
asl_qset_insert_begin(whc, qset); asl_qset_insert_begin(whc, qset);
} else } else
usb_hcd_unlink_urb_from_ep(&whc->wusbhc.usb_hcd, urb); 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, ...@@ -192,19 +192,23 @@ static void whc_endpoint_reset(struct usb_hcd *usb_hcd,
struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
struct whc *whc = wusbhc_to_whc(wusbhc); struct whc *whc = wusbhc_to_whc(wusbhc);
struct whc_qset *qset; struct whc_qset *qset;
unsigned long flags;
spin_lock_irqsave(&whc->lock, flags);
qset = ep->hcpriv; qset = ep->hcpriv;
if (qset) { if (qset) {
qset->remove = 1; qset->remove = 1;
qset->reset = 1;
if (usb_endpoint_xfer_bulk(&ep->desc) if (usb_endpoint_xfer_bulk(&ep->desc)
|| usb_endpoint_xfer_control(&ep->desc)) || usb_endpoint_xfer_control(&ep->desc))
queue_work(whc->workqueue, &whc->async_work); queue_work(whc->workqueue, &whc->async_work);
else else
queue_work(whc->workqueue, &whc->periodic_work); 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) ...@@ -255,11 +255,21 @@ void scan_periodic_work(struct work_struct *work)
/* /*
* Now that the PZL is updated, complete the removal of any * Now that the PZL is updated, complete the removal of any
* removed qsets. * 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); spin_lock_irq(&whc->lock);
list_for_each_entry_safe(qset, t, &whc->periodic_removed_list, list_node) { list_for_each_entry_safe(qset, t, &whc->periodic_removed_list, list_node) {
qset_remove_complete(whc, qset); 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); spin_unlock_irq(&whc->lock);
...@@ -295,7 +305,7 @@ int pzl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags) ...@@ -295,7 +305,7 @@ int pzl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags)
else else
err = qset_add_urb(whc, qset, urb, GFP_ATOMIC); err = qset_add_urb(whc, qset, urb, GFP_ATOMIC);
if (!err) { if (!err) {
if (!qset->in_sw_list) if (!qset->in_sw_list && !qset->remove)
qset_insert_in_sw_list(whc, qset); qset_insert_in_sw_list(whc, qset);
} else } else
usb_hcd_unlink_urb_from_ep(&whc->wusbhc.usb_hcd, urb); 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) ...@@ -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) void qset_clear(struct whc *whc, struct whc_qset *qset)
{ {
qset->td_start = qset->td_end = qset->ntds = 0; 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.link = cpu_to_le32(QH_LINK_NTDS(8) | QH_LINK_T);
qset->qh.status = qset->qh.status & QH_STATUS_SEQ_MASK; qset->qh.status = qset->qh.status & QH_STATUS_SEQ_MASK;
...@@ -125,7 +124,7 @@ void qset_clear(struct whc *whc, struct whc_qset *qset) ...@@ -125,7 +124,7 @@ void qset_clear(struct whc *whc, struct whc_qset *qset)
*/ */
void qset_reset(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.status &= ~QH_STATUS_SEQ_MASK;
qset->qh.cur_window = cpu_to_le32((1 << qset->max_burst) - 1); 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, ...@@ -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) void qset_remove_complete(struct whc *whc, struct whc_qset *qset)
{ {
qset->remove = 0;
list_del_init(&qset->list_node); list_del_init(&qset->list_node);
complete(&qset->remove_complete); complete(&qset->remove_complete);
} }
......
...@@ -264,6 +264,7 @@ struct whc_qset { ...@@ -264,6 +264,7 @@ struct whc_qset {
unsigned in_sw_list:1; unsigned in_sw_list:1;
unsigned in_hw_list:1; unsigned in_hw_list:1;
unsigned remove:1; unsigned remove:1;
unsigned reset:1;
struct urb *pause_after_urb; struct urb *pause_after_urb;
struct completion remove_complete; struct completion remove_complete;
int max_burst; int max_burst;
......
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