Am Dienstag, 22. September 2009 14:58:49 schrieb Nicolas Chevillot: > Oliver, > > Can you create a diff file against kernel 2.6.31 ? > I don't have the top of tree you're referring to. Sure, here you are. HTH Oliver -- --- linux-2.6.31/include/linux/usb/usbnet.h 2009-09-10 00:13:59.000000000 +0200 +++ linus/include/linux/usb/usbnet.h 2009-09-22 10:58:27.000000000 +0200 @@ -53,7 +53,9 @@ struct usbnet { struct sk_buff_head rxq; struct sk_buff_head txq; struct sk_buff_head done; + struct sk_buff_head rxq_pause; struct urb *interrupt; + struct urb *deferred; struct tasklet_struct bh; struct work_struct kevent; @@ -63,6 +65,9 @@ struct usbnet { # define EVENT_RX_MEMORY 2 # define EVENT_STS_SPLIT 3 # define EVENT_LINK_RESET 4 +# define EVENT_RX_PAUSED 5 +# define EVENT_DEV_WAKING 6 +# define EVENT_DEV_ASLEEP 7 }; static inline struct usb_driver *driver_of(struct usb_interface *intf) @@ -86,6 +91,7 @@ struct driver_info { #define FLAG_FRAMING_AX 0x0040 /* AX88772/178 packets */ #define FLAG_WLAN 0x0080 /* use "wlan%d" names */ +#define FLAG_AVOID_UNLINK_URBS 0x0100 /* don't unlink urbs at usbnet_stop() */ /* init device ... can sleep, or cause probe() failure */ @@ -97,9 +103,15 @@ struct driver_info { /* reset device ... can sleep */ int (*reset)(struct usbnet *); + /* stop device ... can sleep */ + int (*stop)(struct usbnet *); + /* see if peer is connected ... can sleep */ int (*check_connect)(struct usbnet *); + /* (dis)activate runtime power management */ + int (*manage_power)(struct usbnet *, int); + /* for status polling */ void (*status)(struct usbnet *, struct urb *); @@ -118,9 +130,8 @@ struct driver_info { * right after minidriver have initialized hardware. */ int (*early_init)(struct usbnet *dev); - /* called by minidriver when link state changes, state: 0=disconnect, - * 1=connect */ - void (*link_change)(struct usbnet *dev, int state); + /* called by minidriver when receiving indication */ + void (*indication)(struct usbnet *dev, void *ind, int indlen); /* for new devices, use the descriptor-reading code instead */ int in; /* rx endpoint */ @@ -177,7 +188,8 @@ struct skb_data { /* skb->cb is one of t extern int usbnet_open (struct net_device *net); extern int usbnet_stop (struct net_device *net); -extern int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net); +extern netdev_tx_t usbnet_start_xmit (struct sk_buff *skb, + struct net_device *net); extern void usbnet_tx_timeout (struct net_device *net); extern int usbnet_change_mtu (struct net_device *net, int new_mtu); @@ -187,6 +199,10 @@ extern void usbnet_defer_kevent (struct extern void usbnet_skb_return (struct usbnet *, struct sk_buff *); extern void usbnet_unlink_rx_urbs(struct usbnet *); +extern void usbnet_pause_rx(struct usbnet *); +extern void usbnet_resume_rx(struct usbnet *); +extern void usbnet_purge_paused_rxq(struct usbnet *); + extern int usbnet_get_settings (struct net_device *net, struct ethtool_cmd *cmd); extern int usbnet_set_settings (struct net_device *net, struct ethtool_cmd *cmd); extern u32 usbnet_get_link (struct net_device *net); --- linux-2.6.31/drivers/net/usb/cdc_ether.c 2009-09-10 00:13:59.000000000 +0200 +++ linus/drivers/net/usb/cdc_ether.c 2009-09-22 10:56:59.000000000 +0200 @@ -411,6 +411,12 @@ static int cdc_bind(struct usbnet *dev, return 0; } +static int cdc_manage_power(struct usbnet *dev, int on) +{ + dev->intf->needs_remote_wakeup = on; + return 0; +} + static const struct driver_info cdc_info = { .description = "CDC Ethernet Device", .flags = FLAG_ETHER, @@ -418,6 +424,7 @@ static const struct driver_info cdc_info .bind = cdc_bind, .unbind = usbnet_cdc_unbind, .status = cdc_status, + .manage_power = cdc_manage_power, }; /*-------------------------------------------------------------------------*/ @@ -570,6 +577,7 @@ static struct usb_driver cdc_driver = { .disconnect = usbnet_disconnect, .suspend = usbnet_suspend, .resume = usbnet_resume, + .supports_autosuspend = 1, }; --- linux-2.6.31/drivers/net/usb/usbnet.c 2009-09-10 00:13:59.000000000 +0200 +++ linus/drivers/net/usb/usbnet.c 2009-09-22 14:00:52.000000000 +0200 @@ -233,6 +233,11 @@ void usbnet_skb_return (struct usbnet *d { int status; + if (test_bit(EVENT_RX_PAUSED, &dev->flags)) { + skb_queue_tail(&dev->rxq_pause, skb); + return; + } + skb->protocol = eth_type_trans (skb, dev->net); dev->net->stats.rx_packets++; dev->net->stats.rx_bytes += skb->len; @@ -526,6 +531,41 @@ static void intr_complete (struct urb *u } /*-------------------------------------------------------------------------*/ +void usbnet_pause_rx(struct usbnet *dev) +{ + set_bit(EVENT_RX_PAUSED, &dev->flags); + + if (netif_msg_rx_status(dev)) + devdbg(dev, "paused rx queue enabled"); +} +EXPORT_SYMBOL_GPL(usbnet_pause_rx); + +void usbnet_resume_rx(struct usbnet *dev) +{ + struct sk_buff *skb; + int num = 0; + + clear_bit(EVENT_RX_PAUSED, &dev->flags); + + while ((skb = skb_dequeue(&dev->rxq_pause)) != NULL) { + usbnet_skb_return(dev, skb); + num++; + } + + tasklet_schedule(&dev->bh); + + if (netif_msg_rx_status(dev)) + devdbg(dev, "paused rx queue disabled, %d skbs requeued", num); +} +EXPORT_SYMBOL_GPL(usbnet_resume_rx); + +void usbnet_purge_paused_rxq(struct usbnet *dev) +{ + skb_queue_purge(&dev->rxq_pause); +} +EXPORT_SYMBOL_GPL(usbnet_purge_paused_rxq); + +/*-------------------------------------------------------------------------*/ // unlink pending rx/tx; completion handlers do all other cleanup @@ -571,13 +611,36 @@ EXPORT_SYMBOL_GPL(usbnet_unlink_rx_urbs) /*-------------------------------------------------------------------------*/ // precondition: never called in_interrupt +static void usbnet_terminate_urbs(struct usbnet *dev) +{ + DECLARE_WAIT_QUEUE_HEAD_ONSTACK (unlink_wakeup); + DECLARE_WAITQUEUE (wait, current); + int temp; + + /* ensure there are no more active urbs */ + add_wait_queue(&unlink_wakeup, &wait); + dev->wait = &unlink_wakeup; + temp = unlink_urbs(dev, &dev->txq) + + unlink_urbs(dev, &dev->rxq); + + /* maybe wait for deletions to finish. */ + while (!skb_queue_empty(&dev->rxq) + && !skb_queue_empty(&dev->txq) + && !skb_queue_empty(&dev->done)) { + msleep(UNLINK_TIMEOUT_MS); + if (netif_msg_ifdown(dev)) + devdbg(dev, "waited for %d urb completions", + temp); + } + dev->wait = NULL; + remove_wait_queue(&unlink_wakeup, &wait); +} int usbnet_stop (struct net_device *net) { struct usbnet *dev = netdev_priv(net); - int temp; - DECLARE_WAIT_QUEUE_HEAD_ONSTACK (unlink_wakeup); - DECLARE_WAITQUEUE (wait, current); + struct driver_info *info = dev->driver_info; + int retval; netif_stop_queue (net); @@ -587,24 +650,25 @@ int usbnet_stop (struct net_device *net) net->stats.rx_errors, net->stats.tx_errors ); - // ensure there are no more active urbs - add_wait_queue (&unlink_wakeup, &wait); - dev->wait = &unlink_wakeup; - temp = unlink_urbs (dev, &dev->txq) + unlink_urbs (dev, &dev->rxq); - - // maybe wait for deletions to finish. - while (!skb_queue_empty(&dev->rxq) - && !skb_queue_empty(&dev->txq) - && !skb_queue_empty(&dev->done)) { - msleep(UNLINK_TIMEOUT_MS); - if (netif_msg_ifdown (dev)) - devdbg (dev, "waited for %d urb completions", temp); + /* allow minidriver to stop correctly (wireless devices to turn off + * radio etc) */ + if (info->stop) { + retval = info->stop(dev); + if (retval < 0 && netif_msg_ifdown(dev)) + devinfo(dev, + "stop fail (%d) usbnet usb-%s-%s, %s", + retval, + dev->udev->bus->bus_name, dev->udev->devpath, + info->description); } - dev->wait = NULL; - remove_wait_queue (&unlink_wakeup, &wait); + + if (!(info->flags & FLAG_AVOID_UNLINK_URBS)) + usbnet_terminate_urbs(dev); usb_kill_urb(dev->interrupt); + usbnet_purge_paused_rxq(dev); + /* deferred work (task, timer, softirq) must also stop. * can't flush_scheduled_work() until we drop rtnl (later), * else workers could deadlock; so make workers a NOP. @@ -612,7 +676,10 @@ int usbnet_stop (struct net_device *net) dev->flags = 0; del_timer_sync (&dev->delay); tasklet_kill (&dev->bh); - usb_autopm_put_interface(dev->intf); + if (info->manage_power) + info->manage_power(dev, 0); + else + usb_autopm_put_interface(dev->intf); return 0; } @@ -693,6 +760,12 @@ int usbnet_open (struct net_device *net) // delay posting reads until we're fully open tasklet_schedule (&dev->bh); + if (info->manage_power) { + retval = info->manage_power(dev, 1); + if (retval < 0) + goto done; + usb_autopm_put_interface(dev->intf); + } return retval; done: usb_autopm_put_interface(dev->intf); @@ -794,7 +867,7 @@ void usbnet_set_msglevel (struct net_dev EXPORT_SYMBOL_GPL(usbnet_set_msglevel); /* drivers may override default ethtool_ops in their bind() routine */ -static struct ethtool_ops usbnet_ethtool_ops = { +static const struct ethtool_ops usbnet_ethtool_ops = { .get_settings = usbnet_get_settings, .set_settings = usbnet_set_settings, .get_link = usbnet_get_link, @@ -822,6 +895,7 @@ kevent (struct work_struct *work) if (test_bit (EVENT_TX_HALT, &dev->flags)) { unlink_urbs (dev, &dev->txq); status = usb_clear_halt (dev->udev, dev->out); + usb_autopm_put_interface(dev->intf); if (status < 0 && status != -EPIPE && status != -ESHUTDOWN) { @@ -893,17 +967,20 @@ static void tx_complete (struct urb *urb if (urb->status == 0) { dev->net->stats.tx_packets++; dev->net->stats.tx_bytes += entry->length; + usb_autopm_put_interface_async(dev->intf); } else { dev->net->stats.tx_errors++; switch (urb->status) { case -EPIPE: + /* we do not allow autosuspension */ usbnet_defer_kevent (dev, EVENT_TX_HALT); break; /* software-driven interface shutdown */ case -ECONNRESET: // async unlink case -ESHUTDOWN: // hardware gone + usb_autopm_put_interface_async(dev->intf); break; // like rx, tx gets controller i/o faults during khubd delays @@ -911,6 +988,7 @@ static void tx_complete (struct urb *urb case -EPROTO: case -ETIME: case -EILSEQ: + usb_mark_last_busy(dev->udev); if (!timer_pending (&dev->delay)) { mod_timer (&dev->delay, jiffies + THROTTLE_JIFFIES); @@ -919,8 +997,10 @@ static void tx_complete (struct urb *urb urb->status); } netif_stop_queue (dev->net); + usb_autopm_put_interface_async(dev->intf); break; default: + usb_autopm_put_interface_async(dev->intf); if (netif_msg_tx_err (dev)) devdbg (dev, "tx err %d", entry->urb->status); break; @@ -947,15 +1027,16 @@ EXPORT_SYMBOL_GPL(usbnet_tx_timeout); /*-------------------------------------------------------------------------*/ -int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net) +netdev_tx_t usbnet_start_xmit (struct sk_buff *skb, + struct net_device *net) { struct usbnet *dev = netdev_priv(net); int length; - int retval = NET_XMIT_SUCCESS; struct urb *urb = NULL; struct skb_data *entry; struct driver_info *info = dev->driver_info; unsigned long flags; + int retval; // some devices want funky USB-level framing, for // win32 driver (usually) and/or hardware quirks @@ -997,6 +1078,23 @@ int usbnet_start_xmit (struct sk_buff *s } spin_lock_irqsave (&dev->txq.lock, flags); + retval = usb_autopm_get_interface_async(dev->intf); + if (retval < 0) { + spin_unlock_irqrestore (&dev->txq.lock, flags); + goto drop; + } + +#ifdef CONFIG_PM + /* if this triggers the device is still a sleep */ + if (test_bit(EVENT_DEV_ASLEEP, &dev->flags)) { + /* transmission will be done in resume */ + dev->deferred = urb; + /* no use to process more packets */ + netif_stop_queue(net); + spin_unlock_irqrestore(&dev->txq.lock, flags); + goto deferred; + } +#endif switch ((retval = usb_submit_urb (urb, GFP_ATOMIC))) { case -EPIPE: @@ -1019,7 +1117,6 @@ int usbnet_start_xmit (struct sk_buff *s if (netif_msg_tx_err (dev)) devdbg (dev, "drop, code %d", retval); drop: - retval = NET_XMIT_SUCCESS; dev->net->stats.tx_dropped++; if (skb) dev_kfree_skb_any (skb); @@ -1028,7 +1125,8 @@ drop: devdbg (dev, "> tx, len %d, type 0x%x", length, skb->protocol); } - return retval; +deferred: + return NETDEV_TX_OK; } EXPORT_SYMBOL_GPL(usbnet_start_xmit); @@ -1095,7 +1193,6 @@ static void usbnet_bh (unsigned long par } - /*------------------------------------------------------------------------- * * USB Device Driver support @@ -1192,6 +1289,7 @@ usbnet_probe (struct usb_interface *udev skb_queue_head_init (&dev->rxq); skb_queue_head_init (&dev->txq); skb_queue_head_init (&dev->done); + skb_queue_head_init(&dev->rxq_pause); dev->bh.func = usbnet_bh; dev->bh.data = (unsigned long) dev; INIT_WORK (&dev->kevent, kevent); @@ -1303,13 +1401,23 @@ int usbnet_suspend (struct usb_interface struct usbnet *dev = usb_get_intfdata(intf); if (!dev->suspend_count++) { + spin_lock_irq(&dev->txq.lock); + /* don't autosuspend while transmitting */ + if (dev->txq.qlen && (message.event & PM_EVENT_AUTO)) { + spin_unlock_irq(&dev->txq.lock); + return -EBUSY; + } else { + set_bit(EVENT_DEV_ASLEEP, &dev->flags); + spin_unlock_irq(&dev->txq.lock); + } /* * accelerate emptying of the rx and queues, to avoid * having everything error out. */ netif_device_detach (dev->net); - (void) unlink_urbs (dev, &dev->rxq); - (void) unlink_urbs (dev, &dev->txq); + usbnet_terminate_urbs(dev); + usb_kill_urb(dev->interrupt); + /* * reattach so runtime management can use and * wake the device @@ -1323,10 +1431,32 @@ EXPORT_SYMBOL_GPL(usbnet_suspend); int usbnet_resume (struct usb_interface *intf) { struct usbnet *dev = usb_get_intfdata(intf); - - if (!--dev->suspend_count) + struct sk_buff *skb; + struct urb *res; + int retval; + + if (!--dev->suspend_count) { + spin_lock_irq(&dev->txq.lock); + res = dev->deferred; + dev->deferred = NULL; + clear_bit(EVENT_DEV_ASLEEP, &dev->flags); + spin_unlock_irq(&dev->txq.lock); + if (res) { + retval = usb_submit_urb(res, GFP_NOIO); + if (retval < 0) { + usb_free_urb(res); + netif_start_queue(dev->net); + usb_autopm_put_interface_async(dev->intf); + } else { + skb = (struct sk_buff *)res->context; + dev->net->trans_start = jiffies; + __skb_queue_tail (&dev->txq, skb); + if (!(dev->txq.qlen >= TX_QLEN(dev))) + netif_start_queue(dev->net); + } + } tasklet_schedule (&dev->bh); - + } return 0; } EXPORT_SYMBOL_GPL(usbnet_resume); -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html