net/usbnet: avoid recursive locking in usbnet_stop()
[linux-flexiantxendom0-3.2.10.git] / drivers / net / usb / usbnet.c
index cff74b8..81b96e3 100644 (file)
@@ -109,7 +109,7 @@ int usbnet_get_endpoints(struct usbnet *dev, struct usb_interface *intf)
 
                /* take the first altsetting with in-bulk + out-bulk;
                 * remember any status endpoint, just in case;
-                * ignore other endpoints and altsetttings.
+                * ignore other endpoints and altsettings.
                 */
                for (ep = 0; ep < alt->desc.bNumEndpoints; ep++) {
                        struct usb_host_endpoint        *e;
@@ -238,6 +238,10 @@ void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb)
        netif_dbg(dev, rx_status, dev->net, "< rx, len %zu, type 0x%x\n",
                  skb->len + sizeof (struct ethhdr), skb->protocol);
        memset (skb->cb, 0, sizeof (struct skb_data));
+
+       if (skb_defer_rx_timestamp(skb))
+               return;
+
        status = netif_rx (skb);
        if (status != NET_RX_SUCCESS)
                netif_dbg(dev, rx_err, dev->net,
@@ -387,8 +391,12 @@ static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
 static inline void rx_process (struct usbnet *dev, struct sk_buff *skb)
 {
        if (dev->driver_info->rx_fixup &&
-           !dev->driver_info->rx_fixup (dev, skb))
-               goto error;
+           !dev->driver_info->rx_fixup (dev, skb)) {
+               /* With RX_ASSEMBLE, rx_fixup() must update counters */
+               if (!(dev->driver_info->flags & FLAG_RX_ASSEMBLE))
+                       dev->net->stats.rx_errors++;
+               goto done;
+       }
        // else network stack removes extra byte if we forced a short packet
 
        if (skb->len) {
@@ -401,8 +409,8 @@ static inline void rx_process (struct usbnet *dev, struct sk_buff *skb)
        }
 
        netif_dbg(dev, rx_err, dev->net, "drop\n");
-error:
        dev->net->stats.rx_errors++;
+done:
        skb_queue_tail(&dev->done, skb);
 }
 
@@ -581,6 +589,7 @@ static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q)
                entry = (struct skb_data *) skb->cb;
                urb = entry->urb;
 
+               spin_unlock_irqrestore(&q->lock, flags);
                // during some PM-driven resume scenarios,
                // these (async) unlinks complete immediately
                retval = usb_unlink_urb (urb);
@@ -588,6 +597,7 @@ static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q)
                        netdev_dbg(dev->net, "unlink urb err, %d\n", retval);
                else
                        count++;
+               spin_lock_irqsave(&q->lock, flags);
        }
        spin_unlock_irqrestore (&q->lock, flags);
        return count;
@@ -641,6 +651,7 @@ int usbnet_stop (struct net_device *net)
        struct driver_info      *info = dev->driver_info;
        int                     retval;
 
+       clear_bit(EVENT_DEV_OPEN, &dev->flags);
        netif_stop_queue (net);
 
        netif_info(dev, ifdown, dev->net,
@@ -732,6 +743,7 @@ int usbnet_open (struct net_device *net)
                }
        }
 
