* [PATCH] usbnet autosuspend
@ 2008-10-14 8:28 Per Hallsmark
0 siblings, 0 replies; only message in thread
From: Per Hallsmark @ 2008-10-14 8:28 UTC (permalink / raw)
To: netdev
[-- Attachment #1: Type: text/plain, Size: 181 bytes --]
Hello,
Per request this patch, which sent to linux-usb maillist, is also sent
to this maillist.
In brief, patch enables autosuspend more aggresively in usbnet.
Best regards,
Per
[-- Attachment #2: linux-2.6.27-usbnet-autosuspend.patch --]
[-- Type: text/x-diff, Size: 8109 bytes --]
diff -rup linux-2.6.27.orig/drivers/net/usb/usbnet.c linux-2.6.27/drivers/net/usb/usbnet.c
--- linux-2.6.27.orig/drivers/net/usb/usbnet.c 2008-10-10 00:13:53.000000000 +0200
+++ linux-2.6.27/drivers/net/usb/usbnet.c 2008-10-13 16:21:03.000000000 +0200
@@ -87,6 +87,8 @@ static int msg_level = -1;
module_param (msg_level, int, 0);
MODULE_PARM_DESC (msg_level, "Override default message level");
+static void waker(struct work_struct *work);
+
/*-------------------------------------------------------------------------*/
/* handles CDC Ethernet and many other network "bulk data" interfaces */
@@ -325,6 +327,7 @@ static void rx_submit (struct usbnet *de
if (netif_running (dev->net)
&& netif_device_present (dev->net)
&& !test_bit (EVENT_RX_HALT, &dev->flags)) {
+ usb_mark_last_busy(dev->udev);
switch (retval = usb_submit_urb (urb, GFP_ATOMIC)) {
case -EPIPE:
usbnet_defer_kevent (dev, EVENT_RX_HALT);
@@ -496,6 +499,7 @@ static void intr_complete (struct urb *u
return;
memset(urb->transfer_buffer, 0, urb->transfer_buffer_length);
+ usb_mark_last_busy(dev->udev);
status = usb_submit_urb (urb, GFP_ATOMIC);
if (status != 0 && netif_msg_timer (dev))
deverr(dev, "intr resubmit --> %d", status);
@@ -589,7 +593,10 @@ static int usbnet_stop (struct net_devic
dev->flags = 0;
del_timer_sync (&dev->delay);
tasklet_kill (&dev->bh);
- usb_autopm_put_interface(dev->intf);
+
+ dev->used--;
+
+ dev->intf->needs_remote_wakeup = 0;
return 0;
}
@@ -669,6 +676,12 @@ static int usbnet_open (struct net_devic
// delay posting reads until we're fully open
tasklet_schedule (&dev->bh);
+
+ dev->used++;
+
+ dev->intf->needs_remote_wakeup = 1;
+ usb_autopm_put_interface(dev->intf);
+
return retval;
done:
usb_autopm_put_interface(dev->intf);
@@ -921,7 +934,7 @@ static void usbnet_tx_timeout (struct ne
/*-------------------------------------------------------------------------*/
-static int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net)
+static int __usbnet_start_xmit (struct sk_buff *skb, struct net_device *net)
{
struct usbnet *dev = netdev_priv(net);
int length;
@@ -955,6 +968,7 @@ static int usbnet_start_xmit (struct sk_
entry->state = tx_start;
entry->length = length;
+ dev->tx_goingon = 1;
usb_fill_bulk_urb (urb, dev->udev, dev->out,
skb->data, skb->len, tx_complete, skb);
@@ -972,6 +986,7 @@ static int usbnet_start_xmit (struct sk_
spin_lock_irqsave (&dev->txq.lock, flags);
+ usb_mark_last_busy(dev->udev);
switch ((retval = usb_submit_urb (urb, GFP_ATOMIC))) {
case -EPIPE:
netif_stop_queue (net);
@@ -987,6 +1002,7 @@ static int usbnet_start_xmit (struct sk_
if (dev->txq.qlen >= TX_QLEN (dev))
netif_stop_queue (net);
}
+
spin_unlock_irqrestore (&dev->txq.lock, flags);
if (retval) {
@@ -1002,9 +1018,32 @@ drop:
devdbg (dev, "> tx, len %d, type 0x%x",
length, skb->protocol);
}
+
return retval;
}
+static int usbnet_start_xmit(struct sk_buff *skb, struct net_device *net)
+{
+ struct usbnet *dev = netdev_priv(net);
+ int retval;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->txq.lock, flags);
+ if (dev->suspend_count) {
+ netif_stop_queue(net);
+ dev->tx_skb = skb;
+ if (!schedule_work (&dev->waker))
+ deverr(dev, "waker may have been dropped");
+ else
+ devdbg(dev, "waker scheduled");
+ spin_unlock_irqrestore(&dev->txq.lock, flags);
+ return NET_XMIT_SUCCESS;
+ }
+ spin_unlock_irqrestore(&dev->txq.lock, flags);
+
+ retval = __usbnet_start_xmit(skb, net);
+ return retval;
+}
/*-------------------------------------------------------------------------*/
@@ -1024,6 +1063,7 @@ static void usbnet_bh (unsigned long par
rx_process (dev, skb);
continue;
case tx_done:
+ dev->tx_goingon = 0;
case rx_cleanup:
usb_free_urb (entry->urb);
dev_kfree_skb (skb);
@@ -1043,6 +1083,7 @@ static void usbnet_bh (unsigned long par
} else if (netif_running (dev->net)
&& netif_device_present (dev->net)
&& !timer_pending (&dev->delay)
+ && !dev->suspend_count
&& !test_bit (EVENT_RX_HALT, &dev->flags)) {
int temp = dev->rxq.qlen;
int qlen = RX_QLEN (dev);
@@ -1161,6 +1202,7 @@ usbnet_probe (struct usb_interface *udev
dev->bh.func = usbnet_bh;
dev->bh.data = (unsigned long) dev;
INIT_WORK (&dev->kevent, kevent);
+ INIT_WORK (&dev->waker, waker);
dev->delay.function = usbnet_bh;
dev->delay.data = (unsigned long) dev;
init_timer (&dev->delay);
@@ -1269,24 +1311,62 @@ EXPORT_SYMBOL_GPL(usbnet_probe);
* resume only when the last interface is resumed
*/
+static void waker(struct work_struct *work)
+{
+ struct usbnet *dev = container_of(work, struct usbnet, waker);
+
+ if (!usb_autopm_get_interface(dev->intf)) {
+ usb_autopm_put_interface(dev->intf);
+ } else {
+ devdbg(dev, "autoresume failed");
+ }
+}
+
+static void stop_traffic(struct usbnet *dev)
+{
+ int temp;
+ DECLARE_WAIT_QUEUE_HEAD_ONSTACK (unlink_wakeup);
+ DECLARE_WAITQUEUE (wait, current);
+
+ // 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);
+
+ usb_kill_urb(dev->interrupt);
+}
+
int usbnet_suspend (struct usb_interface *intf, pm_message_t message)
{
struct usbnet *dev = usb_get_intfdata(intf);
- if (!dev->suspend_count++) {
- /*
- * 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);
- /*
- * reattach so runtime management can use and
- * wake the device
- */
- netif_device_attach (dev->net);
+ if (dev->suspend_count++)
+ return 0;
+
+ /* check for ongoing tx traffic */
+ if (dev->tx_goingon && dev->udev->auto_pm) {
+ dev->suspend_count--;
+ return -EBUSY;
}
+
+ stop_traffic(dev);
+
+ /* cancel work */
+ dev->flags = 0;
+ del_timer_sync(&dev->delay);
+ cancel_work_sync(&dev->kevent);
+
return 0;
}
EXPORT_SYMBOL_GPL(usbnet_suspend);
@@ -1294,9 +1374,41 @@ EXPORT_SYMBOL_GPL(usbnet_suspend);
int usbnet_resume (struct usb_interface *intf)
{
struct usbnet *dev = usb_get_intfdata(intf);
+ int status;
+
+ devdbg(dev, "%s: begin", __FUNCTION__);
+
+ if (--dev->suspend_count)
+ return 0;
+
+ if (dev->used) {
+ status = init_status (dev, dev->intf);
+ if (status) {
+ devdbg(dev, "init_status failed");
+ return status;
+ }
+
+ /* start any status interrupt transfer */
+ if (dev->interrupt) {
+ status = usb_submit_urb (dev->interrupt, GFP_NOIO);
+ if (status < 0) {
+ devdbg(dev, "usb_submit intr urb failed");
+ return status;
+ }
+ }
+
+ tasklet_schedule(&dev->bh);
+
+ /* transmit package that triggered resume */
+ if (dev->tx_skb) {
+ status = __usbnet_start_xmit(dev->tx_skb, dev->net);
+ dev->tx_skb = NULL;
+ }
+
+ netif_wake_queue(dev->net);
+ }
- if (!--dev->suspend_count)
- tasklet_schedule (&dev->bh);
+ devdbg(dev, "%s: end", __FUNCTION__);
return 0;
}
diff -rup linux-2.6.27.orig/include/linux/usb/usbnet.h linux-2.6.27/include/linux/usb/usbnet.h
--- linux-2.6.27.orig/include/linux/usb/usbnet.h 2008-10-10 00:13:53.000000000 +0200
+++ linux-2.6.27/include/linux/usb/usbnet.h 2008-10-13 16:18:26.000000000 +0200
@@ -64,6 +64,12 @@ struct usbnet {
# define EVENT_RX_MEMORY 2
# define EVENT_STS_SPLIT 3
# define EVENT_LINK_RESET 4
+
+ /* autosuspend helpers */
+ struct work_struct waker;
+ int used;
+ int tx_goingon;
+ struct sk_buff *tx_skb; /* skb queued during suspend */
};
static inline struct usb_driver *driver_of(struct usb_interface *intf)
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2008-10-14 8:36 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-10-14 8:28 [PATCH] usbnet autosuspend Per Hallsmark
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.