Linux Power Management development
 help / color / mirror / Atom feed
* [PATCH] PM: add macro to test for runtime PM events
From: Alan Stern @ 2011-08-18 20:06 UTC (permalink / raw)
  To: Greg KH, Rafael J. Wysocki
  Cc: Takashi Iwai, netdev, USB list, linux-bluetooth, linux-input,
	Linux-pm mailing list

This patch (as1482) adds a macro for testing whether or not a
pm_message value represents an autosuspend or autoresume (i.e., a
runtime PM) event.  Encapsulating this notion seems preferable to
open-coding the test all over the place.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>

---

This is a minor change in the PM API, but most of the affected files 
are in the USB subsystem.  Therefore either Rafael or Greg might prefer 
to accept this patch.



 Documentation/usb/power-management.txt |    8 ++++----
 drivers/bluetooth/btusb.c              |    2 +-
 drivers/hid/hid-picolcd.c              |    2 +-
 drivers/hid/usbhid/hid-core.c          |    7 +++----
 drivers/net/usb/usbnet.c               |    2 +-
 drivers/net/wimax/i2400m/usb.c         |    4 ++--
 drivers/usb/class/cdc-acm.c            |    2 +-
 drivers/usb/class/cdc-wdm.c            |    6 +++---
 drivers/usb/core/driver.c              |    9 ++++-----
 drivers/usb/core/hcd.c                 |    4 ++--
 drivers/usb/core/hub.c                 |   10 +++++-----
 drivers/usb/serial/sierra.c            |    2 +-
 drivers/usb/serial/usb_wwan.c          |    2 +-
 include/linux/pm.h                     |    2 ++
 sound/usb/card.c                       |    2 +-
 15 files changed, 32 insertions(+), 32 deletions(-)

Index: usb-3.1/include/linux/pm.h
===================================================================
--- usb-3.1.orig/include/linux/pm.h
+++ usb-3.1/include/linux/pm.h
@@ -366,6 +366,8 @@ extern struct dev_pm_ops generic_subsys_
 #define PMSG_AUTO_RESUME	((struct pm_message) \
 					{ .event = PM_EVENT_AUTO_RESUME, })
 
