# This is a BitKeeper generated patch for the following project: # Project Name: Linux kernel tree # This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas: # ChangeSet 1.551 -> 1.552 # drivers/usb/net/usbnet.c 1.21 -> 1.22 # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 02/05/07 david-b@pacbell.net 1.552 # [PATCH] USB usbnet driver update # # - generalizes/cleans keventd support to also handle # * rx stalls (and usb 2.0 transaction translator unplug) # * rx memory shortfalls (latent bug Oliver noticed) # * cleanup on device disconnect (quiesce first) # - merges Brad's patch to use the IEEE802 "locally assigned" bit # - fixes a couple minor bugs on error paths (leak, bogus diagnostic) # - updates some comments # -------------------------------------------- # diff -Nru a/drivers/usb/net/usbnet.c b/drivers/usb/net/usbnet.c --- a/drivers/usb/net/usbnet.c Tue May 7 15:18:16 2002 +++ b/drivers/usb/net/usbnet.c Tue May 7 15:18:16 2002 @@ -33,19 +33,32 @@ * re-enable queues to get higher bandwidth utilization (without needing * to tweak MTU for larger packets). * - * Add support for more "network cable" chips; interop with their Win32 - * drivers may be a good thing. Test the AnchorChip 2720 support.. - * Figure out the initialization protocol used by the Prolific chips, - * for better robustness ... there's some powerup/reset handshake that's - * needed when only one end reboots. - * - * Use interrupt on PL230x to detect peer connect/disconnect, and call - * netif_carrier_{on,off} (?) appropriately. For Net1080, detect peer - * connect/disconnect with async control messages. - * - * Find some way to report "peer connected" network hotplug events; it'll - * likely mean updating the networking layer. (This has been discussed - * on the netdev list...) + * - AN2720 ... not widely available, but reportedly works well + * + * - Belkin/eTEK ... no known issues + * + * - Both GeneSys and PL-230x use interrupt transfers for driver-to-driver + * handshaking; it'd be worth implementing those as "carrier detect". + * Prefer generic hooks, not minidriver-specific hacks. + * + * - Linux devices ... the www.handhelds.org SA-1100 support works nicely, + * but the Sharp Zaurus uses an incompatible protocol (extra checksums). + * No reason not to merge the Zaurus protocol here too (got patch? :) + * + * - For Netchip, use keventd to poll via control requests to detect hardware + * level "carrier detect". + * + * - PL-230x ... the initialization protocol doesn't seem to match chip data + * sheets, sometimes it's not needed and sometimes it hangs. Prolific has + * not responded to repeated support/information requests. + * + * Interop with more Win32 drivers may be a good thing. + * + * Seems like reporting "peer connected" (carrier present) events may end + * up going through the netlink event system, not hotplug ... that may be + * awkward in terms of automatic configuration though. + * + * There are reports that bridging gives lower-than-usual throughput. * * Craft smarter hotplug policy scripts ... ones that know how to arrange * bridging with "brctl", and can handle static and dynamic ("pump") setups. @@ -88,6 +101,9 @@ * Level of diagnostics is more configurable; they use device * location (usb_device->devpath) instead of address (2.5). * For tx_fixup, memflags can't be NOIO. + * 07-may-2002 Generalize/cleanup keventd support, handling rx stalls (mostly + * for USB 2.0 TTs) and memory shortages (potential) too. (db) + * Use "locally assigned" IEEE802 address space. (Brad Hards) * *-------------------------------------------------------------------------*/ @@ -113,6 +129,7 @@ #include +/* minidrivers _could_ be individually configured */ #define CONFIG_USB_AN2720 #define CONFIG_USB_BELKIN #define CONFIG_USB_GENESYS @@ -121,7 +138,7 @@ #define CONFIG_USB_PL2301 -#define DRIVER_VERSION "26-Apr-2002" +#define DRIVER_VERSION "07-May-2002" /*-------------------------------------------------------------------------*/ @@ -185,7 +202,12 @@ struct sk_buff_head txq; struct sk_buff_head done; struct tasklet_struct bh; - struct tq_struct ctrl_task; + + struct tq_struct kevent; + unsigned long flags; +# define EVENT_TX_HALT 0 +# define EVENT_RX_HALT 1 +# define EVENT_RX_MEMORY 2 }; // device-specific info used by the driver @@ -1238,6 +1260,21 @@ spin_unlock_irqrestore (&dev->done.lock, flags); } +/* some work can't be done in tasklets, so we use keventd + * + * NOTE: annoying asymmetry: if it's active, schedule_task() fails, + * but tasklet_schedule() doesn't. hope the failure is rare. + */ +static void defer_kevent (struct usbnet *dev, int work) +{ + set_bit (work, &dev->flags); + if (!schedule_task (&dev->kevent)) + err ("%s: kevent %d may have been dropped", + dev->net.name, work); + else + dbg ("%s: kevent %d scheduled", dev->net.name, work); +} + /*-------------------------------------------------------------------------*/ static void rx_complete (struct urb *urb); @@ -1264,7 +1301,7 @@ if ((skb = alloc_skb (size, flags)) == 0) { dbg ("no rx skb"); - tasklet_schedule (&dev->bh); + defer_kevent (dev, EVENT_RX_MEMORY); usb_free_urb (urb); return; } @@ -1290,11 +1327,20 @@ spin_lock_irqsave (&dev->rxq.lock, lockflags); - if (netif_running (&dev->net)) { - if ((retval = usb_submit_urb (urb, GFP_ATOMIC)) != 0) { + if (netif_running (&dev->net) + && !test_bit (EVENT_RX_HALT, &dev->flags)) { + switch (retval = usb_submit_urb (urb, GFP_ATOMIC)){ + case -EPIPE: + defer_kevent (dev, EVENT_RX_HALT); + break; + case -ENOMEM: + defer_kevent (dev, EVENT_RX_MEMORY); + break; + default: dbg ("%s rx submit, %d", dev->net.name, retval); tasklet_schedule (&dev->bh); - } else { + break; + case 0: __skb_queue_tail (&dev->rxq, skb); } } else { @@ -1368,12 +1414,20 @@ } break; + // stalls need manual reset. this is rare ... except that + // when going through USB 2.0 TTs, unplug appears this way. + // we avoid the highspeed version of the ETIMEOUT/EILSEQ + // storm, recovering as needed. + case -EPIPE: + defer_kevent (dev, EVENT_RX_HALT); + // FALLTHROUGH + // software-driven interface shutdown - case -ECONNRESET: // usb-ohci, usb-uhci - case -ECONNABORTED: // uhci ... for usb-uhci, INTR - dbg ("%s shutdown, code %d", dev->net.name, urb_status); + case -ECONNRESET: // according to API spec + case -ECONNABORTED: // some (now fixed?) UHCI bugs + dbg ("%s rx shutdown, code %d", dev->net.name, urb_status); entry->state = rx_cleanup; - // do urb frees only in the tasklet + // do urb frees only in the tasklet (UHCI has oopsed ...) entry->urb = urb; urb = 0; break; @@ -1384,8 +1438,9 @@ // FALLTHROUGH default: - // on unplug we'll get a burst of ETIMEDOUT/EILSEQ - // till the khubd gets and handles its interrupt. + // on unplug we get ETIMEDOUT (ohci) or EILSEQ (uhci) + // until khubd sees its interrupt and disconnects us. + // that can easily be hundreds of passes through here. entry->state = rx_cleanup; dev->stats.rx_errors++; dbg ("%s rx: status %d", dev->net.name, urb_status); @@ -1395,10 +1450,12 @@ defer_bh (dev, skb); if (urb) { - if (netif_running (&dev->net)) { + if (netif_running (&dev->net) + && !test_bit (EVENT_RX_HALT, &dev->flags)) { rx_submit (dev, urb, GFP_ATOMIC); return; } + usb_free_urb (urb); } #ifdef VERBOSE dbg ("no read resubmitted"); @@ -1428,7 +1485,7 @@ // during some PM-driven resume scenarios, // these (async) unlinks complete immediately retval = usb_unlink_urb (urb); - if (retval < 0) + if (retval != -EINPROGRESS && retval != 0) dbg ("unlink urb err, %d", retval); else count++; @@ -1600,16 +1657,62 @@ /*-------------------------------------------------------------------------*/ -/* usb_clear_halt cannot be called in interrupt context */ - +/* work that cannot be done in interrupt context uses keventd. + * + * NOTE: "uhci" and "usb-uhci" may have trouble with this since they don't + * queue control transfers to individual devices, and other threads could + * trigger control requests concurrently. hope that's rare. + */ static void -tx_clear_halt (void *data) +kevent (void *data) { struct usbnet *dev = data; + int status; + + /* usb_clear_halt() needs a thread context */ + if (test_bit (EVENT_TX_HALT, &dev->flags)) { + unlink_urbs (&dev->txq); + status = usb_clear_halt (dev->udev, + usb_sndbulkpipe (dev->udev, dev->driver_info->out)); + if (status < 0) + err ("%s: can't clear tx halt, status %d", + dev->net.name, status); + else { + clear_bit (EVENT_TX_HALT, &dev->flags); + netif_wake_queue (&dev->net); + } + } + if (test_bit (EVENT_RX_HALT, &dev->flags)) { + unlink_urbs (&dev->rxq); + status = usb_clear_halt (dev->udev, + usb_rcvbulkpipe (dev->udev, dev->driver_info->in)); + if (status < 0) + err ("%s: can't clear rx halt, status %d", + dev->net.name, status); + else { + clear_bit (EVENT_RX_HALT, &dev->flags); + tasklet_schedule (&dev->bh); + } + } + + /* tasklet could resubmit itself forever if memory is tight */ + if (test_bit (EVENT_RX_MEMORY, &dev->flags)) { + struct urb *urb = 0; + + if (netif_running (&dev->net)) + urb = usb_alloc_urb (0, GFP_KERNEL); + else + clear_bit (EVENT_RX_MEMORY, &dev->flags); + if (urb != 0) { + clear_bit (EVENT_RX_MEMORY, &dev->flags); + rx_submit (dev, urb, GFP_KERNEL); + tasklet_schedule (&dev->bh); + } + } - usb_clear_halt (dev->udev, - usb_sndbulkpipe (dev->udev, dev->driver_info->out)); - netif_wake_queue (&dev->net); + if (dev->flags) + dbg ("%s: kevent done, flags = 0x%lx", + dev->net.name, dev->flags); } /*-------------------------------------------------------------------------*/ @@ -1620,15 +1723,8 @@ struct skb_data *entry = (struct skb_data *) skb->cb; struct usbnet *dev = entry->dev; - if (urb->status == -EPIPE) { - if (dev->ctrl_task.sync == 0) { - dev->ctrl_task.routine = tx_clear_halt; - dev->ctrl_task.data = dev; - schedule_task (&dev->ctrl_task); - } else { - dbg ("Cannot clear TX stall"); - } - } + if (urb->status == -EPIPE) + defer_kevent (dev, EVENT_TX_HALT); urb->dev = 0; entry->state = tx_done; defer_bh (dev, skb); @@ -1725,10 +1821,15 @@ #endif /* CONFIG_USB_NET1080 */ netif_stop_queue (net); - if ((retval = usb_submit_urb (urb, GFP_ATOMIC)) != 0) { + switch ((retval = usb_submit_urb (urb, GFP_ATOMIC))) { + case -EPIPE: + defer_kevent (dev, EVENT_TX_HALT); + break; + default: netif_start_queue (net); dbg ("%s tx: submit urb err %d", net->name, retval); - } else { + break; + case 0: net->trans_start = jiffies; __skb_queue_tail (&dev->txq, skb); if (dev->txq.qlen < TX_QLEN) @@ -1799,7 +1900,8 @@ } // or are we maybe short a few urbs? - } else if (netif_running (&dev->net)) { + } else if (netif_running (&dev->net) + && !test_bit (EVENT_RX_HALT, &dev->flags)) { int temp = dev->rxq.qlen; if (temp < RX_QLEN) { @@ -1845,6 +1947,9 @@ list_del (&dev->dev_list); mutex_unlock (&usbnet_mutex); + // assuming we used keventd, it must quiesce too + flush_scheduled_tasks (); + kfree (dev); usb_dec_dev_use (udev); } @@ -1902,6 +2007,7 @@ skb_queue_head_init (&dev->done); dev->bh.func = usbnet_bh; dev->bh.data = (unsigned long) dev; + INIT_TQUEUE (&dev->kevent, kevent, dev); // set up network interface records net = &dev->net; @@ -2038,6 +2144,7 @@ get_random_bytes (node_id, sizeof node_id); node_id [0] &= 0xfe; // clear multicast bit + node_id [0] |= 0x02; // set local assignment bit (IEEE802) if (usb_register (&usbnet_driver) < 0) return -1;