aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorMichael Grzeschik <m.grzeschik@pengutronix.de>2012-08-08 05:48:10 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-08-10 14:57:37 -0400
commitb1b552a69b8805e7e338074a9e8b670b4a795218 (patch)
treef8da4b8352b4f8e01d25754cb55db0a1b1611e44 /drivers/usb
parentecc8a0cdca18df5c9a7e84ab29160259123f3ae5 (diff)
usb: gadget: u_ether: fix kworker 100% CPU issue with still used interfaces in eth_stop
This patch fixes an issue introduced by patch: 72c973d usb: gadget: add usb_endpoint_descriptor to struct usb_ep Without this patch we see a kworker taking 100% CPU, after this sequence: - Connect gadget to a windows host - load g_ether - ifconfig up <ip>; ifconfig down; ifconfig up - ping <windows host> The "ifconfig down" results in calling eth_stop(), which will call usb_ep_disable() and, if the carrier is still ok, usb_ep_enable(): usb_ep_disable(link->in_ep); usb_ep_disable(link->out_ep); if (netif_carrier_ok(net)) { usb_ep_enable(link->in_ep); usb_ep_enable(link->out_ep); } The ep should stay enabled, but will not, as ep_disable set the desc pointer to NULL, therefore the subsequent ep_enable will fail. This leads to permanent rescheduling of the eth_work() worker as usb_ep_queue() (called by the worker) will fail due to the unconfigured endpoint. We fix this issue by saving the ep descriptors and re-assign them before usb_ep_enable(). Cc: Tatyana Brokhman <tlinder@codeaurora.org> Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de> Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de> Cc: stable <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/gadget/u_ether.c6
1 files changed, 6 insertions, 0 deletions
diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c
index 90e82e288eb9..0e5230926154 100644
--- a/drivers/usb/gadget/u_ether.c
+++ b/drivers/usb/gadget/u_ether.c
@@ -669,6 +669,8 @@ static int eth_stop(struct net_device *net)
669 spin_lock_irqsave(&dev->lock, flags); 669 spin_lock_irqsave(&dev->lock, flags);
670 if (dev->port_usb) { 670 if (dev->port_usb) {
671 struct gether *link = dev->port_usb; 671 struct gether *link = dev->port_usb;
672 const struct usb_endpoint_descriptor *in;
673 const struct usb_endpoint_descriptor *out;
672 674
673 if (link->close) 675 if (link->close)
674 link->close(link); 676 link->close(link);
@@ -682,10 +684,14 @@ static int eth_stop(struct net_device *net)
682 * their own pace; the network stack can handle old packets. 684 * their own pace; the network stack can handle old packets.
683 * For the moment we leave this here, since it works. 685 * For the moment we leave this here, since it works.
684 */ 686 */
687 in = link->in_ep->desc;
688 out = link->out_ep->desc;
685 usb_ep_disable(link->in_ep); 689 usb_ep_disable(link->in_ep);
686 usb_ep_disable(link->out_ep); 690 usb_ep_disable(link->out_ep);
687 if (netif_carrier_ok(net)) { 691 if (netif_carrier_ok(net)) {
688 DBG(dev, "host still using in/out endpoints\n"); 692 DBG(dev, "host still using in/out endpoints\n");
693 link->in_ep->desc = in;
694 link->out_ep->desc = out;
689 usb_ep_enable(link->in_ep); 695 usb_ep_enable(link->in_ep);
690 usb_ep_enable(link->out_ep); 696 usb_ep_enable(link->out_ep);
691 } 697 }