+#define PMSG_IS_AUTO(msg)	(((msg).event & PM_EVENT_AUTO) != 0)
+
 /**
  * Device run-time power management status.
  *
Index: usb-3.1/Documentation/usb/power-management.txt
===================================================================
--- usb-3.1.orig/Documentation/usb/power-management.txt
+++ usb-3.1/Documentation/usb/power-management.txt
@@ -439,10 +439,10 @@ cause autosuspends to fail with -EBUSY i
 device.
 
 External suspend calls should never be allowed to fail in this way,
-only autosuspend calls.  The driver can tell them apart by checking
-the PM_EVENT_AUTO bit in the message.event argument to the suspend
-method; this bit will be set for internal PM events (autosuspend) and
-clear for external PM events.
+only autosuspend calls.  The driver can tell them apart by applying
+the PMSG_IS_AUTO() macro to the message argument to the suspend
+method; it will return True for internal PM events (autosuspend) and
+False for external PM events.
 
 
 	Mutual exclusion
Index: usb-3.1/drivers/net/usb/usbnet.c
===================================================================
--- usb-3.1.orig/drivers/net/usb/usbnet.c
+++ usb-3.1/drivers/net/usb/usbnet.c
@@ -1470,7 +1470,7 @@ int usbnet_suspend (struct usb_interface
 	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 {
Index: usb-3.1/drivers/net/wimax/i2400m/usb.c
===================================================================
--- usb-3.1.orig/drivers/net/wimax/i2400m/usb.c
+++ usb-3.1/drivers/net/wimax/i2400m/usb.c
@@ -599,7 +599,7 @@ void i2400mu_disconnect(struct usb_inter
  *
  *    As well, the device might refuse going to sleep for whichever
  *    reason. In this case we just fail. For system suspend/hibernate,
- *    we *can't* fail. We check PM_EVENT_AUTO to see if the
+ *    we *can't* fail. We check PMSG_IS_AUTO to see if the
  *    suspend call comes from the USB stack or from the system and act
  *    in consequence.
  *
@@ -615,7 +615,7 @@ int i2400mu_suspend(struct usb_interface
 	struct i2400m *i2400m = &i2400mu->i2400m;
 
 #ifdef CONFIG_PM
-	if (pm_msg.event & PM_EVENT_AUTO)
+	if (PMSG_IS_AUTO(pm_msg))
 		is_autosuspend = 1;
 #endif
 
Index: usb-3.1/sound/usb/card.c
===================================================================
--- usb-3.1.orig/sound/usb/card.c
+++ usb-3.1/sound/usb/card.c
@@ -628,7 +628,7 @@ static int usb_audio_suspend(struct usb_
 	if (chip == (void *)-1L)
 		return 0;
 
-	if (!(message.event & PM_EVENT_AUTO)) {
+	if (!PMSG_IS_AUTO(message)) {
 		snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
 		if (!chip->num_suspended_intf++) {
 			list_for_each(p, &chip->pcm_list) {
Index: usb-3.1/drivers/bluetooth/btusb.c
===================================================================
--- usb-3.1.orig/drivers/bluetooth/btusb.c
+++ usb-3.1/drivers/bluetooth/btusb.c
@@ -1103,7 +1103,7 @@ static int btusb_suspend(struct usb_inte
 		return 0;
 
 	spin_lock_irq(&data->txlock);
-	if (!((message.event & PM_EVENT_AUTO) && data->tx_in_flight)) {
+	if (!(PMSG_IS_AUTO(message) && data->tx_in_flight)) {
 		set_bit(BTUSB_SUSPENDING, &data->flags);
 		spin_unlock_irq(&data->txlock);
 	} else {
Index: usb-3.1/drivers/hid/hid-picolcd.c
===================================================================
--- usb-3.1.orig/drivers/hid/hid-picolcd.c
+++ usb-3.1/drivers/hid/hid-picolcd.c
@@ -2409,7 +2409,7 @@ static int picolcd_raw_event(struct hid_
 #ifdef CONFIG_PM
 static int picolcd_suspend(struct hid_device *hdev, pm_message_t message)
 {
-	if (message.event & PM_EVENT_AUTO)
+	if (PMSG_IS_AUTO(message))
 		return 0;
 
 	picolcd_suspend_backlight(hid_get_drvdata(hdev));
Index: usb-3.1/drivers/hid/usbhid/hid-core.c
===================================================================
--- usb-3.1.orig/drivers/hid/usbhid/hid-core.c
+++ usb-3.1/drivers/hid/usbhid/hid-core.c
@@ -1332,7 +1332,7 @@ static int hid_suspend(struct usb_interf
 	struct usbhid_device *usbhid = hid->driver_data;
 	int status;
 
-	if (message.event & PM_EVENT_AUTO) {
+	if (PMSG_IS_AUTO(message)) {
 		spin_lock_irq(&usbhid->lock);	/* Sync with error handler */
 		if (!test_bit(HID_RESET_PENDING, &usbhid->iofl)
 		    && !test_bit(HID_CLEAR_HALT, &usbhid->iofl)
@@ -1367,7 +1367,7 @@ static int hid_suspend(struct usb_interf
 			return -EIO;
 	}
 
-	if (!ignoreled && (message.event & PM_EVENT_AUTO)) {
+	if (!ignoreled && PMSG_IS_AUTO(message)) {
 		spin_lock_irq(&usbhid->lock);
 		if (test_bit(HID_LED_ON, &usbhid->iofl)) {
 			spin_unlock_irq(&usbhid->lock);
@@ -1380,8 +1380,7 @@ static int hid_suspend(struct usb_interf
 	hid_cancel_delayed_stuff(usbhid);
 	hid_cease_io(usbhid);
 
-	if ((message.event & PM_EVENT_AUTO) &&
-			test_bit(HID_KEYS_PRESSED, &usbhid->iofl)) {
+	if (PMSG_IS_AUTO(message) && test_bit(HID_KEYS_PRESSED, &usbhid->iofl)) {
 		/* lost race against keypresses */
 		status = hid_start_in(hid);
 		if (status < 0)
Index: usb-3.1/drivers/usb/class/cdc-acm.c
===================================================================
--- usb-3.1.orig/drivers/usb/class/cdc-acm.c
+++ usb-3.1/drivers/usb/class/cdc-acm.c
@@ -1305,7 +1305,7 @@ static int acm_suspend(struct usb_interf
 	struct acm *acm = usb_get_intfdata(intf);
 	int cnt;
 
-	if (message.event & PM_EVENT_AUTO) {
+	if (PMSG_IS_AUTO(message)) {
 		int b;
 
 		spin_lock_irq(&acm->write_lock);
Index: usb-3.1/drivers/usb/class/cdc-wdm.c
===================================================================
--- usb-3.1.orig/drivers/usb/class/cdc-wdm.c
+++ usb-3.1/drivers/usb/class/cdc-wdm.c
@@ -798,11 +798,11 @@ static int wdm_suspend(struct usb_interf
 	dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor);
 
 	/* if this is an autosuspend the caller does the locking */
-	if (!(message.event & PM_EVENT_AUTO))
+	if (!PMSG_IS_AUTO(message))
 		mutex_lock(&desc->lock);
 	spin_lock_irq(&desc->iuspin);
 
-	if ((message.event & PM_EVENT_AUTO) &&
+	if (PMSG_IS_AUTO(message) &&
 			(test_bit(WDM_IN_USE, &desc->flags)
 			|| test_bit(WDM_RESPONDING, &desc->flags))) {
 		spin_unlock_irq(&desc->iuspin);
@@ -815,7 +815,7 @@ static int wdm_suspend(struct usb_interf
 		kill_urbs(desc);
 		cancel_work_sync(&desc->rxwork);
 	}
-	if (!(message.event & PM_EVENT_AUTO))
+	if (!PMSG_IS_AUTO(message))
 		mutex_unlock(&desc->lock);
 
 	return rv;
Index: usb-3.1/drivers/usb/core/driver.c
===================================================================
--- usb-3.1.orig/drivers/usb/core/driver.c
+++ usb-3.1/drivers/usb/core/driver.c
@@ -1046,8 +1046,7 @@ static int usb_resume_device(struct usb_
 	/* Non-root devices on a full/low-speed bus must wait for their
 	 * companion high-speed root hub, in case a handoff is needed.
 	 */
-	if (!(msg.event & PM_EVENT_AUTO) && udev->parent &&
-			udev->bus->hs_companion)
+	if (!PMSG_IS_AUTO(msg) && udev->parent && udev->bus->hs_companion)
 		device_pm_wait_for_dev(&udev->dev,
 				&udev->bus->hs_companion->root_hub->dev);
 
@@ -1075,7 +1074,7 @@ static int usb_suspend_interface(struct 
 
 	if (driver->suspend) {
 		status = driver->suspend(intf, msg);
-		if (status && !(msg.event & PM_EVENT_AUTO))
+		if (status && !PMSG_IS_AUTO(msg))
 			dev_err(&intf->dev, "%s error %d\n",
 					"suspend", status);
 	} else {
@@ -1189,7 +1188,7 @@ static int usb_suspend_both(struct usb_d
 			status = usb_suspend_interface(udev, intf, msg);
 
 			/* Ignore errors during system sleep transitions */
-			if (!(msg.event & PM_EVENT_AUTO))
+			if (!PMSG_IS_AUTO(msg))
 				status = 0;
 			if (status != 0)
 				break;
@@ -1199,7 +1198,7 @@ static int usb_suspend_both(struct usb_d
 		status = usb_suspend_device(udev, msg);
 
 		/* Again, ignore errors during system sleep transitions */
-		if (!(msg.event & PM_EVENT_AUTO))
+		if (!PMSG_IS_AUTO(msg))
 			status = 0;
 	}
 
Index: usb-3.1/drivers/usb/core/hcd.c
===================================================================
--- usb-3.1.orig/drivers/usb/core/hcd.c
+++ usb-3.1/drivers/usb/core/hcd.c
@@ -1960,7 +1960,7 @@ int hcd_bus_suspend(struct usb_device *r
 	int		old_state = hcd->state;
 
 	dev_dbg(&rhdev->dev, "bus %s%s\n",
-			(msg.event & PM_EVENT_AUTO ? "auto-" : ""), "suspend");
+			(PMSG_IS_AUTO(msg) ? "auto-" : ""), "suspend");
 	if (HCD_DEAD(hcd)) {
 		dev_dbg(&rhdev->dev, "skipped %s of dead bus\n", "suspend");
 		return 0;
@@ -1996,7 +1996,7 @@ int hcd_bus_resume(struct usb_device *rh
 	int		old_state = hcd->state;
 
 	dev_dbg(&rhdev->dev, "usb %s%s\n",
-			(msg.event & PM_EVENT_AUTO ? "auto-" : ""), "resume");
+			(PMSG_IS_AUTO(msg) ? "auto-" : ""), "resume");
 	if (HCD_DEAD(hcd)) {
 		dev_dbg(&rhdev->dev, "skipped %s of dead bus\n", "resume");
 		return 0;
Index: usb-3.1/drivers/usb/core/hub.c
===================================================================
--- usb-3.1.orig/drivers/usb/core/hub.c
+++ usb-3.1/drivers/usb/core/hub.c
@@ -2342,7 +2342,7 @@ int usb_port_suspend(struct usb_device *
 			dev_dbg(&udev->dev, "won't remote wakeup, status %d\n",
 					status);
 			/* bail if autosuspend is requested */
-			if (msg.event & PM_EVENT_AUTO)
+			if (PMSG_IS_AUTO(msg))
 				return status;
 		}
 	}
@@ -2367,12 +2367,12 @@ int usb_port_suspend(struct usb_device *
 				USB_CTRL_SET_TIMEOUT);
 
 		/* System sleep transitions should never fail */
-		if (!(msg.event & PM_EVENT_AUTO))
+		if (!PMSG_IS_AUTO(msg))
 			status = 0;
 	} else {
 		/* device has up to 10 msec to fully suspend */
 		dev_dbg(&udev->dev, "usb %ssuspend\n",
-				(msg.event & PM_EVENT_AUTO ? "auto-" : ""));
+				(PMSG_IS_AUTO(msg) ? "auto-" : ""));
 		usb_set_device_state(udev, USB_STATE_SUSPENDED);
 		msleep(10);
 	}
@@ -2523,7 +2523,7 @@ int usb_port_resume(struct usb_device *u
 	} else {
 		/* drive resume for at least 20 msec */
 		dev_dbg(&udev->dev, "usb %sresume\n",
-				(msg.event & PM_EVENT_AUTO ? "auto-" : ""));
+				(PMSG_IS_AUTO(msg) ? "auto-" : ""));
 		msleep(25);
 
 		/* Virtual root hubs can trigger on GET_PORT_STATUS to
@@ -2625,7 +2625,7 @@ static int hub_suspend(struct usb_interf
 		udev = hdev->children [port1-1];
 		if (udev && udev->can_submit) {
 			dev_warn(&intf->dev, "port %d nyet suspended\n", port1);
-			if (msg.event & PM_EVENT_AUTO)
+			if (PMSG_IS_AUTO(msg))
 				return -EBUSY;
 		}
 	}
Index: usb-3.1/drivers/usb/serial/sierra.c
===================================================================
--- usb-3.1.orig/drivers/usb/serial/sierra.c
+++ usb-3.1/drivers/usb/serial/sierra.c
@@ -1009,7 +1009,7 @@ static int sierra_suspend(struct usb_ser
 	struct sierra_intf_private *intfdata;
 	int b;
 
-	if (message.event & PM_EVENT_AUTO) {
+	if (PMSG_IS_AUTO(message)) {
 		intfdata = serial->private;
 		spin_lock_irq(&intfdata->susp_lock);
 		b = intfdata->in_flight;
Index: usb-3.1/drivers/usb/serial/usb_wwan.c
===================================================================
--- usb-3.1.orig/drivers/usb/serial/usb_wwan.c
+++ usb-3.1/drivers/usb/serial/usb_wwan.c
@@ -651,7 +651,7 @@ int usb_wwan_suspend(struct usb_serial *
 
 	dbg("%s entered", __func__);
 
-	if (message.event & PM_EVENT_AUTO) {
+	if (PMSG_IS_AUTO(message)) {
 		spin_lock_irq(&intfdata->susp_lock);
 		b = intfdata->in_flight;
 		spin_unlock_irq(&intfdata->susp_lock);

^ permalink raw reply

* [PATCH v6 4/4] PM / DEVFREQ: add sysfs interface
From: MyungJoo Ham @ 2011-08-18 10:27 UTC (permalink / raw)
  To: linux-pm; +Cc: Len Brown, Greg Kroah-Hartman, Kyungmin Park, Thomas Gleixner
In-Reply-To: <1313663262-15308-1-git-send-email-myungjoo.ham@samsung.com>

Device specific sysfs interface /sys/devices/.../power/devfreq_*
- governor	R: name of governor
- cur_freq	R: current frequency
- max_freq	R: maximum operable frequency
- min_freq	R: minimum operable frequency
- set_freq	R: read user specified frequency (0 if not specified yet)
		W: set user specified frequency
- polling_interval	R: polling interval in ms given with devfreq profile

Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>

--
Changed from v5
- updated devferq_update usage.

Changed from v4
- removed system-wide sysfs interface
- removed tickling sysfs interface
- added set_freq for userspace governor (and any other governors that
  require user input)

Changed from v3
- corrected sysfs API usage
- corrected error messages
- moved sysfs entry location
- added sysfs entries

Changed from v2
- add ABI entries for devfreq sysfs interface
---
 Documentation/ABI/testing/sysfs-devices-power |   45 +++++++
 drivers/devfreq/devfreq.c                     |  152 +++++++++++++++++++++++++
 2 files changed, 197 insertions(+), 0 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-devices-power b/Documentation/ABI/testing/sysfs-devices-power
index 8ffbc25..6e77604 100644
--- a/Documentation/ABI/testing/sysfs-devices-power
+++ b/Documentation/ABI/testing/sysfs-devices-power
@@ -165,3 +165,48 @@ Description:
 
 		Not all drivers support this attribute.  If it isn't supported,
 		attempts to read or write it will yield I/O errors.
+
+What:		/sys/devices/.../power/devfreq_governor
+Date:		July 2011
+Contact:	MyungJoo Ham <myungjoo.ham@samsung.com>
+Description:
+		The /sys/devices/.../power/devfreq_governor shows the name
+		of the governor used by the corresponding device.
+
+What:		/sys/devices/.../power/devfreq_cur_freq
+Date:		July 2011
+Contact:	MyungJoo Ham <myungjoo.ham@samsung.com>
+Description:
+		The /sys/devices/.../power/devfreq_cur_freq shows the current
+		frequency of the corresponding device.
+
+What:		/sys/devices/.../power/devfreq_max_freq
+Date:		July 2011
+Contact:	MyungJoo Ham <myungjoo.ham@samsung.com>
+Description:
+		The /sys/devices/.../power/devfreq_cur_freq shows the
+		maximum operable frequency of the corresponding device.
+
+What:		/sys/devices/.../power/devfreq_min_freq
+Date:		July 2011
+Contact:	MyungJoo Ham <myungjoo.ham@samsung.com>
+Description:
+		The /sys/devices/.../power/devfreq_cur_freq shows the
+		minimum operable frequency of the corresponding device.
+
+What:		/sys/devices/.../power/devfreq_set_freq
+Date:		August 2011
+Contact:	MyungJoo Ham <myungjoo.ham@samsung.com>
+Description:
+		The /sys/devices/.../power/devfreq_set_freq sets and shows
+		the user specified desired frequency of the device. The
+		governor may and may not use the value. With the basic
+		governors given with devfreq.c, userspace governor is
+		using the value.
+
+What:		/sys/devices/.../power/devfreq_polling_interval
+Date:		July 2011
+Contact:	MyungJoo Ham <myungjoo.ham@samsung.com>
+Description:
+		The /sys/devices/.../power/devfreq_polling_interval shows the
+		requested polling interval of the corresponding device.
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
index 2036f2c..13b422f 100644
--- a/drivers/devfreq/devfreq.c
+++ b/drivers/devfreq/devfreq.c
@@ -37,6 +37,8 @@ static struct delayed_work devfreq_work;
 static LIST_HEAD(devfreq_list);
 static DEFINE_MUTEX(devfreq_list_lock);
 
+static struct attribute_group dev_attr_group;
+
 /**
  * find_device_devfreq() - find devfreq struct using device pointer
  * @dev:	device pointer used to lookup device devfreq.
@@ -154,6 +156,8 @@ static void devfreq_monitor(struct work_struct *work)
 				dev_err(devfreq->dev, "Due to devfreq_do error(%d), devfreq(%s) is removed from the device\n",
 					error, devfreq->governor->name);
 
+				sysfs_unmerge_group(&devfreq->dev->kobj,
+						    &dev_attr_group);
 				list_del(&devfreq->node);
 				kfree(devfreq);
 
@@ -244,6 +248,8 @@ int devfreq_add_device(struct device *dev, struct devfreq_dev_profile *profile,
 		queue_delayed_work(devfreq_wq, &devfreq_work,
 				   devfreq->next_polling);
 	}
+
+	sysfs_merge_group(&dev->kobj, &dev_attr_group);
 out:
 	mutex_unlock(&devfreq_list_lock);
 
@@ -276,6 +282,8 @@ int devfreq_remove_device(struct device *dev)
 		goto out;
 	}
 
+	sysfs_unmerge_group(&dev->kobj, &dev_attr_group);
+
 	list_del(&devfreq->node);
 	srcu_notifier_chain_unregister(nh, &devfreq->nb);
 	kfree(devfreq);
@@ -284,6 +292,150 @@ out:
 	return 0;
 }
 
+static ssize_t show_governor(struct device *dev,
+			     struct device_attribute *attr, char *buf)
+{
+	struct devfreq *df = find_device_devfreq(dev);
+
+	if (IS_ERR(df))
+		return PTR_ERR(df);
+	if (!df->governor)
+		return -EINVAL;
+
+	return sprintf(buf, "%s\n", df->governor->name);
+}
+
+static ssize_t show_freq(struct device *dev,
+			 struct device_attribute *attr, char *buf)
+{
+	struct devfreq *df = find_device_devfreq(dev);
+
+	if (IS_ERR(df))
+		return PTR_ERR(df);
+
+	return sprintf(buf, "%lu\n", df->previous_freq);
+}
+
+static ssize_t show_max_freq(struct device *dev,
+			     struct device_attribute *attr, char *buf)
+{
+	struct devfreq *df = find_device_devfreq(dev);
+	unsigned long freq = ULONG_MAX;
+	struct opp *opp;
+
+	if (IS_ERR(df))
+		return PTR_ERR(df);
+	if (!df->dev)
+		return -EINVAL;
+
+	opp = opp_find_freq_floor(df->dev, &freq);
+	if (IS_ERR(opp))
+		return PTR_ERR(opp);
+
+	return sprintf(buf, "%lu\n", freq);
+}
+
+static ssize_t show_min_freq(struct device *dev,
+			     struct device_attribute *attr, char *buf)
+{
+	struct devfreq *df = find_device_devfreq(dev);
+	unsigned long freq = 0;
+	struct opp *opp;
+
+	if (IS_ERR(df))
+		return PTR_ERR(df);
+	if (!df->dev)
+		return -EINVAL;
+
+	opp = opp_find_freq_ceil(df->dev, &freq);
+	if (IS_ERR(opp))
+		return PTR_ERR(opp);
+
+	return sprintf(buf, "%lu\n", freq);
+}
+
+static ssize_t show_polling_interval(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct devfreq *df = find_device_devfreq(dev);
+
+	if (IS_ERR(df))
+		return PTR_ERR(df);
+	if (!df->profile)
+		return -EINVAL;
+
+	return sprintf(buf, "%d\n", df->profile->polling_ms);
+}
+
+static ssize_t set_user_frequency(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t count)
+{
+	struct devfreq *devfreq;
+	unsigned long wanted;
+	int err = 0;
+
+	sscanf(buf, "%lu", &wanted);
+
+	mutex_lock(&devfreq_list_lock);
+	devfreq = find_device_devfreq(dev);
+
+	if (IS_ERR(devfreq)) {
+		err = PTR_ERR(devfreq);
+		goto out;
+	}
+
+	devfreq->user_set_freq = wanted;
+	err = count;
+out:
+	mutex_unlock(&devfreq_list_lock);
+	if (err >= 0)
+		devfreq_update(&devfreq->nb, 0, NULL);
+	return err;
+}
+
+static ssize_t show_user_frequency(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	struct devfreq *devfreq;
+	int err = 0;
+
+	mutex_lock(&devfreq_list_lock);
+	devfreq = find_device_devfreq(dev);
+
+	if (IS_ERR(devfreq)) {
+		err = PTR_ERR(devfreq);
+		goto out;
+	}
+
+	err = sprintf(buf, "%lu\n", devfreq->user_set_freq);
+out:
+	mutex_unlock(&devfreq_list_lock);
+	return err;
+
+}
+
+static DEVICE_ATTR(devfreq_governor, 0444, show_governor, NULL);
+static DEVICE_ATTR(devfreq_cur_freq, 0444, show_freq, NULL);
+static DEVICE_ATTR(devfreq_max_freq, 0444, show_max_freq, NULL);
+static DEVICE_ATTR(devfreq_min_freq, 0444, show_min_freq, NULL);
+static DEVICE_ATTR(devfreq_polling_interval, 0444, show_polling_interval, NULL);
+static DEVICE_ATTR(devfreq_set_freq, 0644, show_user_frequency,
+		   set_user_frequency);
+static struct attribute *dev_entries[] = {
+	&dev_attr_devfreq_governor.attr,
+	&dev_attr_devfreq_cur_freq.attr,
+	&dev_attr_devfreq_max_freq.attr,
+	&dev_attr_devfreq_min_freq.attr,
+	&dev_attr_devfreq_polling_interval.attr,
+	&dev_attr_devfreq_set_freq.attr,
+	NULL,
+};
+static struct attribute_group dev_attr_group = {
+	.name	= power_group_name,
+	.attrs	= dev_entries,
+};
+
 /**
  * devfreq_init() - Initialize data structure for devfreq framework and
  *		  start polling registered devfreq devices.
-- 
1.7.4.1

^ permalink raw reply related

* [PATCH v6 3/4] PM / DEVFREQ: add basic governors
From: MyungJoo Ham @ 2011-08-18 10:27 UTC (permalink / raw)
  To: linux-pm; +Cc: Len Brown, Greg Kroah-Hartman, Kyungmin Park, Thomas Gleixner
In-Reply-To: <1313663262-15308-1-git-send-email-myungjoo.ham@samsung.com>

Four CPUFREQ-like governors are provided as examples.

powersave: use the lowest frequency possible. The user (device) should
set the polling_ms as 0 because polling is useless for this governor.

performance: use the highest freqeuncy possible. The user (device)
should set the polling_ms as 0 because polling is useless for this
governor.

userspace: use the user specified frequency stored at
devfreq.user_set_freq. With sysfs support in the following patch, a user
may set the value with the sysfs interface.

simple_ondemand: simplified version of CPUFREQ's ONDEMAND governor.

When a user updates OPP entries (enable/disable/add), OPP framework
automatically notifies DEVFREQ to update operating frequency
accordingly. Thus, DEVFREQ users (device drivers) do not need to update
DEVFREQ manually with OPP entry updates or set polling_ms for powersave
, performance, userspace, or any other "static" governors.

Note that these are given only as basic examples for governors and any
devices with DEVFREQ may implement their own governors with the drivers
and use them.

Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>

---
Changed from v5
- Seperated governor files from devfreq.c
- Allow simple ondemand to be tuned for each device
---
 drivers/devfreq/Kconfig                   |   36 ++++++++++++
 drivers/devfreq/Makefile                  |    4 +
 drivers/devfreq/governor_performance.c    |   24 ++++++++
 drivers/devfreq/governor_powersave.c      |   24 ++++++++
 drivers/devfreq/governor_simpleondemand.c |   88 +++++++++++++++++++++++++++++
 drivers/devfreq/governor_userspace.c      |   27 +++++++++
 include/linux/devfreq.h                   |   41 +++++++++++++
 7 files changed, 244 insertions(+), 0 deletions(-)
 create mode 100644 drivers/devfreq/governor_performance.c
 create mode 100644 drivers/devfreq/governor_powersave.c
 create mode 100644 drivers/devfreq/governor_simpleondemand.c
 create mode 100644 drivers/devfreq/governor_userspace.c

diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig
index 1fb42de..643b055 100644
--- a/drivers/devfreq/Kconfig
+++ b/drivers/devfreq/Kconfig
@@ -34,6 +34,42 @@ menuconfig PM_DEVFREQ
 
 if PM_DEVFREQ
 
+comment "DEVFREQ Governors"
+
+config DEVFREQ_GOV_SIMPLE_ONDEMAND
+	bool "Simple Ondemand"
+	help
+	  Chooses frequency based on the recent load on the device. Works
+	  similar as ONDEMAND governor of CPUFREQ does. A device with
+	  Simple-Ondemand should be able to provide busy/total counter
+	  values that imply the usage rate. A device may provide tuned
+	  values to the governor with data field at devfreq_add_device().
+
+config DEVFREQ_GOV_PERFORMANCE
+	bool "Performance"
+	help
+	  Sets the frequency at the maximum available frequency.
+	  This governor always returns UINT_MAX as frequency so that
+	  the DEVFREQ framework returns the highest frequency available
+	  at any time.
+
+config DEVFREQ_GOV_POWERSAVE
+	bool "Powersave"
+	help
+	  Sets the frequency at the minimum available frequency.
+	  This governor always returns 0 as frequency so that
+	  the DEVFREQ framework returns the lowest frequency available
+	  at any time.
+
+config DEVFREQ_GOV_USERSPACE
+	bool "Userspace"
+	help
+	  Sets the frequency at the user specified one.
+	  This governor returns the user configured frequency if there
+	  has been an input to /sys/devices/.../power/devfreq_set_freq.
+	  Otherwise, the governor does not change the frequnecy
+	  given at the initialization.
+
 comment "DEVFREQ Drivers"
 
 endif # PM_DEVFREQ
diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile
index 168934a..4564a89 100644
--- a/drivers/devfreq/Makefile
+++ b/drivers/devfreq/Makefile
@@ -1 +1,5 @@
 obj-$(CONFIG_PM_DEVFREQ)	+= devfreq.o
+obj-$(CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND)	+= governor_simpleondemand.o
+obj-$(CONFIG_DEVFREQ_GOV_PERFORMANCE)	+= governor_performance.o
+obj-$(CONFIG_DEVFREQ_GOV_POWERSAVE)	+= governor_powersave.o
+obj-$(CONFIG_DEVFREQ_GOV_USERSPACE)	+= governor_userspace.o
diff --git a/drivers/devfreq/governor_performance.c b/drivers/devfreq/governor_performance.c
new file mode 100644
index 0000000..c47eff8
--- /dev/null
+++ b/drivers/devfreq/governor_performance.c
@@ -0,0 +1,24 @@
+/*
+ *  linux/drivers/devfreq/governor_performance.c
+ *
+ *  Copyright (C) 2011 Samsung Electronics
+ *	MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/devfreq.h>
+
+static int devfreq_performance_func(struct devfreq *df,
+				    unsigned long *freq)
+{
+	*freq = UINT_MAX; /* devfreq_do will run "floor" */
+	return 0;
+}
+
+struct devfreq_governor devfreq_performance = {
+	.name = "performance",
+	.get_target_freq = devfreq_performance_func,
+};
diff --git a/drivers/devfreq/governor_powersave.c b/drivers/devfreq/governor_powersave.c
new file mode 100644
index 0000000..4f128d8
--- /dev/null
+++ b/drivers/devfreq/governor_powersave.c
@@ -0,0 +1,24 @@
+/*
+ *  linux/drivers/devfreq/governor_powersave.c
+ *
+ *  Copyright (C) 2011 Samsung Electronics
+ *	MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/devfreq.h>
+
+static int devfreq_powersave_func(struct devfreq *df,
+				  unsigned long *freq)
+{
+	*freq = 0; /* devfreq_do will run "ceiling" to 0 */
+	return 0;
+}
+
+struct devfreq_governor devfreq_powersave = {
+	.name = "powersave",
+	.get_target_freq = devfreq_powersave_func,
+};
diff --git a/drivers/devfreq/governor_simpleondemand.c b/drivers/devfreq/governor_simpleondemand.c
new file mode 100644
index 0000000..18fe8be
--- /dev/null
+++ b/drivers/devfreq/governor_simpleondemand.c
@@ -0,0 +1,88 @@
+/*
+ *  linux/drivers/devfreq/governor_simpleondemand.c
+ *
+ *  Copyright (C) 2011 Samsung Electronics
+ *	MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/errno.h>
+#include <linux/devfreq.h>
+#include <linux/math64.h>
+
+/* Default constants for DevFreq-Simple-Ondemand (DFSO) */
+#define DFSO_UPTHRESHOLD	(90)
+#define DFSO_DOWNDIFFERENCTIAL	(5)
+static int devfreq_simple_ondemand_func(struct devfreq *df,
+					unsigned long *freq)
+{
+	struct devfreq_dev_status stat;
+	int err = df->profile->get_dev_status(df->dev, &stat);
+	unsigned long long a, b;
+	unsigned int dfso_upthreshold = DFSO_UPTHRESHOLD;
+	unsigned int dfso_downdifferential = DFSO_DOWNDIFFERENCTIAL;
+	struct devfreq_simple_ondemand_data *data = df->data;
+
+	if (err)
+		return err;
+
+	if (data) {
+		if (data->upthreshold)
+			dfso_upthreshold = data->upthreshold;
+		if (data->downdifferential)
+			dfso_downdifferential = data->downdifferential;
+	}
+	if (dfso_upthreshold > 100 ||
+	    dfso_upthreshold < dfso_downdifferential)
+		return -EINVAL;
+
+	/* Assume MAX if it is going to be divided by zero */
+	if (stat.total_time == 0) {
+		*freq = UINT_MAX;
+		return 0;
+	}
+
+	/* Prevent overflow */
+	if (stat.busy_time >= (1 << 24) || stat.total_time >= (1 << 24)) {
+		stat.busy_time >>= 7;
+		stat.total_time >>= 7;
+	}
+
+	/* Set MAX if it's busy enough */
+	if (stat.busy_time * 100 >
+	    stat.total_time * dfso_upthreshold) {
+		*freq = UINT_MAX;
+		return 0;
+	}
+
+	/* Set MAX if we do not know the initial frequency */
+	if (stat.current_frequency == 0) {
+		*freq = UINT_MAX;
+		return 0;
+	}
+
+	/* Keep the current frequency */
+	if (stat.busy_time * 100 >
+	    stat.total_time * (dfso_upthreshold - dfso_downdifferential)) {
+		*freq = stat.current_frequency;
+		return 0;
+	}
+
+	/* Set the desired frequency based on the load */
+	a = stat.busy_time;
+	a *= stat.current_frequency;
+	b = div_u64(a, stat.total_time);
+	b *= 100;
+	b = div_u64(b, (dfso_upthreshold - dfso_downdifferential / 2));
+	*freq = (unsigned long) b;
+
+	return 0;
+}
+
+struct devfreq_governor devfreq_simple_ondemand = {
+	.name = "simple_ondemand",
+	.get_target_freq = devfreq_simple_ondemand_func,
+};
diff --git a/drivers/devfreq/governor_userspace.c b/drivers/devfreq/governor_userspace.c
new file mode 100644
index 0000000..6af8b6d
--- /dev/null
+++ b/drivers/devfreq/governor_userspace.c
@@ -0,0 +1,27 @@
+/*
+ *  linux/drivers/devfreq/governor_simpleondemand.c
+ *
+ *  Copyright (C) 2011 Samsung Electronics
+ *	MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/devfreq.h>
+
+static int devfreq_userspace_func(struct devfreq *df, unsigned long *freq)
+{
+	if (df->user_set_freq == 0)
+		*freq = df->previous_freq; /* No user freq specified yet */
+	else
+		*freq = df->user_set_freq;
+
+	return 0;
+}
+
+struct devfreq_governor devfreq_userspace = {
+	.name = "userspace",
+	.get_target_freq = devfreq_userspace_func,
+};
diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h
index 13ddf49..d088ad3 100644
--- a/include/linux/devfreq.h
+++ b/include/linux/devfreq.h
@@ -13,6 +13,7 @@
 #ifndef __LINUX_DEVFREQ_H__
 #define __LINUX_DEVFREQ_H__
 
+#include <linux/opp.h>
 #include <linux/notifier.h>
 
 #define DEVFREQ_NAME_LEN 16
@@ -62,6 +63,8 @@ struct devfreq_governor {
  *			"devfreq_monitor" executions to reevaluate
  *			frequency/voltage of the device. Set by
  *			profile's polling_ms interval.
+ * @user_set_freq	User specified adequete frequency value (thru sysfs
+ *		interface). Governors may and may not use this value.
  * @data	Private data of the governor. The devfreq framework does not
  *		touch this.
  *
@@ -78,6 +81,7 @@ struct devfreq {
 	unsigned long previous_freq;
 	unsigned int next_polling;
 
+	unsigned long user_set_freq; /* governors may ignore this. */
 	void *data; /* private data for governors */
 };
 
@@ -87,6 +91,37 @@ extern int devfreq_add_device(struct device *dev,
 			   struct devfreq_governor *governor,
 			   void *data);
 extern int devfreq_remove_device(struct device *dev);
+
+#ifdef CONFIG_DEVFREQ_GOV_POWERSAVE
+extern struct devfreq_governor devfreq_powersave;
+#endif
+#ifdef CONFIG_DEVFREQ_GOV_PERFORMANCE
+extern struct devfreq_governor devfreq_performance;
+#endif
+#ifdef CONFIG_DEVFREQ_GOV_USERSPACE
+extern struct devfreq_governor devfreq_userspace;
+#endif
+#ifdef CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND
+extern struct devfreq_governor devfreq_simple_ondemand;
+/**
+ * struct devfreq_simple_ondemand_data - void *data fed to struct devfreq
+ *	and devfreq_add_device
+ * @ upthreshold	If the load is over this value, the frequency jumps.
+ *			Specify 0 to use the default. Valid value = 0 to 100.
+ * @ downdifferential	If the load is under upthreshold - downdifferential,
+ *			the governor may consider slowing the frequency down.
+ *			Specify 0 to use the default. Valid value = 0 to 100.
+ *			downdifferential < upthreshold must hold.
+ *
+ * If the fed devfreq_simple_ondemand_data pointer is NULL to the governor,
+ * the governor uses the default values.
+ */
+struct devfreq_simple_ondemand_data {
+	unsigned int upthreshold;
+	unsigned int downdifferential;
+};
+#endif
+
 #else /* !CONFIG_PM_DEVFREQ */
 static int devfreq_add_device(struct device *dev,
 			   struct devfreq_dev_profile *profile,
@@ -100,6 +135,12 @@ static int devfreq_remove_device(struct device *dev)
 {
 	return 0;
 }
+
+#define devfreq_powersave	NULL
+#define devfreq_performance	NULL
+#define devfreq_userspace	NULL
+#define devfreq_simple_ondemand	NULL
+
 #endif /* CONFIG_PM_DEVFREQ */
 
 #endif /* __LINUX_DEVFREQ_H__ */
-- 
1.7.4.1

^ permalink raw reply related

* [PATCH v6 2/4] PM: Introduce DEVFREQ: generic DVFS framework with device-specific OPPs
From: MyungJoo Ham @ 2011-08-18 10:27 UTC (permalink / raw)
  To: linux-pm; +Cc: Len Brown, Greg Kroah-Hartman, Kyungmin Park, Thomas Gleixner
In-Reply-To: <1313663262-15308-1-git-send-email-myungjoo.ham@samsung.com>

With OPPs, a device may have multiple operable frequency and voltage
sets. However, there can be multiple possible operable sets and a system
will need to choose one from them. In order to reduce the power
consumption (by reducing frequency and voltage) without affecting the
performance too much, a Dynamic Voltage and Frequency Scaling (DVFS)
scheme may be used.

This patch introduces the DVFS capability to non-CPU devices with OPPs.
DVFS is a techique whereby the frequency and supplied voltage of a
device is adjusted on-the-fly. DVFS usually sets the frequency as low
as possible with given conditions (such as QoS assurance) and adjusts
voltage according to the chosen frequency in order to reduce power
consumption and heat dissipation.

The generic DVFS for devices, DEVFREQ, may appear quite similar with
/drivers/cpufreq.  However, CPUFREQ does not allow to have multiple
devices registered and is not suitable to have multiple heterogenous
devices with different (but simple) governors.

Normally, DVFS mechanism controls frequency based on the demand for
the device, and then, chooses voltage based on the chosen frequency.
DEVFREQ also controls the frequency based on the governor's frequency
recommendation and let OPP pick up the pair of frequency and voltage
based on the recommended frequency. Then, the chosen OPP is passed to
device driver's "target" callback.

When PM QoS is going to be used with the DEVFREQ device, the device
driver should enable OPPs that are appropriate with the current PM QoS
requests. In order to do so, the device driver may call opp_enable and
opp_disable at the notifier callback of PM QoS so that PM QoS's
update_target() call enables the appropriate OPPs. Note that at least
one of OPPs should be enabled at any time; be careful when there is a
transition.

Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>

---
The test code with board support for Exynos4-NURI is at
http://git.infradead.org/users/kmpark/linux-2.6-samsung/shortlog/refs/heads/devfreq

---
Thank you for your valuable comments, Rafael, Greg, Pavel, Colin, Mike,
and Kevin.

Changes from v5
- Uses OPP availability change notifier
- Removed devfreq_interval. Uses one jiffy instead. DEVFREQ adjusts
  polling interval based on the interval requirement of DEVFREQ
  devices.
- Moved devfreq to /drivers/devfreq to accomodate devfreq-related files
  including governors and devfreq drivers.
- Coding style revised.
- Updated devfreq_add_device interface to get tunable values.

Changed from v4
- Removed tickle, which is a duplicated feature; PM QoS can do the same.
- Allow to extend polling interval if devices have longer polling intervals.
- Relocated private data of governors.
- Removed system-wide sysfs

Changed from v3
- In kerneldoc comments, DEVFREQ has ben replaced by devfreq
- Revised removing devfreq entries with error mechanism
- Added and revised comments
- Removed unnecessary codes
- Allow to give a name to a governor
- Bugfix: a tickle call may cancel an older tickle call that is still in
  effect.

Changed from v2
- Code style revised and cleaned up.
- Remove DEVFREQ entries that incur errors except for EAGAIN
- Bug fixed: tickle for devices without polling governors

Changes from v1(RFC)
- Rename: DVFS --> DEVFREQ
- Revised governor design
	. Governor receives the whole struct devfreq
	. Governor should gather usage information (thru get_dev_status) itself
- Periodic monitoring runs only when needed.
- DEVFREQ no more deals with voltage information directly
- Removed some printks.
- Some cosmetics update
- Use freezable_wq.
---
 drivers/Kconfig           |    2 +
 drivers/Makefile          |    2 +
 drivers/devfreq/Kconfig   |   39 ++++++
 drivers/devfreq/Makefile  |    1 +
 drivers/devfreq/devfreq.c |  302 +++++++++++++++++++++++++++++++++++++++++++++
 include/linux/devfreq.h   |  105 ++++++++++++++++
 6 files changed, 451 insertions(+), 0 deletions(-)
 create mode 100644 drivers/devfreq/Kconfig
 create mode 100644 drivers/devfreq/Makefile
 create mode 100644 drivers/devfreq/devfreq.c
 create mode 100644 include/linux/devfreq.h

diff --git a/drivers/Kconfig b/drivers/Kconfig
index 95b9e7e..a1efd75 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -130,4 +130,6 @@ source "drivers/iommu/Kconfig"
 
 source "drivers/virt/Kconfig"
 
+source "drivers/devfreq/Kconfig"
+
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 7fa433a..97c957b 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -127,3 +127,5 @@ obj-$(CONFIG_IOMMU_SUPPORT)	+= iommu/
 
 # Virtualization drivers
 obj-$(CONFIG_VIRT_DRIVERS)	+= virt/
+
+obj-$(CONFIG_PM_DEVFREQ)	+= devfreq/
diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig
new file mode 100644
index 0000000..1fb42de
--- /dev/null
+++ b/drivers/devfreq/Kconfig
@@ -0,0 +1,39 @@
+config ARCH_HAS_DEVFREQ
+	bool
+	depends on ARCH_HAS_OPP
+	help
+	  Denotes that the architecture supports DEVFREQ. If the architecture
+	  supports multiple OPP entries per device and the frequency of the
+	  devices with OPPs may be altered dynamically, the architecture
+	  supports DEVFREQ.
+
+menuconfig PM_DEVFREQ
+	bool "Generic Dynamic Voltage and Frequency Scaling (DVFS) support"
+	depends on PM_OPP && ARCH_HAS_DEVFREQ
+	help
+	  With OPP support, a device may have a list of frequencies and
+	  voltages available. DEVFREQ, a generic DVFS framework can be
+	  registered for a device with OPP support in order to let the
+	  governor provided to DEVFREQ choose an operating frequency
+	  based on the OPP's list and the policy given with DEVFREQ.
+
+	  Each device may have its own governor and policy. DEVFREQ can
+	  reevaluate the device state periodically and/or based on the
+	  OPP list changes (each frequency/voltage pair in OPP may be
+	  disabled or enabled).
+
+	  Like some CPUs with CPUFREQ, a device may have multiple clocks.
+	  However, because the clock frequencies of a single device are
+	  determined by the single device's state, an instance of DEVFREQ
+	  is attached to a single device and returns a "representative"
+	  clock frequency from the OPP of the device, which is also attached
+	  to a device by 1-to-1. The device registering DEVFREQ takes the
+	  responsiblity to "interpret" the frequency listed in OPP and
+	  to set its every clock accordingly with the "target" callback
+	  given to DEVFREQ.
+
+if PM_DEVFREQ
+
+comment "DEVFREQ Drivers"
+
+endif # PM_DEVFREQ
diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile
new file mode 100644
index 0000000..168934a
--- /dev/null
+++ b/drivers/devfreq/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_PM_DEVFREQ)	+= devfreq.o
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
new file mode 100644
index 0000000..2036f2c
--- /dev/null
+++ b/drivers/devfreq/devfreq.c
@@ -0,0 +1,302 @@
+/*
+ * devfreq: Generic Dynamic Voltage and Frequency Scaling (DVFS) Framework
+ *	    for Non-CPU Devices Based on OPP.
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ *	MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/opp.h>
+#include <linux/devfreq.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/list.h>
+#include <linux/printk.h>
+#include <linux/hrtimer.h>
+
+/*
+ * devfreq_work periodically monitors every registered device.
+ * The minimum polling interval is one jiffy. The polling interval is
+ * determined by the minimum polling period among all polling devfreq
+ * devices. The resolution of polling interval is one jiffy.
+ */
+static bool polling;
+static struct workqueue_struct *devfreq_wq;
+static struct delayed_work devfreq_work;
+
+/* The list of all device-devfreq */
+static LIST_HEAD(devfreq_list);
+static DEFINE_MUTEX(devfreq_list_lock);
+
+/**
+ * find_device_devfreq() - find devfreq struct using device pointer
+ * @dev:	device pointer used to lookup device devfreq.
+ *
+ * Search the list of device devfreqs and return the matched device's
+ * devfreq info. devfreq_list_lock should be held by the caller.
+ */
+static struct devfreq *find_device_devfreq(struct device *dev)
+{
+	struct devfreq *tmp_devfreq;
+
+	if (unlikely(IS_ERR_OR_NULL(dev))) {
+		pr_err("DEVFREQ: %s: Invalid parameters\n", __func__);
+		return ERR_PTR(-EINVAL);
+	}
+
+	list_for_each_entry(tmp_devfreq, &devfreq_list, node) {
+		if (tmp_devfreq->dev == dev)
+			return tmp_devfreq;
+	}
+
+	return ERR_PTR(-ENODEV);
+}
+
+/**
+ * devfreq_do() - Check the usage profile of a given device and configure
+ *		frequency and voltage accordingly
+ * @devfreq:	devfreq info of the given device
+ */
+static int devfreq_do(struct devfreq *devfreq)
+{
+	struct opp *opp;
+	unsigned long freq;
+	int err;
+
+	err = devfreq->governor->get_target_freq(devfreq, &freq);
+	if (err)
+		return err;
+
+	opp = opp_find_freq_ceil(devfreq->dev, &freq);
+	if (opp == ERR_PTR(-ENODEV))
+		opp = opp_find_freq_floor(devfreq->dev, &freq);
+
+	if (IS_ERR(opp))
+		return PTR_ERR(opp);
+
+	if (devfreq->previous_freq == freq)
+		return 0;
+
+	err = devfreq->profile->target(devfreq->dev, opp);
+	if (err)
+		return err;
+
+	devfreq->previous_freq = freq;
+	return 0;
+}
+
+/**
+ * devfreq_update() - Notify that the device OPP has been changed.
+ * @dev:	the device whose OPP has been changed.
+ */
+static int devfreq_update(struct notifier_block *nb, unsigned long type,
+			  void *devp)
+{
+	struct devfreq *devfreq;
+	int err = 0;
+
+	mutex_lock(&devfreq_list_lock);
+	devfreq = container_of(nb, struct devfreq, nb);
+	/* Reevaluate the proper frequency */
+	err = devfreq_do(devfreq);
+	mutex_unlock(&devfreq_list_lock);
+	return err;
+}
+
+/**
+ * devfreq_monitor() - Periodically run devfreq_do()
+ * @work: the work struct used to run devfreq_monitor periodically.
+ *
+ */
+static void devfreq_monitor(struct work_struct *work)
+{
+	static ktime_t last_polled_at;
+	struct devfreq *devfreq, *tmp;
+	int error;
+	unsigned int next_jiffies = UINT_MAX;
+	ktime_t now = ktime_get();
+	int jiffies_passed;
+
+	/* Initially last_polled_at = 0, polling every device at bootup */
+	jiffies_passed = msecs_to_jiffies(ktime_to_ms(
+				ktime_sub(now, last_polled_at)));
+	last_polled_at = now;
+
+	if (jiffies_passed == 0)
+		jiffies_passed = 1;
+	if (jiffies_passed < 0) /* "Infinite Timeout" */
+		jiffies_passed = INT_MAX;
+
+	mutex_lock(&devfreq_list_lock);
+
+	list_for_each_entry_safe(devfreq, tmp, &devfreq_list, node) {
+		if (devfreq->next_polling == 0)
+			continue;
+
+		/*
+		 * Reduce more next_polling if devfreq_wq took an extra
+		 * delay. (i.e., CPU has been idled.)
+		 */
+		if (devfreq->next_polling <= jiffies_passed) {
+			error = devfreq_do(devfreq);
+
+			/* Remove a devfreq with an error. */
+			if (error && error != -EAGAIN) {
+				dev_err(devfreq->dev, "Due to devfreq_do error(%d), devfreq(%s) is removed from the device\n",
+					error, devfreq->governor->name);
+
+				list_del(&devfreq->node);
+				kfree(devfreq);
+
+				continue;
+			}
+			devfreq->next_polling = msecs_to_jiffies(
+						devfreq->profile->polling_ms);
+
+			/* No more polling required (polling_ms changed) */
+			if (devfreq->next_polling == 0)
+				continue;
+		} else {
+			devfreq->next_polling -= jiffies_passed;
+		}
+
+		next_jiffies = (next_jiffies > devfreq->next_polling) ?
+				devfreq->next_polling : next_jiffies;
+	}
+
+	if (next_jiffies > 0 && next_jiffies < UINT_MAX) {
+		polling = true;
+		queue_delayed_work(devfreq_wq, &devfreq_work, next_jiffies);
+	} else {
+		polling = false;
+	}
+
+	mutex_unlock(&devfreq_list_lock);
+}
+
+/**
+ * devfreq_add_device() - Add devfreq feature to the device
+ * @dev:	the device to add devfreq feature.
+ * @profile:	device-specific profile to run devfreq.
+ * @governor:	the policy to choose frequency.
+ * @data:	private data for the governor. The devfreq framework does not
+ *		touch this value.
+ */
+int devfreq_add_device(struct device *dev, struct devfreq_dev_profile *profile,
+		       struct devfreq_governor *governor, void *data)
+{
+	struct devfreq *devfreq;
+	struct srcu_notifier_head *nh;
+	int err = 0;
+
+	if (!dev || !profile || !governor) {
+		dev_err(dev, "%s: Invalid parameters.\n", __func__);
+		return -EINVAL;
+	}
+
+	mutex_lock(&devfreq_list_lock);
+
+	devfreq = find_device_devfreq(dev);
+	if (!IS_ERR(devfreq)) {
+		dev_err(dev, "%s: Unable to create devfreq for the device. It already has one.\n", __func__);
+		err = -EINVAL;
+		goto out;
+	}
+
+	devfreq = kzalloc(sizeof(struct devfreq), GFP_KERNEL);
+	if (!devfreq) {
+		dev_err(dev, "%s: Unable to create devfreq for the device\n",
+			__func__);
+		err = -ENOMEM;
+		goto out;
+	}
+
+	devfreq->dev = dev;
+	devfreq->profile = profile;
+	devfreq->governor = governor;
+	devfreq->next_polling = msecs_to_jiffies(profile->polling_ms);
+	devfreq->previous_freq = profile->initial_freq;
+	devfreq->data = data;
+
+	devfreq->nb.notifier_call = devfreq_update;
+	nh = opp_get_notifier(dev);
+	if (IS_ERR(nh)) {
+		err = PTR_ERR(nh);
+		goto out;
+	}
+	err = srcu_notifier_chain_register(nh, &devfreq->nb);
+	if (err)
+		goto out;
+
+	list_add(&devfreq->node, &devfreq_list);
+
+	if (devfreq_wq && devfreq->next_polling && !polling) {
+		polling = true;
+		queue_delayed_work(devfreq_wq, &devfreq_work,
+				   devfreq->next_polling);
+	}
+out:
+	mutex_unlock(&devfreq_list_lock);
+
+	return err;
+}
+
+/**
+ * devfreq_remove_device() - Remove devfreq feature from a device.
+ * @device:	the device to remove devfreq feature.
+ */
+int devfreq_remove_device(struct device *dev)
+{
+	struct devfreq *devfreq;
+	struct srcu_notifier_head *nh;
+	int err = 0;
+
+	if (!dev)
+		return -EINVAL;
+
+	mutex_lock(&devfreq_list_lock);
+	devfreq = find_device_devfreq(dev);
+	if (IS_ERR(devfreq)) {
+		err = PTR_ERR(devfreq);
+		goto out;
+	}
+
+	nh = opp_get_notifier(dev);
+	if (IS_ERR(nh)) {
+		err = PTR_ERR(nh);
+		goto out;
+	}
+
+	list_del(&devfreq->node);
+	srcu_notifier_chain_unregister(nh, &devfreq->nb);
+	kfree(devfreq);
+out:
+	mutex_unlock(&devfreq_list_lock);
+	return 0;
+}
+
+/**
+ * devfreq_init() - Initialize data structure for devfreq framework and
+ *		  start polling registered devfreq devices.
+ */
+static int __init devfreq_init(void)
+{
+	mutex_lock(&devfreq_list_lock);
+	polling = false;
+	devfreq_wq = create_freezable_workqueue("devfreq_wq");
+	INIT_DELAYED_WORK_DEFERRABLE(&devfreq_work, devfreq_monitor);
+	mutex_unlock(&devfreq_list_lock);
+
+	devfreq_monitor(&devfreq_work.work);
+	return 0;
+}
+late_initcall(devfreq_init);
diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h
new file mode 100644
index 0000000..13ddf49
--- /dev/null
+++ b/include/linux/devfreq.h
@@ -0,0 +1,105 @@
+/*
+ * devfreq: Generic Dynamic Voltage and Frequency Scaling (DVFS) Framework
+ *	    for Non-CPU Devices Based on OPP.
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ *	MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __LINUX_DEVFREQ_H__
+#define __LINUX_DEVFREQ_H__
+
+#include <linux/notifier.h>
+
+#define DEVFREQ_NAME_LEN 16
+
+struct devfreq;
+struct devfreq_dev_status {
+	/* both since the last measure */
+	unsigned long total_time;
+	unsigned long busy_time;
+	unsigned long current_frequency;
+};
+
+struct devfreq_dev_profile {
+	unsigned long max_freq; /* may be larger than the actual value */
+	unsigned long initial_freq;
+	int polling_ms;	/* 0 for at opp change only */
+
+	int (*target)(struct device *dev, struct opp *opp);
+	int (*get_dev_status)(struct device *dev,
+			      struct devfreq_dev_status *stat);
+};
+
+/**
+ * struct devfreq_governor - Devfreq policy governor
+ * @name		Governor's name
+ * @get_target_freq	Returns desired operating frequency for the device.
+ *			Basically, get_target_freq will run
+ *			devfreq_dev_profile.get_dev_status() to get the
+ *			status of the device (load = busy_time / total_time).
+ */
+struct devfreq_governor {
+	char name[DEVFREQ_NAME_LEN];
+	int (*get_target_freq)(struct devfreq *this, unsigned long *freq);
+};
+
+/**
+ * struct devfreq - Device devfreq structure
+ * @node	list node - contains the devices with devfreq that have been
+ *		registered.
+ * @dev		device pointer
+ * @profile	device-specific devfreq profile
+ * @governor	method how to choose frequency based on the usage.
+ * @nb		notifier block registered to the corresponding OPP to get
+ *		notified for frequency availability updates.
+ * @previous_freq	previously configured frequency value.
+ * @next_polling	the number of remaining jiffies to poll with
+ *			"devfreq_monitor" executions to reevaluate
+ *			frequency/voltage of the device. Set by
+ *			profile's polling_ms interval.
+ * @data	Private data of the governor. The devfreq framework does not
+ *		touch this.
+ *
+ * This structure stores the devfreq information for a give device.
+ */
+struct devfreq {
+	struct list_head node;
+
+	struct device *dev;
+	struct devfreq_dev_profile *profile;
+	struct devfreq_governor *governor;
+	struct notifier_block nb;
+
+	unsigned long previous_freq;
+	unsigned int next_polling;
+
+	void *data; /* private data for governors */
+};
+
+#if defined(CONFIG_PM_DEVFREQ)
+extern int devfreq_add_device(struct device *dev,
+			   struct devfreq_dev_profile *profile,
+			   struct devfreq_governor *governor,
+			   void *data);
+extern int devfreq_remove_device(struct device *dev);
+#else /* !CONFIG_PM_DEVFREQ */
+static int devfreq_add_device(struct device *dev,
+			   struct devfreq_dev_profile *profile,
+			   struct devfreq_governor *governor,
+			   void *data)
+{
+	return 0;
+}
+
+static int devfreq_remove_device(struct device *dev)
+{
+	return 0;
+}
+#endif /* CONFIG_PM_DEVFREQ */
+
+#endif /* __LINUX_DEVFREQ_H__ */
-- 
1.7.4.1

^ permalink raw reply related

* [PATCH v6 1/4] PM / OPP: Add OPP availability change notifier.
From: MyungJoo Ham @ 2011-08-18 10:27 UTC (permalink / raw)
  To: linux-pm; +Cc: Len Brown, Greg Kroah-Hartman, Kyungmin Park, Thomas Gleixner
In-Reply-To: <1313663262-15308-1-git-send-email-myungjoo.ham@samsung.com>

The patch enables to register notifier_block for an OPP-device in order
to get notified for any changes in the availability of OPPs of the
device. For example, if a new OPP is inserted or enable/disable status
of an OPP is changed, the notifier is executed.

This enables the usage of opp_add, opp_enable, and opp_disable to
directly take effect with any connected entities such as CPUFREQ or
DEVFREQ.

Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>

---
Added at devfreq patch set v6 replacing devfreq_update calls at OPP.
---
 drivers/base/power/opp.c |   28 ++++++++++++++++++++++++++++
 include/linux/opp.h      |   12 ++++++++++++
 2 files changed, 40 insertions(+), 0 deletions(-)

diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c
index b23de18..39e16c3 100644
--- a/drivers/base/power/opp.c
+++ b/drivers/base/power/opp.c
@@ -73,6 +73,7 @@ struct opp {
  *		RCU usage: nodes are not modified in the list of device_opp,
  *		however addition is possible and is secured by dev_opp_list_lock
  * @dev:	device pointer
+ * @head:	notifier head to notify the OPP availability changes.
  * @opp_list:	list of opps
  *
  * This is an internal data structure maintaining the link to opps attached to
@@ -83,6 +84,8 @@ struct device_opp {
 	struct list_head node;
 
 	struct device *dev;
+	struct srcu_notifier_head head;
+
 	struct list_head opp_list;
 };
 
@@ -404,6 +407,7 @@ int opp_add(struct device *dev, unsigned long freq, unsigned long u_volt)
 		}
 
 		dev_opp->dev = dev;
+		srcu_init_notifier_head(&dev_opp->head);
 		INIT_LIST_HEAD(&dev_opp->opp_list);
 
 		/* Secure the device list modification */
@@ -428,6 +432,11 @@ int opp_add(struct device *dev, unsigned long freq, unsigned long u_volt)
 	list_add_rcu(&new_opp->node, head);
 	mutex_unlock(&dev_opp_list_lock);
 
+	/*
+	 * Notify the changes in the availability of the operable
+	 * frequency/voltage list.
+	 */
+	srcu_notifier_call_chain(&dev_opp->head, OPP_EVENT_ADD, new_opp);
 	return 0;
 }
 
@@ -504,6 +513,11 @@ static int opp_set_availability(struct device *dev, unsigned long freq,
 	mutex_unlock(&dev_opp_list_lock);
 	synchronize_rcu();
 
+	/* Notify the change of the OPP availability */
+	srcu_notifier_call_chain(&dev_opp->head, availability_req ?
+				 OPP_EVENT_ENABLE : OPP_EVENT_DISABLE,
+				 new_opp);
+
 	/* clean up old opp */
 	new_opp = opp;
 	goto out;
@@ -512,6 +526,7 @@ unlock:
 	mutex_unlock(&dev_opp_list_lock);
 out:
 	kfree(new_opp);
+
 	return r;
 }
 
@@ -643,3 +658,16 @@ void opp_free_cpufreq_table(struct device *dev,
 	*table = NULL;
 }
 #endif		/* CONFIG_CPU_FREQ */
+
+/** opp_get_notifier() - find notifier_head of the device with opp
+ * @dev:	device pointer used to lookup device OPPs.
+ */
+struct srcu_notifier_head *opp_get_notifier(struct device *dev)
+{
+	struct device_opp *dev_opp = find_device_opp(dev);
+
+	if (IS_ERR(dev_opp))
+		return ERR_PTR(PTR_ERR(dev_opp)); /* matching type */
+
+	return &dev_opp->head;
+}
diff --git a/include/linux/opp.h b/include/linux/opp.h
index 7020e97..53d4538 100644
--- a/include/linux/opp.h
+++ b/include/linux/opp.h
@@ -16,9 +16,14 @@
 
 #include <linux/err.h>
 #include <linux/cpufreq.h>
+#include <linux/notifier.h>
 
 struct opp;
 
+enum opp_event {
+	OPP_EVENT_ADD, OPP_EVENT_ENABLE, OPP_EVENT_DISABLE, OPP_EVENT_MAX
+};
+
 #if defined(CONFIG_PM_OPP)
 
 unsigned long opp_get_voltage(struct opp *opp);
@@ -40,6 +45,8 @@ int opp_enable(struct device *dev, unsigned long freq);
 
 int opp_disable(struct device *dev, unsigned long freq);
 
+struct srcu_notifier_head *opp_get_notifier(struct device *dev);
+
 #else
 static inline unsigned long opp_get_voltage(struct opp *opp)
 {
@@ -89,6 +96,11 @@ static inline int opp_disable(struct device *dev, unsigned long freq)
 {
 	return 0;
 }
+
+struct srcu_notifier_head *opp_get_notifier(struct device *dev)
+{
+	return ERR_PTR(-EINVAL);
+}
 #endif		/* CONFIG_PM */
 
 #if defined(CONFIG_CPU_FREQ) && defined(CONFIG_PM_OPP)
-- 
1.7.4.1

^ permalink raw reply related

* [PATCH v6 0/4] DEVFREQ, DVFS Framework for Non-CPU Devices.
From: MyungJoo Ham @ 2011-08-18 10:27 UTC (permalink / raw)
  To: linux-pm; +Cc: Len Brown, Greg Kroah-Hartman, Kyungmin Park, Thomas Gleixner

For a usage example, please look at
http://git.infradead.org/users/kmpark/linux-2.6-samsung/shortlog/refs/heads/devfreq

In the above git tree, DVFS (dynamic voltage and frequency scaling) mechanism
is applied to the memory bus of Exynos4210 for Exynos4210-NURI boards.
In the example, the LPDDR2 DRAM frequency changes between 133, 266, and 400MHz
and other related clocks simply follow the determined DDR RAM clock.

The DEVFREQ driver for Exynos4210 memory bus is at
/drivers/devfreq/exynos4210_memorybus.c in the git tree.

In the dd (writing and reading 360MiB) test with NURI board, the memory
throughput was not changed (the performance is not deteriorated) while
the SoC power consumption has been reduced by 1%. When the memory access
is not that intense while the CPU is heavily used, the SoC power consumption
has been reduced by 6%. The power consumption has been compared with the
case using the conventional Exynos4210 CPUFREQ driver, which sets memory
bus frequency according to the CPU core frequency. Besides, when the CPU core
running slow and the memory access is intense, the performance (memory
throughput) has been increased by 11% (with higher SoC power consumption of
5%). The tested governor is "simple-ondemand".

Major changes since the last revision (v5)
- Moved from drivers/base/power to drivers/devfreq
- Removed devfreq_update() interface.
- Added OPP availability update notifier
- Removed devfreq_interval, using jiffy as the basis interval
- Seperated governors from devfreq core.
- Allow tunable values to be given at devfreq_add_device().
- And some minor revisions.

MyungJoo Ham (4):
  PM / OPP: Add OPP availability change notifier.
  PM: Introduce DEVFREQ: generic DVFS framework with device-specific
    OPPs
  PM / DEVFREQ: add basic governors
  PM / DEVFREQ: add sysfs interface

 Documentation/ABI/testing/sysfs-devices-power |   45 +++
 drivers/Kconfig                               |    2 +
 drivers/Makefile                              |    2 +
 drivers/base/power/opp.c                      |   28 ++
 drivers/devfreq/Kconfig                       |   75 ++++
 drivers/devfreq/Makefile                      |    5 +
 drivers/devfreq/devfreq.c                     |  454 +++++++++++++++++++++++++
 drivers/devfreq/governor_performance.c        |   24 ++
 drivers/devfreq/governor_powersave.c          |   24 ++
 drivers/devfreq/governor_simpleondemand.c     |   88 +++++
 drivers/devfreq/governor_userspace.c          |   27 ++
 include/linux/devfreq.h                       |  146 ++++++++
 include/linux/opp.h                           |   12 +
 13 files changed, 932 insertions(+), 0 deletions(-)
 create mode 100644 drivers/devfreq/Kconfig
 create mode 100644 drivers/devfreq/Makefile
 create mode 100644 drivers/devfreq/devfreq.c
 create mode 100644 drivers/devfreq/governor_performance.c
 create mode 100644 drivers/devfreq/governor_powersave.c
 create mode 100644 drivers/devfreq/governor_simpleondemand.c
 create mode 100644 drivers/devfreq/governor_userspace.c
 create mode 100644 include/linux/devfreq.h

-- 
1.7.4.1

^ permalink raw reply

* Re: [PATCH] thermal: Rename generate_netlink_event
From: Rafael J. Wysocki @ 2011-08-18  8:51 UTC (permalink / raw)
  To: Jean Delvare
  Cc: Len Brown, Linux PM mailing list, R.Durgadoss, LKML,
	ACPI Devel Mailing List
In-Reply-To: <20110818094944.3ce83401@endymion.delvare>

On Thursday, August 18, 2011, Jean Delvare wrote:
> It doesn't seem right for the thermal subsystem to export a symbol
> named generate_netlink_event. This function is thermal-specific and
> its name should reflect that fact. Rename it to
> thermal_generate_netlink_event.
> 
> Signed-off-by: Jean Delvare <khali@linux-fr.org>
> Cc: Rafael J. Wysocki <rjw@sisk.pl>
> Cc: R.Durgadoss <durgadoss.r@intel.com>
> Cc: Len Brown <len.brown@intel.com>

Acked-by: Rafael J. Wysocki <rjw@sisk.pl>

> ---
>  Documentation/thermal/sysfs-api.txt |    2 +-
>  drivers/thermal/thermal_sys.c       |    4 ++--
>  include/linux/thermal.h             |    4 ++--
>  3 files changed, 5 insertions(+), 5 deletions(-)
> 
> --- linux-3.1-rc2.orig/Documentation/thermal/sysfs-api.txt	2011-07-22 04:17:23.000000000 +0200
> +++ linux-3.1-rc2/Documentation/thermal/sysfs-api.txt	2011-08-17 22:36:57.000000000 +0200
> @@ -284,7 +284,7 @@ method, the sys I/F structure will be bu
>  The framework includes a simple notification mechanism, in the form of a
>  netlink event. Netlink socket initialization is done during the _init_
>  of the framework. Drivers which intend to use the notification mechanism
> -just need to call generate_netlink_event() with two arguments viz
> +just need to call thermal_generate_netlink_event() with two arguments viz
>  (originator, event). Typically the originator will be an integer assigned
>  to a thermal_zone_device when it registers itself with the framework. The
>  event will be one of:{THERMAL_AUX0, THERMAL_AUX1, THERMAL_CRITICAL,
> --- linux-3.1-rc2.orig/drivers/thermal/thermal_sys.c	2011-08-16 11:49:57.000000000 +0200
> +++ linux-3.1-rc2/drivers/thermal/thermal_sys.c	2011-08-17 22:37:22.000000000 +0200
> @@ -1304,7 +1304,7 @@ static struct genl_multicast_group therm
>  	.name = THERMAL_GENL_MCAST_GROUP_NAME,
>  };
>  
> -int generate_netlink_event(u32 orig, enum events event)
> +int thermal_generate_netlink_event(u32 orig, enum events event)
>  {
>  	struct sk_buff *skb;
>  	struct nlattr *attr;
> @@ -1363,7 +1363,7 @@ int generate_netlink_event(u32 orig, enu
>  
>  	return result;
>  }
> -EXPORT_SYMBOL(generate_netlink_event);
> +EXPORT_SYMBOL(thermal_generate_netlink_event);
>  
>  static int genetlink_init(void)
>  {
> --- linux-3.1-rc2.orig/include/linux/thermal.h	2011-08-16 11:50:04.000000000 +0200
> +++ linux-3.1-rc2/include/linux/thermal.h	2011-08-17 22:36:47.000000000 +0200
> @@ -152,9 +152,9 @@ struct thermal_cooling_device *thermal_c
>  void thermal_cooling_device_unregister(struct thermal_cooling_device *);
>  
>  #ifdef CONFIG_NET
> -extern int generate_netlink_event(u32 orig, enum events event);
> +extern int thermal_generate_netlink_event(u32 orig, enum events event);
>  #else
> -static inline int generate_netlink_event(u32 orig, enum events event)
> +static inline int thermal_generate_netlink_event(u32 orig, enum events event)
>  {
>  	return 0;
>  }
> 
> 
> 

^ permalink raw reply

* Re: [PATCH v6 0/7] PM QoS: add a per-device latency constraints framework
From: Rafael J. Wysocki @ 2011-08-18  7:59 UTC (permalink / raw)
  To: jean.pihet
  Cc: markgross, Mark Brown, Linux PM mailing list, linux-omap,
	Jean Pihet
In-Reply-To: <1313609965-6568-1-git-send-email-j-pihet@ti.com>

Hi,

On Wednesday, August 17, 2011, jean.pihet@newoldbits.com wrote:
> From: Jean Pihet <j-pihet@ti.com>
> 
> High level implementation:
> 
> 1. Preparation of the PM QoS for the addition of a device PM QoS constraints
>    framework:
>   . rename and move of the PM QoS implementation files to kernel/power/qos.c
>     and include/linux/pm_qos.h
>   . rename of API parameters and internal fields names
>   . Move around the PM QoS misc devices management code for better readability
>   . re-organize the internal data structs
>   . generalize and export the constraints management core code
> 
> 2. Implementation of the per-device PM QoS constraints:
>   . create drivers/base/power/qos.c for the implementation
>   . create a device PM QoS API, which calls the PM QoS constraints management
>     core code
>   . the per-device latency constraints data strctures are stored in the device
>     dev_pm_info struct
>   . the device PM code calls the init and destroy of the per-device constraints
>     data struct in order to support the dynamic insertion and removal of the
>     devices in the system.
>   . to minimize the data usage by the per-device constraints, the data struct
>     is only allocated at the first call to dev_pm_qos_add_request. The data
>     is later free'd when the device is removed from the system
>   . per-device notification callbacks can be registered and called upon a
>     change to the aggregated constraint value
>   . a global mutex protects the constraints users from the data being
>     allocated and free'd.
> 
> 3. add a global notification mechanism for the device constraints
>   . add a global notification chain that gets called upon changes to the
>     aggregated constraint value for any device.
>   . the notification callbacks are passing the full constraint request data
>     in order for the callees to have access to it. The current use is for the
>     platform low-level code to access the target device of the constraint
> 
> 
> Questions:
> 1. the user space API is still under discussions on linux-omap and linux-pm MLs,
>    cf. [1]. The idea is to add a user-space API for the devices constratins
>    PM QoS, using a sysfs entry per device
> 
> [1] http://marc.info/?l=linux-omap&m=131232344503327&w=2
> 
> ToDo:
> 1. write Documentation for the new PM QoS class, once the code is agreed on
> 2. submit patches for the OMAP low level code to control the power domains
>    states from the device constraints, using a global notification callback
> 
>    
> Based on the master branch of the linux-omap git tree (3.1.0-rc1). Compile
> tested using OMAP and x86 generic defconfigs.
> 
> Tested on OMAP3 Beagleboard (ES2.x).
> Need testing on platforms other than OMAP, because of the impact on the
> device insertion/removal in device_pm_add/remove

OK

If that were my code, I'd probably change a couple of things more, but
it looks good enough to me to take as is.  I'm going to push this patchset
for 3.2.

Thanks a lot for your hard work on it,
Rafael

^ permalink raw reply

* [PATCH 7/7] PM QoS: add a global notification mechanism for the device constraints
From: jean.pihet @ 2011-08-17 19:39 UTC (permalink / raw)
  To: Mark Brown, Kevin Hilman, markgross, Linux PM mailing list; +Cc: Jean Pihet
In-Reply-To: <1313609965-6568-1-git-send-email-j-pihet@ti.com>

From: Jean Pihet <j-pihet@ti.com>

Add a global notification chain that gets called upon changes to the
aggregated constraint value for any device.
The notification callbacks are passing the full constraint request data
in order for the callees to have access to it. The current use is for the
platform low-level code to access the target device of the constraint.

Signed-off-by: Jean Pihet <j-pihet@ti.com>
---
 drivers/base/power/qos.c |   89 ++++++++++++++++++++++++++++++++++++++-------
 include/linux/pm_qos.h   |   11 ++++++
 kernel/power/qos.c       |    2 +-
 3 files changed, 87 insertions(+), 15 deletions(-)

diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c
index 9c0a00d..708d86d 100644
--- a/drivers/base/power/qos.c
+++ b/drivers/base/power/qos.c
@@ -17,6 +17,12 @@
  *
  * This QoS design is best effort based. Dependents register their QoS needs.
  * Watchers register to keep track of the current QoS needs of the system.
+ * Watchers can register different types of notification callbacks:
+ *  . a per-device notification callback using the dev_pm_qos_*_notifier API.
+ *    The notification chain data is stored in the per-device constraint
+ *    data struct.
+ *  . a system-wide notification callback using the dev_pm_qos_*_global_notifier
+ *    API. The notification chain data is stored in a static variable.
  *
  * Note about the per-device constraint data struct allocation:
  * . The per-device constraints data struct ptr is tored into the device
@@ -45,6 +51,36 @@
 
 
 static DEFINE_MUTEX(dev_pm_qos_mtx);
+static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers);
+
+/*
+ * apply_constraint
+ * @req: constraint request to apply
+ * @action: action to perform add/update/remove, of type enum pm_qos_req_action
+ * @value: defines the qos request
+ *
+ * Internal function to update the constraints list using the PM QoS core
+ * code and if needed call the per-device and the global notification
+ * callbacks
+ */
+static int apply_constraint(struct dev_pm_qos_request *req,
+			    enum pm_qos_req_action action, int value)
+{
+	int ret, curr_value;
+
+	ret = pm_qos_update_target(req->dev->power.constraints,
+				   &req->node, action, value);
+
+	if (ret) {
+		/* Call the global callbacks if needed */
+		curr_value = pm_qos_read_value(req->dev->power.constraints);
+		blocking_notifier_call_chain(&dev_pm_notifiers,
+					     (unsigned long)curr_value,
+					     req);
+	}
+
+	return ret;
+}
 
 /*
  * dev_pm_qos_constraints_allocate
@@ -111,12 +147,11 @@ void dev_pm_qos_constraints_destroy(struct device *dev)
 					  &dev->power.constraints->list,
 					  node) {
 			/*
-			 * Update constraints list and call the per-device
+			 * Update constraints list and call the notification
 			 * callbacks if needed
 			 */
-			pm_qos_update_target(req->dev->power.constraints,
-					   &req->node, PM_QOS_REMOVE_REQ,
-					   PM_QOS_DEFAULT_VALUE);
+			apply_constraint(req, PM_QOS_REMOVE_REQ,
+					 PM_QOS_DEFAULT_VALUE);
 			memset(req, 0, sizeof(*req));
 		}
 
@@ -147,7 +182,7 @@ void dev_pm_qos_constraints_destroy(struct device *dev)
  * removed from the system
  */
 int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
-			    s32 value)
+			   s32 value)
 {
 	int ret = 0;
 
@@ -178,8 +213,7 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
 		ret = dev_pm_qos_constraints_allocate(dev);
 
 	if (!ret)
-		ret = pm_qos_update_target(dev->power.constraints, &req->node,
-					   PM_QOS_ADD_REQ, value);
+		ret = apply_constraint(req, PM_QOS_ADD_REQ, value);
 
 out:
 	mutex_unlock(&dev_pm_qos_mtx);
@@ -220,10 +254,8 @@ int dev_pm_qos_update_request(struct dev_pm_qos_request *req,
 
 	if (req->dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
 		if (new_value != req->node.prio)
-			ret = pm_qos_update_target(req->dev->power.constraints,
-						   &req->node,
-						   PM_QOS_UPDATE_REQ,
-						   new_value);
+			ret = apply_constraint(req, PM_QOS_UPDATE_REQ,
+					       new_value);
 	} else {
 		/* Return if the device has been removed */
 		ret = -ENODEV;
@@ -262,9 +294,8 @@ int dev_pm_qos_remove_request(struct dev_pm_qos_request *req)
 	mutex_lock(&dev_pm_qos_mtx);
 
 	if (req->dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
-		ret = pm_qos_update_target(req->dev->power.constraints,
-					   &req->node, PM_QOS_REMOVE_REQ,
-					   PM_QOS_DEFAULT_VALUE);
+		ret = apply_constraint(req, PM_QOS_REMOVE_REQ,
+				       PM_QOS_DEFAULT_VALUE);
 		memset(req, 0, sizeof(*req));
 	} else {
 		/* Return if the device has been removed */
@@ -337,3 +368,33 @@ out:
 }
 EXPORT_SYMBOL_GPL(dev_pm_qos_remove_notifier);
 
+/**
+ * dev_pm_qos_add_global_notifier - sets notification entry for changes to
+ * target value of the PM QoS constraints for any device
+ *
+ * @notifier: notifier block managed by caller.
+ *
+ * Will register the notifier into a notification chain that gets called
+ * upon changes to the target value for any device.
+ */
+int dev_pm_qos_add_global_notifier(struct notifier_block *notifier)
+{
+	return blocking_notifier_chain_register(&dev_pm_notifiers, notifier);
+}
+EXPORT_SYMBOL_GPL(dev_pm_qos_add_global_notifier);
+
+/**
+ * dev_pm_qos_remove_global_notifier - deletes notification for changes to
+ * target value of PM QoS constraints for any device
+ *
+ * @notifier: notifier block to be removed.
+ *
+ * Will remove the notifier from the notification chain that gets called
+ * upon changes to the target value for any device.
+ */
+int dev_pm_qos_remove_global_notifier(struct notifier_block *notifier)
+{
+	return blocking_notifier_chain_unregister(&dev_pm_notifiers, notifier);
+}
+EXPORT_SYMBOL_GPL(dev_pm_qos_remove_global_notifier);
+
diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h
index c35e189..bde9071 100644
--- a/include/linux/pm_qos.h
+++ b/include/linux/pm_qos.h
@@ -75,6 +75,7 @@ int pm_qos_request(int pm_qos_class);
 int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier);
 int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier);
 int pm_qos_request_active(struct pm_qos_request *req);
+s32 pm_qos_read_value(struct pm_qos_constraints *c);
 
 int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
 			   s32 value);
@@ -84,6 +85,8 @@ int dev_pm_qos_add_notifier(struct device *dev,
 			    struct notifier_block *notifier);
 int dev_pm_qos_remove_notifier(struct device *dev,
 			       struct notifier_block *notifier);
+int dev_pm_qos_add_global_notifier(struct notifier_block *notifier);
+int dev_pm_qos_remove_global_notifier(struct notifier_block *notifier);
 void dev_pm_qos_constraints_init(struct device *dev);
 void dev_pm_qos_constraints_destroy(struct device *dev);
 #else
@@ -111,6 +114,8 @@ static inline int pm_qos_remove_notifier(int pm_qos_class,
 			{ return 0; }
 static inline int pm_qos_request_active(struct pm_qos_request *req)
 			{ return 0; }
+static inline s32 pm_qos_read_value(struct pm_qos_constraints *c)
+			{ return 0; }
 
 static inline int dev_pm_qos_add_request(struct device *dev,
 					 struct dev_pm_qos_request *req,
@@ -127,6 +132,12 @@ static inline int dev_pm_qos_add_notifier(struct device *dev,
 static inline int dev_pm_qos_remove_notifier(struct device *dev,
 					     struct notifier_block *notifier)
 			{ return 0; }
+static inline int dev_pm_qos_add_global_notifier(
+					struct notifier_block *notifier)
+			{ return 0; }
+static inline int dev_pm_qos_remove_global_notifier(
+					struct notifier_block *notifier)
+			{ return 0; }
 static inline void dev_pm_qos_constraints_init(struct device *dev)
 			{ return; }
 static inline void dev_pm_qos_constraints_destroy(struct device *dev)
diff --git a/kernel/power/qos.c b/kernel/power/qos.c
index 7c7cd18..1c1797d 100644
--- a/kernel/power/qos.c
+++ b/kernel/power/qos.c
@@ -140,7 +140,7 @@ static inline int pm_qos_get_value(struct pm_qos_constraints *c)
 	}
 }
 
-static inline s32 pm_qos_read_value(struct pm_qos_constraints *c)
+s32 pm_qos_read_value(struct pm_qos_constraints *c)
 {
 	return c->target_value;
 }
-- 
1.7.4.1

^ permalink raw reply related

* [PATCH 6/7] PM QoS: implement the per-device PM QoS constraints
From: jean.pihet @ 2011-08-17 19:39 UTC (permalink / raw)
  To: Mark Brown, Kevin Hilman, markgross, Linux PM mailing list; +Cc: Jean Pihet
In-Reply-To: <1313609965-6568-1-git-send-email-j-pihet@ti.com>

From: Jean Pihet <j-pihet@ti.com>

Implement the per-device PM QoS constraints by creating a device
PM QoS API, which calls the PM QoS constraints management core code.

The per-device latency constraints data strctures are stored
in the device dev_pm_info struct.

The device PM code calls the init and destroy of the per-device constraints
data struct in order to support the dynamic insertion and removal of the
devices in the system.

To minimize the data usage by the per-device constraints, the data struct
is only allocated at the first call to dev_pm_qos_add_request.
The data is later free'd when the device is removed from the system.
A global mutex protects the constraints users from the data being
allocated and free'd.

Signed-off-by: Jean Pihet <j-pihet@ti.com>
---
 drivers/base/power/Makefile |    4 +-
 drivers/base/power/main.c   |    3 +
 drivers/base/power/qos.c    |  339 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/pm.h          |    9 +
 include/linux/pm_qos.h      |   42 ++++++
 5 files changed, 395 insertions(+), 2 deletions(-)
 create mode 100644 drivers/base/power/qos.c

diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile
index 2639ae7..b707447 100644
--- a/drivers/base/power/Makefile
+++ b/drivers/base/power/Makefile
@@ -1,4 +1,4 @@
-obj-$(CONFIG_PM)	+= sysfs.o generic_ops.o
+obj-$(CONFIG_PM)	+= sysfs.o generic_ops.o qos.o
 obj-$(CONFIG_PM_SLEEP)	+= main.o wakeup.o
 obj-$(CONFIG_PM_RUNTIME)	+= runtime.o
 obj-$(CONFIG_PM_TRACE_RTC)	+= trace.o
@@ -6,4 +6,4 @@ obj-$(CONFIG_PM_OPP)	+= opp.o
 obj-$(CONFIG_PM_GENERIC_DOMAINS)	+=  domain.o
 obj-$(CONFIG_HAVE_CLK)	+= clock_ops.o
 
-ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
\ No newline at end of file
+ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index a854591..956443f 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -22,6 +22,7 @@
 #include <linux/mutex.h>
 #include <linux/pm.h>
 #include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
 #include <linux/resume-trace.h>
 #include <linux/interrupt.h>
 #include <linux/sched.h>
@@ -97,6 +98,7 @@ void device_pm_add(struct device *dev)
 			dev_name(dev->parent));
 	list_add_tail(&dev->power.entry, &dpm_list);
 	mutex_unlock(&dpm_list_mtx);
+	dev_pm_qos_constraints_init(dev);
 }
 
 /**
@@ -107,6 +109,7 @@ void device_pm_remove(struct device *dev)
 {
 	pr_debug("PM: Removing info for %s:%s\n",
 		 dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
+	dev_pm_qos_constraints_destroy(dev);
 	complete_all(&dev->power.completion);
 	mutex_lock(&dpm_list_mtx);
 	list_del_init(&dev->power.entry);
diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c
new file mode 100644
index 0000000..9c0a00d
--- /dev/null
+++ b/drivers/base/power/qos.c
@@ -0,0 +1,339 @@
+/*
+ * Devices PM QoS constraints management
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *
+ * This module exposes the interface to kernel space for specifying
+ * per-device PM QoS dependencies. It provides infrastructure for registration
+ * of:
+ *
+ * Dependents on a QoS value : register requests
+ * Watchers of QoS value : get notified when target QoS value changes
+ *
+ * This QoS design is best effort based. Dependents register their QoS needs.
+ * Watchers register to keep track of the current QoS needs of the system.
+ *
+ * Note about the per-device constraint data struct allocation:
+ * . The per-device constraints data struct ptr is tored into the device
+ *    dev_pm_info.
+ * . To minimize the data usage by the per-device constraints, the data struct
+ *   is only allocated at the first call to dev_pm_qos_add_request.
+ * . The data is later free'd when the device is removed from the system.
+ * . The constraints_state variable from dev_pm_info tracks the data struct
+ *    allocation state:
+ *    DEV_PM_QOS_NO_DEVICE: No device present or device removed, no data
+ *     allocated,
+ *    DEV_PM_QOS_DEVICE_PRESENT: Device present, data not allocated and will be
+ *     allocated at the first call to dev_pm_qos_add_request,
+ *    DEV_PM_QOS_ALLOCATED: Device present, data allocated. The per-device
+ *     PM QoS constraints framework is operational and constraints can be
+ *     added, updated or removed using the dev_pm_qos_* API.
+ *  . A global mutex protects the constraints users from the data being
+ *     allocated and free'd.
+ */
+
+#include <linux/pm_qos.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+
+
+static DEFINE_MUTEX(dev_pm_qos_mtx);
+
+/*
+ * dev_pm_qos_constraints_allocate
+ * @dev: device to allocate data for
+ *
+ * Called at the first call to add_request, for constraint data allocation
+ * Must be called with the dev_pm_qos_mtx mutex held
+ */
+static int dev_pm_qos_constraints_allocate(struct device *dev)
+{
+	struct pm_qos_constraints *c;
+	struct blocking_notifier_head *n;
+
+	c = kzalloc(sizeof(*c), GFP_KERNEL);
+	if (!c)
+		return -ENOMEM;
+
+	n = kzalloc(sizeof(*n), GFP_KERNEL);
+	if (!n) {
+		kfree(c);
+		return -ENOMEM;
+	}
+	BLOCKING_INIT_NOTIFIER_HEAD(n);
+
+	dev->power.constraints = c;
+	plist_head_init(&dev->power.constraints->list);
+	dev->power.constraints->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+	dev->power.constraints->default_value =	PM_QOS_DEV_LAT_DEFAULT_VALUE;
+	dev->power.constraints->type = PM_QOS_MIN;
+	dev->power.constraints->notifiers = n;
+	dev->power.constraints_state = DEV_PM_QOS_ALLOCATED;
+
+	return 0;
+}
+
+/**
+ * dev_pm_qos_constraints_init
+ * @dev: target device
+ *
+ * Called from the device PM subsystem at device insertion
+ */
+void dev_pm_qos_constraints_init(struct device *dev)
+{
+	mutex_lock(&dev_pm_qos_mtx);
+	dev->power.constraints_state = DEV_PM_QOS_DEVICE_PRESENT;
+	mutex_unlock(&dev_pm_qos_mtx);
+}
+
+/**
+ * dev_pm_qos_constraints_destroy
+ * @dev: target device
+ *
+ * Called from the device PM subsystem at device removal
+ */
+void dev_pm_qos_constraints_destroy(struct device *dev)
+{
+	struct dev_pm_qos_request *req, *tmp;
+
+	mutex_lock(&dev_pm_qos_mtx);
+
+	if (dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
+		/* Flush the constraints list for the device */
+		plist_for_each_entry_safe(req, tmp,
+					  &dev->power.constraints->list,
+					  node) {
+			/*
+			 * Update constraints list and call the per-device
+			 * callbacks if needed
+			 */
+			pm_qos_update_target(req->dev->power.constraints,
+					   &req->node, PM_QOS_REMOVE_REQ,
+					   PM_QOS_DEFAULT_VALUE);
+			memset(req, 0, sizeof(*req));
+		}
+
+		kfree(dev->power.constraints->notifiers);
+		kfree(dev->power.constraints);
+		dev->power.constraints = NULL;
+	}
+	dev->power.constraints_state = DEV_PM_QOS_NO_DEVICE;
+
+	mutex_unlock(&dev_pm_qos_mtx);
+}
+
+/**
+ * dev_pm_qos_add_request - inserts new qos request into the list
+ * @dev: target device for the constraint
+ * @req: pointer to a preallocated handle
+ * @value: defines the qos request
+ *
+ * This function inserts a new entry in the device constraints list of
+ * requested qos performance characteristics. It recomputes the aggregate
+ * QoS expectations of parameters and initializes the dev_pm_qos_request
+ * handle.  Caller needs to save this handle for later use in updates and
+ * removal.
+ *
+ * Returns 1 if the aggregated constraint value has changed,
+ * 0 if the aggregated constraint value has not changed,
+ * -EINVAL in case of wrong parameters, -ENODEV if the device has been
+ * removed from the system
+ */
+int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
+			    s32 value)
+{
+	int ret = 0;
+
+	if (!dev || !req) /*guard against callers passing in null */
+		return -EINVAL;
+
+	if (dev_pm_qos_request_active(req)) {
+		WARN(1, KERN_ERR "dev_pm_qos_add_request() called for already "
+			"added request\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&dev_pm_qos_mtx);
+	req->dev = dev;
+
+	/* Return if the device has been removed */
+	if (req->dev->power.constraints_state == DEV_PM_QOS_NO_DEVICE) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+	/*
+	 * Allocate the constraints data on the first call to add_request,
+	 * i.e. only if the data is not already allocated and if the device has
+	 * not been removed
+	 */
+	if (dev->power.constraints_state == DEV_PM_QOS_DEVICE_PRESENT)
+		ret = dev_pm_qos_constraints_allocate(dev);
+
+	if (!ret)
+		ret = pm_qos_update_target(dev->power.constraints, &req->node,
+					   PM_QOS_ADD_REQ, value);
+
+out:
+	mutex_unlock(&dev_pm_qos_mtx);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dev_pm_qos_add_request);
+
+/**
+ * dev_pm_qos_update_request - modifies an existing qos request
+ * @req : handle to list element holding a dev_pm_qos request to use
+ * @new_value: defines the qos request
+ *
+ * Updates an existing dev PM qos request along with updating the
+ * target value.
+ *
+ * Attempts are made to make this code callable on hot code paths.
+ *
+ * Returns 1 if the aggregated constraint value has changed,
+ * 0 if the aggregated constraint value has not changed,
+ * -EINVAL in case of wrong parameters, -ENODEV if the device has been
+ * removed from the system
+ */
+int dev_pm_qos_update_request(struct dev_pm_qos_request *req,
+			      s32 new_value)
+{
+	int ret = 0;
+
+	if (!req) /*guard against callers passing in null */
+		return -EINVAL;
+
+	if (!dev_pm_qos_request_active(req)) {
+		WARN(1, KERN_ERR "dev_pm_qos_update_request() called for "
+			"unknown object\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&dev_pm_qos_mtx);
+
+	if (req->dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
+		if (new_value != req->node.prio)
+			ret = pm_qos_update_target(req->dev->power.constraints,
+						   &req->node,
+						   PM_QOS_UPDATE_REQ,
+						   new_value);
+	} else {
+		/* Return if the device has been removed */
+		ret = -ENODEV;
+	}
+
+	mutex_unlock(&dev_pm_qos_mtx);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dev_pm_qos_update_request);
+
+/**
+ * dev_pm_qos_remove_request - modifies an existing qos request
+ * @req: handle to request list element
+ *
+ * Will remove pm qos request from the list of constraints and
+ * recompute the current target value. Call this on slow code paths.
+ *
+ * Returns 1 if the aggregated constraint value has changed,
+ * 0 if the aggregated constraint value has not changed,
+ * -EINVAL in case of wrong parameters, -ENODEV if the device has been
+ * removed from the system
+ */
+int dev_pm_qos_remove_request(struct dev_pm_qos_request *req)
+{
+	int ret = 0;
+
+	if (!req) /*guard against callers passing in null */
+		return -EINVAL;
+
+	if (!dev_pm_qos_request_active(req)) {
+		WARN(1, KERN_ERR "dev_pm_qos_remove_request() called for "
+			"unknown object\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&dev_pm_qos_mtx);
+
+	if (req->dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) {
+		ret = pm_qos_update_target(req->dev->power.constraints,
+					   &req->node, PM_QOS_REMOVE_REQ,
+					   PM_QOS_DEFAULT_VALUE);
+		memset(req, 0, sizeof(*req));
+	} else {
+		/* Return if the device has been removed */
+		ret = -ENODEV;
+	}
+
+	mutex_unlock(&dev_pm_qos_mtx);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dev_pm_qos_remove_request);
+
+/**
+ * dev_pm_qos_add_notifier - sets notification entry for changes to target value
+ * of per-device PM QoS constraints
+ *
+ * @dev: target device for the constraint
+ * @notifier: notifier block managed by caller.
+ *
+ * Will register the notifier into a notification chain that gets called
+ * upon changes to the target value for the device.
+ */
+int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier)
+{
+	int retval = 0;
+
+	mutex_lock(&dev_pm_qos_mtx);
+
+	/* Silently return if the device has been removed */
+	if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
+		goto out;
+
+	retval = blocking_notifier_chain_register(
+			dev->power.constraints->notifiers,
+			notifier);
+
+out:
+	mutex_unlock(&dev_pm_qos_mtx);
+	return retval;
+}
+EXPORT_SYMBOL_GPL(dev_pm_qos_add_notifier);
+
+/**
+ * dev_pm_qos_remove_notifier - deletes notification for changes to target value
+ * of per-device PM QoS constraints
+ *
+ * @dev: target device for the constraint
+ * @notifier: notifier block to be removed.
+ *
+ * Will remove the notifier from the notification chain that gets called
+ * upon changes to the target value.
+ */
+int dev_pm_qos_remove_notifier(struct device *dev,
+			       struct notifier_block *notifier)
+{
+	int retval = 0;
+
+	mutex_lock(&dev_pm_qos_mtx);
+
+	/* Silently return if the device has been removed */
+	if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
+		goto out;
+
+	retval = blocking_notifier_chain_unregister(
+			dev->power.constraints->notifiers,
+			notifier);
+
+out:
+	mutex_unlock(&dev_pm_qos_mtx);
+	return retval;
+}
+EXPORT_SYMBOL_GPL(dev_pm_qos_remove_notifier);
+
diff --git a/include/linux/pm.h b/include/linux/pm.h
index f7c84c9..7a48951 100644
--- a/include/linux/pm.h
+++ b/include/linux/pm.h
@@ -421,6 +421,13 @@ enum rpm_request {
 
 struct wakeup_source;
 
+/* Per-device PM QoS constraints data struct state */
+enum dev_pm_qos_state {
+	DEV_PM_QOS_NO_DEVICE,		/* No device present */
+	DEV_PM_QOS_DEVICE_PRESENT,	/* Device present, data not allocated */
+	DEV_PM_QOS_ALLOCATED,		/* Device present, data allocated */
+};
+
 struct dev_pm_info {
 	pm_message_t		power_state;
 	unsigned int		can_wakeup:1;
@@ -463,6 +470,8 @@ struct dev_pm_info {
 	unsigned long		accounting_timestamp;
 #endif
 	void			*subsys_data;  /* Owned by the subsystem. */
+	struct pm_qos_constraints *constraints;
+	enum dev_pm_qos_state	constraints_state;
 };
 
 extern void update_pm_runtime_accounting(struct device *dev);
diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h
index 84aa150..c35e189 100644
--- a/include/linux/pm_qos.h
+++ b/include/linux/pm_qos.h
@@ -19,12 +19,18 @@
 #define PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE	(2000 * USEC_PER_SEC)
 #define PM_QOS_NETWORK_LAT_DEFAULT_VALUE	(2000 * USEC_PER_SEC)
 #define PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE	0
+#define PM_QOS_DEV_LAT_DEFAULT_VALUE		0
 
 struct pm_qos_request {
 	struct plist_node node;
 	int pm_qos_class;
 };
 
+struct dev_pm_qos_request {
+	struct plist_node node;
+	struct device *dev;
+};
+
 enum pm_qos_type {
 	PM_QOS_UNITIALIZED,
 	PM_QOS_MAX,		/* return the largest value */
@@ -51,6 +57,11 @@ enum pm_qos_req_action {
 	PM_QOS_REMOVE_REQ	/* Remove an existing request */
 };
 
+static inline int dev_pm_qos_request_active(struct dev_pm_qos_request *req)
+{
+	return req->dev != 0;
+}
+
 #ifdef CONFIG_PM
 int pm_qos_update_target(struct pm_qos_constraints *c, struct plist_node *node,
 			 enum pm_qos_req_action action, int value);
@@ -64,6 +75,17 @@ int pm_qos_request(int pm_qos_class);
 int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier);
 int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier);
 int pm_qos_request_active(struct pm_qos_request *req);
+
+int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
+			   s32 value);
+int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value);
+int dev_pm_qos_remove_request(struct dev_pm_qos_request *req);
+int dev_pm_qos_add_notifier(struct device *dev,
+			    struct notifier_block *notifier);
+int dev_pm_qos_remove_notifier(struct device *dev,
+			       struct notifier_block *notifier);
+void dev_pm_qos_constraints_init(struct device *dev);
+void dev_pm_qos_constraints_destroy(struct device *dev);
 #else
 static inline int pm_qos_update_target(struct pm_qos_constraints *c,
 				       struct plist_node *node,
@@ -89,6 +111,26 @@ static inline int pm_qos_remove_notifier(int pm_qos_class,
 			{ return 0; }
 static inline int pm_qos_request_active(struct pm_qos_request *req)
 			{ return 0; }
+
+static inline int dev_pm_qos_add_request(struct device *dev,
+					 struct dev_pm_qos_request *req,
+					 s32 value)
+			{ return; }
+static inline int dev_pm_qos_update_request(struct dev_pm_qos_request *req,
+					    s32 new_value)
+			{ return; }
+static inline int dev_pm_qos_remove_request(struct dev_pm_qos_request *req)
+			{ return; }
+static inline int dev_pm_qos_add_notifier(struct device *dev,
+					  struct notifier_block *notifier)
+			{ return 0; }
+static inline int dev_pm_qos_remove_notifier(struct device *dev,
+					     struct notifier_block *notifier)
+			{ return 0; }
+static inline void dev_pm_qos_constraints_init(struct device *dev)
+			{ return; }
+static inline void dev_pm_qos_constraints_destroy(struct device *dev)
+			{ return; }
 #endif
 
 #endif
-- 
1.7.4.1

^ permalink raw reply related

* [PATCH 5/7] PM QoS: generalize and export the constraints management code
From: jean.pihet @ 2011-08-17 19:39 UTC (permalink / raw)
  To: Mark Brown, Kevin Hilman, markgross, Linux PM mailing list; +Cc: Jean Pihet
In-Reply-To: <1313609965-6568-1-git-send-email-j-pihet@ti.com>

From: Jean Pihet <j-pihet@ti.com>

In preparation for the per-device constratins support:
- rename update_target to pm_qos_update_target
- generalize and export pm_qos_update_target for usage by the upcoming
per-device latency constraints framework:
   . operate on struct pm_qos_constraints for constraints management,
   . introduce an 'action' parameter for constraints add/update/remove,
   . the return value indicates if the aggregated constraint value has
     changed,
- update the internal code to operate on struct pm_qos_constraints
- add a NULL pointer check in the API functions

Signed-off-by: Jean Pihet <j-pihet@ti.com>
---
 include/linux/pm_qos.h |   14 ++++++
 kernel/power/qos.c     |  123 ++++++++++++++++++++++++++----------------------
 2 files changed, 81 insertions(+), 56 deletions(-)

diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h
index 9772311..84aa150 100644
--- a/include/linux/pm_qos.h
+++ b/include/linux/pm_qos.h
@@ -44,7 +44,16 @@ struct pm_qos_constraints {
 	struct blocking_notifier_head *notifiers;
 };
 
+/* Action requested to pm_qos_update_target */
+enum pm_qos_req_action {
+	PM_QOS_ADD_REQ,		/* Add a new request */
+	PM_QOS_UPDATE_REQ,	/* Update an existing request */
+	PM_QOS_REMOVE_REQ	/* Remove an existing request */
+};
+
 #ifdef CONFIG_PM
+int pm_qos_update_target(struct pm_qos_constraints *c, struct plist_node *node,
+			 enum pm_qos_req_action action, int value);
 void pm_qos_add_request(struct pm_qos_request *req, int pm_qos_class,
 			s32 value);
 void pm_qos_update_request(struct pm_qos_request *req,
@@ -56,6 +65,11 @@ int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier);
 int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier);
 int pm_qos_request_active(struct pm_qos_request *req);
 #else
+static inline int pm_qos_update_target(struct pm_qos_constraints *c,
+				       struct plist_node *node,
+				       enum pm_qos_req_action action,
+				       int value)
+			{ return 0; }
 static inline void pm_qos_add_request(struct pm_qos_request *req,
 				      int pm_qos_class, s32 value)
 			{ return; }
diff --git a/kernel/power/qos.c b/kernel/power/qos.c
index 4a35fe5..7c7cd18 100644
--- a/kernel/power/qos.c
+++ b/kernel/power/qos.c
@@ -122,17 +122,17 @@ static const struct file_operations pm_qos_power_fops = {
 };
 
 /* unlocked internal variant */
-static inline int pm_qos_get_value(struct pm_qos_object *o)
+static inline int pm_qos_get_value(struct pm_qos_constraints *c)
 {
-	if (plist_head_empty(&o->constraints->list))
-		return o->constraints->default_value;
+	if (plist_head_empty(&c->list))
+		return c->default_value;
 
-	switch (o->constraints->type) {
+	switch (c->type) {
 	case PM_QOS_MIN:
-		return plist_first(&o->constraints->list)->prio;
+		return plist_first(&c->list)->prio;
 
 	case PM_QOS_MAX:
-		return plist_last(&o->constraints->list)->prio;
+		return plist_last(&c->list)->prio;
 
 	default:
 		/* runtime check for not using enum */
@@ -140,47 +140,73 @@ static inline int pm_qos_get_value(struct pm_qos_object *o)
 	}
 }
 
-static inline s32 pm_qos_read_value(struct pm_qos_object *o)
+static inline s32 pm_qos_read_value(struct pm_qos_constraints *c)
 {
-	return o->constraints->target_value;
+	return c->target_value;
 }
 
-static inline void pm_qos_set_value(struct pm_qos_object *o, s32 value)
+static inline void pm_qos_set_value(struct pm_qos_constraints *c, s32 value)
 {
-	o->constraints->target_value = value;
+	c->target_value = value;
 }
 
-static void update_target(struct pm_qos_object *o, struct plist_node *node,
-			  int del, int value)
+/**
+ * pm_qos_update_target - manages the constraints list and calls the notifiers
+ *  if needed
+ * @c: constraints data struct
+ * @node: request to add to the list, to update or to remove
+ * @action: action to take on the constraints list
+ * @value: value of the request to add or update
+ *
+ * This function returns 1 if the aggregated constraint value has changed, 0
+ *  otherwise.
+ */
+int pm_qos_update_target(struct pm_qos_constraints *c, struct plist_node *node,
+			 enum pm_qos_req_action action, int value)
 {
 	unsigned long flags;
-	int prev_value, curr_value;
+	int prev_value, curr_value, new_value;
 
 	spin_lock_irqsave(&pm_qos_lock, flags);
-	prev_value = pm_qos_get_value(o);
-	/* PM_QOS_DEFAULT_VALUE is a signal that the value is unchanged */
-	if (value != PM_QOS_DEFAULT_VALUE) {
+	prev_value = pm_qos_get_value(c);
+	if (value == PM_QOS_DEFAULT_VALUE)
+		new_value = c->default_value;
+	else
+		new_value = value;
+
+	switch (action) {
+	case PM_QOS_REMOVE_REQ:
+		plist_del(node, &c->list);
+		break;
+	case PM_QOS_UPDATE_REQ:
 		/*
 		 * to change the list, we atomically remove, reinit
 		 * with new value and add, then see if the extremal
 		 * changed
 		 */
-		plist_del(node, &o->constraints->list);
-		plist_node_init(node, value);
-		plist_add(node, &o->constraints->list);
-	} else if (del) {
-		plist_del(node, &o->constraints->list);
-	} else {
-		plist_add(node, &o->constraints->list);
+		plist_del(node, &c->list);
+	case PM_QOS_ADD_REQ:
+		plist_node_init(node, new_value);
+		plist_add(node, &c->list);
+		break;
+	default:
+		/* no action */
+		;
 	}
-	curr_value = pm_qos_get_value(o);
-	pm_qos_set_value(o, curr_value);
+
+	curr_value = pm_qos_get_value(c);
+	pm_qos_set_value(c, curr_value);
+
 	spin_unlock_irqrestore(&pm_qos_lock, flags);
 
-	if (prev_value != curr_value)
-		blocking_notifier_call_chain(o->constraints->notifiers,
+	if (prev_value != curr_value) {
+		blocking_notifier_call_chain(c->notifiers,
 					     (unsigned long)curr_value,
 					     NULL);
+		return 1;
+	} else {
+		return 0;
+	}
 }
 
 /**
@@ -191,7 +217,7 @@ static void update_target(struct pm_qos_object *o, struct plist_node *node,
  */
 int pm_qos_request(int pm_qos_class)
 {
-	return pm_qos_read_value(pm_qos_array[pm_qos_class]);
+	return pm_qos_read_value(pm_qos_array[pm_qos_class]->constraints);
 }
 EXPORT_SYMBOL_GPL(pm_qos_request);
 
@@ -217,20 +243,16 @@ EXPORT_SYMBOL_GPL(pm_qos_request_active);
 void pm_qos_add_request(struct pm_qos_request *req,
 			int pm_qos_class, s32 value)
 {
-	struct pm_qos_object *o =  pm_qos_array[pm_qos_class];
-	int new_value;
+	if (!req) /*guard against callers passing in null */
+		return;
 
 	if (pm_qos_request_active(req)) {
 		WARN(1, KERN_ERR "pm_qos_add_request() called for already added request\n");
 		return;
 	}
-	if (value == PM_QOS_DEFAULT_VALUE)
-		new_value = o->constraints->default_value;
-	else
-		new_value = value;
-	plist_node_init(&req->node, new_value);
 	req->pm_qos_class = pm_qos_class;
-	update_target(o, &req->node, 0, PM_QOS_DEFAULT_VALUE);
+	pm_qos_update_target(pm_qos_array[pm_qos_class]->constraints,
+			     &req->node, PM_QOS_ADD_REQ, value);
 }
 EXPORT_SYMBOL_GPL(pm_qos_add_request);
 
@@ -247,9 +269,6 @@ EXPORT_SYMBOL_GPL(pm_qos_add_request);
 void pm_qos_update_request(struct pm_qos_request *req,
 			   s32 new_value)
 {
-	s32 temp;
-	struct pm_qos_object *o;
-
 	if (!req) /*guard against callers passing in null */
 		return;
 
@@ -258,15 +277,10 @@ void pm_qos_update_request(struct pm_qos_request *req,
 		return;
 	}
 
-	o = pm_qos_array[req->pm_qos_class];
-
-	if (new_value == PM_QOS_DEFAULT_VALUE)
-		temp = o->constraints->default_value;
-	else
-		temp = new_value;
-
-	if (temp != req->node.prio)
-		update_target(o, &req->node, 0, temp);
+	if (new_value != req->node.prio)
+		pm_qos_update_target(
+			pm_qos_array[req->pm_qos_class]->constraints,
+			&req->node, PM_QOS_UPDATE_REQ, new_value);
 }
 EXPORT_SYMBOL_GPL(pm_qos_update_request);
 
@@ -280,9 +294,7 @@ EXPORT_SYMBOL_GPL(pm_qos_update_request);
  */
 void pm_qos_remove_request(struct pm_qos_request *req)
 {
-	struct pm_qos_object *o;
-
-	if (req == NULL)
+	if (!req) /*guard against callers passing in null */
 		return;
 		/* silent return to keep pcm code cleaner */
 
@@ -291,8 +303,9 @@ void pm_qos_remove_request(struct pm_qos_request *req)
 		return;
 	}
 
-	o = pm_qos_array[req->pm_qos_class];
-	update_target(o, &req->node, 1, PM_QOS_DEFAULT_VALUE);
+	pm_qos_update_target(pm_qos_array[req->pm_qos_class]->constraints,
+			     &req->node, PM_QOS_REMOVE_REQ,
+			     PM_QOS_DEFAULT_VALUE);
 	memset(req, 0, sizeof(*req));
 }
 EXPORT_SYMBOL_GPL(pm_qos_remove_request);
@@ -396,7 +409,6 @@ static ssize_t pm_qos_power_read(struct file *filp, char __user *buf,
 {
 	s32 value;
 	unsigned long flags;
-	struct pm_qos_object *o;
 	struct pm_qos_request *req = filp->private_data;
 
 	if (!req)
@@ -404,9 +416,8 @@ static ssize_t pm_qos_power_read(struct file *filp, char __user *buf,
 	if (!pm_qos_request_active(req))
 		return -EINVAL;
 
-	o = pm_qos_array[req->pm_qos_class];
 	spin_lock_irqsave(&pm_qos_lock, flags);
-	value = pm_qos_get_value(o);
+	value = pm_qos_get_value(pm_qos_array[req->pm_qos_class]->constraints);
 	spin_unlock_irqrestore(&pm_qos_lock, flags);
 
 	return simple_read_from_buffer(buf, count, f_pos, &value, sizeof(s32));
-- 
1.7.4.1

^ permalink raw reply related

* [PATCH 4/7] PM QoS: re-organize data structs
From: jean.pihet @ 2011-08-17 19:39 UTC (permalink / raw)
  To: Mark Brown, Kevin Hilman, markgross, Linux PM mailing list; +Cc: Jean Pihet
In-Reply-To: <1313609965-6568-1-git-send-email-j-pihet@ti.com>

From: Jean Pihet <j-pihet@ti.com>

In preparation for the per-device constratins support, re-organize
the data strctures:
- add a struct pm_qos_constraints which contains the constraints
related data
- update struct pm_qos_object contents to the PM QoS internal object
data. Add a pointer to struct pm_qos_constraints
- update the internal code to use the new data structs.

Signed-off-by: Jean Pihet <j-pihet@ti.com>
---
 include/linux/pm_qos.h |   19 +++++++++++
 kernel/power/qos.c     |   85 +++++++++++++++++++++++-------------------------
 2 files changed, 60 insertions(+), 44 deletions(-)

diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h
index 6b0968f..9772311 100644
--- a/include/linux/pm_qos.h
+++ b/include/linux/pm_qos.h
@@ -25,6 +25,25 @@ struct pm_qos_request {
 	int pm_qos_class;
 };
 
+enum pm_qos_type {
+	PM_QOS_UNITIALIZED,
+	PM_QOS_MAX,		/* return the largest value */
+	PM_QOS_MIN		/* return the smallest value */
+};
+
+/*
+ * Note: The lockless read path depends on the CPU accessing
+ * target_value atomically.  Atomic access is only guaranteed on all CPU
+ * types linux supports for 32 bit quantites
+ */
+struct pm_qos_constraints {
+	struct plist_head list;
+	s32 target_value;	/* Do not change to 64 bit */
+	s32 default_value;
+	enum pm_qos_type type;
+	struct blocking_notifier_head *notifiers;
+};
+
 #ifdef CONFIG_PM
 void pm_qos_add_request(struct pm_qos_request *req, int pm_qos_class,
 			s32 value);
diff --git a/kernel/power/qos.c b/kernel/power/qos.c
index 788c4cf..4a35fe5 100644
--- a/kernel/power/qos.c
+++ b/kernel/power/qos.c
@@ -49,58 +49,53 @@
  * or pm_qos_object list and pm_qos_objects need to happen with pm_qos_lock
  * held, taken with _irqsave.  One lock to rule them all
  */
-enum pm_qos_type {
-	PM_QOS_MAX,		/* return the largest value */
-	PM_QOS_MIN		/* return the smallest value */
-};
-
-/*
- * Note: The lockless read path depends on the CPU accessing
- * target_value atomically.  Atomic access is only guaranteed on all CPU
- * types linux supports for 32 bit quantites
- */
 struct pm_qos_object {
-	struct plist_head constraints;
-	struct blocking_notifier_head *notifiers;
+	struct pm_qos_constraints *constraints;
 	struct miscdevice pm_qos_power_miscdev;
 	char *name;
-	s32 target_value;	/* Do not change to 64 bit */
-	s32 default_value;
-	enum pm_qos_type type;
 };
 
 static DEFINE_SPINLOCK(pm_qos_lock);
 
 static struct pm_qos_object null_pm_qos;
+
 static BLOCKING_NOTIFIER_HEAD(cpu_dma_lat_notifier);
-static struct pm_qos_object cpu_dma_pm_qos = {
-	.constraints = PLIST_HEAD_INIT(cpu_dma_pm_qos.constraints),
-	.notifiers = &cpu_dma_lat_notifier,
-	.name = "cpu_dma_latency",
+static struct pm_qos_constraints cpu_dma_constraints = {
+	.list = PLIST_HEAD_INIT(cpu_dma_constraints.list),
 	.target_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE,
 	.default_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE,
 	.type = PM_QOS_MIN,
+	.notifiers = &cpu_dma_lat_notifier,
+};
+static struct pm_qos_object cpu_dma_pm_qos = {
+	.constraints = &cpu_dma_constraints,
 };
 
 static BLOCKING_NOTIFIER_HEAD(network_lat_notifier);
-static struct pm_qos_object network_lat_pm_qos = {
-	.constraints = PLIST_HEAD_INIT(network_lat_pm_qos.constraints),
-	.notifiers = &network_lat_notifier,
-	.name = "network_latency",
+static struct pm_qos_constraints network_lat_constraints = {
+	.list = PLIST_HEAD_INIT(network_lat_constraints.list),
 	.target_value = PM_QOS_NETWORK_LAT_DEFAULT_VALUE,
 	.default_value = PM_QOS_NETWORK_LAT_DEFAULT_VALUE,
-	.type = PM_QOS_MIN
+	.type = PM_QOS_MIN,
+	.notifiers = &network_lat_notifier,
+};
+static struct pm_qos_object network_lat_pm_qos = {
+	.constraints = &network_lat_constraints,
+	.name = "network_latency",
 };
 
 
 static BLOCKING_NOTIFIER_HEAD(network_throughput_notifier);
-static struct pm_qos_object network_throughput_pm_qos = {
-	.constraints = PLIST_HEAD_INIT(network_throughput_pm_qos.constraints),
-	.notifiers = &network_throughput_notifier,
-	.name = "network_throughput",
+static struct pm_qos_constraints network_tput_constraints = {
+	.list = PLIST_HEAD_INIT(network_tput_constraints.list),
 	.target_value = PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE,
 	.default_value = PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE,
 	.type = PM_QOS_MAX,
+	.notifiers = &network_throughput_notifier,
+};
+static struct pm_qos_object network_throughput_pm_qos = {
+	.constraints = &network_tput_constraints,
+	.name = "network_throughput",
 };
 
 
@@ -129,15 +124,15 @@ static const struct file_operations pm_qos_power_fops = {
 /* unlocked internal variant */
 static inline int pm_qos_get_value(struct pm_qos_object *o)
 {
-	if (plist_head_empty(&o->constraints))
-		return o->default_value;
+	if (plist_head_empty(&o->constraints->list))
+		return o->constraints->default_value;
 
-	switch (o->type) {
+	switch (o->constraints->type) {
 	case PM_QOS_MIN:
-		return plist_first(&o->constraints)->prio;
+		return plist_first(&o->constraints->list)->prio;
 
 	case PM_QOS_MAX:
-		return plist_last(&o->constraints)->prio;
+		return plist_last(&o->constraints->list)->prio;
 
 	default:
 		/* runtime check for not using enum */
@@ -147,12 +142,12 @@ static inline int pm_qos_get_value(struct pm_qos_object *o)
 
 static inline s32 pm_qos_read_value(struct pm_qos_object *o)
 {
-	return o->target_value;
+	return o->constraints->target_value;
 }
 
 static inline void pm_qos_set_value(struct pm_qos_object *o, s32 value)
 {
-	o->target_value = value;
+	o->constraints->target_value = value;
 }
 
 static void update_target(struct pm_qos_object *o, struct plist_node *node,
@@ -170,20 +165,20 @@ static void update_target(struct pm_qos_object *o, struct plist_node *node,
 		 * with new value and add, then see if the extremal
 		 * changed
 		 */
-		plist_del(node, &o->constraints);
+		plist_del(node, &o->constraints->list);
 		plist_node_init(node, value);
-		plist_add(node, &o->constraints);
+		plist_add(node, &o->constraints->list);
 	} else if (del) {
-		plist_del(node, &o->constraints);
+		plist_del(node, &o->constraints->list);
 	} else {
-		plist_add(node, &o->constraints);
+		plist_add(node, &o->constraints->list);
 	}
 	curr_value = pm_qos_get_value(o);
 	pm_qos_set_value(o, curr_value);
 	spin_unlock_irqrestore(&pm_qos_lock, flags);
 
 	if (prev_value != curr_value)
-		blocking_notifier_call_chain(o->notifiers,
+		blocking_notifier_call_chain(o->constraints->notifiers,
 					     (unsigned long)curr_value,
 					     NULL);
 }
@@ -230,7 +225,7 @@ void pm_qos_add_request(struct pm_qos_request *req,
 		return;
 	}
 	if (value == PM_QOS_DEFAULT_VALUE)
-		new_value = o->default_value;
+		new_value = o->constraints->default_value;
 	else
 		new_value = value;
 	plist_node_init(&req->node, new_value);
@@ -266,7 +261,7 @@ void pm_qos_update_request(struct pm_qos_request *req,
 	o = pm_qos_array[req->pm_qos_class];
 
 	if (new_value == PM_QOS_DEFAULT_VALUE)
-		temp = o->default_value;
+		temp = o->constraints->default_value;
 	else
 		temp = new_value;
 
@@ -315,7 +310,8 @@ int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier)
 	int retval;
 
 	retval = blocking_notifier_chain_register(
-			pm_qos_array[pm_qos_class]->notifiers, notifier);
+			pm_qos_array[pm_qos_class]->constraints->notifiers,
+			notifier);
 
 	return retval;
 }
@@ -334,7 +330,8 @@ int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier)
 	int retval;
 
 	retval = blocking_notifier_chain_unregister(
-			pm_qos_array[pm_qos_class]->notifiers, notifier);
+			pm_qos_array[pm_qos_class]->constraints->notifiers,
+			notifier);
 
 	return retval;
 }
-- 
1.7.4.1

^ permalink raw reply related

* [PATCH 3/7] PM QoS: code re-organization
From: jean.pihet @ 2011-08-17 19:39 UTC (permalink / raw)
  To: Mark Brown, Kevin Hilman, markgross, Linux PM mailing list; +Cc: Jean Pihet
In-Reply-To: <1313609965-6568-1-git-send-email-j-pihet@ti.com>

From: Jean Pihet <j-pihet@ti.com>

Move around the PM QoS misc devices management code
for better readability.

Signed-off-by: Jean Pihet <j-pihet@ti.com>
---
 kernel/power/qos.c |   45 +++++++++++++++++++++++----------------------
 1 files changed, 23 insertions(+), 22 deletions(-)

diff --git a/kernel/power/qos.c b/kernel/power/qos.c
index aa52c44..788c4cf 100644
--- a/kernel/power/qos.c
+++ b/kernel/power/qos.c
@@ -188,28 +188,6 @@ static void update_target(struct pm_qos_object *o, struct plist_node *node,
 					     NULL);
 }
 
-static int register_pm_qos_misc(struct pm_qos_object *qos)
-{
-	qos->pm_qos_power_miscdev.minor = MISC_DYNAMIC_MINOR;
-	qos->pm_qos_power_miscdev.name = qos->name;
-	qos->pm_qos_power_miscdev.fops = &pm_qos_power_fops;
-
-	return misc_register(&qos->pm_qos_power_miscdev);
-}
-
-static int find_pm_qos_object_by_minor(int minor)
-{
-	int pm_qos_class;
-
-	for (pm_qos_class = 0;
-		pm_qos_class < PM_QOS_NUM_CLASSES; pm_qos_class++) {
-		if (minor ==
-			pm_qos_array[pm_qos_class]->pm_qos_power_miscdev.minor)
-			return pm_qos_class;
-	}
-	return -1;
-}
-
 /**
  * pm_qos_request - returns current system wide qos expectation
  * @pm_qos_class: identification of which qos value is requested
@@ -362,6 +340,29 @@ int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier)
 }
 EXPORT_SYMBOL_GPL(pm_qos_remove_notifier);
 
+/* User space interface to PM QoS classes via misc devices */
+static int register_pm_qos_misc(struct pm_qos_object *qos)
+{
+	qos->pm_qos_power_miscdev.minor = MISC_DYNAMIC_MINOR;
+	qos->pm_qos_power_miscdev.name = qos->name;
+	qos->pm_qos_power_miscdev.fops = &pm_qos_power_fops;
+
+	return misc_register(&qos->pm_qos_power_miscdev);
+}
+
+static int find_pm_qos_object_by_minor(int minor)
+{
+	int pm_qos_class;
+
+	for (pm_qos_class = 0;
+		pm_qos_class < PM_QOS_NUM_CLASSES; pm_qos_class++) {
+		if (minor ==
+			pm_qos_array[pm_qos_class]->pm_qos_power_miscdev.minor)
+			return pm_qos_class;
+	}
+	return -1;
+}
+
 static int pm_qos_power_open(struct inode *inode, struct file *filp)
 {
 	long pm_qos_class;
-- 
1.7.4.1

^ permalink raw reply related

* [PATCH 2/7] PM QoS: minor clean-ups
From: jean.pihet @ 2011-08-17 19:39 UTC (permalink / raw)
  To: Mark Brown, Kevin Hilman, markgross, Linux PM mailing list; +Cc: Jean Pihet
In-Reply-To: <1313609965-6568-1-git-send-email-j-pihet@ti.com>

From: Jean Pihet <j-pihet@ti.com>

- Misc fixes to improve code readability:
 . rename struct pm_qos_request_list to struct pm_qos_request,
 . rename pm_qos_req parameter to req in internal code,
   consistenly use req in the API parameters,
 . update the in-kernel API callers to the new parameters names,
 . rename of fields names (requests, list, node, constraints)

Signed-off-by: Jean Pihet <j-pihet@ti.com>
---
 drivers/media/video/via-camera.c       |    2 +-
 drivers/net/wireless/ipw2x00/ipw2100.c |    2 +-
 include/linux/netdevice.h              |    2 +-
 include/linux/pm_qos.h                 |   22 ++++----
 include/sound/pcm.h                    |    2 +-
 kernel/power/qos.c                     |   88 ++++++++++++++++----------------
 6 files changed, 59 insertions(+), 59 deletions(-)

diff --git a/drivers/media/video/via-camera.c b/drivers/media/video/via-camera.c
index b3ca389..fba6c64 100644
--- a/drivers/media/video/via-camera.c
+++ b/drivers/media/video/via-camera.c
@@ -69,7 +69,7 @@ struct via_camera {
 	struct mutex lock;
 	enum viacam_opstate opstate;
 	unsigned long flags;
-	struct pm_qos_request_list qos_request;
+	struct pm_qos_request qos_request;
 	/*
 	 * GPIO info for power/reset management
 	 */
diff --git a/drivers/net/wireless/ipw2x00/ipw2100.c b/drivers/net/wireless/ipw2x00/ipw2100.c
index aaab76c..db35f99 100644
--- a/drivers/net/wireless/ipw2x00/ipw2100.c
+++ b/drivers/net/wireless/ipw2x00/ipw2100.c
@@ -174,7 +174,7 @@ that only one external action is invoked at a time.
 #define DRV_DESCRIPTION	"Intel(R) PRO/Wireless 2100 Network Driver"
 #define DRV_COPYRIGHT	"Copyright(c) 2003-2006 Intel Corporation"
 
-static struct pm_qos_request_list ipw2100_pm_qos_req;
+static struct pm_qos_request ipw2100_pm_qos_req;
 
 /* Debugging stuff */
 #ifdef CONFIG_IPW2100_DEBUG
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index f72ac6b..f38ab5b 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -964,7 +964,7 @@ struct net_device {
 	 */
 	char			name[IFNAMSIZ];
 
-	struct pm_qos_request_list pm_qos_req;
+	struct pm_qos_request	pm_qos_req;
 
 	/* device name hash chain */
 	struct hlist_node	name_hlist;
diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h
index 7ba67541..6b0968f 100644
--- a/include/linux/pm_qos.h
+++ b/include/linux/pm_qos.h
@@ -20,30 +20,30 @@
 #define PM_QOS_NETWORK_LAT_DEFAULT_VALUE	(2000 * USEC_PER_SEC)
 #define PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE	0
 
-struct pm_qos_request_list {
-	struct plist_node list;
+struct pm_qos_request {
+	struct plist_node node;
 	int pm_qos_class;
 };
 
 #ifdef CONFIG_PM
-void pm_qos_add_request(struct pm_qos_request_list *l,
-			int pm_qos_class, s32 value);
-void pm_qos_update_request(struct pm_qos_request_list *pm_qos_req,
+void pm_qos_add_request(struct pm_qos_request *req, int pm_qos_class,
+			s32 value);
+void pm_qos_update_request(struct pm_qos_request *req,
 			   s32 new_value);
-void pm_qos_remove_request(struct pm_qos_request_list *pm_qos_req);
+void pm_qos_remove_request(struct pm_qos_request *req);
 
 int pm_qos_request(int pm_qos_class);
 int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier);
 int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier);
-int pm_qos_request_active(struct pm_qos_request_list *req);
+int pm_qos_request_active(struct pm_qos_request *req);
 #else
-static inline void pm_qos_add_request(struct pm_qos_request_list *l,
+static inline void pm_qos_add_request(struct pm_qos_request *req,
 				      int pm_qos_class, s32 value)
 			{ return; }
-static inline void pm_qos_update_request(struct pm_qos_request_list *pm_qos_req,
+static inline void pm_qos_update_request(struct pm_qos_request *req,
 					 s32 new_value)
 			{ return; }
-static inline void pm_qos_remove_request(struct pm_qos_request_list *pm_qos_req)
+static inline void pm_qos_remove_request(struct pm_qos_request *req)
 			{ return; }
 
 static inline int pm_qos_request(int pm_qos_class)
@@ -54,7 +54,7 @@ static inline int pm_qos_add_notifier(int pm_qos_class,
 static inline int pm_qos_remove_notifier(int pm_qos_class,
 					 struct notifier_block *notifier)
 			{ return 0; }
-static inline int pm_qos_request_active(struct pm_qos_request_list *req)
+static inline int pm_qos_request_active(struct pm_qos_request *req)
 			{ return 0; }
 #endif
 
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 666ee91..54cb079 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -373,7 +373,7 @@ struct snd_pcm_substream {
 	int number;
 	char name[32];			/* substream name */
 	int stream;			/* stream (direction) */
-	struct pm_qos_request_list latency_pm_qos_req; /* pm_qos request */
+	struct pm_qos_request latency_pm_qos_req; /* pm_qos request */
 	size_t buffer_bytes_max;	/* limit ring buffer size */
 	struct snd_dma_buffer dma_buffer;
 	unsigned int dma_buf_id;
diff --git a/kernel/power/qos.c b/kernel/power/qos.c
index 61b4738..aa52c44 100644
--- a/kernel/power/qos.c
+++ b/kernel/power/qos.c
@@ -45,7 +45,7 @@
 #include <linux/uaccess.h>
 
 /*
- * locking rule: all changes to requests or notifiers lists
+ * locking rule: all changes to constraints or notifiers lists
  * or pm_qos_object list and pm_qos_objects need to happen with pm_qos_lock
  * held, taken with _irqsave.  One lock to rule them all
  */
@@ -60,7 +60,7 @@ enum pm_qos_type {
  * types linux supports for 32 bit quantites
  */
 struct pm_qos_object {
-	struct plist_head requests;
+	struct plist_head constraints;
 	struct blocking_notifier_head *notifiers;
 	struct miscdevice pm_qos_power_miscdev;
 	char *name;
@@ -74,7 +74,7 @@ static DEFINE_SPINLOCK(pm_qos_lock);
 static struct pm_qos_object null_pm_qos;
 static BLOCKING_NOTIFIER_HEAD(cpu_dma_lat_notifier);
 static struct pm_qos_object cpu_dma_pm_qos = {
-	.requests = PLIST_HEAD_INIT(cpu_dma_pm_qos.requests),
+	.constraints = PLIST_HEAD_INIT(cpu_dma_pm_qos.constraints),
 	.notifiers = &cpu_dma_lat_notifier,
 	.name = "cpu_dma_latency",
 	.target_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE,
@@ -84,7 +84,7 @@ static struct pm_qos_object cpu_dma_pm_qos = {
 
 static BLOCKING_NOTIFIER_HEAD(network_lat_notifier);
 static struct pm_qos_object network_lat_pm_qos = {
-	.requests = PLIST_HEAD_INIT(network_lat_pm_qos.requests),
+	.constraints = PLIST_HEAD_INIT(network_lat_pm_qos.constraints),
 	.notifiers = &network_lat_notifier,
 	.name = "network_latency",
 	.target_value = PM_QOS_NETWORK_LAT_DEFAULT_VALUE,
@@ -95,7 +95,7 @@ static struct pm_qos_object network_lat_pm_qos = {
 
 static BLOCKING_NOTIFIER_HEAD(network_throughput_notifier);
 static struct pm_qos_object network_throughput_pm_qos = {
-	.requests = PLIST_HEAD_INIT(network_throughput_pm_qos.requests),
+	.constraints = PLIST_HEAD_INIT(network_throughput_pm_qos.constraints),
 	.notifiers = &network_throughput_notifier,
 	.name = "network_throughput",
 	.target_value = PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE,
@@ -129,15 +129,15 @@ static const struct file_operations pm_qos_power_fops = {
 /* unlocked internal variant */
 static inline int pm_qos_get_value(struct pm_qos_object *o)
 {
-	if (plist_head_empty(&o->requests))
+	if (plist_head_empty(&o->constraints))
 		return o->default_value;
 
 	switch (o->type) {
 	case PM_QOS_MIN:
-		return plist_first(&o->requests)->prio;
+		return plist_first(&o->constraints)->prio;
 
 	case PM_QOS_MAX:
-		return plist_last(&o->requests)->prio;
+		return plist_last(&o->constraints)->prio;
 
 	default:
 		/* runtime check for not using enum */
@@ -170,13 +170,13 @@ static void update_target(struct pm_qos_object *o, struct plist_node *node,
 		 * with new value and add, then see if the extremal
 		 * changed
 		 */
-		plist_del(node, &o->requests);
+		plist_del(node, &o->constraints);
 		plist_node_init(node, value);
-		plist_add(node, &o->requests);
+		plist_add(node, &o->constraints);
 	} else if (del) {
-		plist_del(node, &o->requests);
+		plist_del(node, &o->constraints);
 	} else {
-		plist_add(node, &o->requests);
+		plist_add(node, &o->constraints);
 	}
 	curr_value = pm_qos_get_value(o);
 	pm_qos_set_value(o, curr_value);
@@ -222,7 +222,7 @@ int pm_qos_request(int pm_qos_class)
 }
 EXPORT_SYMBOL_GPL(pm_qos_request);
 
-int pm_qos_request_active(struct pm_qos_request_list *req)
+int pm_qos_request_active(struct pm_qos_request *req)
 {
 	return req->pm_qos_class != 0;
 }
@@ -230,24 +230,24 @@ EXPORT_SYMBOL_GPL(pm_qos_request_active);
 
 /**
  * pm_qos_add_request - inserts new qos request into the list
- * @dep: pointer to a preallocated handle
+ * @req: pointer to a preallocated handle
  * @pm_qos_class: identifies which list of qos request to use
  * @value: defines the qos request
  *
  * This function inserts a new entry in the pm_qos_class list of requested qos
  * performance characteristics.  It recomputes the aggregate QoS expectations
- * for the pm_qos_class of parameters and initializes the pm_qos_request_list
+ * for the pm_qos_class of parameters and initializes the pm_qos_request
  * handle.  Caller needs to save this handle for later use in updates and
  * removal.
  */
 
-void pm_qos_add_request(struct pm_qos_request_list *dep,
+void pm_qos_add_request(struct pm_qos_request *req,
 			int pm_qos_class, s32 value)
 {
 	struct pm_qos_object *o =  pm_qos_array[pm_qos_class];
 	int new_value;
 
-	if (pm_qos_request_active(dep)) {
+	if (pm_qos_request_active(req)) {
 		WARN(1, KERN_ERR "pm_qos_add_request() called for already added request\n");
 		return;
 	}
@@ -255,15 +255,15 @@ void pm_qos_add_request(struct pm_qos_request_list *dep,
 		new_value = o->default_value;
 	else
 		new_value = value;
-	plist_node_init(&dep->list, new_value);
-	dep->pm_qos_class = pm_qos_class;
-	update_target(o, &dep->list, 0, PM_QOS_DEFAULT_VALUE);
+	plist_node_init(&req->node, new_value);
+	req->pm_qos_class = pm_qos_class;
+	update_target(o, &req->node, 0, PM_QOS_DEFAULT_VALUE);
 }
 EXPORT_SYMBOL_GPL(pm_qos_add_request);
 
 /**
  * pm_qos_update_request - modifies an existing qos request
- * @pm_qos_req : handle to list element holding a pm_qos request to use
+ * @req : handle to list element holding a pm_qos request to use
  * @value: defines the qos request
  *
  * Updates an existing qos request for the pm_qos_class of parameters along
@@ -271,56 +271,56 @@ EXPORT_SYMBOL_GPL(pm_qos_add_request);
  *
  * Attempts are made to make this code callable on hot code paths.
  */
-void pm_qos_update_request(struct pm_qos_request_list *pm_qos_req,
+void pm_qos_update_request(struct pm_qos_request *req,
 			   s32 new_value)
 {
 	s32 temp;
 	struct pm_qos_object *o;
 
-	if (!pm_qos_req) /*guard against callers passing in null */
+	if (!req) /*guard against callers passing in null */
 		return;
 
-	if (!pm_qos_request_active(pm_qos_req)) {
+	if (!pm_qos_request_active(req)) {
 		WARN(1, KERN_ERR "pm_qos_update_request() called for unknown object\n");
 		return;
 	}
 
-	o = pm_qos_array[pm_qos_req->pm_qos_class];
+	o = pm_qos_array[req->pm_qos_class];
 
 	if (new_value == PM_QOS_DEFAULT_VALUE)
 		temp = o->default_value;
 	else
 		temp = new_value;
 
-	if (temp != pm_qos_req->list.prio)
-		update_target(o, &pm_qos_req->list, 0, temp);
+	if (temp != req->node.prio)
+		update_target(o, &req->node, 0, temp);
 }
 EXPORT_SYMBOL_GPL(pm_qos_update_request);
 
 /**
  * pm_qos_remove_request - modifies an existing qos request
- * @pm_qos_req: handle to request list element
+ * @req: handle to request list element
  *
- * Will remove pm qos request from the list of requests and
+ * Will remove pm qos request from the list of constraints and
  * recompute the current target value for the pm_qos_class.  Call this
  * on slow code paths.
  */
-void pm_qos_remove_request(struct pm_qos_request_list *pm_qos_req)
+void pm_qos_remove_request(struct pm_qos_request *req)
 {
 	struct pm_qos_object *o;
 
-	if (pm_qos_req == NULL)
+	if (req == NULL)
 		return;
 		/* silent return to keep pcm code cleaner */
 
-	if (!pm_qos_request_active(pm_qos_req)) {
+	if (!pm_qos_request_active(req)) {
 		WARN(1, KERN_ERR "pm_qos_remove_request() called for unknown object\n");
 		return;
 	}
 
-	o = pm_qos_array[pm_qos_req->pm_qos_class];
-	update_target(o, &pm_qos_req->list, 1, PM_QOS_DEFAULT_VALUE);
-	memset(pm_qos_req, 0, sizeof(*pm_qos_req));
+	o = pm_qos_array[req->pm_qos_class];
+	update_target(o, &req->node, 1, PM_QOS_DEFAULT_VALUE);
+	memset(req, 0, sizeof(*req));
 }
 EXPORT_SYMBOL_GPL(pm_qos_remove_request);
 
@@ -368,7 +368,7 @@ static int pm_qos_power_open(struct inode *inode, struct file *filp)
 
 	pm_qos_class = find_pm_qos_object_by_minor(iminor(inode));
 	if (pm_qos_class >= 0) {
-               struct pm_qos_request_list *req = kzalloc(sizeof(*req), GFP_KERNEL);
+		struct pm_qos_request *req = kzalloc(sizeof(*req), GFP_KERNEL);
 		if (!req)
 			return -ENOMEM;
 
@@ -383,7 +383,7 @@ static int pm_qos_power_open(struct inode *inode, struct file *filp)
 
 static int pm_qos_power_release(struct inode *inode, struct file *filp)
 {
-	struct pm_qos_request_list *req;
+	struct pm_qos_request *req;
 
 	req = filp->private_data;
 	pm_qos_remove_request(req);
@@ -399,14 +399,14 @@ static ssize_t pm_qos_power_read(struct file *filp, char __user *buf,
 	s32 value;
 	unsigned long flags;
 	struct pm_qos_object *o;
-	struct pm_qos_request_list *pm_qos_req = filp->private_data;
+	struct pm_qos_request *req = filp->private_data;
 
-	if (!pm_qos_req)
+	if (!req)
 		return -EINVAL;
-	if (!pm_qos_request_active(pm_qos_req))
+	if (!pm_qos_request_active(req))
 		return -EINVAL;
 
-	o = pm_qos_array[pm_qos_req->pm_qos_class];
+	o = pm_qos_array[req->pm_qos_class];
 	spin_lock_irqsave(&pm_qos_lock, flags);
 	value = pm_qos_get_value(o);
 	spin_unlock_irqrestore(&pm_qos_lock, flags);
@@ -418,7 +418,7 @@ static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
 		size_t count, loff_t *f_pos)
 {
 	s32 value;
-	struct pm_qos_request_list *pm_qos_req;
+	struct pm_qos_request *req;
 
 	if (count == sizeof(s32)) {
 		if (copy_from_user(&value, buf, sizeof(s32)))
@@ -449,8 +449,8 @@ static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
 		return -EINVAL;
 	}
 
-	pm_qos_req = filp->private_data;
-	pm_qos_update_request(pm_qos_req, value);
+	req = filp->private_data;
+	pm_qos_update_request(req, value);
 
 	return count;
 }
-- 
1.7.4.1

^ permalink raw reply related

* [PATCH 1/7] PM QoS: move and rename the implementation files
From: jean.pihet @ 2011-08-17 19:39 UTC (permalink / raw)
  To: Mark Brown, Kevin Hilman, markgross, Linux PM mailing list; +Cc: Jean Pihet
In-Reply-To: <1313609965-6568-1-git-send-email-j-pihet@ti.com>

From: Jean Pihet <j-pihet@ti.com>

The PM QoS implementation files are better named
kernel/power/qos.c and include/linux/pm_qos.h.

The PM QoS support is compiled under the CONFIG_PM option.

Signed-off-by: Jean Pihet <j-pihet@ti.com>
---
 arch/arm/mach-msm/clock.c              |    2 +-
 drivers/acpi/processor_idle.c          |    2 +-
 drivers/cpuidle/cpuidle.c              |    2 +-
 drivers/cpuidle/governors/ladder.c     |    2 +-
 drivers/cpuidle/governors/menu.c       |    2 +-
 drivers/media/video/via-camera.c       |    2 +-
 drivers/net/e1000e/netdev.c            |    2 +-
 drivers/net/wireless/ipw2x00/ipw2100.c |    2 +-
 include/linux/netdevice.h              |    2 +-
 include/linux/pm_qos.h                 |   61 ++++
 include/linux/pm_qos_params.h          |   38 ---
 include/sound/pcm.h                    |    2 +-
 kernel/Makefile                        |    2 +-
 kernel/pm_qos_params.c                 |  481 --------------------------------
 kernel/power/Makefile                  |    2 +-
 kernel/power/qos.c                     |  481 ++++++++++++++++++++++++++++++++
 net/mac80211/main.c                    |    2 +-
 net/mac80211/mlme.c                    |    2 +-
 net/mac80211/scan.c                    |    2 +-
 sound/core/pcm_native.c                |    2 +-
 20 files changed, 558 insertions(+), 535 deletions(-)
 create mode 100644 include/linux/pm_qos.h
 delete mode 100644 include/linux/pm_qos_params.h
 delete mode 100644 kernel/pm_qos_params.c
 create mode 100644 kernel/power/qos.c

diff --git a/arch/arm/mach-msm/clock.c b/arch/arm/mach-msm/clock.c
index 22a5376..d9145df 100644
--- a/arch/arm/mach-msm/clock.c
+++ b/arch/arm/mach-msm/clock.c
@@ -18,7 +18,7 @@
 #include <linux/list.h>
 #include <linux/err.h>
 #include <linux/spinlock.h>
-#include <linux/pm_qos_params.h>
+#include <linux/pm_qos.h>
 #include <linux/mutex.h>
 #include <linux/clk.h>
 #include <linux/string.h>
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
index 431ab11..2e69e09 100644
--- a/drivers/acpi/processor_idle.c
+++ b/drivers/acpi/processor_idle.c
@@ -37,7 +37,7 @@
 #include <linux/dmi.h>
 #include <linux/moduleparam.h>
 #include <linux/sched.h>	/* need_resched() */
-#include <linux/pm_qos_params.h>
+#include <linux/pm_qos.h>
 #include <linux/clockchips.h>
 #include <linux/cpuidle.h>
 #include <linux/irqflags.h>
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index d4c5423..0df0141 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -12,7 +12,7 @@
 #include <linux/mutex.h>
 #include <linux/sched.h>
 #include <linux/notifier.h>
-#include <linux/pm_qos_params.h>
+#include <linux/pm_qos.h>
 #include <linux/cpu.h>
 #include <linux/cpuidle.h>
 #include <linux/ktime.h>
diff --git a/drivers/cpuidle/governors/ladder.c b/drivers/cpuidle/governors/ladder.c
index 12c9890..f62fde2 100644
--- a/drivers/cpuidle/governors/ladder.c
+++ b/drivers/cpuidle/governors/ladder.c
@@ -14,7 +14,7 @@
 
 #include <linux/kernel.h>
 #include <linux/cpuidle.h>
-#include <linux/pm_qos_params.h>
+#include <linux/pm_qos.h>
 #include <linux/moduleparam.h>
 #include <linux/jiffies.h>
 
diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c
index c47f3d0..3600f19 100644
--- a/drivers/cpuidle/governors/menu.c
+++ b/drivers/cpuidle/governors/menu.c
@@ -12,7 +12,7 @@
 
 #include <linux/kernel.h>
 #include <linux/cpuidle.h>
-#include <linux/pm_qos_params.h>
+#include <linux/pm_qos.h>
 #include <linux/time.h>
 #include <linux/ktime.h>
 #include <linux/hrtimer.h>
diff --git a/drivers/media/video/via-camera.c b/drivers/media/video/via-camera.c
index 85d3048..b3ca389 100644
--- a/drivers/media/video/via-camera.c
+++ b/drivers/media/video/via-camera.c
@@ -21,7 +21,7 @@
 #include <media/videobuf-dma-sg.h>
 #include <linux/delay.h>
 #include <linux/dma-mapping.h>
-#include <linux/pm_qos_params.h>
+#include <linux/pm_qos.h>
 #include <linux/via-core.h>
 #include <linux/via-gpio.h>
 #include <linux/via_i2c.h>
diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c
index ab4be80..40deb44 100644
--- a/drivers/net/e1000e/netdev.c
+++ b/drivers/net/e1000e/netdev.c
@@ -47,7 +47,7 @@
 #include <linux/if_vlan.h>
 #include <linux/cpu.h>
 #include <linux/smp.h>
-#include <linux/pm_qos_params.h>
+#include <linux/pm_qos.h>
 #include <linux/pm_runtime.h>
 #include <linux/aer.h>
 #include <linux/prefetch.h>
diff --git a/drivers/net/wireless/ipw2x00/ipw2100.c b/drivers/net/wireless/ipw2x00/ipw2100.c
index 3774dd0..aaab76c 100644
--- a/drivers/net/wireless/ipw2x00/ipw2100.c
+++ b/drivers/net/wireless/ipw2x00/ipw2100.c
@@ -161,7 +161,7 @@ that only one external action is invoked at a time.
 #include <linux/firmware.h>
 #include <linux/acpi.h>
 #include <linux/ctype.h>
-#include <linux/pm_qos_params.h>
+#include <linux/pm_qos.h>
 
 #include <net/lib80211.h>
 
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index ddee79b..f72ac6b 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -31,7 +31,7 @@
 #include <linux/if_link.h>
 
 #ifdef __KERNEL__
-#include <linux/pm_qos_params.h>
+#include <linux/pm_qos.h>
 #include <linux/timer.h>
 #include <linux/delay.h>
 #include <linux/atomic.h>
diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h
new file mode 100644
index 0000000..7ba67541
--- /dev/null
+++ b/include/linux/pm_qos.h
@@ -0,0 +1,61 @@
+#ifndef _LINUX_PM_QOS_H
+#define _LINUX_PM_QOS_H
+/* interface for the pm_qos_power infrastructure of the linux kernel.
+ *
+ * Mark Gross <mgross@linux.intel.com>
+ */
+#include <linux/plist.h>
+#include <linux/notifier.h>
+#include <linux/miscdevice.h>
+
+#define PM_QOS_RESERVED 0
+#define PM_QOS_CPU_DMA_LATENCY 1
+#define PM_QOS_NETWORK_LATENCY 2
+#define PM_QOS_NETWORK_THROUGHPUT 3
+
+#define PM_QOS_NUM_CLASSES 4
+#define PM_QOS_DEFAULT_VALUE -1
+
+#define PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE	(2000 * USEC_PER_SEC)
+#define PM_QOS_NETWORK_LAT_DEFAULT_VALUE	(2000 * USEC_PER_SEC)
+#define PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE	0
+
+struct pm_qos_request_list {
+	struct plist_node list;
+	int pm_qos_class;
+};
+
+#ifdef CONFIG_PM
+void pm_qos_add_request(struct pm_qos_request_list *l,
+			int pm_qos_class, s32 value);
+void pm_qos_update_request(struct pm_qos_request_list *pm_qos_req,
+			   s32 new_value);
+void pm_qos_remove_request(struct pm_qos_request_list *pm_qos_req);
+
+int pm_qos_request(int pm_qos_class);
+int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier);
+int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier);
+int pm_qos_request_active(struct pm_qos_request_list *req);
+#else
+static inline void pm_qos_add_request(struct pm_qos_request_list *l,
+				      int pm_qos_class, s32 value)
+			{ return; }
+static inline void pm_qos_update_request(struct pm_qos_request_list *pm_qos_req,
+					 s32 new_value)
+			{ return; }
+static inline void pm_qos_remove_request(struct pm_qos_request_list *pm_qos_req)
+			{ return; }
+
+static inline int pm_qos_request(int pm_qos_class)
+			{ return 0; }
+static inline int pm_qos_add_notifier(int pm_qos_class,
+				      struct notifier_block *notifier)
+			{ return 0; }
+static inline int pm_qos_remove_notifier(int pm_qos_class,
+					 struct notifier_block *notifier)
+			{ return 0; }
+static inline int pm_qos_request_active(struct pm_qos_request_list *req)
+			{ return 0; }
+#endif
+
+#endif
diff --git a/include/linux/pm_qos_params.h b/include/linux/pm_qos_params.h
deleted file mode 100644
index a7d87f9..0000000
--- a/include/linux/pm_qos_params.h
+++ /dev/null
@@ -1,38 +0,0 @@
-#ifndef _LINUX_PM_QOS_PARAMS_H
-#define _LINUX_PM_QOS_PARAMS_H
-/* interface for the pm_qos_power infrastructure of the linux kernel.
- *
- * Mark Gross <mgross@linux.intel.com>
- */
-#include <linux/plist.h>
-#include <linux/notifier.h>
-#include <linux/miscdevice.h>
-
-#define PM_QOS_RESERVED 0
-#define PM_QOS_CPU_DMA_LATENCY 1
-#define PM_QOS_NETWORK_LATENCY 2
-#define PM_QOS_NETWORK_THROUGHPUT 3
-
-#define PM_QOS_NUM_CLASSES 4
-#define PM_QOS_DEFAULT_VALUE -1
-
-#define PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE	(2000 * USEC_PER_SEC)
-#define PM_QOS_NETWORK_LAT_DEFAULT_VALUE	(2000 * USEC_PER_SEC)
-#define PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE	0
-
-struct pm_qos_request_list {
-	struct plist_node list;
-	int pm_qos_class;
-};
-
-void pm_qos_add_request(struct pm_qos_request_list *l, int pm_qos_class, s32 value);
-void pm_qos_update_request(struct pm_qos_request_list *pm_qos_req,
-		s32 new_value);
-void pm_qos_remove_request(struct pm_qos_request_list *pm_qos_req);
-
-int pm_qos_request(int pm_qos_class);
-int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier);
-int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier);
-int pm_qos_request_active(struct pm_qos_request_list *req);
-
-#endif
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 57e71fa..666ee91 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -29,7 +29,7 @@
 #include <linux/poll.h>
 #include <linux/mm.h>
 #include <linux/bitops.h>
-#include <linux/pm_qos_params.h>
+#include <linux/pm_qos.h>
 
 #define snd_pcm_substream_chip(substream) ((substream)->private_data)
 #define snd_pcm_chip(pcm) ((pcm)->private_data)
diff --git a/kernel/Makefile b/kernel/Makefile
index d06467f..e2f8f00 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -9,7 +9,7 @@ obj-y     = sched.o fork.o exec_domain.o panic.o printk.o \
 	    rcupdate.o extable.o params.o posix-timers.o \
 	    kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \
 	    hrtimer.o rwsem.o nsproxy.o srcu.o semaphore.o \
-	    notifier.o ksysfs.o pm_qos_params.o sched_clock.o cred.o \
+	    notifier.o ksysfs.o sched_clock.o cred.o \
 	    async.o range.o jump_label.o
 obj-y += groups.o
 
diff --git a/kernel/pm_qos_params.c b/kernel/pm_qos_params.c
deleted file mode 100644
index 37f05d0..0000000
--- a/kernel/pm_qos_params.c
+++ /dev/null
@@ -1,481 +0,0 @@
-/*
- * This module exposes the interface to kernel space for specifying
- * QoS dependencies.  It provides infrastructure for registration of:
- *
- * Dependents on a QoS value : register requests
- * Watchers of QoS value : get notified when target QoS value changes
- *
- * This QoS design is best effort based.  Dependents register their QoS needs.
- * Watchers register to keep track of the current QoS needs of the system.
- *
- * There are 3 basic classes of QoS parameter: latency, timeout, throughput
- * each have defined units:
- * latency: usec
- * timeout: usec <-- currently not used.
- * throughput: kbs (kilo byte / sec)
- *
- * There are lists of pm_qos_objects each one wrapping requests, notifiers
- *
- * User mode requests on a QOS parameter register themselves to the
- * subsystem by opening the device node /dev/... and writing there request to
- * the node.  As long as the process holds a file handle open to the node the
- * client continues to be accounted for.  Upon file release the usermode
- * request is removed and a new qos target is computed.  This way when the
- * request that the application has is cleaned up when closes the file
- * pointer or exits the pm_qos_object will get an opportunity to clean up.
- *
- * Mark Gross <mgross@linux.intel.com>
- */
-
-/*#define DEBUG*/
-
-#include <linux/pm_qos_params.h>
-#include <linux/sched.h>
-#include <linux/spinlock.h>
-#include <linux/slab.h>
-#include <linux/time.h>
-#include <linux/fs.h>
-#include <linux/device.h>
-#include <linux/miscdevice.h>
-#include <linux/string.h>
-#include <linux/platform_device.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-
-#include <linux/uaccess.h>
-
-/*
- * locking rule: all changes to requests or notifiers lists
- * or pm_qos_object list and pm_qos_objects need to happen with pm_qos_lock
- * held, taken with _irqsave.  One lock to rule them all
- */
-enum pm_qos_type {
-	PM_QOS_MAX,		/* return the largest value */
-	PM_QOS_MIN		/* return the smallest value */
-};
-
-/*
- * Note: The lockless read path depends on the CPU accessing
- * target_value atomically.  Atomic access is only guaranteed on all CPU
- * types linux supports for 32 bit quantites
- */
-struct pm_qos_object {
-	struct plist_head requests;
-	struct blocking_notifier_head *notifiers;
-	struct miscdevice pm_qos_power_miscdev;
-	char *name;
-	s32 target_value;	/* Do not change to 64 bit */
-	s32 default_value;
-	enum pm_qos_type type;
-};
-
-static DEFINE_SPINLOCK(pm_qos_lock);
-
-static struct pm_qos_object null_pm_qos;
-static BLOCKING_NOTIFIER_HEAD(cpu_dma_lat_notifier);
-static struct pm_qos_object cpu_dma_pm_qos = {
-	.requests = PLIST_HEAD_INIT(cpu_dma_pm_qos.requests),
-	.notifiers = &cpu_dma_lat_notifier,
-	.name = "cpu_dma_latency",
-	.target_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE,
-	.default_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE,
-	.type = PM_QOS_MIN,
-};
-
-static BLOCKING_NOTIFIER_HEAD(network_lat_notifier);
-static struct pm_qos_object network_lat_pm_qos = {
-	.requests = PLIST_HEAD_INIT(network_lat_pm_qos.requests),
-	.notifiers = &network_lat_notifier,
-	.name = "network_latency",
-	.target_value = PM_QOS_NETWORK_LAT_DEFAULT_VALUE,
-	.default_value = PM_QOS_NETWORK_LAT_DEFAULT_VALUE,
-	.type = PM_QOS_MIN
-};
-
-
-static BLOCKING_NOTIFIER_HEAD(network_throughput_notifier);
-static struct pm_qos_object network_throughput_pm_qos = {
-	.requests = PLIST_HEAD_INIT(network_throughput_pm_qos.requests),
-	.notifiers = &network_throughput_notifier,
-	.name = "network_throughput",
-	.target_value = PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE,
-	.default_value = PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE,
-	.type = PM_QOS_MAX,
-};
-
-
-static struct pm_qos_object *pm_qos_array[] = {
-	&null_pm_qos,
-	&cpu_dma_pm_qos,
-	&network_lat_pm_qos,
-	&network_throughput_pm_qos
-};
-
-static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
-		size_t count, loff_t *f_pos);
-static ssize_t pm_qos_power_read(struct file *filp, char __user *buf,
-		size_t count, loff_t *f_pos);
-static int pm_qos_power_open(struct inode *inode, struct file *filp);
-static int pm_qos_power_release(struct inode *inode, struct file *filp);
-
-static const struct file_operations pm_qos_power_fops = {
-	.write = pm_qos_power_write,
-	.read = pm_qos_power_read,
-	.open = pm_qos_power_open,
-	.release = pm_qos_power_release,
-	.llseek = noop_llseek,
-};
-
-/* unlocked internal variant */
-static inline int pm_qos_get_value(struct pm_qos_object *o)
-{
-	if (plist_head_empty(&o->requests))
-		return o->default_value;
-
-	switch (o->type) {
-	case PM_QOS_MIN:
-		return plist_first(&o->requests)->prio;
-
-	case PM_QOS_MAX:
-		return plist_last(&o->requests)->prio;
-
-	default:
-		/* runtime check for not using enum */
-		BUG();
-	}
-}
-
-static inline s32 pm_qos_read_value(struct pm_qos_object *o)
-{
-	return o->target_value;
-}
-
-static inline void pm_qos_set_value(struct pm_qos_object *o, s32 value)
-{
-	o->target_value = value;
-}
-
-static void update_target(struct pm_qos_object *o, struct plist_node *node,
-			  int del, int value)
-{
-	unsigned long flags;
-	int prev_value, curr_value;
-
-	spin_lock_irqsave(&pm_qos_lock, flags);
-	prev_value = pm_qos_get_value(o);
-	/* PM_QOS_DEFAULT_VALUE is a signal that the value is unchanged */
-	if (value != PM_QOS_DEFAULT_VALUE) {
-		/*
-		 * to change the list, we atomically remove, reinit
-		 * with new value and add, then see if the extremal
-		 * changed
-		 */
-		plist_del(node, &o->requests);
-		plist_node_init(node, value);
-		plist_add(node, &o->requests);
-	} else if (del) {
-		plist_del(node, &o->requests);
-	} else {
-		plist_add(node, &o->requests);
-	}
-	curr_value = pm_qos_get_value(o);
-	pm_qos_set_value(o, curr_value);
-	spin_unlock_irqrestore(&pm_qos_lock, flags);
-
-	if (prev_value != curr_value)
-		blocking_notifier_call_chain(o->notifiers,
-					     (unsigned long)curr_value,
-					     NULL);
-}
-
-static int register_pm_qos_misc(struct pm_qos_object *qos)
-{
-	qos->pm_qos_power_miscdev.minor = MISC_DYNAMIC_MINOR;
-	qos->pm_qos_power_miscdev.name = qos->name;
-	qos->pm_qos_power_miscdev.fops = &pm_qos_power_fops;
-
-	return misc_register(&qos->pm_qos_power_miscdev);
-}
-
-static int find_pm_qos_object_by_minor(int minor)
-{
-	int pm_qos_class;
-
-	for (pm_qos_class = 0;
-		pm_qos_class < PM_QOS_NUM_CLASSES; pm_qos_class++) {
-		if (minor ==
-			pm_qos_array[pm_qos_class]->pm_qos_power_miscdev.minor)
-			return pm_qos_class;
-	}
-	return -1;
-}
-
-/**
- * pm_qos_request - returns current system wide qos expectation
- * @pm_qos_class: identification of which qos value is requested
- *
- * This function returns the current target value.
- */
-int pm_qos_request(int pm_qos_class)
-{
-	return pm_qos_read_value(pm_qos_array[pm_qos_class]);
-}
-EXPORT_SYMBOL_GPL(pm_qos_request);
-
-int pm_qos_request_active(struct pm_qos_request_list *req)
-{
-	return req->pm_qos_class != 0;
-}
-EXPORT_SYMBOL_GPL(pm_qos_request_active);
-
-/**
- * pm_qos_add_request - inserts new qos request into the list
- * @dep: pointer to a preallocated handle
- * @pm_qos_class: identifies which list of qos request to use
- * @value: defines the qos request
- *
- * This function inserts a new entry in the pm_qos_class list of requested qos
- * performance characteristics.  It recomputes the aggregate QoS expectations
- * for the pm_qos_class of parameters and initializes the pm_qos_request_list
- * handle.  Caller needs to save this handle for later use in updates and
- * removal.
- */
-
-void pm_qos_add_request(struct pm_qos_request_list *dep,
-			int pm_qos_class, s32 value)
-{
-	struct pm_qos_object *o =  pm_qos_array[pm_qos_class];
-	int new_value;
-
-	if (pm_qos_request_active(dep)) {
-		WARN(1, KERN_ERR "pm_qos_add_request() called for already added request\n");
-		return;
-	}
-	if (value == PM_QOS_DEFAULT_VALUE)
-		new_value = o->default_value;
-	else
-		new_value = value;
-	plist_node_init(&dep->list, new_value);
-	dep->pm_qos_class = pm_qos_class;
-	update_target(o, &dep->list, 0, PM_QOS_DEFAULT_VALUE);
-}
-EXPORT_SYMBOL_GPL(pm_qos_add_request);
-
-/**
- * pm_qos_update_request - modifies an existing qos request
- * @pm_qos_req : handle to list element holding a pm_qos request to use
- * @value: defines the qos request
- *
- * Updates an existing qos request for the pm_qos_class of parameters along
- * with updating the target pm_qos_class value.
- *
- * Attempts are made to make this code callable on hot code paths.
- */
-void pm_qos_update_request(struct pm_qos_request_list *pm_qos_req,
-			   s32 new_value)
-{
-	s32 temp;
-	struct pm_qos_object *o;
-
-	if (!pm_qos_req) /*guard against callers passing in null */
-		return;
-
-	if (!pm_qos_request_active(pm_qos_req)) {
-		WARN(1, KERN_ERR "pm_qos_update_request() called for unknown object\n");
-		return;
-	}
-
-	o = pm_qos_array[pm_qos_req->pm_qos_class];
-
-	if (new_value == PM_QOS_DEFAULT_VALUE)
-		temp = o->default_value;
-	else
-		temp = new_value;
-
-	if (temp != pm_qos_req->list.prio)
-		update_target(o, &pm_qos_req->list, 0, temp);
-}
-EXPORT_SYMBOL_GPL(pm_qos_update_request);
-
-/**
- * pm_qos_remove_request - modifies an existing qos request
- * @pm_qos_req: handle to request list element
- *
- * Will remove pm qos request from the list of requests and
- * recompute the current target value for the pm_qos_class.  Call this
- * on slow code paths.
- */
-void pm_qos_remove_request(struct pm_qos_request_list *pm_qos_req)
-{
-	struct pm_qos_object *o;
-
-	if (pm_qos_req == NULL)
-		return;
-		/* silent return to keep pcm code cleaner */
-
-	if (!pm_qos_request_active(pm_qos_req)) {
-		WARN(1, KERN_ERR "pm_qos_remove_request() called for unknown object\n");
-		return;
-	}
-
-	o = pm_qos_array[pm_qos_req->pm_qos_class];
-	update_target(o, &pm_qos_req->list, 1, PM_QOS_DEFAULT_VALUE);
-	memset(pm_qos_req, 0, sizeof(*pm_qos_req));
-}
-EXPORT_SYMBOL_GPL(pm_qos_remove_request);
-
-/**
- * pm_qos_add_notifier - sets notification entry for changes to target value
- * @pm_qos_class: identifies which qos target changes should be notified.
- * @notifier: notifier block managed by caller.
- *
- * will register the notifier into a notification chain that gets called
- * upon changes to the pm_qos_class target value.
- */
-int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier)
-{
-	int retval;
-
-	retval = blocking_notifier_chain_register(
-			pm_qos_array[pm_qos_class]->notifiers, notifier);
-
-	return retval;
-}
-EXPORT_SYMBOL_GPL(pm_qos_add_notifier);
-
-/**
- * pm_qos_remove_notifier - deletes notification entry from chain.
- * @pm_qos_class: identifies which qos target changes are notified.
- * @notifier: notifier block to be removed.
- *
- * will remove the notifier from the notification chain that gets called
- * upon changes to the pm_qos_class target value.
- */
-int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier)
-{
-	int retval;
-
-	retval = blocking_notifier_chain_unregister(
-			pm_qos_array[pm_qos_class]->notifiers, notifier);
-
-	return retval;
-}
-EXPORT_SYMBOL_GPL(pm_qos_remove_notifier);
-
-static int pm_qos_power_open(struct inode *inode, struct file *filp)
-{
-	long pm_qos_class;
-
-	pm_qos_class = find_pm_qos_object_by_minor(iminor(inode));
-	if (pm_qos_class >= 0) {
-               struct pm_qos_request_list *req = kzalloc(sizeof(*req), GFP_KERNEL);
-		if (!req)
-			return -ENOMEM;
-
-		pm_qos_add_request(req, pm_qos_class, PM_QOS_DEFAULT_VALUE);
-		filp->private_data = req;
-
-		if (filp->private_data)
-			return 0;
-	}
-	return -EPERM;
-}
-
-static int pm_qos_power_release(struct inode *inode, struct file *filp)
-{
-	struct pm_qos_request_list *req;
-
-	req = filp->private_data;
-	pm_qos_remove_request(req);
-	kfree(req);
-
-	return 0;
-}
-
-
-static ssize_t pm_qos_power_read(struct file *filp, char __user *buf,
-		size_t count, loff_t *f_pos)
-{
-	s32 value;
-	unsigned long flags;
-	struct pm_qos_object *o;
-	struct pm_qos_request_list *pm_qos_req = filp->private_data;
-
-	if (!pm_qos_req)
-		return -EINVAL;
-	if (!pm_qos_request_active(pm_qos_req))
-		return -EINVAL;
-
-	o = pm_qos_array[pm_qos_req->pm_qos_class];
-	spin_lock_irqsave(&pm_qos_lock, flags);
-	value = pm_qos_get_value(o);
-	spin_unlock_irqrestore(&pm_qos_lock, flags);
-
-	return simple_read_from_buffer(buf, count, f_pos, &value, sizeof(s32));
-}
-
-static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
-		size_t count, loff_t *f_pos)
-{
-	s32 value;
-	struct pm_qos_request_list *pm_qos_req;
-
-	if (count == sizeof(s32)) {
-		if (copy_from_user(&value, buf, sizeof(s32)))
-			return -EFAULT;
-	} else if (count <= 11) { /* ASCII perhaps? */
-		char ascii_value[11];
-		unsigned long int ulval;
-		int ret;
-
-		if (copy_from_user(ascii_value, buf, count))
-			return -EFAULT;
-
-		if (count > 10) {
-			if (ascii_value[10] == '\n')
-				ascii_value[10] = '\0';
-			else
-				return -EINVAL;
-		} else {
-			ascii_value[count] = '\0';
-		}
-		ret = strict_strtoul(ascii_value, 16, &ulval);
-		if (ret) {
-			pr_debug("%s, 0x%lx, 0x%x\n", ascii_value, ulval, ret);
-			return -EINVAL;
-		}
-		value = (s32)lower_32_bits(ulval);
-	} else {
-		return -EINVAL;
-	}
-
-	pm_qos_req = filp->private_data;
-	pm_qos_update_request(pm_qos_req, value);
-
-	return count;
-}
-
-
-static int __init pm_qos_power_init(void)
-{
-	int ret = 0;
-
-	ret = register_pm_qos_misc(&cpu_dma_pm_qos);
-	if (ret < 0) {
-		printk(KERN_ERR "pm_qos_param: cpu_dma_latency setup failed\n");
-		return ret;
-	}
-	ret = register_pm_qos_misc(&network_lat_pm_qos);
-	if (ret < 0) {
-		printk(KERN_ERR "pm_qos_param: network_latency setup failed\n");
-		return ret;
-	}
-	ret = register_pm_qos_misc(&network_throughput_pm_qos);
-	if (ret < 0)
-		printk(KERN_ERR
-			"pm_qos_param: network_throughput setup failed\n");
-
-	return ret;
-}
-
-late_initcall(pm_qos_power_init);
diff --git a/kernel/power/Makefile b/kernel/power/Makefile
index c5ebc6a..ad6bdd8 100644
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -1,7 +1,7 @@
 
 ccflags-$(CONFIG_PM_DEBUG)	:= -DDEBUG
 
-obj-$(CONFIG_PM)		+= main.o
+obj-$(CONFIG_PM)		+= main.o qos.o
 obj-$(CONFIG_PM_SLEEP)		+= console.o
 obj-$(CONFIG_FREEZER)		+= process.o
 obj-$(CONFIG_SUSPEND)		+= suspend.o
diff --git a/kernel/power/qos.c b/kernel/power/qos.c
new file mode 100644
index 0000000..61b4738
--- /dev/null
+++ b/kernel/power/qos.c
@@ -0,0 +1,481 @@
+/*
+ * This module exposes the interface to kernel space for specifying
+ * QoS dependencies.  It provides infrastructure for registration of:
+ *
+ * Dependents on a QoS value : register requests
+ * Watchers of QoS value : get notified when target QoS value changes
+ *
+ * This QoS design is best effort based.  Dependents register their QoS needs.
+ * Watchers register to keep track of the current QoS needs of the system.
+ *
+ * There are 3 basic classes of QoS parameter: latency, timeout, throughput
+ * each have defined units:
+ * latency: usec
+ * timeout: usec <-- currently not used.
+ * throughput: kbs (kilo byte / sec)
+ *
+ * There are lists of pm_qos_objects each one wrapping requests, notifiers
+ *
+ * User mode requests on a QOS parameter register themselves to the
+ * subsystem by opening the device node /dev/... and writing there request to
+ * the node.  As long as the process holds a file handle open to the node the
+ * client continues to be accounted for.  Upon file release the usermode
+ * request is removed and a new qos target is computed.  This way when the
+ * request that the application has is cleaned up when closes the file
+ * pointer or exits the pm_qos_object will get an opportunity to clean up.
+ *
+ * Mark Gross <mgross@linux.intel.com>
+ */
+
+/*#define DEBUG*/
+
+#include <linux/pm_qos.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/string.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+
+#include <linux/uaccess.h>
+
+/*
+ * locking rule: all changes to requests or notifiers lists
+ * or pm_qos_object list and pm_qos_objects need to happen with pm_qos_lock
+ * held, taken with _irqsave.  One lock to rule them all
+ */
+enum pm_qos_type {
+	PM_QOS_MAX,		/* return the largest value */
+	PM_QOS_MIN		/* return the smallest value */
+};
+
+/*
+ * Note: The lockless read path depends on the CPU accessing
+ * target_value atomically.  Atomic access is only guaranteed on all CPU
+ * types linux supports for 32 bit quantites
+ */
+struct pm_qos_object {
+	struct plist_head requests;
+	struct blocking_notifier_head *notifiers;
+	struct miscdevice pm_qos_power_miscdev;
+	char *name;
+	s32 target_value;	/* Do not change to 64 bit */
+	s32 default_value;
+	enum pm_qos_type type;
+};
+
+static DEFINE_SPINLOCK(pm_qos_lock);
+
+static struct pm_qos_object null_pm_qos;
+static BLOCKING_NOTIFIER_HEAD(cpu_dma_lat_notifier);
+static struct pm_qos_object cpu_dma_pm_qos = {
+	.requests = PLIST_HEAD_INIT(cpu_dma_pm_qos.requests),
+	.notifiers = &cpu_dma_lat_notifier,
+	.name = "cpu_dma_latency",
+	.target_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE,
+	.default_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE,
+	.type = PM_QOS_MIN,
+};
+
+static BLOCKING_NOTIFIER_HEAD(network_lat_notifier);
+static struct pm_qos_object network_lat_pm_qos = {
+	.requests = PLIST_HEAD_INIT(network_lat_pm_qos.requests),
+	.notifiers = &network_lat_notifier,
+	.name = "network_latency",
+	.target_value = PM_QOS_NETWORK_LAT_DEFAULT_VALUE,
+	.default_value = PM_QOS_NETWORK_LAT_DEFAULT_VALUE,
+	.type = PM_QOS_MIN
+};
+
+
+static BLOCKING_NOTIFIER_HEAD(network_throughput_notifier);
+static struct pm_qos_object network_throughput_pm_qos = {
+	.requests = PLIST_HEAD_INIT(network_throughput_pm_qos.requests),
+	.notifiers = &network_throughput_notifier,
+	.name = "network_throughput",
+	.target_value = PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE,
+	.default_value = PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE,
+	.type = PM_QOS_MAX,
+};
+
+
+static struct pm_qos_object *pm_qos_array[] = {
+	&null_pm_qos,
+	&cpu_dma_pm_qos,
+	&network_lat_pm_qos,
+	&network_throughput_pm_qos
+};
+
+static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
+		size_t count, loff_t *f_pos);
+static ssize_t pm_qos_power_read(struct file *filp, char __user *buf,
+		size_t count, loff_t *f_pos);
+static int pm_qos_power_open(struct inode *inode, struct file *filp);
+static int pm_qos_power_release(struct inode *inode, struct file *filp);
+
+static const struct file_operations pm_qos_power_fops = {
+	.write = pm_qos_power_write,
+	.read = pm_qos_power_read,
+	.open = pm_qos_power_open,
+	.release = pm_qos_power_release,
+	.llseek = noop_llseek,
+};
+
+/* unlocked internal variant */
+static inline int pm_qos_get_value(struct pm_qos_object *o)
+{
+	if (plist_head_empty(&o->requests))
+		return o->default_value;
+
+	switch (o->type) {
+	case PM_QOS_MIN:
+		return plist_first(&o->requests)->prio;
+
+	case PM_QOS_MAX:
+		return plist_last(&o->requests)->prio;
+
+	default:
+		/* runtime check for not using enum */
+		BUG();
+	}
+}
+
+static inline s32 pm_qos_read_value(struct pm_qos_object *o)
+{
+	return o->target_value;
+}
+
+static inline void pm_qos_set_value(struct pm_qos_object *o, s32 value)
+{
+	o->target_value = value;
+}
+
+static void update_target(struct pm_qos_object *o, struct plist_node *node,
+			  int del, int value)
+{
+	unsigned long flags;
+	int prev_value, curr_value;
+
+	spin_lock_irqsave(&pm_qos_lock, flags);
+	prev_value = pm_qos_get_value(o);
+	/* PM_QOS_DEFAULT_VALUE is a signal that the value is unchanged */
+	if (value != PM_QOS_DEFAULT_VALUE) {
+		/*
+		 * to change the list, we atomically remove, reinit
+		 * with new value and add, then see if the extremal
+		 * changed
+		 */
+		plist_del(node, &o->requests);
+		plist_node_init(node, value);
+		plist_add(node, &o->requests);
+	} else if (del) {
+		plist_del(node, &o->requests);
+	} else {
+		plist_add(node, &o->requests);
+	}
+	curr_value = pm_qos_get_value(o);
+	pm_qos_set_value(o, curr_value);
+	spin_unlock_irqrestore(&pm_qos_lock, flags);
+
+	if (prev_value != curr_value)
+		blocking_notifier_call_chain(o->notifiers,
+					     (unsigned long)curr_value,
+					     NULL);
+}
+
+static int register_pm_qos_misc(struct pm_qos_object *qos)
+{
+	qos->pm_qos_power_miscdev.minor = MISC_DYNAMIC_MINOR;
+	qos->pm_qos_power_miscdev.name = qos->name;
+	qos->pm_qos_power_miscdev.fops = &pm_qos_power_fops;
+
+	return misc_register(&qos->pm_qos_power_miscdev);
+}
+
+static int find_pm_qos_object_by_minor(int minor)
+{
+	int pm_qos_class;
+
+	for (pm_qos_class = 0;
+		pm_qos_class < PM_QOS_NUM_CLASSES; pm_qos_class++) {
+		if (minor ==
+			pm_qos_array[pm_qos_class]->pm_qos_power_miscdev.minor)
+			return pm_qos_class;
+	}
+	return -1;
+}
+
+/**
+ * pm_qos_request - returns current system wide qos expectation
+ * @pm_qos_class: identification of which qos value is requested
+ *
+ * This function returns the current target value.
+ */
+int pm_qos_request(int pm_qos_class)
+{
+	return pm_qos_read_value(pm_qos_array[pm_qos_class]);
+}
+EXPORT_SYMBOL_GPL(pm_qos_request);
+
+int pm_qos_request_active(struct pm_qos_request_list *req)
+{
+	return req->pm_qos_class != 0;
+}
+EXPORT_SYMBOL_GPL(pm_qos_request_active);
+
+/**
+ * pm_qos_add_request - inserts new qos request into the list
+ * @dep: pointer to a preallocated handle
+ * @pm_qos_class: identifies which list of qos request to use
+ * @value: defines the qos request
+ *
+ * This function inserts a new entry in the pm_qos_class list of requested qos
+ * performance characteristics.  It recomputes the aggregate QoS expectations
+ * for the pm_qos_class of parameters and initializes the pm_qos_request_list
+ * handle.  Caller needs to save this handle for later use in updates and
+ * removal.
+ */
+
+void pm_qos_add_request(struct pm_qos_request_list *dep,
+			int pm_qos_class, s32 value)
+{
+	struct pm_qos_object *o =  pm_qos_array[pm_qos_class];
+	int new_value;
+
+	if (pm_qos_request_active(dep)) {
+		WARN(1, KERN_ERR "pm_qos_add_request() called for already added request\n");
+		return;
+	}
+	if (value == PM_QOS_DEFAULT_VALUE)
+		new_value = o->default_value;
+	else
+		new_value = value;
+	plist_node_init(&dep->list, new_value);
+	dep->pm_qos_class = pm_qos_class;
+	update_target(o, &dep->list, 0, PM_QOS_DEFAULT_VALUE);
+}
+EXPORT_SYMBOL_GPL(pm_qos_add_request);
+
+/**
+ * pm_qos_update_request - modifies an existing qos request
+ * @pm_qos_req : handle to list element holding a pm_qos request to use
+ * @value: defines the qos request
+ *
+ * Updates an existing qos request for the pm_qos_class of parameters along
+ * with updating the target pm_qos_class value.
+ *
+ * Attempts are made to make this code callable on hot code paths.
+ */
+void pm_qos_update_request(struct pm_qos_request_list *pm_qos_req,
+			   s32 new_value)
+{
+	s32 temp;
+	struct pm_qos_object *o;
+
+	if (!pm_qos_req) /*guard against callers passing in null */
+		return;
+
+	if (!pm_qos_request_active(pm_qos_req)) {
+		WARN(1, KERN_ERR "pm_qos_update_request() called for unknown object\n");
+		return;
+	}
+
+	o = pm_qos_array[pm_qos_req->pm_qos_class];
+
+	if (new_value == PM_QOS_DEFAULT_VALUE)
+		temp = o->default_value;
+	else
+		temp = new_value;
+
+	if (temp != pm_qos_req->list.prio)
+		update_target(o, &pm_qos_req->list, 0, temp);
+}
+EXPORT_SYMBOL_GPL(pm_qos_update_request);
+
+/**
+ * pm_qos_remove_request - modifies an existing qos request
+ * @pm_qos_req: handle to request list element
+ *
+ * Will remove pm qos request from the list of requests and
+ * recompute the current target value for the pm_qos_class.  Call this
+ * on slow code paths.
+ */
+void pm_qos_remove_request(struct pm_qos_request_list *pm_qos_req)
+{
+	struct pm_qos_object *o;
+
+	if (pm_qos_req == NULL)
+		return;
+		/* silent return to keep pcm code cleaner */
+
+	if (!pm_qos_request_active(pm_qos_req)) {
+		WARN(1, KERN_ERR "pm_qos_remove_request() called for unknown object\n");
+		return;
+	}
+
+	o = pm_qos_array[pm_qos_req->pm_qos_class];
+	update_target(o, &pm_qos_req->list, 1, PM_QOS_DEFAULT_VALUE);
+	memset(pm_qos_req, 0, sizeof(*pm_qos_req));
+}
+EXPORT_SYMBOL_GPL(pm_qos_remove_request);
+
+/**
+ * pm_qos_add_notifier - sets notification entry for changes to target value
+ * @pm_qos_class: identifies which qos target changes should be notified.
+ * @notifier: notifier block managed by caller.
+ *
+ * will register the notifier into a notification chain that gets called
+ * upon changes to the pm_qos_class target value.
+ */
+int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier)
+{
+	int retval;
+
+	retval = blocking_notifier_chain_register(
+			pm_qos_array[pm_qos_class]->notifiers, notifier);
+
+	return retval;
+}
+EXPORT_SYMBOL_GPL(pm_qos_add_notifier);
+
+/**
+ * pm_qos_remove_notifier - deletes notification entry from chain.
+ * @pm_qos_class: identifies which qos target changes are notified.
+ * @notifier: notifier block to be removed.
+ *
+ * will remove the notifier from the notification chain that gets called
+ * upon changes to the pm_qos_class target value.
+ */
+int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier)
+{
+	int retval;
+
+	retval = blocking_notifier_chain_unregister(
+			pm_qos_array[pm_qos_class]->notifiers, notifier);
+
+	return retval;
+}
+EXPORT_SYMBOL_GPL(pm_qos_remove_notifier);
+
+static int pm_qos_power_open(struct inode *inode, struct file *filp)
+{
+	long pm_qos_class;
+
+	pm_qos_class = find_pm_qos_object_by_minor(iminor(inode));
+	if (pm_qos_class >= 0) {
+               struct pm_qos_request_list *req = kzalloc(sizeof(*req), GFP_KERNEL);
+		if (!req)
+			return -ENOMEM;
+
+		pm_qos_add_request(req, pm_qos_class, PM_QOS_DEFAULT_VALUE);
+		filp->private_data = req;
+
+		if (filp->private_data)
+			return 0;
+	}
+	return -EPERM;
+}
+
+static int pm_qos_power_release(struct inode *inode, struct file *filp)
+{
+	struct pm_qos_request_list *req;
+
+	req = filp->private_data;
+	pm_qos_remove_request(req);
+	kfree(req);
+
+	return 0;
+}
+
+
+static ssize_t pm_qos_power_read(struct file *filp, char __user *buf,
+		size_t count, loff_t *f_pos)
+{
+	s32 value;
+	unsigned long flags;
+	struct pm_qos_object *o;
+	struct pm_qos_request_list *pm_qos_req = filp->private_data;
+
+	if (!pm_qos_req)
+		return -EINVAL;
+	if (!pm_qos_request_active(pm_qos_req))
+		return -EINVAL;
+
+	o = pm_qos_array[pm_qos_req->pm_qos_class];
+	spin_lock_irqsave(&pm_qos_lock, flags);
+	value = pm_qos_get_value(o);
+	spin_unlock_irqrestore(&pm_qos_lock, flags);
+
+	return simple_read_from_buffer(buf, count, f_pos, &value, sizeof(s32));
+}
+
+static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
+		size_t count, loff_t *f_pos)
+{
+	s32 value;
+	struct pm_qos_request_list *pm_qos_req;
+
+	if (count == sizeof(s32)) {
+		if (copy_from_user(&value, buf, sizeof(s32)))
+			return -EFAULT;
+	} else if (count <= 11) { /* ASCII perhaps? */
+		char ascii_value[11];
+		unsigned long int ulval;
+		int ret;
+
+		if (copy_from_user(ascii_value, buf, count))
+			return -EFAULT;
+
+		if (count > 10) {
+			if (ascii_value[10] == '\n')
+				ascii_value[10] = '\0';
+			else
+				return -EINVAL;
+		} else {
+			ascii_value[count] = '\0';
+		}
+		ret = strict_strtoul(ascii_value, 16, &ulval);
+		if (ret) {
+			pr_debug("%s, 0x%lx, 0x%x\n", ascii_value, ulval, ret);
+			return -EINVAL;
+		}
+		value = (s32)lower_32_bits(ulval);
+	} else {
+		return -EINVAL;
+	}
+
+	pm_qos_req = filp->private_data;
+	pm_qos_update_request(pm_qos_req, value);
+
+	return count;
+}
+
+
+static int __init pm_qos_power_init(void)
+{
+	int ret = 0;
+
+	ret = register_pm_qos_misc(&cpu_dma_pm_qos);
+	if (ret < 0) {
+		printk(KERN_ERR "pm_qos_param: cpu_dma_latency setup failed\n");
+		return ret;
+	}
+	ret = register_pm_qos_misc(&network_lat_pm_qos);
+	if (ret < 0) {
+		printk(KERN_ERR "pm_qos_param: network_latency setup failed\n");
+		return ret;
+	}
+	ret = register_pm_qos_misc(&network_throughput_pm_qos);
+	if (ret < 0)
+		printk(KERN_ERR
+			"pm_qos_param: network_throughput setup failed\n");
+
+	return ret;
+}
+
+late_initcall(pm_qos_power_init);
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 866f269..bb771e9 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -19,7 +19,7 @@
 #include <linux/if_arp.h>
 #include <linux/rtnetlink.h>
 #include <linux/bitmap.h>
-#include <linux/pm_qos_params.h>
+#include <linux/pm_qos.h>
 #include <linux/inetdevice.h>
 #include <net/net_namespace.h>
 #include <net/cfg80211.h>
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index d6470c7..9604abc 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -17,7 +17,7 @@
 #include <linux/if_arp.h>
 #include <linux/etherdevice.h>
 #include <linux/rtnetlink.h>
-#include <linux/pm_qos_params.h>
+#include <linux/pm_qos.h>
 #include <linux/crc32.h>
 #include <linux/slab.h>
 #include <net/mac80211.h>
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 6f09eca..beefb0a 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -14,7 +14,7 @@
 
 #include <linux/if_arp.h>
 #include <linux/rtnetlink.h>
-#include <linux/pm_qos_params.h>
+#include <linux/pm_qos.h>
 #include <net/sch_generic.h>
 #include <linux/slab.h>
 #include <net/mac80211.h>
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 1c6be91..c74e228 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -23,7 +23,7 @@
 #include <linux/file.h>
 #include <linux/slab.h>
 #include <linux/time.h>
-#include <linux/pm_qos_params.h>
+#include <linux/pm_qos.h>
 #include <linux/uio.h>
 #include <linux/dma-mapping.h>
 #include <sound/core.h>
-- 
1.7.4.1

^ permalink raw reply related

* [PATCH v6 0/7] PM QoS: add a per-device latency constraints framework
From: jean.pihet @ 2011-08-17 19:39 UTC (permalink / raw)
  To: Mark Brown, Kevin Hilman, markgross, Linux PM mailing list; +Cc: Jean Pihet

From: Jean Pihet <j-pihet@ti.com>

High level implementation:

1. Preparation of the PM QoS for the addition of a device PM QoS constraints
   framework:
  . rename and move of the PM QoS implementation files to kernel/power/qos.c
    and include/linux/pm_qos.h
  . rename of API parameters and internal fields names
  . Move around the PM QoS misc devices management code for better readability
  . re-organize the internal data structs
  . generalize and export the constraints management core code

2. Implementation of the per-device PM QoS constraints:
  . create drivers/base/power/qos.c for the implementation
  . create a device PM QoS API, which calls the PM QoS constraints management
    core code
  . the per-device latency constraints data strctures are stored in the device
    dev_pm_info struct
  . the device PM code calls the init and destroy of the per-device constraints
    data struct in order to support the dynamic insertion and removal of the
    devices in the system.
  . to minimize the data usage by the per-device constraints, the data struct
    is only allocated at the first call to dev_pm_qos_add_request. The data
    is later free'd when the device is removed from the system
  . per-device notification callbacks can be registered and called upon a
    change to the aggregated constraint value
  . a global mutex protects the constraints users from the data being
    allocated and free'd.

3. add a global notification mechanism for the device constraints
  . add a global notification chain that gets called upon changes to the
    aggregated constraint value for any device.
  . the notification callbacks are passing the full constraint request data
    in order for the callees to have access to it. The current use is for the
    platform low-level code to access the target device of the constraint


Questions:
1. the user space API is still under discussions on linux-omap and linux-pm MLs,
   cf. [1]. The idea is to add a user-space API for the devices constratins
   PM QoS, using a sysfs entry per device

[1] http://marc.info/?l=linux-omap&m=131232344503327&w=2

ToDo:
1. write Documentation for the new PM QoS class, once the code is agreed on
2. submit patches for the OMAP low level code to control the power domains
   states from the device constraints, using a global notification callback

   
Based on the master branch of the linux-omap git tree (3.1.0-rc1). Compile
tested using OMAP and x86 generic defconfigs.

Tested on OMAP3 Beagleboard (ES2.x).
Need testing on platforms other than OMAP, because of the impact on the
device insertion/removal in device_pm_add/remove


Changelog:

v6:
. Split the code into the generic PM QoS code and OMAP specific code; only
  the PM QoS is included in this release
. Added copyright in the device PM QoS code
. Re-organize the code for improved readability
. Changed the dev_pm_qos_* API functions type to int and improved the
  error checking
. Added kerneldoc compliant functions headers

v5:
. Added a global mutex to protect the per-device data allocation/free from
  the users
. More robust error checking in the device PM QoS code
. Clean-up of the device PM QoS code: refactor some duplicated code, removal
  of unneeded includes

v4:
. Complete devices PM QoS re-design:
  . Separation from the existing PM QoS classes by adding a device PM QoS
    API. The device code is re-using the PM QoS core code for constraints
    lists management and notification mechanism
  . Addition of a devices PM QoS global notification mechanism, for interaction
    with the low level platform code

v3:
. Complete PM QoS re-design after the comments on MLs
. Patch set split up for improved readability and easier maintenance

v2:
. Rework after comments on the mailing lists
. Added a notification of device insertion/removal from the device PM framework
. Validated on OMAP3 HW

v1:
. Initial implementation



Jean Pihet (7):
  PM QoS: move and rename the implementation files
  PM QoS: minor clean-ups
  PM QoS: code re-organization
  PM QoS: re-organize data structs
  PM QoS: generalize and export the constraints management code
  PM QoS: implement the per-device PM QoS constraints
  PM QoS: add a global notification mechanism for the device
    constraints

 arch/arm/mach-msm/clock.c              |    2 +-
 drivers/acpi/processor_idle.c          |    2 +-
 drivers/base/power/Makefile            |    4 +-
 drivers/base/power/main.c              |    3 +
 drivers/base/power/qos.c               |  400 ++++++++++++++++++++++++++
 drivers/cpuidle/cpuidle.c              |    2 +-
 drivers/cpuidle/governors/ladder.c     |    2 +-
 drivers/cpuidle/governors/menu.c       |    2 +-
 drivers/media/video/via-camera.c       |    4 +-
 drivers/net/e1000e/netdev.c            |    2 +-
 drivers/net/wireless/ipw2x00/ipw2100.c |    4 +-
 include/linux/netdevice.h              |    4 +-
 include/linux/pm.h                     |    9 +
 include/linux/pm_qos.h                 |  147 ++++++++++
 include/linux/pm_qos_params.h          |   38 ---
 include/sound/pcm.h                    |    4 +-
 kernel/Makefile                        |    2 +-
 kernel/pm_qos_params.c                 |  481 -------------------------------
 kernel/power/Makefile                  |    2 +-
 kernel/power/qos.c                     |  490 ++++++++++++++++++++++++++++++++
 net/mac80211/main.c                    |    2 +-
 net/mac80211/mlme.c                    |    2 +-
 net/mac80211/scan.c                    |    2 +-
 sound/core/pcm_native.c                |    2 +-
 24 files changed, 1071 insertions(+), 541 deletions(-)
 create mode 100644 drivers/base/power/qos.c
 create mode 100644 include/linux/pm_qos.h
 delete mode 100644 include/linux/pm_qos_params.h
 delete mode 100644 kernel/pm_qos_params.c
 create mode 100644 kernel/power/qos.c

-- 
1.7.4.1

^ permalink raw reply

* [GIT PULL] Power management build fix for 3.1
From: Rafael J. Wysocki @ 2011-08-17 18:41 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Linux PM mailing list, LKML, linux-sh

Hi Linus,

Please pull a power management build fix for 3.1 from:

git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git pm-fixes

This makes the new generic PM domains code build correctly
if CONFIG_PM_RUNTIME is unset.

Thanks!


 drivers/base/power/domain.c |   30 +++++++++++++++---------------
 include/linux/pm_domain.h   |   10 +++++++---
 kernel/power/Kconfig        |    4 ++++
 3 files changed, 26 insertions(+), 18 deletions(-)

---------------

Rafael J. Wysocki (1):
      PM / Domains: Fix build for CONFIG_PM_RUNTIME unset

^ permalink raw reply

* Re: 3.0-rc2 failed s2ram: Freezing of tasks failed after 20.00 seconds
From: Carlos R. Mafra @ 2011-08-17 13:47 UTC (permalink / raw)
  To: Rafael J. Wysocki; +Cc: Linux PM mailing list, LKML
In-Reply-To: <201108171114.45096.rjw@sisk.pl>

On Wed, 17 Aug 2011 at 11:14:44 +0200, Rafael J. Wysocki wrote:
> On Wednesday, August 17, 2011, Carlos R. Mafra wrote:
> > On Tue, 16 Aug 2011 at 23:20:28 +0200, Rafael J. Wysocki wrote:
> > > On Tuesday, August 16, 2011, Carlos R. Mafra wrote:
> > > > On Tue, 16 Aug 2011 at 20:13:55 +0200, Rafael J. Wysocki wrote:
> > > > > On Tuesday, August 16, 2011, Carlos R. Mafra wrote:
> > > > > > I started testing 3.0-rc2 yesterday and after a few successful suspend to ram
> > > > > > it did not suspend anymore and I got this:
> > > > > > 
> > > > > >  PM: Syncing filesystems ... done.
> > > > > >  PM: Preparing system for mem sleep
> > > > > >  Freezing user space processes ... 
> > > > > >  Freezing of tasks failed after 20.00 seconds (1 tasks refusing to freeze, wq_busy=0):
> > > > > >  udisks-daemon   D ffff8800a641e5d0     0  5848   5845 0x00800004
> > > > > >   ffff8800a6741928 0000000000000082 ffff880000000000 00000000000105c0
> > > > > >   ffff8800a641e200 00000000000105c0 ffff8800a6741fd8 00000000000105c0
> > > > > >   00000000000105c0 ffff8800a6740000 ffff8800a6741fd8 00000000000105c0
> > > > > >  Call Trace:
> > > > > >   [<ffffffff814cf355>] schedule_timeout+0x1c5/0x230
> > > > > >   [<ffffffff814ce8c9>] ? schedule+0x399/0x8a0
> > > > > >   [<ffffffff814ce3e0>] wait_for_common+0xc0/0x160
> > > > > >   [<ffffffff8103ec60>] ? try_to_wake_up+0x290/0x290
> > > > > >   [<ffffffff814d10ba>] ? _raw_spin_unlock_irq+0x2a/0x40
> > > > > >   [<ffffffff814ce528>] wait_for_completion+0x18/0x20
> > > > > >   [<ffffffff81059afb>] flush_work+0x2b/0x40
> > > > > >   [<ffffffff81058fd0>] ? do_work_for_cpu+0x30/0x30
> > > > > >   [<ffffffff8105a076>] flush_delayed_work+0x46/0x50
> > > > > >   [<ffffffff8121e6b6>] disk_clear_events+0x76/0x110
> > > > > >   [<ffffffff81106052>] check_disk_change+0x32/0x80
> > > > > >   [<ffffffff812ee649>] sd_open+0xb9/0x190
> > > > > >   [<ffffffff81106f81>] __blkdev_get+0x91/0x3d0
> > > > > >   [<ffffffff81107600>] ? blkdev_get+0x340/0x340
> > > > > >   [<ffffffff8110730e>] blkdev_get+0x4e/0x340
> > > > > >   [<ffffffff810e1b17>] ? do_lookup+0xb7/0x380
> > > > > >   [<ffffffff81107600>] ? blkdev_get+0x340/0x340
> > > > > >   [<ffffffff8110765d>] blkdev_open+0x5d/0x80
> > > > > >   [<ffffffff810d3d80>] __dentry_open+0x130/0x320
> > > > > >   [<ffffffff810d5021>] nameidata_to_filp+0x71/0x80
> > > > > >   [<ffffffff810e2ab1>] do_last+0xb1/0x800
> > > > > >   [<ffffffff810e4603>] path_openat+0xd3/0x3f0
> > > > > >   [<ffffffff81229077>] ? kobject_put+0x27/0x60
> > > > > >   [<ffffffff812ced62>] ? put_device+0x12/0x20
> > > > > >   [<ffffffff810e4984>] do_filp_open+0x44/0xa0
> > > > > >   [<ffffffff810f0a44>] ? alloc_fd+0xf4/0x150
> > > > > >   [<ffffffff810d512c>] do_sys_open+0xfc/0x1e0
> > > > > >   [<ffffffff810d3a86>] ? filp_close+0x56/0x80
> > > > > >   [<ffffffff810d522b>] sys_open+0x1b/0x20
> > > > > >   [<ffffffff814d1c3b>] system_call_fastpath+0x16/0x1b
> > > > > >  
> > > > > >  Restarting tasks ... done.
> > > > > >  video LNXVIDEO:01: Restoring backlight state
> > > > > >  acpid: 1 client rule loaded
> > > > > >  EXT4-fs (sda2): re-mounted. Opts: discard,commit=600
> > > > > >  INFO: task udisks-daemon:5848 blocked for more than 120 seconds.
> > > > > >  "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
> > > > > >  udisks-daemon   D ffff8800a641e5d0     0  5848   5845 0x00000004
> > > > > >   ffff8800a6741928 0000000000000082 ffff880000000000 00000000000105c0
> > > > > >   ffff8800a641e200 00000000000105c0 ffff8800a6741fd8 00000000000105c0
> > > > > >   00000000000105c0 ffff8800a6740000 ffff8800a6741fd8 00000000000105c0
> > > > > >  Call Trace:
> > > > > >   [<ffffffff814cf355>] schedule_timeout+0x1c5/0x230
> > > > > >   [<ffffffff814ce8c9>] ? schedule+0x399/0x8a0
> > > > > >   [<ffffffff814ce3e0>] wait_for_common+0xc0/0x160
> > > > > >   [<ffffffff8103ec60>] ? try_to_wake_up+0x290/0x290
> > > > > >   [<ffffffff814d10ba>] ? _raw_spin_unlock_irq+0x2a/0x40
> > > > > >   [<ffffffff814ce528>] wait_for_completion+0x18/0x20
> > > > > >   [<ffffffff81059afb>] flush_work+0x2b/0x40
> > > > > >   [<ffffffff81058fd0>] ? do_work_for_cpu+0x30/0x30
> > > > > >   [<ffffffff8105a076>] flush_delayed_work+0x46/0x50
> > > > > >   [<ffffffff8121e6b6>] disk_clear_events+0x76/0x110
> > > > > >   [<ffffffff81106052>] check_disk_change+0x32/0x80
> > > > > >   [<ffffffff812ee649>] sd_open+0xb9/0x190
> > > > > >   [<ffffffff81106f81>] __blkdev_get+0x91/0x3d0
> > > > > >   [<ffffffff81107600>] ? blkdev_get+0x340/0x340
> > > > > >   [<ffffffff8110730e>] blkdev_get+0x4e/0x340
> > > > > >   [<ffffffff810e1b17>] ? do_lookup+0xb7/0x380
> > > > > >   [<ffffffff81107600>] ? blkdev_get+0x340/0x340
> > > > > >   [<ffffffff8110765d>] blkdev_open+0x5d/0x80
> > > > > >   [<ffffffff810d3d80>] __dentry_open+0x130/0x320
> > > > > >   [<ffffffff810d5021>] nameidata_to_filp+0x71/0x80
> > > > > >   [<ffffffff810e2ab1>] do_last+0xb1/0x800
> > > > > >   [<ffffffff810e4603>] path_openat+0xd3/0x3f0
> > > > > >   [<ffffffff81229077>] ? kobject_put+0x27/0x60
> > > > > >   [<ffffffff812ced62>] ? put_device+0x12/0x20
> > > > > >   [<ffffffff810e4984>] do_filp_open+0x44/0xa0
> > > > > >   [<ffffffff810f0a44>] ? alloc_fd+0xf4/0x150
> > > > > >   [<ffffffff810d512c>] do_sys_open+0xfc/0x1e0
> > > > > >   [<ffffffff810d3a86>] ? filp_close+0x56/0x80
> > > > > >   [<ffffffff810d522b>] sys_open+0x1b/0x20
> > > > > >   [<ffffffff814d1c3b>] system_call_fastpath+0x16/0x1b
> > > > > >   
> > > > > > 
> > > > > > and there are some more of these traces in the logs, but they all look the 
> > > > > > same.
> > > > > 
> > > > > It looks like udisks-daemon is waiting for a completion that's
> > > > > never completed.
> > > > > 
> > > > > Do you use any removable storage devices?
> > > > 
> > > > Yes, external harddisks connected via USB. But none were mounted
> > > > when I closed the lid to suspend.
> > > 
> > > Were they connected to the USB ports?
> > 
> > No.
> > 
> > > > PS: I made a mistake in the Subject:, the kernel is 3.1-rc2 and
> > > > not 3.0-rc2.
> > > 
> > > Did 3.0 work correctly?
> > 
> > I didn't test 3.0, but 2.6.39 always worked.
> 
> Can you try 3.0.y too, please?  It would give us the time frame the
> error started to happen.

I tested 3.0.2 this morning and it survived (suspended it about 5-6 times
with some mount/unmount of external harddisks in between).

Now I'm back to testing 3.1-rc2 and it survived the first two suspends.
So this is not something which I can bisect with 100% certainty. I was 
hoping the above traces could somehow indicate where the problem might be.

I will keep testing though.

^ permalink raw reply

* Re: [PATCH v4 0/3] DEVFREQ, DVFS framework for non-CPU devices
From: MyungJoo Ham @ 2011-08-17 10:07 UTC (permalink / raw)
  To: Turquette, Mike
  Cc: Len Brown, Greg Kroah-Hartman, Kyungmin Park, Thomas Gleixner,
	linux-pm
In-Reply-To: <CAJOA=zNRidM+y6FokWH9xorinQMWKHTyCGXoTNYCpLOfNPsYNA@mail.gmail.com>

On Thu, Aug 11, 2011 at 10:28 AM, Turquette, Mike <mturquette@ti.com> wrote:
> On Mon, Aug 8, 2011 at 10:27 PM, MyungJoo Ham <myungjoo.ham@gmail.com> wrote:
>> On Tue, Aug 9, 2011 at 4:13 AM, Turquette, Mike <mturquette@ti.com> wrote:
>>> I'm not getting too bogged down with the OPP specifics because I don't
>>> know if that interface is going to be used in the future, and I don't
>>> think that devfreq will need to know about OPPs once the DVFS QoS API
>>> exists.  In that case, devfreq can just requests clock frequencies
>>> through the QoS API.  I view devfreq's usage of the OPP library as
>>> temporary.
>>>
>>> The real issue here is that we don't want some weird feedback loop
>>> with device QoS requests and devfreq targets stepping on each other.
>>>
>>> One way to handle this is to partition QoS use in drivers away from
>>> devfreq usage.  For example, if a GPU supports performance counters
>>> and can introspect its own usage, then it is a perfect candidate for
>>> devfreq; on the flip-side device drivers should *not* be allowed to
>>> put performance/qos constraints on this particular GPU, since we
>>> assume that the performance counters/devfreq governor will handle the
>>> whole job for us.  Since this centralizes the decision-making for the
>>> GPU it is safe for the devfreq->target() call to use the QoS APIs for
>>> controlling the GPU, since no one else will.  This avoids the feedback
>>> loop.
>>>
>>> On the other hand, if the GPU does not support performance counters
>>> then it should not use devfreq at all and rely 100% of QoS constraints
>>> from various sources: the GPU driver might request a high OPP every
>>> time work comes in, coupled with a timeout; if a QoS knob is exported
>>> to userspace then some OpenGL library might hold constraints through
>>> it; or some other kernel driver (video-related?) needs to use the GPU
>>> then it can hold a constraint through the QoS API.
>>>
>>> So there is a clear partition of QoS API usage between devices that
>>> support performance counters and ones that do not.  We want to avoid a
>>> feedback loop here.
>>
>> Such weird feedback loop may happen if the result frequency ranges
>> from QoS are inconsistent with those from DEVFREQ. However, if DEVFREQ
>> provides frequencies that do not violate QoS requirements, there would
>> be no such feedback loop. If they are consistent (DEVFREQ never gives
>> frequencies that do NOT satisfy QoS), DEVFREQ is transparent to PM
>> QoS.
>
> If two devices, x and y both place frequency constraints on device z,
> then QoS should probably aggregate their requests instead of just
> taking the max.  Then if devfreq also determines that it needs to make
> a QoS request against device z, then that will get aggregated on top
> of x and y's requests.  This isn't really what we want since x and y's
> requests may be adequate and devfreq's performance counter sampling is
> just requesting performance for operations which have already placed
> requests.

DEVFREQ won't give contradicting frequencies with PM QoS inputs to the
target device. DEVFREQ will choose one from frequencies that satisfy
the PM QoS inputs from both x and y (probably aggregated by PM QoS
framework) as long as the PM QoS part of the target device driver is
properly implemented by enabling satisfying OPPs and disabling
not-satisfying OPPs.

>
>> For now, with OPPs, DEVFREQ will always select frequencies that
>> satisfy QoS requirements if the driver implementation is correct.
>>
>> However, as you mentioned, if we assume that OPP is going to disappear
>> in the future kernel, then we will somehow need to design an interface
>> for device drivers to provide lists of available frequencies to
>> DEVFREQ and those lists are going to be runtime-update-able and where
>> QoS requirements are applied. As with the current DEVFREQ
>
> I'm not saying that OPP is going to disappear because I have no idea
> what the future holds.
>
> My point is that devfreq doesn't need to know about OPP at all.  The
> code assumes that OPPs exist for the device and take a frequency as
> part of devfreq->profile->get_target_freq (or something like that) and
> they can just pass that frequency to devfreq->profile->target.  If
> that device has support for the OPP library than it can do the job of
> looking up OPPs, etc.

As discussed in the other thread, OPP is the one that describes the
data every DVFS-able device have. Is there any reason to redundantly
define the same data structure and functions that are publicly
available and represent the same things? Every DEVFREQ device will
have multiple pairs of frequencies and voltages associated with the
specific device and use the functions equivalent to those in OPP
library.

>
> Regards,
> Mike
>

Cheers!
MyungJoo
-- 
MyungJoo Ham, Ph.D.
Mobile Software Platform Lab,
Digital Media and Communications (DMC) Business
Samsung Electronics
cell: 82-10-6714-2858

^ permalink raw reply

* Re: [PATCH v5 1/3] PM: Introduce DEVFREQ: generic DVFS framework with device-specific OPPs
From: MyungJoo Ham @ 2011-08-17  9:40 UTC (permalink / raw)
  To: Turquette, Mike
  Cc: Len Brown, Greg Kroah-Hartman, Kyungmin Park, linux-pm,
	Thomas Gleixner
In-Reply-To: <CAJOA=zNb_S-CXDBE0VH98A-0OTB7HRiCAeV89LW7t8ZoaydBVg@mail.gmail.com>

On Thu, Aug 11, 2011 at 10:00 AM, Turquette, Mike <mturquette@ti.com> wrote:
>> +static unsigned int devfreq_interval = 20;
>
> Instead of devfreq_interval, how about devices specify their
> devfreq->profile->polling_ms which gets turned into a jiffies value
> called devfreq->profile->next_monitor?  devfreq->profile->next_monitor
> is a jiffies value some time in the future.
>
> When the monitor is run then devfreq->profile->next_monitor will
> simply be "jiffies + msecs_to_jiffies(devfreq->profile->polling_ms)".
> That would remove the unnecessary interval which is pretty restrictive
> for someone that *really* wants a 50ms polling interval (not a
> multiple of 20).
>
> Then something like the following can be done in devfreq_monitor:
>
>        unsigned long next_monitor = UINT_MAX;
>        for_each_entry(devfreq, &devfreq_list, node) {
>                next_monitor = minimum(next_monitor, devfreq->next_monitor);
>        }
>        queue_delayed_work(devfreq_wq, &devfreq_work, next_monitor);
>

Thanks. In the patchset v6, I'll let it based on simply JIFFY. In the
v6 rc, I've removed devfreq_interval.

>> +
>> +/*
>> + * devfreq_work periodically (given by devfreq_interval) monitors every
>> + * registered device.
>> + */
>> +static bool polling;
>
> Since patchset v5 removes tickle (so as not to duplicate a QoS
> constraint), devfreq should no longer handle any non-polling DVFS
> transitions, which can also be expressed through a QoS constraint.
> Any static constraint requests from other drivers will use the QoS API
> and not devfreq.  As such "polling" can be removed and all the code
> that touches it since polling is assumed.  Why use devfreq if you're
> not going to poll?
>
> This also makes the performance and powersave governors redundant, and
> possibly the userspace governor as well since QoS requests can just as
> easily achieve the sort of constraints desirable for a non-polling
> implementation.
>

I guess this is resolved in the other thread regarding patch 2/3.

> This also touches on another point that I'll elaborate more on below,
> which is that timer queueing/scheduling should be implemented by the
> governors, not the core code.
[]
> Polling should not be implemented in core code.  Instead the devfreq
> devices should define their own delayed_work and governors should
> schedule that work, possibly with governor-specific work queues, or
> using the kernel global workqueue.  E.g: simple-ondemand should have
> its own workqueue and each device have it's own delayed_work;
> something like devfreq->governor->wq and devfreq->work would suffice.
>
> This is better aligned with CPUfreq, which leaves polling details to
> the governors.  For example cpufreq-ondemand has delayed_work for each
> CPU and that governor schedules the work, not core code.  Core code
> handles little more than registration and sysfs events (including
> switching governors).

The first paragraphs in the reply of the thread of patch 2/3 discusses
this issue: in brief,
unlike CPUFREQ, allowing governors to loop itself increases overhead
and duplicated code.

>
> Some other benefits are increased debugging capability (we know which
> governor failed, we know which device failed, etc) by separating the
> work queues and the work structs and it simplifies having to figure
> out some min_polling/next_polling since workqueues do that for us when
> we schedule multiple pieces of work, all with different delays.

At an error in DEVFREQ polling process, the corresponding device's
DEVFREQ is removed from polling and an error message is printed. Thus,
the user should be able to know which device has failed with what
errno already. The governor is designated by DEVFREQ user, so the user
should know which governor is failing by knowing which device is
failing. Or, we may simply add governor name at the error message in
devfreq_monitor().

>
>> +static int devfreq_do(struct devfreq *devfreq)
>> +{
>> +       struct opp *opp;
>> +       unsigned long freq;
>> +       int err;
>> +
>> +       err = devfreq->governor->get_target_freq(devfreq, &freq);
>
> Following along with my comments above, the semantics of calling
> get_target_freq in devfreq_do are backwards.  This code should go in
> the governor and is analogous to the various dbs_check_cpu functions
> in those governors.

By limiting the governors' role as recommending appropriate
frequencies only, we reduce redundant code across governors and
simplifies implementation of governors. As long as we might need to
implement custom governors for different type of devices (especially
for GPUs and MMCs), this can ease the effort later. I don't think we
need to create redundant code here across governors at least at this
stage.

>
>> +       if (err)
>> +               return err;
>> +
>> +       opp = opp_find_freq_ceil(devfreq->dev, &freq);
>
> I know that devfreq was originally developed with OPP as a
> requirement, but there is no reason to include such details in
> devfreq.  Many platforms may not adopt the OPP libary.
>
> How about the governor calls devfreq->profile->get_target_freq in it's
> decision making function (e.g. dbs_check_cpu) and then calls
> devfreq->profile->target with the frequency from that same function,
> only this time passing in the frequency instead of an OPP?
>
> That target function can determine what to do with the frequency.  In
> the case of Exoyns4 the target function can use the OPP library.  In
> the case of a simpler device it might just make a clk_set_rate call.
> This also removes the drama from V1 of this patchset where the use of
> OPP library itself was disputed and allows devfreq to be more generic.
>

A device that is capable of DVFS has multiple pairs of frequency and
voltage. OPP is a data structure to represent multiple pairs of
frequency and voltage of a device. Without OPP, DEVFREQ requires to
implement data structure to represent multiple pairs of frequency and
voltage per device. Why would we implement another data structure that
represents the same thing? Besides, for devices already with OPPs,
their device driver needs to make two copies of data with different
type representing the exactly same things.

Actually, later on (seems not just yet anyway), OPP may need to loosen
up the condition in Kconfig in case non-SoC-dependent devices starting
to use DVFS feature, or even, CPUFREQ may use OPP as its data
structure for listing available frequencies.

>> +               next_poll_min = (next_poll_min > devfreq->next_polling) ?
>> +                               devfreq->next_polling : next_poll_min;
>> +       }
>
> Again, I find this whole method of using devfreq_interval and
> evaluating every single device in a single delayed_work in a single
> workqueue unintuitive.  workqueues already do the job of taking many
> pieces of future work and ordering them based on their delay, so why
> should we replicate that pattern here by grouping all work together
> and manually determining when to run the monitor?


>
>> +       struct devfreq *new_devfreq, *devfreq;
>
> Why use new_devfreq at all?  devfreq can be reused after checking for
> error conditions.

Thanks. I will remove new_devfreq.

>
>> +       if (!IS_ERR(devfreq)) {
>> +               dev_err(dev, "%s: Unable to create devfreq for the device. "
>> +                       "It already has one.\n", __func__);
>
> Put the error string all on one line, even if it goes past 80 chars.

will correct that one.

>> +int devfreq_update(struct device *dev)
>
> OPP library should implement notifiers which devfreq can subscribe to,
> instead of hacking this code into the OPP libary.  I've looped in
> Nishanth Menon, author of OPP library, to comment.
>
>> +       devfreq_update(dev);
>
> Same comment as above.
>
>> +       devfreq_update(dev);
>
> Same comment as above.
>

The next revision won't use devfreq_update. Thanks.


Cheers!
MyungJoo

-- 
MyungJoo Ham (함명주), Ph.D.
Mobile Software Platform Lab,
Digital Media and Communications (DMC) Business
Samsung Electronics
cell: 82-10-6714-2858
_______________________________________________
linux-pm mailing list
linux-pm@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

^ permalink raw reply

* Re: [patch 1/1] [PATCH] include storage keys in hibernation image.
From: Rafael J. Wysocki @ 2011-08-17  9:15 UTC (permalink / raw)
  To: Martin Schwidefsky; +Cc: linux-s390, linux-pm, Jiri Slaby, linux-kernel
In-Reply-To: <20110817094347.3c106bd9@mschwide>

On Wednesday, August 17, 2011, Martin Schwidefsky wrote:
> On Tue, 16 Aug 2011 19:56:47 +0200
> "Rafael J. Wysocki" <rjw@sisk.pl> wrote:
> 
> > On Tuesday, August 16, 2011, Martin Schwidefsky wrote:
> > > From: Martin Schwidefsky <schwidefsky@de.ibm.com>
> > > 
> > > For s390 there is one additional byte associated with each page,
> > > the storage key. This byte contains the referenced and changed
> > > bits and needs to be included into the hibernation image.
> > > If the storage keys are not restored to their previous state all
> > > original pages would appear to be dirty. This can cause
> > > inconsistencies e.g. with read-only filesystems.
> > > 
> > > Cc: Pavel Machek <pavel@ucw.cz>
> > > Cc: Rafael J. Wysocki <rjw@sisk.pl>
> > > Cc: Jiri Slaby <jslaby@suse.cz>
> > > Cc: linux-pm@lists.linux-foundation.org
> > > Cc: linux-kernel@vger.kernel.org
> > > Cc: linux-s390@vger.kernel.org
> > > Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
> > 
> > OK, I don't have any complaints.  Do you want me to take this
> > patch or do you want to push it through the s390 tree?
> 
> The patch affects the common power management code. At minimum I
> would like an acked-by to take it into the s390 tree. I would feel
> more comfortable if you'd take it via the power management tree
> though.

OK, I will.  Expect it to appear in 3.2. :-)

Thanks,
Rafael

^ permalink raw reply

* Re: 3.0-rc2 failed s2ram: Freezing of tasks failed after 20.00 seconds
From: Rafael J. Wysocki @ 2011-08-17  9:14 UTC (permalink / raw)
  To: Carlos R. Mafra; +Cc: Linux PM mailing list, LKML
In-Reply-To: <20110816224500.GA3152@Pilar.site>

On Wednesday, August 17, 2011, Carlos R. Mafra wrote:
> On Tue, 16 Aug 2011 at 23:20:28 +0200, Rafael J. Wysocki wrote:
> > On Tuesday, August 16, 2011, Carlos R. Mafra wrote:
> > > On Tue, 16 Aug 2011 at 20:13:55 +0200, Rafael J. Wysocki wrote:
> > > > On Tuesday, August 16, 2011, Carlos R. Mafra wrote:
> > > > > I started testing 3.0-rc2 yesterday and after a few successful suspend to ram
> > > > > it did not suspend anymore and I got this:
> > > > > 
> > > > >  PM: Syncing filesystems ... done.
> > > > >  PM: Preparing system for mem sleep
> > > > >  Freezing user space processes ... 
> > > > >  Freezing of tasks failed after 20.00 seconds (1 tasks refusing to freeze, wq_busy=0):
> > > > >  udisks-daemon   D ffff8800a641e5d0     0  5848   5845 0x00800004
> > > > >   ffff8800a6741928 0000000000000082 ffff880000000000 00000000000105c0
> > > > >   ffff8800a641e200 00000000000105c0 ffff8800a6741fd8 00000000000105c0
> > > > >   00000000000105c0 ffff8800a6740000 ffff8800a6741fd8 00000000000105c0
> > > > >  Call Trace:
> > > > >   [<ffffffff814cf355>] schedule_timeout+0x1c5/0x230
> > > > >   [<ffffffff814ce8c9>] ? schedule+0x399/0x8a0
> > > > >   [<ffffffff814ce3e0>] wait_for_common+0xc0/0x160
> > > > >   [<ffffffff8103ec60>] ? try_to_wake_up+0x290/0x290
> > > > >   [<ffffffff814d10ba>] ? _raw_spin_unlock_irq+0x2a/0x40
> > > > >   [<ffffffff814ce528>] wait_for_completion+0x18/0x20
> > > > >   [<ffffffff81059afb>] flush_work+0x2b/0x40
> > > > >   [<ffffffff81058fd0>] ? do_work_for_cpu+0x30/0x30
> > > > >   [<ffffffff8105a076>] flush_delayed_work+0x46/0x50
> > > > >   [<ffffffff8121e6b6>] disk_clear_events+0x76/0x110
> > > > >   [<ffffffff81106052>] check_disk_change+0x32/0x80
> > > > >   [<ffffffff812ee649>] sd_open+0xb9/0x190
> > > > >   [<ffffffff81106f81>] __blkdev_get+0x91/0x3d0
> > > > >   [<ffffffff81107600>] ? blkdev_get+0x340/0x340
> > > > >   [<ffffffff8110730e>] blkdev_get+0x4e/0x340
> > > > >   [<ffffffff810e1b17>] ? do_lookup+0xb7/0x380
> > > > >   [<ffffffff81107600>] ? blkdev_get+0x340/0x340
> > > > >   [<ffffffff8110765d>] blkdev_open+0x5d/0x80
> > > > >   [<ffffffff810d3d80>] __dentry_open+0x130/0x320
> > > > >   [<ffffffff810d5021>] nameidata_to_filp+0x71/0x80
> > > > >   [<ffffffff810e2ab1>] do_last+0xb1/0x800
> > > > >   [<ffffffff810e4603>] path_openat+0xd3/0x3f0
> > > > >   [<ffffffff81229077>] ? kobject_put+0x27/0x60
> > > > >   [<ffffffff812ced62>] ? put_device+0x12/0x20
> > > > >   [<ffffffff810e4984>] do_filp_open+0x44/0xa0
> > > > >   [<ffffffff810f0a44>] ? alloc_fd+0xf4/0x150
> > > > >   [<ffffffff810d512c>] do_sys_open+0xfc/0x1e0
> > > > >   [<ffffffff810d3a86>] ? filp_close+0x56/0x80
> > > > >   [<ffffffff810d522b>] sys_open+0x1b/0x20
> > > > >   [<ffffffff814d1c3b>] system_call_fastpath+0x16/0x1b
> > > > >  
> > > > >  Restarting tasks ... done.
> > > > >  video LNXVIDEO:01: Restoring backlight state
> > > > >  acpid: 1 client rule loaded
> > > > >  EXT4-fs (sda2): re-mounted. Opts: discard,commit=600
> > > > >  INFO: task udisks-daemon:5848 blocked for more than 120 seconds.
> > > > >  "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
> > > > >  udisks-daemon   D ffff8800a641e5d0     0  5848   5845 0x00000004
> > > > >   ffff8800a6741928 0000000000000082 ffff880000000000 00000000000105c0
> > > > >   ffff8800a641e200 00000000000105c0 ffff8800a6741fd8 00000000000105c0
> > > > >   00000000000105c0 ffff8800a6740000 ffff8800a6741fd8 00000000000105c0
> > > > >  Call Trace:
> > > > >   [<ffffffff814cf355>] schedule_timeout+0x1c5/0x230
> > > > >   [<ffffffff814ce8c9>] ? schedule+0x399/0x8a0
> > > > >   [<ffffffff814ce3e0>] wait_for_common+0xc0/0x160
> > > > >   [<ffffffff8103ec60>] ? try_to_wake_up+0x290/0x290
> > > > >   [<ffffffff814d10ba>] ? _raw_spin_unlock_irq+0x2a/0x40
> > > > >   [<ffffffff814ce528>] wait_for_completion+0x18/0x20
> > > > >   [<ffffffff81059afb>] flush_work+0x2b/0x40
> > > > >   [<ffffffff81058fd0>] ? do_work_for_cpu+0x30/0x30
> > > > >   [<ffffffff8105a076>] flush_delayed_work+0x46/0x50
> > > > >   [<ffffffff8121e6b6>] disk_clear_events+0x76/0x110
> > > > >   [<ffffffff81106052>] check_disk_change+0x32/0x80
> > > > >   [<ffffffff812ee649>] sd_open+0xb9/0x190
> > > > >   [<ffffffff81106f81>] __blkdev_get+0x91/0x3d0
> > > > >   [<ffffffff81107600>] ? blkdev_get+0x340/0x340
> > > > >   [<ffffffff8110730e>] blkdev_get+0x4e/0x340
> > > > >   [<ffffffff810e1b17>] ? do_lookup+0xb7/0x380
> > > > >   [<ffffffff81107600>] ? blkdev_get+0x340/0x340
> > > > >   [<ffffffff8110765d>] blkdev_open+0x5d/0x80
> > > > >   [<ffffffff810d3d80>] __dentry_open+0x130/0x320
> > > > >   [<ffffffff810d5021>] nameidata_to_filp+0x71/0x80
> > > > >   [<ffffffff810e2ab1>] do_last+0xb1/0x800
> > > > >   [<ffffffff810e4603>] path_openat+0xd3/0x3f0
> > > > >   [<ffffffff81229077>] ? kobject_put+0x27/0x60
> > > > >   [<ffffffff812ced62>] ? put_device+0x12/0x20
> > > > >   [<ffffffff810e4984>] do_filp_open+0x44/0xa0
> > > > >   [<ffffffff810f0a44>] ? alloc_fd+0xf4/0x150
> > > > >   [<ffffffff810d512c>] do_sys_open+0xfc/0x1e0
> > > > >   [<ffffffff810d3a86>] ? filp_close+0x56/0x80
> > > > >   [<ffffffff810d522b>] sys_open+0x1b/0x20
> > > > >   [<ffffffff814d1c3b>] system_call_fastpath+0x16/0x1b
> > > > >   
> > > > > 
> > > > > and there are some more of these traces in the logs, but they all look the 
> > > > > same.
> > > > 
> > > > It looks like udisks-daemon is waiting for a completion that's
> > > > never completed.
> > > > 
> > > > Do you use any removable storage devices?
> > > 
> > > Yes, external harddisks connected via USB. But none were mounted
> > > when I closed the lid to suspend.
> > 
> > Were they connected to the USB ports?
> 
> No.
> 
> > > PS: I made a mistake in the Subject:, the kernel is 3.1-rc2 and
> > > not 3.0-rc2.
> > 
> > Did 3.0 work correctly?
> 
> I didn't test 3.0, but 2.6.39 always worked.

Can you try 3.0.y too, please?  It would give us the time frame the
error started to happen.

Thanks,
Rafael

^ permalink raw reply

* Re: [patch 1/1] [PATCH] include storage keys in hibernation image.
From: Martin Schwidefsky @ 2011-08-17  7:43 UTC (permalink / raw)
  To: Rafael J. Wysocki; +Cc: linux-s390, linux-pm, Jiri Slaby, linux-kernel
In-Reply-To: <201108161956.47434.rjw@sisk.pl>

On Tue, 16 Aug 2011 19:56:47 +0200
"Rafael J. Wysocki" <rjw@sisk.pl> wrote:

> On Tuesday, August 16, 2011, Martin Schwidefsky wrote:
> > From: Martin Schwidefsky <schwidefsky@de.ibm.com>
> > 
> > For s390 there is one additional byte associated with each page,
> > the storage key. This byte contains the referenced and changed
> > bits and needs to be included into the hibernation image.
> > If the storage keys are not restored to their previous state all
> > original pages would appear to be dirty. This can cause
> > inconsistencies e.g. with read-only filesystems.
> > 
> > Cc: Pavel Machek <pavel@ucw.cz>
> > Cc: Rafael J. Wysocki <rjw@sisk.pl>
> > Cc: Jiri Slaby <jslaby@suse.cz>
> > Cc: linux-pm@lists.linux-foundation.org
> > Cc: linux-kernel@vger.kernel.org
> > Cc: linux-s390@vger.kernel.org
> > Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
> 
> OK, I don't have any complaints.  Do you want me to take this
> patch or do you want to push it through the s390 tree?

The patch affects the common power management code. At minimum I
would like an acked-by to take it into the s390 tree. I would feel
more comfortable if you'd take it via the power management tree
though.

-- 
blue skies,
   Martin.

"Reality continues to ruin my life." - Calvin.

^ permalink raw reply

* Re: [PATCH v5 2/3] PM / DEVFREQ: add basic governors
From: MyungJoo Ham @ 2011-08-17  7:32 UTC (permalink / raw)
  To: Turquette, Mike
  Cc: Len Brown, Greg Kroah-Hartman, Kyungmin Park, linux-pm,
	Thomas Gleixner
In-Reply-To: <CAJOA=zMesvkapbU8LLFBw_=abtpp2CB1HSn4scwB4O06L-1Pjg@mail.gmail.com>

On Wed, Aug 17, 2011 at 3:11 AM, Turquette, Mike <mturquette@ti.com> wrote:
> On Tue, Aug 16, 2011 at 1:52 AM, MyungJoo Ham <myungjoo.ham@samsung.com> wrote:
>>
>> First, in case where we want to let each DVFS-capable device have
>> exact polling frequency (up to jiffy resolution), we only need to set
>> polling_interval = jiffies_to_msecs(1);.
>
> That requires a source code change for anyone that wants to do it.  My
> main complaint with this method is that it is restrictive to begin
> with and the whole method for determining the next_polling time
> reproduces what workqueues already give us.
>
>> In case of CPUFREQ, there would be only one polling loop at most for
>> each core. However, in case of DEVFREQ, there could be multiple
>> polling loops at a core if CPUFREQ-like looping logic is introduced.
>> Why don't we reduce that overhead while their function is same, it is
>> easily doable, and it reduces redundancy?
>
> I'm afraid I don't follow.  I was thinking of having a single wq loop
> for each device.  Under what conditions would a single device have
> multiple wq loops operating against it?

I meant a single wq loop for each device and multiple DEVFREQ devices
for a system.

I aggregated multiple instances of DEVFREQ polling into one polling loop because
1. remove redundant polling loops,
2. simplify governors implementation; I'm presuming that some devices
(especially GPUs and MMC hosts) might want their own custom governors
although the governors won't be complex, and
3. reduce overhead.

I don't see any benefit of looping at each DEVFREQ device, yet.

The case of CPUFREQ is a bit different because each CPUFREQ device
(CPU) is capable of looping itself and each looping should represent
one CPU. Each looping device (CPU) will have only up to one CPUFREQ
polling loop and each instance of CPUFREQ should be executed on the
corresponding CPU in order to avoid being slept while the represented
CPU is running.

>
>>>> When a user updates OPP entries (enable/disable/add), OPP framework
>>>> automatically notifies DEVFREQ to update operating frequency
>>>> accordingly. Thus, DEVFREQ users (device drivers) do not need to update
>>>
>>> It would be nice if OPP library "notified" devfreq but it does not
>>> today.  OPP library needs notifiers and devfreq can provide handlers
>>> for them.
>>
>> That's why devfreq_update() is added in the patch. While DEVFREQ is
>> the only one requiring notifications from OPP, do you think we may
>> incur the overhead of notifier at OPP by replacing devfreq_update with
>> notifier? If we somehow add another module that requires notifications
>> from OPP for frequency availability changes, we will need to implement
>> notifier at OPP side, but not just yet, I guess: (discussed before at
>> https://lists.linux-foundation.org/pipermail/linux-pm/2011-July/032053.html
>> )
>
> Reading that thread makes me think that we really should implement
> notifiers in the OPP library.  An obvious user of OPP notifiers would
> be CPUfreq.  I think it is safe to say that there may be
> implementations of devfreq and CPUfreq that live side-by-side in the
> near future; OPPs might be enabled/disabled dynamically, which means
> both of them need callbacks.  Better to abstract it out early, I
> think.

Ok, assuming that there would be another requesting notifications from
OPP, I've added "opp_get_notifier()" that returns struct
srcu_notifier_head * removing devfreq_update() at patchset v6
candidate.

>
>>>
>> []
>>>> ---
>>>>  drivers/base/power/devfreq.c |  100 ++++++++++++++++++++++++++++++++++++++++++
>>>>  include/linux/devfreq.h      |    8 +++
>>>>  2 files changed, 108 insertions(+), 0 deletions(-)
>>>
>>> Governors should be split out into their own file, especially since
>>> they need to grow to include polling/queueing logic.
>>
>> We will need to decide where to settle devfreq core, drivers, and
>> governors first. Would /drivers/devfreq/ be appropriate?
>
> I think GKH already ACK'd drivers/devfreq in a previous thread:
> https://lists.linux-foundation.org/pipermail/linux-pm/2011-August/032537.html

Yup. I'm moving them to /drivers/devfreq/

>
>> []
>>>> +
>>>> +       /* Set MAX if it's busy enough */
>>>> +       if (stat.busy_time * 100 >
>>>> +           stat.total_time * DFSO_UPTHRESHOLD) {
>>>
>>> Thresholds should not be constants, but should be tuneable parameters,
>>> per-device.  This is yet another reason for revising the existing
>>> relationship between devfreq core code, governors and devices.
>>>
>>
>> I agree. I think I should add governor specific "setup" value at
>> devfreq_add_device(); modifying the interface from
>>
>> extern int devfreq_add_device(struct device *dev, struct
>> devfreq_dev_profile *profile, struct devfreq_governor *governor);
>> ==>
>> extern int devfreq_add_device(struct device *dev, struct
>> devfreq_dev_profile *profile, struct devfreq_governor *governor, void
>> *gov_data);
>>
>> where gov_data is fed to struct devfreq's data field.
>
> It would be nice for the threshold values to be run-time tunable via
> sysfs.  CPUfreq does this well today for ondemand/conservative
> governors and it really helps when doing power/performance tuning.
>
> Regards,
> Mike
>

This will be done with the next revision.

Thank you.

MyungJoo

>>>> +               *freq = UINT_MAX;
>>>> +               return 0;
>>>> +       }
>>>> +
>>>> +       /* Set MAX if we do not know the initial frequency */
>>>> +       if (stat.current_frequency == 0) {
>>>> +               *freq = UINT_MAX;
>>>> +               return 0;
>>>> +       }
>>>> +
>>>> +       /* Keep the current frequency */
>>>> +       if (stat.busy_time * 100 >
>>>> +           stat.total_time * (DFSO_UPTHRESHOLD - DFSO_DOWNDIFFERENCTIAL)) {
>>>
>>> Same as above.
>> Yes.
>>
>>>
>>>> +               *freq = stat.current_frequency;
>>>> +               return 0;
>>>> +       }
>>>> +
>>>> +       /* Set the desired frequency based on the load */
>>>> +       a = stat.busy_time;
>>>> +       a *= stat.current_frequency;
>>>> +       b = div_u64(a, stat.total_time);
>>>> +       b *= 100;
>>>> +       b = div_u64(b, (DFSO_UPTHRESHOLD - DFSO_DOWNDIFFERENCTIAL / 2));
>>>
>>> Same as above.
>> Yes.
>>
>>>
>>> Regards,
>>> Mike
>>>
>>
>>
>> Cheers!
>> MyungJoo
>>
>> --
>> MyungJoo Ham (함명주), Ph.D.
>> Mobile Software Platform Lab,
>> Digital Media and Communications (DMC) Business
>> Samsung Electronics
>> cell: 82-10-6714-2858
>>
>



-- 
MyungJoo Ham, Ph.D.
Mobile Software Platform Lab,
Digital Media and Communications (DMC) Business
Samsung Electronics
cell: 82-10-6714-2858
_______________________________________________
linux-pm mailing list
linux-pm@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

^ permalink raw reply

* Re: [PATCH v4 1/2] Input: enable i8042-level wakeup control
From: Dmitry Torokhov @ 2011-08-17  7:03 UTC (permalink / raw)
  To: Daniel Drake; +Cc: linux-pm, dilinger, linux-input
In-Reply-To: <CAMLZHHTie_u40NZ40o+LCwffk=3LbY96YTwxQt5wcVtbTc53qg@mail.gmail.com>

Hi Daniel,

On Thu, Aug 11, 2011 at 07:02:17PM +0100, Daniel Drake wrote:
> Hi Dmitry,
> 
> On Fri, Aug 5, 2011 at 4:24 PM, Daniel Drake <dsd@laptop.org> wrote:
> > I've started looking at this, but my first problem is that when the
> > input layer asks atkbd to turn off LEDs, atkbd schedules a workqueue
> > item to undertake this work. However, as there is no synchronization,
> > this work only seems to get executed during or after system resume.
> > This makes it hard to catch with such a scheme. Any ideas?
> 
> I've looked closer, and am keeping my eyes open for other options too.
> 
> During suspend of the input layer, input_dev_suspend() calls
> input_dev_toggle(dev, false) which turns off LEDs and sounds.
> 
> On the XO laptops, we don't have LEDs, nor sounds, and we don't have
> num lock, scroll lock or caps lock keys. So the suspend code does
> nothing - there are no lights to turn off. So far so good.
> 
> Moving to the resume case: input_dev_resume() calls
> input_reset_device(). This calls input_dev_toggle(dev, true), which
> asks atkbd to turn the non-existent keyboard LEDs off. I guess this is
> harmless and is unlikely to be the cause of the problems which led us
> to need to disable this code in light of the following:
> 
> After calling input_dev_toggle(), input_dev_resume() does this:
> 
> 		/*
> 		 * Keys that have been pressed at suspend time are unlikely
> 		 * to be still pressed when we resume.
> 		 */
> 		spin_lock_irq(&dev->event_lock);
> 		input_dev_release_keys(dev);
> 		spin_unlock_irq(&dev->event_lock);
> 
> We definitely don't want this to happen in our case, where we have
> maintained full control and power of the device during suspend/resume.
> 
> This particular important code snippet would not even have been
> disabled by your suggestion of blocking data transfer at the
> i8042-level.
> 
> So, I think we need a new approach at solving this, and I think it has
> to be one that interacts at the input_dev layer.

OK, so we have the following scenario:

- we do not need to worry about SND and LED on OLPC;

- we do not need to worry about releasing the key that was pressed
  when we went to sleep because they system would not go to sleep while
  there is a key pressed. So the code in input_dev_resume() won't
  actually release any keys unless a key was held pressed and we forced
  system to sleep somehow.

- we need to make sure we do not loose key press (and following events)
  when we are waking up.

Can atkbd driver stash away serio byte data (when not in command mode)
and replay it when pm notifier signals us that resume process is done?

Thanks.

-- 
Dmitry

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox