netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH 0/3] usbnet: support runtime PM triggered by link change
@ 2012-09-15 17:48 Ming Lei
       [not found] ` <1347731299-29898-1-git-send-email-ming.lei-Z7WLFzj8eWMS+FvcfC7Uqw@public.gmane.org>
                   ` (3 more replies)
  0 siblings, 4 replies; 16+ messages in thread
From: Ming Lei @ 2012-09-15 17:48 UTC (permalink / raw)
  To: David S. Miller, Greg Kroah-Hartman
  Cc: Oliver Neukum, Fink Dmitry, Rafael Wysocki, Alan Stern, netdev,
	linux-usb

Hi,

Currently only very few usbnet devices support the traffic based
runtime PM, eg. wake up devices if there are packets to be transmitted.

For the below situation, it should make sense to runtime suspend usbnet
device to save power:
	
	- after network link becomes down

This patch implements the runtime PM triggered by network link change
event, and it works basically on asix usbnet device after a simple
runtime PM test.

 drivers/net/usb/asix_devices.c |    6 +-
 drivers/net/usb/cdc_ether.c    |    5 +-
 drivers/net/usb/cdc_ncm.c      |    9 +-
 drivers/net/usb/dm9601.c       |    7 +-
 drivers/net/usb/mcs7830.c      |    6 +-
 drivers/net/usb/sierra_net.c   |    6 +-
 drivers/net/usb/usbnet.c       |  224 +++++++++++++++++++++++++++++++++++++++-
 include/linux/usb/usbnet.h     |   21 +++-
 8 files changed, 250 insertions(+), 34 deletions(-)


Thanks
--
Ming Lei

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [RFC PATCH 1/3] usbnet: introduce usbnet_link_change API
       [not found] ` <1347731299-29898-1-git-send-email-ming.lei-Z7WLFzj8eWMS+FvcfC7Uqw@public.gmane.org>
@ 2012-09-15 17:48   ` Ming Lei
  0 siblings, 0 replies; 16+ messages in thread
From: Ming Lei @ 2012-09-15 17:48 UTC (permalink / raw)
  To: David S. Miller, Greg Kroah-Hartman
  Cc: Oliver Neukum, Fink Dmitry, Rafael Wysocki, Alan Stern,
	netdev-u79uwXL29TY76Z2rM5mHXA, linux-usb-u79uwXL29TY76Z2rM5mHXA,
	Ming Lei

This patch introduces the API of usbnet_link_change, so that
usbnet can trace the link change, which may help to implement
the later runtime PM triggered by usb ethernet link change.

Signed-off-by: Ming Lei <ming.lei-Z7WLFzj8eWMS+FvcfC7Uqw@public.gmane.org>
---
 drivers/net/usb/usbnet.c   |   11 +++++++++++
 include/linux/usb/usbnet.h |    2 +-
 2 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index e944109..95a96b1 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -1591,6 +1591,17 @@ int usbnet_resume (struct usb_interface *intf)
 }
 EXPORT_SYMBOL_GPL(usbnet_resume);
 
+void usbnet_link_change(struct usbnet *dev, int link, int need_reset)
+{
+	if (link)
+		netif_carrier_on(dev->net);
+	else
+		netif_carrier_off(dev->net);
+
+	if (need_reset && link)
+		usbnet_defer_kevent(dev, EVENT_LINK_RESET);
+}
+EXPORT_SYMBOL_GPL(usbnet_link_change);
 
 /*-------------------------------------------------------------------------*/
 
diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h
index f87cf62..1937b74 100644
--- a/include/linux/usb/usbnet.h
+++ b/include/linux/usb/usbnet.h
@@ -160,7 +160,7 @@ extern int usbnet_probe(struct usb_interface *, const struct usb_device_id *);
 extern int usbnet_suspend(struct usb_interface *, pm_message_t);
 extern int usbnet_resume(struct usb_interface *);
 extern void usbnet_disconnect(struct usb_interface *);

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [RFC PATCH 2/3] usbnet: apply usbnet_link_change
  2012-09-15 17:48 [RFC PATCH 0/3] usbnet: support runtime PM triggered by link change Ming Lei
       [not found] ` <1347731299-29898-1-git-send-email-ming.lei-Z7WLFzj8eWMS+FvcfC7Uqw@public.gmane.org>
@ 2012-09-15 17:48 ` Ming Lei
  2012-09-15 17:48 ` [RFC PATCH 3/3] usbnet: support runtime PM triggered by link change Ming Lei
  2012-09-17  8:04 ` [RFC PATCH 0/3] " Oliver Neukum
  3 siblings, 0 replies; 16+ messages in thread
From: Ming Lei @ 2012-09-15 17:48 UTC (permalink / raw)
  To: David S. Miller, Greg Kroah-Hartman
  Cc: Oliver Neukum, Fink Dmitry, Rafael Wysocki, Alan Stern, netdev,
	linux-usb, Ming Lei

This patch applies the introduce usbnet_link_change API.

Signed-off-by: Ming Lei <ming.lei@canonical.com>
---
 drivers/net/usb/asix_devices.c |    6 +-----
 drivers/net/usb/cdc_ether.c    |    5 +----
 drivers/net/usb/cdc_ncm.c      |    9 +++------
 drivers/net/usb/dm9601.c       |    7 +------
 drivers/net/usb/mcs7830.c      |    6 +-----
 drivers/net/usb/sierra_net.c   |    3 +--
 drivers/net/usb/usbnet.c       |    2 +-
 7 files changed, 9 insertions(+), 29 deletions(-)

diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c
index 4fd48df..c354bb1 100644
--- a/drivers/net/usb/asix_devices.c
+++ b/drivers/net/usb/asix_devices.c
@@ -55,11 +55,7 @@ static void asix_status(struct usbnet *dev, struct urb *urb)
 	event = urb->transfer_buffer;
 	link = event->link & 0x01;
 	if (netif_carrier_ok(dev->net) != link) {
-		if (link) {
-			netif_carrier_on(dev->net);
-			usbnet_defer_kevent (dev, EVENT_LINK_RESET );
-		} else
-			netif_carrier_off(dev->net);
+		usbnet_link_change(dev, link, 1);
 		netdev_dbg(dev->net, "Link Status is: %d\n", link);
 	}
 }
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index a03de71..c6e4be5 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -406,10 +406,7 @@ void usbnet_cdc_status(struct usbnet *dev, struct urb *urb)
 	case USB_CDC_NOTIFY_NETWORK_CONNECTION:
 		netif_dbg(dev, timer, dev->net, "CDC: carrier %s\n",
 			  event->wValue ? "on" : "off");
-		if (event->wValue)
-			netif_carrier_on(dev->net);
-		else
-			netif_carrier_off(dev->net);
+		usbnet_link_change(dev, event->wValue, 0);
 		break;
 	case USB_CDC_NOTIFY_SPEED_CHANGE:	/* tx/rx rates */
 		netif_dbg(dev, timer, dev->net, "CDC: speed change (len %d)\n",
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
index 4cd582a..f425c2c 100644
--- a/drivers/net/usb/cdc_ncm.c
+++ b/drivers/net/usb/cdc_ncm.c
@@ -593,7 +593,7 @@ advance:
 	 * (carrier is OFF) during attach, so the IP network stack does not
 	 * start IPv6 negotiation and more.
 	 */
-	netif_carrier_off(dev->net);
+	usbnet_link_change(dev, 0, 0);
 	ctx->tx_speed = ctx->rx_speed = 0;
 	return 0;
 
@@ -1131,12 +1131,9 @@ static void cdc_ncm_status(struct usbnet *dev, struct urb *urb)
 			" %sconnected\n",
 			ctx->netdev->name, ctx->connected ? "" : "dis");
 
-		if (ctx->connected)
-			netif_carrier_on(dev->net);
-		else {
-			netif_carrier_off(dev->net);
+		usbnet_link_change(dev, ctx->connected, 0);
+		if (!ctx->connected)
 			ctx->tx_speed = ctx->rx_speed = 0;
-		}
 		break;
 
 	case USB_CDC_NOTIFY_SPEED_CHANGE:
diff --git a/drivers/net/usb/dm9601.c b/drivers/net/usb/dm9601.c
index e0433ce..7422d5a 100644
--- a/drivers/net/usb/dm9601.c
+++ b/drivers/net/usb/dm9601.c
@@ -587,12 +587,7 @@ static void dm9601_status(struct usbnet *dev, struct urb *urb)
 
 	link = !!(buf[0] & 0x40);
 	if (netif_carrier_ok(dev->net) != link) {
-		if (link) {
-			netif_carrier_on(dev->net);
-			usbnet_defer_kevent (dev, EVENT_LINK_RESET);
-		}
-		else
-			netif_carrier_off(dev->net);
+		usbnet_link_change(dev, link, 1);
 		netdev_dbg(dev->net, "Link Status is: %d\n", link);
 	}
 }
diff --git a/drivers/net/usb/mcs7830.c b/drivers/net/usb/mcs7830.c
index 03c2d8d..49a98b7 100644
--- a/drivers/net/usb/mcs7830.c
+++ b/drivers/net/usb/mcs7830.c
@@ -639,11 +639,7 @@ static void mcs7830_status(struct usbnet *dev, struct urb *urb)
 
 	link = !(buf[1] & 0x20);
 	if (netif_carrier_ok(dev->net) != link) {
-		if (link) {
-			netif_carrier_on(dev->net);
-			usbnet_defer_kevent(dev, EVENT_LINK_RESET);
-		} else
-			netif_carrier_off(dev->net);
+		usbnet_link_change(dev, link, 1);
 		netdev_dbg(dev->net, "Link Status is: %d\n", link);
 	}
 }
diff --git a/drivers/net/usb/sierra_net.c b/drivers/net/usb/sierra_net.c
index 7ae70e9..08ed9e5 100644
--- a/drivers/net/usb/sierra_net.c
+++ b/drivers/net/usb/sierra_net.c
@@ -414,11 +414,10 @@ static void sierra_net_handle_lsi(struct usbnet *dev, char *data,
 	if (link_up) {
 		sierra_net_set_ctx_index(priv, hh->msgspecific.byte);
 		priv->link_up = 1;
-		netif_carrier_on(dev->net);
 	} else {
 		priv->link_up = 0;
-		netif_carrier_off(dev->net);
 	}
+	usbnet_link_change(dev, link_up, 0);
 }
 
 static void sierra_net_dosync(struct usbnet *dev)
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 95a96b1..054ffd8 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -1487,7 +1487,7 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
 	netif_device_attach (net);
 
 	if (dev->driver_info->flags & FLAG_LINK_INTR)
-		netif_carrier_off(net);
+		usbnet_link_change(dev, 0, 0);
 
 	return 0;
 
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [RFC PATCH 3/3] usbnet: support runtime PM triggered by link change
  2012-09-15 17:48 [RFC PATCH 0/3] usbnet: support runtime PM triggered by link change Ming Lei
       [not found] ` <1347731299-29898-1-git-send-email-ming.lei-Z7WLFzj8eWMS+FvcfC7Uqw@public.gmane.org>
  2012-09-15 17:48 ` [RFC PATCH 2/3] usbnet: apply usbnet_link_change Ming Lei
@ 2012-09-15 17:48 ` Ming Lei
  2012-09-17  8:50   ` Oliver Neukum
  2012-09-17  8:04 ` [RFC PATCH 0/3] " Oliver Neukum
  3 siblings, 1 reply; 16+ messages in thread
From: Ming Lei @ 2012-09-15 17:48 UTC (permalink / raw)
  To: David S. Miller, Greg Kroah-Hartman
  Cc: Oliver Neukum, Fink Dmitry, Rafael Wysocki, Alan Stern, netdev,
	linux-usb, Ming Lei

This patch implements runtime PM triggered by link change event
for devices which haven't defined manage_power() callback, based
on the below consideration:

- this kind of runtime PM has been supported by some PCI network
interfaces already, and it does make sense to suspend the usb
device to save power if no link is detected

- link down triggered runtime needn't to be implemented for devices
which have already supported traffic based runtime PM by .manage_power,
because runtime suspend can be triggered when no tx frames are to be
transmitted after link becoms down.

Unfortunately, some usbnet devices don't support remote wakeup,
or some devices may support it but the remote wakup can't be enabled
for link change event for some reason(no documents are public, not
supported ...).

This patch takes a periodic timer to wake up devices for detecting
the link change event if remote wakeup by link change can't be
supported. If the link is found to be down, put the device into
suspend immediately.

For the devices which support remote wakeup by link change and
don't support remote wakeup by incoming packets(not implement
manage_power callback), the patch can still make link change
triggered runtime PM working on these devices.

Signed-off-by: Ming Lei <ming.lei@canonical.com>
---
 drivers/net/usb/sierra_net.c |    3 +-
 drivers/net/usb/usbnet.c     |  211 +++++++++++++++++++++++++++++++++++++++++-
 include/linux/usb/usbnet.h   |   19 ++++
 3 files changed, 229 insertions(+), 4 deletions(-)

diff --git a/drivers/net/usb/sierra_net.c b/drivers/net/usb/sierra_net.c
index 08ed9e5..0993f2d 100644
--- a/drivers/net/usb/sierra_net.c
+++ b/drivers/net/usb/sierra_net.c
@@ -418,6 +418,7 @@ static void sierra_net_handle_lsi(struct usbnet *dev, char *data,
 		priv->link_up = 0;
 	}
 	usbnet_link_change(dev, link_up, 0);
+	usbnet_link_updated(dev);
 }
 
 static void sierra_net_dosync(struct usbnet *dev)
@@ -915,7 +916,7 @@ static struct sk_buff *sierra_net_tx_fixup(struct usbnet *dev,
 
 static const struct driver_info sierra_net_info_direct_ip = {
 	.description = "Sierra Wireless USB-to-WWAN Modem",
-	.flags = FLAG_WWAN | FLAG_SEND_ZLP,
+	.flags = FLAG_WWAN | FLAG_SEND_ZLP | FLAG_LINK_UPDATE_BY_DRIVER,
 	.bind = sierra_net_bind,
 	.unbind = sierra_net_unbind,
 	.status = sierra_net_status,
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 054ffd8..8db1618 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -677,6 +677,186 @@ static void usbnet_terminate_urbs(struct usbnet *dev)
 	remove_wait_queue(&unlink_wakeup, &wait);
 }
 
+void usbnet_link_updated(struct usbnet *dev)
+{
+	complete(&dev->link_update_completion);
+}
+EXPORT_SYMBOL_GPL(usbnet_link_updated);
+
+#define usbnet_link_suspend(dev) do { \
+	dev_dbg(&dev->intf->dev, "%s:link suspend", __func__); \
+	usb_autopm_put_interface_async(dev->intf); \
+} while(0)
+
+#define usbnet_link_resume(dev) do { \
+	dev_dbg(&dev->intf->dev, "%s:link resume", __func__); \
+	usb_autopm_get_interface_async(dev->intf); \
+} while(0)
+
+static int need_link_runtime_pm(struct usbnet *dev)
+{
+	if (dev->driver_info->manage_power)
+		return 0;
+
+	if (!dev->driver_info->status)
+		return 0;
+
+	return 1;
+}
+
+/* called by usbnet_suspend */
+static void start_link_detect(struct usbnet *dev)
+{
+	if (!dev->link_rpm_enabled)
+		return;
+
+	if (dev->link_remote_wakeup)
+		return;
+
+	if (dev->link_check_started)
+		return;
+
+	dev->link_check_started = 1;
+	schedule_delayed_work(&dev->link_detect_work,
+			      msecs_to_jiffies(3000));
+}
+
+/* called by usbnet_resume */
+static void end_link_detect(struct usbnet *dev, int force_cancel)
+{
+	if (!dev->link_rpm_enabled)
+		return;
+
+	if (!dev->link_check_started)
+		return;
+
+	/*
+	 * cancel the link detect work if usbnet is resumed
+	 * not by link detect work
+	 */
+	if (!dev->link_checking || force_cancel)
+		cancel_delayed_work_sync(&dev->link_detect_work);
+
+	dev->link_check_started = 0;
+}
+
+/* called by usbnet_open */
+static void enable_link_runtime_pm(struct usbnet *dev)
+{
+	dev->link_rpm_enabled = 1;
+
+	if (!dev->link_remote_wakeup) {
+		dev->old_autosuspend_delay =
+			dev->udev->dev.power.autosuspend_delay;
+		pm_runtime_set_autosuspend_delay(&dev->udev->dev, 1);
+	}
+
+	if (!netif_carrier_ok(dev->net)) {
+		dev->link_open_suspend = 1;
+		usbnet_link_suspend(dev);
+	}
+}
+
+/* called by usbnet_stop */
+static void disable_link_runtime_pm(struct usbnet *dev)
+{
+	if (!dev->link_rpm_enabled)
+		return;
+	dev->link_rpm_enabled = 0;
+	end_link_detect(dev, 1);
+	if (dev->link_open_suspend) {
+		usbnet_link_resume(dev);
+		dev->link_open_suspend = 0;
+	}
+	if (!dev->link_remote_wakeup)
+		pm_runtime_set_autosuspend_delay(&dev->udev->dev,
+						 dev->old_autosuspend_delay);
+}
+
+static void update_link_state(struct usbnet *dev)
+{
+	char		*buf = NULL;
+	unsigned	pipe = 0;
+	unsigned	maxp;
+	int		ret, act_len, timeout;
+	struct urb	urb;
+
+	pipe = usb_rcvintpipe(dev->udev,
+			      dev->status->desc.bEndpointAddress
+				& USB_ENDPOINT_NUMBER_MASK);
+	maxp = usb_maxpacket(dev->udev, pipe, 0);
+
+	/*
+	 * Take default timeout as 2 times of period.
+	 * It is observed that asix device can update its link
+	 * state duing one period(128ms). Low level driver can set
+	 * its default update link time in bind() callback.
+	 */
+	if (!dev->link_update_timeout) {
+		timeout = max((int) dev->status->desc.bInterval,
+			(dev->udev->speed == USB_SPEED_HIGH) ? 7 : 3);
+		timeout = 1 << timeout;
+		if (dev->udev->speed == USB_SPEED_HIGH)
+			timeout /= 8;
+		if (timeout < 128)
+			timeout = 128;
+	} else
+		timeout = dev->link_update_timeout;
+
+	buf = kmalloc(maxp, GFP_KERNEL);
+	if (!buf)
+		return;
+
+	dev_dbg(&dev->intf->dev, "%s: timeout %dms\n", __func__, timeout);
+	ret = usb_interrupt_msg(dev->udev, pipe, buf, maxp,
+			&act_len, timeout);
+	if (!ret) {
+		urb.status = 0;
+		urb.actual_length = act_len;
+		urb.transfer_buffer = buf;
+		urb.transfer_buffer_length = maxp;
+		dev->driver_info->status(dev, &urb);
+		if (dev->driver_info->flags &
+		    FLAG_LINK_UPDATE_BY_DRIVER)
+			wait_for_completion(&dev->link_update_completion);
+		dev_dbg(&dev->intf->dev, "%s: link updated\n", __func__);
+	} else
+		dev_dbg(&dev->intf->dev, "%s: link update failed %d\n",
+				__func__, ret);
+	kfree(buf);
+}
+
+static void link_detect_work(struct work_struct *work)
+{
+	struct usbnet	*dev = container_of(work, struct usbnet,
+					    link_detect_work.work);
+
+	dev_dbg(&dev->intf->dev, "%s: link resume\n", __func__);
+
+	dev->link_checking = 1;
+	usb_autopm_get_interface(dev->intf);
+	update_link_state(dev);
+	dev->link_checking = 0;
+
+	dev_dbg(&dev->intf->dev, "%s: link state %d\n",
+		__func__, netif_carrier_ok(dev->net));
+
+	if (!netif_carrier_ok(dev->net))
+		usb_autopm_put_interface(dev->intf);
+	else
+		usb_submit_urb(dev->interrupt, GFP_NOIO);
+}
+
+static void init_link_rpm(struct usbnet *dev)
+{
+	INIT_DELAYED_WORK(&dev->link_detect_work, link_detect_work);
+	init_completion(&dev->link_update_completion);
+
+	dev->link_remote_wakeup = !!(dev->driver_info->flags &
+				  FLAG_LINK_SUPPORT_REMOTE_WAKEUP);
+	dev->link_state = 1;
+}
+
 int usbnet_stop (struct net_device *net)
 {
 	struct usbnet		*dev = netdev_priv(net);
@@ -719,8 +899,10 @@ int usbnet_stop (struct net_device *net)
 	tasklet_kill (&dev->bh);
 	if (info->manage_power)
 		info->manage_power(dev, 0);
-	else
+	else {
+		disable_link_runtime_pm(dev);
 		usb_autopm_put_interface(dev->intf);
+	}
 
 	return 0;
 }
@@ -795,6 +977,9 @@ int usbnet_open (struct net_device *net)
 		if (retval < 0)
 			goto done_manage_power_error;
 		usb_autopm_put_interface(dev->intf);
+	} else {
+		if (need_link_runtime_pm(dev))
+			enable_link_runtime_pm(dev);
 	}
 	return retval;
 
@@ -1489,6 +1674,8 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
 	if (dev->driver_info->flags & FLAG_LINK_INTR)
 		usbnet_link_change(dev, 0, 0);
 
+	init_link_rpm(dev);
+
 	return 0;
 
 out4:
@@ -1538,6 +1725,9 @@ int usbnet_suspend (struct usb_interface *intf, pm_message_t message)
 		 * wake the device
 		 */
 		netif_device_attach (dev->net);
+
+		if (PMSG_IS_AUTO(message))
+			start_link_detect(dev);
 	}
 	return 0;
 }
@@ -1552,8 +1742,10 @@ int usbnet_resume (struct usb_interface *intf)
 
 	if (!--dev->suspend_count) {
 		/* resume interrupt URBs */
-		if (dev->interrupt && test_bit(EVENT_DEV_OPEN, &dev->flags))
-			usb_submit_urb(dev->interrupt, GFP_NOIO);
+		if (dev->interrupt && test_bit(EVENT_DEV_OPEN, &dev->flags)) {
+			if (!dev->link_checking)
+				usb_submit_urb(dev->interrupt, GFP_NOIO);
+		}
 
 		spin_lock_irq(&dev->txq.lock);
 		while ((res = usb_get_from_anchor(&dev->deferred))) {
@@ -1586,6 +1778,8 @@ int usbnet_resume (struct usb_interface *intf)
 				netif_tx_wake_all_queues(dev->net);
 			tasklet_schedule (&dev->bh);
 		}
+
+		end_link_detect(dev, 0);
 	}
 	return 0;
 }
@@ -1593,6 +1787,9 @@ EXPORT_SYMBOL_GPL(usbnet_resume);
 
 void usbnet_link_change(struct usbnet *dev, int link, int need_reset)
 {
+	dev_dbg(&dev->intf->dev, "%s: old_link=%d link=%d\n", __func__,
+		dev->link_state, link);
+
 	if (link)
 		netif_carrier_on(dev->net);
 	else
@@ -1600,6 +1797,14 @@ void usbnet_link_change(struct usbnet *dev, int link, int need_reset)
 
 	if (need_reset && link)
 		usbnet_defer_kevent(dev, EVENT_LINK_RESET);
+
+	if (dev->link_rpm_enabled) {
+		if (!link && dev->link_state)
+			usbnet_link_suspend(dev);
+		else if (link && !dev->link_state && dev->link_remote_wakeup)
+			usbnet_link_resume(dev);
+	}
+	dev->link_state = link;
 }
 EXPORT_SYMBOL_GPL(usbnet_link_change);
 
diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h
index 1937b74..c96a623 100644
--- a/include/linux/usb/usbnet.h
+++ b/include/linux/usb/usbnet.h
@@ -68,6 +68,18 @@ struct usbnet {
 #		define EVENT_RX_PAUSED	5
 #		define EVENT_DEV_ASLEEP 6
 #		define EVENT_DEV_OPEN	7
+
+	/* link down triggered runtime PM */
+	struct delayed_work	link_detect_work;
+	struct completion	link_update_completion;
+	int			link_update_timeout;
+	int			old_autosuspend_delay;
+	unsigned int		link_rpm_enabled:1;
+	unsigned int		link_check_started:1;
+	unsigned int		link_checking:1;
+	unsigned int		link_open_suspend:1;
+	unsigned int		link_state:1;
+	unsigned int		link_remote_wakeup:1;
 };
 
 static inline struct usb_driver *driver_of(struct usb_interface *intf)
@@ -106,6 +118,12 @@ struct driver_info {
 #define FLAG_MULTI_PACKET	0x2000
 #define FLAG_RX_ASSEMBLE	0x4000	/* rx packets may span >1 frames */
 
+/* some drivers may not update link state in .status */
+#define FLAG_LINK_UPDATE_BY_DRIVER	0x8000
+
+/* device support remote wakeup by link change */
+#define FLAG_LINK_SUPPORT_REMOTE_WAKEUP	0x10000
+
 	/* init device ... can sleep, or cause probe() failure */
 	int	(*bind)(struct usbnet *, struct usb_interface *);
 
@@ -161,6 +179,7 @@ extern int usbnet_suspend(struct usb_interface *, pm_message_t);
 extern int usbnet_resume(struct usb_interface *);
 extern void usbnet_disconnect(struct usb_interface *);
 extern void usbnet_link_change(struct usbnet *dev, int link, int need_reset);
+extern void usbnet_link_updated(struct usbnet *dev);
 
 /* Drivers that reuse some of the standard USB CDC infrastructure
  * (notably, using multiple interfaces according to the CDC
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 0/3] usbnet: support runtime PM triggered by link change
  2012-09-15 17:48 [RFC PATCH 0/3] usbnet: support runtime PM triggered by link change Ming Lei
                   ` (2 preceding siblings ...)
  2012-09-15 17:48 ` [RFC PATCH 3/3] usbnet: support runtime PM triggered by link change Ming Lei
@ 2012-09-17  8:04 ` Oliver Neukum
       [not found]   ` <2236952.YSZj5xxzHO-ugxBuEnWX9yG/4A2pS7c2Q@public.gmane.org>
  3 siblings, 1 reply; 16+ messages in thread
From: Oliver Neukum @ 2012-09-17  8:04 UTC (permalink / raw)
  To: Ming Lei
  Cc: David S. Miller, Greg Kroah-Hartman, Fink Dmitry, Rafael Wysocki,
	Alan Stern, netdev, linux-usb

On Sunday 16 September 2012 01:48:16 Ming Lei wrote:

Hi,

> Currently only very few usbnet devices support the traffic based
> runtime PM, eg. wake up devices if there are packets to be transmitted.
> 
> For the below situation, it should make sense to runtime suspend usbnet
> device to save power:
>         
>         - after network link becomes down

Basically cool design, but it raises two fundamental questions
and some detail questions.

> This patch implements the runtime PM triggered by network link change
> event, and it works basically on asix usbnet device after a simple
> runtime PM test.

1) Does it actually save power? You are constantly waking up a CPU.
>From that perspective it is possible that leaving on the ethernet is actually
better in terms of power. Only measurements can answer that question.

2) Do we have many devices that would be serviced with this approach?

	Regards
		Oliver

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 0/3] usbnet: support runtime PM triggered by link change
       [not found]   ` <2236952.YSZj5xxzHO-ugxBuEnWX9yG/4A2pS7c2Q@public.gmane.org>
@ 2012-09-17  8:25     ` Ming Lei
  2012-09-20  8:30       ` Bjørn Mork
  0 siblings, 1 reply; 16+ messages in thread
From: Ming Lei @ 2012-09-17  8:25 UTC (permalink / raw)
  To: Oliver Neukum
  Cc: David S. Miller, Greg Kroah-Hartman, Fink Dmitry, Rafael Wysocki,
	Alan Stern, netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-usb-u79uwXL29TY76Z2rM5mHXA

On Mon, Sep 17, 2012 at 4:04 PM, Oliver Neukum <oneukum-l3A5Bk7waGM@public.gmane.org> wrote:
> On Sunday 16 September 2012 01:48:16 Ming Lei wrote:
>
> Hi,
>
>> Currently only very few usbnet devices support the traffic based
>> runtime PM, eg. wake up devices if there are packets to be transmitted.
>>
>> For the below situation, it should make sense to runtime suspend usbnet
>> device to save power:
>>
>>         - after network link becomes down
>
> Basically cool design, but it raises two fundamental questions
> and some detail questions.
>
>> This patch implements the runtime PM triggered by network link change
>> event, and it works basically on asix usbnet device after a simple
>> runtime PM test.
>
> 1) Does it actually save power? You are constantly waking up a CPU.

Of course, it does. I don't know it will save how much power just on
usbnet device, but it may save power from usb hub and usb host
controller in the bus at least.

Anyway we don't need to waste power if the link of usbnet is down.

It just wake up CPU one time each 3sec. In modern linux distribution,
the CPU will be wakup tens times per second, so it shouldn't be a
big problem.

> From that perspective it is possible that leaving on the ethernet is actually
> better in terms of power. Only measurements can answer that question.

You know it is a bit difficult to test power save for this situation. And
most of runtime PM patches didn't provide power save data. In fact,
I'd like to do it, but I have not some equipment to measure it, :-(.

>
> 2) Do we have many devices that would be serviced with this approach?

At least I found asix can be serviced by this approach. Considered asix
is quite popular, it is worthy of the effort. Also the below devices can be
serviced by the patch in theory:

                   dm9601.c / mcs7830.c / sierra_net.c

In fact, it might be used by other non-usbnet devices too.

Thanks,
--
Ming Lei
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 3/3] usbnet: support runtime PM triggered by link change
  2012-09-15 17:48 ` [RFC PATCH 3/3] usbnet: support runtime PM triggered by link change Ming Lei
@ 2012-09-17  8:50   ` Oliver Neukum
       [not found]     ` <3287943.Bzm0t1oGWG-ugxBuEnWX9yG/4A2pS7c2Q@public.gmane.org>
  2012-09-17 13:07     ` Ming Lei
  0 siblings, 2 replies; 16+ messages in thread
From: Oliver Neukum @ 2012-09-17  8:50 UTC (permalink / raw)
  To: Ming Lei
  Cc: David S. Miller, Greg Kroah-Hartman, Fink Dmitry, Rafael Wysocki,
	Alan Stern, netdev, linux-usb

On Sunday 16 September 2012 01:48:19 Ming Lei wrote:

 
> +void usbnet_link_updated(struct usbnet *dev)
> +{
> +	complete(&dev->link_update_completion);
> +}
> +EXPORT_SYMBOL_GPL(usbnet_link_updated);

Isn't that a bit too trivial to get the _GPL version?

> +#define usbnet_link_suspend(dev) do { \
> +	dev_dbg(&dev->intf->dev, "%s:link suspend", __func__); \
> +	usb_autopm_put_interface_async(dev->intf); \
> +} while(0)
> +
> +#define usbnet_link_resume(dev) do { \
> +	dev_dbg(&dev->intf->dev, "%s:link resume", __func__); \
> +	usb_autopm_get_interface_async(dev->intf); \
> +} while(0)

Why macros?


[..]
> +/* called by usbnet_open */
> +static void enable_link_runtime_pm(struct usbnet *dev)
> +{
> +	dev->link_rpm_enabled = 1;
> +
> +	if (!dev->link_remote_wakeup) {
> +		dev->old_autosuspend_delay =
> +			dev->udev->dev.power.autosuspend_delay;
> +		pm_runtime_set_autosuspend_delay(&dev->udev->dev, 1);

This is a problem. Suppose the user changes the autosuspend timeout.
You cannot assume that the old value remains valid.

> +	}
> +
> +	if (!netif_carrier_ok(dev->net)) {
> +		dev->link_open_suspend = 1;
> +		usbnet_link_suspend(dev);
> +	}
> +}

> +static void update_link_state(struct usbnet *dev)
> +{
> +	char		*buf = NULL;
> +	unsigned	pipe = 0;
> +	unsigned	maxp;
> +	int		ret, act_len, timeout;
> +	struct urb	urb;
> +
> +	pipe = usb_rcvintpipe(dev->udev,
> +			      dev->status->desc.bEndpointAddress
> +				& USB_ENDPOINT_NUMBER_MASK);
> +	maxp = usb_maxpacket(dev->udev, pipe, 0);
> +
> +	/*
> +	 * Take default timeout as 2 times of period.
> +	 * It is observed that asix device can update its link
> +	 * state duing one period(128ms). Low level driver can set
> +	 * its default update link time in bind() callback.
> +	 */
> +	if (!dev->link_update_timeout) {
> +		timeout = max((int) dev->status->desc.bInterval,
> +			(dev->udev->speed == USB_SPEED_HIGH) ? 7 : 3);
> +		timeout = 1 << timeout;
> +		if (dev->udev->speed == USB_SPEED_HIGH)
> +			timeout /= 8;
> +		if (timeout < 128)
> +			timeout = 128;
> +	} else
> +		timeout = dev->link_update_timeout;
> +
> +	buf = kmalloc(maxp, GFP_KERNEL);
> +	if (!buf)
> +		return;
> +
> +	dev_dbg(&dev->intf->dev, "%s: timeout %dms\n", __func__, timeout);
> +	ret = usb_interrupt_msg(dev->udev, pipe, buf, maxp,
> +			&act_len, timeout);
> +	if (!ret) {
> +		urb.status = 0;
> +		urb.actual_length = act_len;
> +		urb.transfer_buffer = buf;
> +		urb.transfer_buffer_length = maxp;
> +		dev->driver_info->status(dev, &urb);
> +		if (dev->driver_info->flags &
> +		    FLAG_LINK_UPDATE_BY_DRIVER)
> +			wait_for_completion(&dev->link_update_completion);

If a driver calls usbnet_link_updated() from the same workqueue this
will deadlock.

> +		dev_dbg(&dev->intf->dev, "%s: link updated\n", __func__);
> +	} else
> +		dev_dbg(&dev->intf->dev, "%s: link update failed %d\n",
> +				__func__, ret);
> +	kfree(buf);
> +}

[..]
> @@ -795,6 +977,9 @@ int usbnet_open (struct net_device *net)
>  		if (retval < 0)
>  			goto done_manage_power_error;
>  		usb_autopm_put_interface(dev->intf);
> +	} else {
> +		if (need_link_runtime_pm(dev))
> +			enable_link_runtime_pm(dev);
>  	}
>  	return retval;
>  
> @@ -1489,6 +1674,8 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
>  	if (dev->driver_info->flags & FLAG_LINK_INTR)
>  		usbnet_link_change(dev, 0, 0);
>  
> +	init_link_rpm(dev);
> +
>  	return 0;
>  
>  out4:
> @@ -1538,6 +1725,9 @@ int usbnet_suspend (struct usb_interface *intf, pm_message_t message)
>  		 * wake the device
>  		 */
>  		netif_device_attach (dev->net);
> +
> +		if (PMSG_IS_AUTO(message))
> +			start_link_detect(dev);

What happens if the device is autosuspended, then the system is suspended
and the work is executed while the suspension is underway?

>  	}
>  	return 0;
>  }
> @@ -1552,8 +1742,10 @@ int usbnet_resume (struct usb_interface *intf)
>  
>  	if (!--dev->suspend_count) {
>  		/* resume interrupt URBs */
> -		if (dev->interrupt && test_bit(EVENT_DEV_OPEN, &dev->flags))
> -			usb_submit_urb(dev->interrupt, GFP_NOIO);
> +		if (dev->interrupt && test_bit(EVENT_DEV_OPEN, &dev->flags)) {
> +			if (!dev->link_checking)

That is impossible. If the device is resumed the interrupt URB must
be restarted in every case (if it exists).
You cannot assume that its only function is checking the link state.
And it introduces a race with the workqueue.

> +				usb_submit_urb(dev->interrupt, GFP_NOIO);
> +		}
>  
>  		spin_lock_irq(&dev->txq.lock);
>  		while ((res = usb_get_from_anchor(&dev->deferred))) {
> @@ -1586,6 +1778,8 @@ int usbnet_resume (struct usb_interface *intf)
>  				netif_tx_wake_all_queues(dev->net);
>  			tasklet_schedule (&dev->bh);
>  		}
> +
> +		end_link_detect(dev, 0);
>  	}
>  	return 0;
>  }
> @@ -1593,6 +1787,9 @@ EXPORT_SYMBOL_GPL(usbnet_resume);

	Regards
		Oliver

^ permalink raw reply	[flat|nested] 16+ messages in thread

* RE: [RFC PATCH 3/3] usbnet: support runtime PM triggered by link change
       [not found]     ` <3287943.Bzm0t1oGWG-ugxBuEnWX9yG/4A2pS7c2Q@public.gmane.org>
@ 2012-09-17  9:02       ` David Laight
       [not found]         ` <AE90C24D6B3A694183C094C60CF0A2F6026B6FF0-CgBM+Bx2aUAnGFn1LkZF6NBPR1lH4CV8@public.gmane.org>
  0 siblings, 1 reply; 16+ messages in thread
From: David Laight @ 2012-09-17  9:02 UTC (permalink / raw)
  To: Oliver Neukum, Ming Lei
  Cc: David S. Miller, Greg Kroah-Hartman, Fink Dmitry, Rafael Wysocki,
	Alan Stern, netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-usb-u79uwXL29TY76Z2rM5mHXA

> > +void usbnet_link_updated(struct usbnet *dev)
> > +{
> > +	complete(&dev->link_update_completion);
> > +}
> > +EXPORT_SYMBOL_GPL(usbnet_link_updated);
> 
> Isn't that a bit too trivial to get the _GPL version?

Particularly if the usb infrastructure (that I presume this
is part of) might be reasonably usable by non-gpl drivers.

A few years back the function to release a reference on
the kernel 'pid' structure was added as GPL - making
it impossible for non-GPL to hold the reference.

	David
 


--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 3/3] usbnet: support runtime PM triggered by link change
       [not found]         ` <AE90C24D6B3A694183C094C60CF0A2F6026B6FF0-CgBM+Bx2aUAnGFn1LkZF6NBPR1lH4CV8@public.gmane.org>
@ 2012-09-17 11:15           ` Greg Kroah-Hartman
  0 siblings, 0 replies; 16+ messages in thread
From: Greg Kroah-Hartman @ 2012-09-17 11:15 UTC (permalink / raw)
  To: David Laight
  Cc: Oliver Neukum, Ming Lei, David S. Miller, Fink Dmitry,
	Rafael Wysocki, Alan Stern, netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-usb-u79uwXL29TY76Z2rM5mHXA

On Mon, Sep 17, 2012 at 10:02:27AM +0100, David Laight wrote:
> > > +void usbnet_link_updated(struct usbnet *dev)
> > > +{
> > > +	complete(&dev->link_update_completion);
> > > +}
> > > +EXPORT_SYMBOL_GPL(usbnet_link_updated);
> > 
> > Isn't that a bit too trivial to get the _GPL version?
> 
> Particularly if the usb infrastructure (that I presume this
> is part of) might be reasonably usable by non-gpl drivers.

It can not be, and has not been able to, for quite a number of years now
(since the 2.6.15 kernel release or so I think.)  So this should not be
an issue.

greg k-h
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 3/3] usbnet: support runtime PM triggered by link change
  2012-09-17  8:50   ` Oliver Neukum
       [not found]     ` <3287943.Bzm0t1oGWG-ugxBuEnWX9yG/4A2pS7c2Q@public.gmane.org>
@ 2012-09-17 13:07     ` Ming Lei
  1 sibling, 0 replies; 16+ messages in thread
From: Ming Lei @ 2012-09-17 13:07 UTC (permalink / raw)
  To: Oliver Neukum
  Cc: David S. Miller, Greg Kroah-Hartman, Fink Dmitry, Rafael Wysocki,
	Alan Stern, netdev, linux-usb

On Mon, Sep 17, 2012 at 4:50 PM, Oliver Neukum <oneukum@suse.de> wrote:
> On Sunday 16 September 2012 01:48:19 Ming Lei wrote:
>
>
>> +void usbnet_link_updated(struct usbnet *dev)
>> +{
>> +     complete(&dev->link_update_completion);
>> +}
>> +EXPORT_SYMBOL_GPL(usbnet_link_updated);
>
> Isn't that a bit too trivial to get the _GPL version?

OK, will use the non GPL version.

>
>> +#define usbnet_link_suspend(dev) do { \
>> +     dev_dbg(&dev->intf->dev, "%s:link suspend", __func__); \
>> +     usb_autopm_put_interface_async(dev->intf); \
>> +} while(0)
>> +
>> +#define usbnet_link_resume(dev) do { \
>> +     dev_dbg(&dev->intf->dev, "%s:link resume", __func__); \
>> +     usb_autopm_get_interface_async(dev->intf); \
>> +} while(0)
>
> Why macros?

Just for easy debug by dumping the caller function name.

>
>
> [..]
>> +/* called by usbnet_open */
>> +static void enable_link_runtime_pm(struct usbnet *dev)
>> +{
>> +     dev->link_rpm_enabled = 1;
>> +
>> +     if (!dev->link_remote_wakeup) {
>> +             dev->old_autosuspend_delay =
>> +                     dev->udev->dev.power.autosuspend_delay;
>> +             pm_runtime_set_autosuspend_delay(&dev->udev->dev, 1);
>
> This is a problem. Suppose the user changes the autosuspend timeout.
> You cannot assume that the old value remains valid.

Good catch, I will fix it in -v1 by reading again the timeout value in
disable_link_runtime_pm().

>> +     }
>> +
>> +     if (!netif_carrier_ok(dev->net)) {
>> +             dev->link_open_suspend = 1;
>> +             usbnet_link_suspend(dev);
>> +     }
>> +}
>
>> +static void update_link_state(struct usbnet *dev)
>> +{
>> +     char            *buf = NULL;
>> +     unsigned        pipe = 0;
>> +     unsigned        maxp;
>> +     int             ret, act_len, timeout;
>> +     struct urb      urb;
>> +
>> +     pipe = usb_rcvintpipe(dev->udev,
>> +                           dev->status->desc.bEndpointAddress
>> +                             & USB_ENDPOINT_NUMBER_MASK);
>> +     maxp = usb_maxpacket(dev->udev, pipe, 0);
>> +
>> +     /*
>> +      * Take default timeout as 2 times of period.
>> +      * It is observed that asix device can update its link
>> +      * state duing one period(128ms). Low level driver can set
>> +      * its default update link time in bind() callback.
>> +      */
>> +     if (!dev->link_update_timeout) {
>> +             timeout = max((int) dev->status->desc.bInterval,
>> +                     (dev->udev->speed == USB_SPEED_HIGH) ? 7 : 3);
>> +             timeout = 1 << timeout;
>> +             if (dev->udev->speed == USB_SPEED_HIGH)
>> +                     timeout /= 8;
>> +             if (timeout < 128)
>> +                     timeout = 128;
>> +     } else
>> +             timeout = dev->link_update_timeout;
>> +
>> +     buf = kmalloc(maxp, GFP_KERNEL);
>> +     if (!buf)
>> +             return;
>> +
>> +     dev_dbg(&dev->intf->dev, "%s: timeout %dms\n", __func__, timeout);
>> +     ret = usb_interrupt_msg(dev->udev, pipe, buf, maxp,
>> +                     &act_len, timeout);
>> +     if (!ret) {
>> +             urb.status = 0;
>> +             urb.actual_length = act_len;
>> +             urb.transfer_buffer = buf;
>> +             urb.transfer_buffer_length = maxp;
>> +             dev->driver_info->status(dev, &urb);
>> +             if (dev->driver_info->flags &
>> +                 FLAG_LINK_UPDATE_BY_DRIVER)
>> +                     wait_for_completion(&dev->link_update_completion);
>
> If a driver calls usbnet_link_updated() from the same workqueue this
> will deadlock.

Good catch, I will fix it in -v1 by using system_freezable_wq.

>
>> +             dev_dbg(&dev->intf->dev, "%s: link updated\n", __func__);
>> +     } else
>> +             dev_dbg(&dev->intf->dev, "%s: link update failed %d\n",
>> +                             __func__, ret);
>> +     kfree(buf);
>> +}
>
> [..]
>> @@ -795,6 +977,9 @@ int usbnet_open (struct net_device *net)
>>               if (retval < 0)
>>                       goto done_manage_power_error;
>>               usb_autopm_put_interface(dev->intf);
>> +     } else {
>> +             if (need_link_runtime_pm(dev))
>> +                     enable_link_runtime_pm(dev);
>>       }
>>       return retval;
>>
>> @@ -1489,6 +1674,8 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
>>       if (dev->driver_info->flags & FLAG_LINK_INTR)
>>               usbnet_link_change(dev, 0, 0);
>>
>> +     init_link_rpm(dev);
>> +
>>       return 0;
>>
>>  out4:
>> @@ -1538,6 +1725,9 @@ int usbnet_suspend (struct usb_interface *intf, pm_message_t message)
>>                * wake the device
>>                */
>>               netif_device_attach (dev->net);
>> +
>> +             if (PMSG_IS_AUTO(message))
>> +                     start_link_detect(dev);
>
> What happens if the device is autosuspended, then the system is suspended
> and the work is executed while the suspension is underway?

IMO we can avoid the problem by scheduling 'link_detect_work' on the
workqueue of 'system_freezable_wq', which will be introduced in -v1.

>
>>       }
>>       return 0;
>>  }
>> @@ -1552,8 +1742,10 @@ int usbnet_resume (struct usb_interface *intf)
>>
>>       if (!--dev->suspend_count) {
>>               /* resume interrupt URBs */
>> -             if (dev->interrupt && test_bit(EVENT_DEV_OPEN, &dev->flags))
>> -                     usb_submit_urb(dev->interrupt, GFP_NOIO);
>> +             if (dev->interrupt && test_bit(EVENT_DEV_OPEN, &dev->flags)) {
>> +                     if (!dev->link_checking)
>
> That is impossible. If the device is resumed the interrupt URB must
> be restarted in every case (if it exists).

The interrupt URB will be submitted later in link_detect_work() under
the situation of being resumed by link detect work.

> You cannot assume that its only function is checking the link state.
> And it introduces a race with the workqueue.

Looks no race because usbnet_resume() will be run in workqueue
task context under the situation of being resumed by link detect work.


Thanks,
--
Ming Lei

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 0/3] usbnet: support runtime PM triggered by link change
  2012-09-17  8:25     ` Ming Lei
@ 2012-09-20  8:30       ` Bjørn Mork
  2012-09-20 21:02         ` David Miller
  2012-09-24 17:10         ` Ming Lei
  0 siblings, 2 replies; 16+ messages in thread
From: Bjørn Mork @ 2012-09-20  8:30 UTC (permalink / raw)
  To: Ming Lei
  Cc: Oliver Neukum, David S. Miller, Greg Kroah-Hartman, Fink Dmitry,
	Rafael Wysocki, Alan Stern, netdev, linux-usb

Ming Lei <ming.lei@canonical.com> writes:
> On Mon, Sep 17, 2012 at 4:04 PM, Oliver Neukum <oneukum@suse.de> wrote:
>
>> 1) Does it actually save power? You are constantly waking up a CPU.
>
> Of course, it does. I don't know it will save how much power just on
> usbnet device, but it may save power from usb hub and usb host
> controller in the bus at least.
>
> Anyway we don't need to waste power if the link of usbnet is down.
>
> It just wake up CPU one time each 3sec. In modern linux distribution,
> the CPU will be wakup tens times per second, so it shouldn't be a
> big problem.

I do not buy that constantly polling a device necessarily saves any
power compared to keeping the USB link to the device alive.  You need to
measure the savings if you want us to believe that.

You are not only waking the host CPU.  You are also waking the device
CPU. 

Any usbnet device will consist of more than one building block, having
at least a network block, a USB block and a CPU.  For all you know, the
device CPU might be in deep sleep until it has to service either the
network or USB block, and those might also be sleeping until they see an
interrupt.  Constantly polling the device to receive network link status
might use considerably more power than keeping the USB link up waiting
for a link notification.

If you were to implement this feature anyway, then I would prefer a
userspace based approach instead.  I see no reason to keep the timer in
the kernel.  You could decide to suspend whenever the netdev is down,
and only poll the link when userspace tries to bring up the netdev.
That would let a userspace daemon implement the same feature by trying
to bring up the netdev every 3 seconds (or whatever frequency the user
selected).  And it would allow me to avoid the polling until I know I
have plugged in a cable.

>> From that perspective it is possible that leaving on the ethernet is actually
>> better in terms of power. Only measurements can answer that question.
>
> You know it is a bit difficult to test power save for this situation. And
> most of runtime PM patches didn't provide power save data. In fact,
> I'd like to do it, but I have not some equipment to measure it, :-(.

We don't know, you don't know.  But you claim that your change saves
power, so please provide some documentation showing that it does.

>> 2) Do we have many devices that would be serviced with this approach?
>
> At least I found asix can be serviced by this approach. Considered asix
> is quite popular, it is worthy of the effort. Also the below devices can be
> serviced by the patch in theory:
>
>                    dm9601.c / mcs7830.c / sierra_net.c

The sierra_net.c driver is only used for wireless devices AFAIK. I
really don't see the use case for network link based resume of that.
There is no cable to plug.  Userspace will have to initiate a
connection.

And the DirectIP device I've got (MC7710) supports remote wakeup.  I
assume that will be the case for all such devices, given that this is
mostly a firmware feature. So the correct fix for sierra_net.c is to add
support for remote wakeup.  Then you will be able to suspend the device
regardless of whether the link is up or down, without the constant
polling.


Bjørn

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 0/3] usbnet: support runtime PM triggered by link change
  2012-09-20  8:30       ` Bjørn Mork
@ 2012-09-20 21:02         ` David Miller
       [not found]           ` <20120920.170227.258356702969458329.davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
  2012-09-24 17:10         ` Ming Lei
  1 sibling, 1 reply; 16+ messages in thread
From: David Miller @ 2012-09-20 21:02 UTC (permalink / raw)
  To: bjorn; +Cc: ming.lei, oneukum, gregkh, finik, rjw, stern, netdev, linux-usb


There seems to be some discussion about the legitimacy of doing things
this way, and in any event the patches were an RFC.

Please resubmit as a non-RFC once all the issues have been worked
out, if appropriate.

Thanks.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 0/3] usbnet: support runtime PM triggered by link change
       [not found]           ` <20120920.170227.258356702969458329.davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
@ 2012-09-20 21:04             ` Oliver Neukum
       [not found]               ` <1703568.mhE1zQzG7o-ugxBuEnWX9yG/4A2pS7c2Q@public.gmane.org>
  0 siblings, 1 reply; 16+ messages in thread
From: Oliver Neukum @ 2012-09-20 21:04 UTC (permalink / raw)
  To: David Miller
  Cc: bjorn-yOkvZcmFvRU, ming.lei-Z7WLFzj8eWMS+FvcfC7Uqw,
	gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r, finik-l0cyMroinI0,
	rjw-KKrjLPT3xs0, stern-nwvwT67g6+6dFdvTe/nMLpVzexx5G7lz,
	netdev-u79uwXL29TY76Z2rM5mHXA, linux-usb-u79uwXL29TY76Z2rM5mHXA

On Thursday 20 September 2012 17:02:27 David Miller wrote:
> 
> There seems to be some discussion about the legitimacy of doing things
> this way, and in any event the patches were an RFC.
> 
> Please resubmit as a non-RFC once all the issues have been worked
> out, if appropriate.

Just to make this clear, I'd like to state that the discussion involved
only the third, last patch in the series. The first two are fine and make
sense by themselves.

	Regards
		Oliver

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 0/3] usbnet: support runtime PM triggered by link change
       [not found]               ` <1703568.mhE1zQzG7o-ugxBuEnWX9yG/4A2pS7c2Q@public.gmane.org>
@ 2012-09-20 21:16                 ` David Miller
  2012-09-21  1:44                   ` Ming Lei
  0 siblings, 1 reply; 16+ messages in thread
From: David Miller @ 2012-09-20 21:16 UTC (permalink / raw)
  To: oliver-GvhC2dPhHPQdnm+yROfE0A
  Cc: bjorn-yOkvZcmFvRU, ming.lei-Z7WLFzj8eWMS+FvcfC7Uqw,
	gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r, finik-l0cyMroinI0,
	rjw-KKrjLPT3xs0, stern-nwvwT67g6+6dFdvTe/nMLpVzexx5G7lz,
	netdev-u79uwXL29TY76Z2rM5mHXA, linux-usb-u79uwXL29TY76Z2rM5mHXA

From: Oliver Neukum <oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org>
Date: Thu, 20 Sep 2012 23:04:38 +0200

> On Thursday 20 September 2012 17:02:27 David Miller wrote:
>> 
>> There seems to be some discussion about the legitimacy of doing things
>> this way, and in any event the patches were an RFC.
>> 
>> Please resubmit as a non-RFC once all the issues have been worked
>> out, if appropriate.
> 
> Just to make this clear, I'd like to state that the discussion involved
> only the third, last patch in the series. The first two are fine and make
> sense by themselves.

I want changes in those, see my replies.
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 0/3] usbnet: support runtime PM triggered by link change
  2012-09-20 21:16                 ` David Miller
@ 2012-09-21  1:44                   ` Ming Lei
  0 siblings, 0 replies; 16+ messages in thread
