summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastian Andrzej Siewior <bigeasy@linutronix.de>2018-06-14 12:36:46 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2018-06-28 06:36:07 -0400
commit2df6948428542c5a22fbf9c7c36c66ccc9363c7d (patch)
treebcdf8346983bf0da88cc971d3285ccd4a1df43a2
parent4327059a14bb6d9578bfb82a6b7c59b3bbf78335 (diff)
USB: cdc-wdm: don't enable interrupts in USB-giveback
In the code path __usb_hcd_giveback_urb() -> wdm_in_callback() -> service_outstanding_interrupt() The function service_outstanding_interrupt() will unconditionally enable interrupts during unlock and invoke usb_submit_urb() with GFP_KERNEL. If the HCD completes in BH (like ehci does) then the context remains atomic due local_bh_disable() and enabling interrupts does not change this. Defer the error case handling to a workqueue as suggested by Oliver Neukum. In case of an error the worker performs the read out and wakes the user. Fixes: c1da59dad0eb ("cdc-wdm: Clear read pipeline in case of error") Cc: Robert Foss <robert.foss@collabora.com> Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Acked-by: Oliver Neukum <oneukum@suse.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/usb/class/cdc-wdm.c31
1 files changed, 24 insertions, 7 deletions
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c
index a0d284ef3f40..203bbd378858 100644
--- a/drivers/usb/class/cdc-wdm.c
+++ b/drivers/usb/class/cdc-wdm.c
@@ -96,6 +96,7 @@ struct wdm_device {
96 struct mutex rlock; 96 struct mutex rlock;
97 wait_queue_head_t wait; 97 wait_queue_head_t wait;
98 struct work_struct rxwork; 98 struct work_struct rxwork;
99 struct work_struct service_outs_intr;
99 int werr; 100 int werr;
100 int rerr; 101 int rerr;
101 int resp_count; 102 int resp_count;
@@ -151,9 +152,6 @@ static void wdm_out_callback(struct urb *urb)
151 wake_up(&desc->wait); 152 wake_up(&desc->wait);
152} 153}
153 154
154/* forward declaration */
155static int service_outstanding_interrupt(struct wdm_device *desc);
156
157static void wdm_in_callback(struct urb *urb) 155static void wdm_in_callback(struct urb *urb)
158{ 156{
159 struct wdm_device *desc = urb->context; 157 struct wdm_device *desc = urb->context;
@@ -209,8 +207,6 @@ static void wdm_in_callback(struct urb *urb)
209 } 207 }
210 } 208 }
211skip_error: 209skip_error:
212 set_bit(WDM_READ, &desc->flags);
213 wake_up(&desc->wait);
214 210
215 if (desc->rerr) { 211 if (desc->rerr) {
216 /* 212 /*
@@ -219,9 +215,11 @@ skip_error:
219 * We should respond to further attempts from the device to send 215 * We should respond to further attempts from the device to send
220 * data, so that we can get unstuck. 216 * data, so that we can get unstuck.
221 */ 217 */
222 service_outstanding_interrupt(desc); 218 schedule_work(&desc->service_outs_intr);
219 } else {
220 set_bit(WDM_READ, &desc->flags);
221 wake_up(&desc->wait);
223 } 222 }
224
225 spin_unlock(&desc->iuspin); 223 spin_unlock(&desc->iuspin);
226} 224}
227 225
@@ -758,6 +756,21 @@ static void wdm_rxwork(struct work_struct *work)
758 } 756 }
759} 757}
760 758
759static void service_interrupt_work(struct work_struct *work)
760{
761 struct wdm_device *desc;
762
763 desc = container_of(work, struct wdm_device, service_outs_intr);
764
765 spin_lock_irq(&desc->iuspin);
766 service_outstanding_interrupt(desc);
767 if (!desc->resp_count) {
768 set_bit(WDM_READ, &desc->flags);
769 wake_up(&desc->wait);
770 }
771 spin_unlock_irq(&desc->iuspin);
772}
773
761/* --- hotplug --- */ 774/* --- hotplug --- */
762 775
763static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor *ep, 776static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor *ep,
@@ -779,6 +792,7 @@ static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor
779 desc->inum = cpu_to_le16((u16)intf->cur_altsetting->desc.bInterfaceNumber); 792 desc->inum = cpu_to_le16((u16)intf->cur_altsetting->desc.bInterfaceNumber);
780 desc->intf = intf; 793 desc->intf = intf;
781 INIT_WORK(&desc->rxwork, wdm_rxwork); 794 INIT_WORK(&desc->rxwork, wdm_rxwork);
795 INIT_WORK(&desc->service_outs_intr, service_interrupt_work);
782 796
783 rv = -EINVAL; 797 rv = -EINVAL;
784 if (!usb_endpoint_is_int_in(ep)) 798 if (!usb_endpoint_is_int_in(ep))
@@ -964,6 +978,7 @@ static void wdm_disconnect(struct usb_interface *intf)
964 mutex_lock(&desc->wlock); 978 mutex_lock(&desc->wlock);
965 kill_urbs(desc); 979 kill_urbs(desc);
966 cancel_work_sync(&desc->rxwork); 980 cancel_work_sync(&desc->rxwork);
981 cancel_work_sync(&desc->service_outs_intr);
967 mutex_unlock(&desc->wlock); 982 mutex_unlock(&desc->wlock);
968 mutex_unlock(&desc->rlock); 983 mutex_unlock(&desc->rlock);
969 984
@@ -1006,6 +1021,7 @@ static int wdm_suspend(struct usb_interface *intf, pm_message_t message)
1006 /* callback submits work - order is essential */ 1021 /* callback submits work - order is essential */
1007 kill_urbs(desc); 1022 kill_urbs(desc);
1008 cancel_work_sync(&desc->rxwork); 1023 cancel_work_sync(&desc->rxwork);
1024 cancel_work_sync(&desc->service_outs_intr);
1009 } 1025 }
1010 if (!PMSG_IS_AUTO(message)) { 1026 if (!PMSG_IS_AUTO(message)) {
1011 mutex_unlock(&desc->wlock); 1027 mutex_unlock(&desc->wlock);
@@ -1065,6 +1081,7 @@ static int wdm_pre_reset(struct usb_interface *intf)
1065 mutex_lock(&desc->wlock); 1081 mutex_lock(&desc->wlock);
1066 kill_urbs(desc); 1082 kill_urbs(desc);
1067 cancel_work_sync(&desc->rxwork); 1083 cancel_work_sync(&desc->rxwork);
1084 cancel_work_sync(&desc->service_outs_intr);
1068 return 0; 1085 return 0;
1069} 1086}
1070 1087