Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+11 −1
Original line number Original line Diff line number Diff line
@@ -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)
	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);
+6 −2
Original line number Original line Diff line number Diff line
@@ -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);
}
}




+11 −1
Original line number Original line Diff line number Diff line
@@ -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)
	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);
+2 −2
Original line number Original line Diff line number Diff line
@@ -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)
 */
 */
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,


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);
}
}
+1 −0
Original line number Original line Diff line number Diff line
@@ -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;