From: Ming Lei @ 2012-09-21  1:44 UTC (permalink / raw)
  To: David Miller; +Cc: oliver, bjorn, gregkh, finik, rjw, stern, netdev, linux-usb

On Fri, Sep 21, 2012 at 5:16 AM, David Miller <davem@davemloft.net> wrote:
> From: Oliver Neukum <oliver@neukum.org>
> Date: Thu, 20 Sep 2012 23:04:38 +0200
>
>> On Thursday 20 September 2012 17:02:27 David Miller wrote:
>>>
>>> There seems to be some discussion about the legitimacy of doing things
>>> this way, and in any event the patches were an RFC.
>>>
>>> Please resubmit as a non-RFC once all the issues have been worked
>>> out, if appropriate.
>>
>> Just to make this clear, I'd like to state that the discussion involved
>> only the third, last patch in the series. The first two are fine and make
>> sense by themselves.
>
> I want changes in those, see my replies.

No problem, I will send out -v2 of the first two patches later.

thanks,
--
Ming Lei

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH 0/3] usbnet: support runtime PM triggered by link change
  2012-09-20  8:30       ` Bjørn Mork
  2012-09-20 21:02         ` David Miller
@ 2012-09-24 17:10         ` Ming Lei
  1 sibling, 0 replies; 16+ messages in thread
From: Ming Lei @ 2012-09-24 17:10 UTC (permalink / raw)
  To: Bjørn Mork
  Cc: Oliver Neukum, David S. Miller, Greg Kroah-Hartman, Fink Dmitry,
	Rafael Wysocki, Alan Stern, netdev, linux-usb

On Thu, Sep 20, 2012 at 4:30 PM, Bjørn Mork <bjorn@mork.no> wrote:
> Ming Lei <ming.lei@canonical.com> writes:
>> On Mon, Sep 17, 2012 at 4:04 PM, Oliver Neukum <oneukum@suse.de> wrote:
>>
>>> 1) Does it actually save power? You are constantly waking up a CPU.
>>
>> Of course, it does. I don't know it will save how much power just on
>> usbnet device, but it may save power from usb hub and usb host
>> controller in the bus at least.
>>
>> Anyway we don't need to waste power if the link of usbnet is down.
>>
>> It just wake up CPU one time each 3sec. In modern linux distribution,
>> the CPU will be wakup tens times per second, so it shouldn't be a
>> big problem.
>
> I do not buy that constantly polling a device necessarily saves any
> power compared to keeping the USB link to the device alive.  You need to
> measure the savings if you want us to believe that.

Yes, good suggestion.

>
> You are not only waking the host CPU.  You are also waking the device
> CPU.

Some usbnet devices doesn't have CPU, and I don't think there are
one in smsc95xx or asix, :-)

>
> Any usbnet device will consist of more than one building block, having
> at least a network block, a USB block and a CPU.  For all you know, the
> device CPU might be in deep sleep until it has to service either the
> network or USB block, and those might also be sleeping until they see an
> interrupt.  Constantly polling the device to receive network link status
> might use considerably more power than keeping the USB link up waiting
> for a link notification.
>
> If you were to implement this feature anyway, then I would prefer a
> userspace based approach instead.  I see no reason to keep the timer in
> the kernel.  You could decide to suspend whenever the netdev is down,
> and only poll the link when userspace tries to bring up the netdev.

I am wondering if userspace knows when it should bring up the interface,
maybe it depends on the link becomes up, maybe udev always bring it up.

> That would let a userspace daemon implement the same feature by trying
> to bring up the netdev every 3 seconds (or whatever frequency the user
> selected).  And it would allow me to avoid the polling until I know I
> have plugged in a cable.

I don't see any advantage by using a userspace timer, and it will convert to
a kernel timer finally.

Also putting the interface down may introduce some side effect on user
space: the IP address may be lost, etc.

>
>>> From that perspective it is possible that leaving on the ethernet is actually
>>> better in terms of power. Only measurements can answer that question.
>>
>> You know it is a bit difficult to test power save for this situation. And
>> most of runtime PM patches didn't provide power save data. In fact,
>> I'd like to do it, but I have not some equipment to measure it, :-(.
>
> We don't know, you don't know.  But you claim that your change saves
> power, so please provide some documentation showing that it does.

The motivation of the patch is that suspending the usbnet device may put
all usb devices in the bus into suspend.

I am trying to get some data by powertop, but looks it is not reliable enough.

I even found that the discharge rate of battery when the asix is disconnected
and let the whole bus suspend is even more than when the asix is connected
with ethernet cable link down and all devices in the whole usb bus are active.
There is about 1w difference, so odd.

I will try to get some correct power data.

>
>>> 2) Do we have many devices that would be serviced with this approach?
>>
>> At least I found asix can be serviced by this approach. Considered asix
>> is quite popular, it is worthy of the effort. Also the below devices can be
>> serviced by the patch in theory:
>>
>>                    dm9601.c / mcs7830.c / sierra_net.c
>
> The sierra_net.c driver is only used for wireless devices AFAIK. I
> really don't see the use case for network link based resume of that.
> There is no cable to plug.  Userspace will have to initiate a
> connection.

Wireless link is still one kind of link, :-)

Otherwise, why sierra_net.c bother to use netif_carrier_on?

>
> And the DirectIP device I've got (MC7710) supports remote wakeup.  I
> assume that will be the case for all such devices, given that this is
> mostly a firmware feature. So the correct fix for sierra_net.c is to add
> support for remote wakeup.  Then you will be able to suspend the device
> regardless of whether the link is up or down, without the constant
> polling.

In fact, the patch supports link change via remote wakeup, and it may be
useful if remote wakeup is not supported by incoming packet.

Not mention the power save data, the patch is not mature enough, for example,
ethtool_ops interface may access a suspended device.

I will update it if some progresses are got.

Thanks,
--
Ming Lei

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2012-09-24 17:10 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-09-15 17:48 [RFC PATCH 0/3] usbnet: support runtime PM triggered by link change Ming Lei
     [not found] ` <1347731299-29898-1-git-send-email-ming.lei-Z7WLFzj8eWMS+FvcfC7Uqw@public.gmane.org>
2012-09-15 17:48   ` [RFC PATCH 1/3] usbnet: introduce usbnet_link_change API Ming Lei
2012-09-15 17:48 ` [RFC PATCH 2/3] usbnet: apply usbnet_link_change Ming Lei
2012-09-15 17:48 ` [RFC PATCH 3/3] usbnet: support runtime PM triggered by link change Ming Lei
2012-09-17  8:50   ` Oliver Neukum
     [not found]     ` <3287943.Bzm0t1oGWG-ugxBuEnWX9yG/4A2pS7c2Q@public.gmane.org>
2012-09-17  9:02       ` David Laight
     [not found]         ` <AE90C24D6B3A694183C094C60CF0A2F6026B6FF0-CgBM+Bx2aUAnGFn1LkZF6NBPR1lH4CV8@public.gmane.org>
2012-09-17 11:15           ` Greg Kroah-Hartman
2012-09-17 13:07     ` Ming Lei
2012-09-17  8:04 ` [RFC PATCH 0/3] " Oliver Neukum
     [not found]   ` <2236952.YSZj5xxzHO-ugxBuEnWX9yG/4A2pS7c2Q@public.gmane.org>
2012-09-17  8:25     ` Ming Lei
2012-09-20  8:30       ` Bjørn Mork
2012-09-20 21:02         ` David Miller
     [not found]           ` <20120920.170227.258356702969458329.davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
2012-09-20 21:04             ` Oliver Neukum
     [not found]               ` <1703568.mhE1zQzG7o-ugxBuEnWX9yG/4A2pS7c2Q@public.gmane.org>
2012-09-20 21:16                 ` David Miller
2012-09-21  1:44                   ` Ming Lei
2012-09-24 17:10         ` Ming Lei

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).