+       set_bit(EVENT_DEV_OPEN, &dev->flags);
        netif_start_queue (net);
        netif_info(dev, ifup, dev->net,
                   "open: enable queueing (rx %d, tx %d) mtu %d %s framing\n",
@@ -931,8 +943,10 @@ fail_halt:
                if (urb != NULL) {
                        clear_bit (EVENT_RX_MEMORY, &dev->flags);
                        status = usb_autopm_get_interface(dev->intf);
-                       if (status < 0)
+                       if (status < 0) {
+                               usb_free_urb(urb);
                                goto fail_lowmem;
+                       }
                        if (rx_submit (dev, urb, GFP_KERNEL) == -ENOLINK)
                                resched = 0;
                        usb_autopm_put_interface(dev->intf);
@@ -1045,6 +1059,9 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,
        unsigned long           flags;
        int retval;
 
+       if (skb)
+               skb_tx_timestamp(skb);
+
        // some devices want funky USB-level framing, for
        // win32 driver (usually) and/or hardware quirks
        if (info->tx_fixup) {
@@ -1248,12 +1265,14 @@ void usbnet_disconnect (struct usb_interface *intf)
        net = dev->net;
        unregister_netdev (net);
 
-       /* we don't hold rtnl here ... */
-       flush_scheduled_work ();
+       cancel_work_sync(&dev->kevent);
 
        if (dev->driver_info->unbind)
                dev->driver_info->unbind (dev, intf);
 
+       usb_kill_urb(dev->interrupt);
+       usb_free_urb(dev->interrupt);
+
        free_netdev(net);
        usb_put_dev (xdev);
 }
@@ -1375,7 +1394,8 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
                // else "eth%d" when there's reasonable doubt.  userspace
                // can rename the link if it knows better.
                if ((dev->driver_info->flags & FLAG_ETHER) != 0 &&
-                   (net->dev_addr [0] & 0x02) == 0)
+                   ((dev->driver_info->flags & FLAG_POINTTOPOINT) == 0 ||
+                    (net->dev_addr [0] & 0x02) == 0))
                        strcpy (net->name, "eth%d");
                /* WLAN devices should always be named "wlan%d" */
                if ((dev->driver_info->flags & FLAG_WLAN) != 0)
@@ -1459,7 +1479,7 @@ int usbnet_suspend (struct usb_interface *intf, pm_message_t message)
        if (!dev->suspend_count++) {
                spin_lock_irq(&dev->txq.lock);
                /* don't autosuspend while transmitting */
-               if (dev->txq.qlen && (message.event & PM_EVENT_AUTO)) {
+               if (dev->txq.qlen && PMSG_IS_AUTO(message)) {
                        spin_unlock_irq(&dev->txq.lock);
                        return -EBUSY;
                } else {
@@ -1492,6 +1512,10 @@ int usbnet_resume (struct usb_interface *intf)
        int                     retval;
 
        if (!--dev->suspend_count) {
+               /* resume interrupt URBs */
+               if (dev->interrupt && test_bit(EVENT_DEV_OPEN, &dev->flags))
+                       usb_submit_urb(dev->interrupt, GFP_NOIO);
+
                spin_lock_irq(&dev->txq.lock);
                while ((res = usb_get_from_anchor(&dev->deferred))) {
 
@@ -1510,9 +1534,12 @@ int usbnet_resume (struct usb_interface *intf)
                smp_mb();
                clear_bit(EVENT_DEV_ASLEEP, &dev->flags);
                spin_unlock_irq(&dev->txq.lock);
-               if (!(dev->txq.qlen >= TX_QLEN(dev)))
-                       netif_start_queue(dev->net);
-               tasklet_schedule (&dev->bh);
+
+               if (test_bit(EVENT_DEV_OPEN, &dev->flags)) {
+                       if (!(dev->txq.qlen >= TX_QLEN(dev)))
+                               netif_start_queue(dev->net);
+                       tasklet_schedule (&dev->bh);
+               }
        }
        return 0;
 }
@@ -1523,9 +1550,9 @@ EXPORT_SYMBOL_GPL(usbnet_resume);
 
 static int __init usbnet_init(void)
 {
-       /* compiler should optimize this out */
-       BUILD_BUG_ON (sizeof (((struct sk_buff *)0)->cb)
-                       < sizeof (struct skb_data));
+       /* Compiler should optimize this out. */
+       BUILD_BUG_ON(
+               FIELD_SIZEOF(struct sk_buff, cb) < sizeof(struct skb_data));
 
        random_ether_addr(node_id);
        return 0;