Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 468f4d1a authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull power management updates from Rafael Wysocki:

 - Implementation of opportunistic suspend (autosleep) and user space
   interface for manipulating wakeup sources.

 - Hibernate updates from Bojan Smojver and Minho Ban.

 - Updates of the runtime PM core and generic PM domains framework
   related to PM QoS.

 - Assorted fixes.

* tag 'pm-for-3.5' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (25 commits)
  epoll: Fix user space breakage related to EPOLLWAKEUP
  PM / Domains: Make it possible to add devices to inactive domains
  PM / Hibernate: Use get_gendisk to verify partition if resume_file is integer format
  PM / Domains: Fix computation of maximum domain off time
  PM / Domains: Fix link checking when add subdomain
  PM / Sleep: User space wakeup sources garbage collector Kconfig option
  PM / Sleep: Make the limit of user space wakeup sources configurable
  PM / Documentation: suspend-and-cpuhotplug.txt: Fix typo
  PM / Domains: Cache device stop and domain power off governor results, v3
  PM / Domains: Make device removal more straightforward
  PM / Sleep: Fix a mistake in a conditional in autosleep_store()
  epoll: Add a flag, EPOLLWAKEUP, to prevent suspend while epoll events are ready
  PM / QoS: Create device constraints objects on notifier registration
  PM / Runtime: Remove device fields related to suspend time, v2
  PM / Domains: Rework default domain power off governor function, v2
  PM / Domains: Rework default device stop governor function, v2
  PM / Sleep: Add user space interface for manipulating wakeup sources, v3
  PM / Sleep: Add "prevent autosleep time" statistics to wakeup sources
  PM / Sleep: Implement opportunistic sleep, v2
  PM / Sleep: Add wakeup_source_activate and wakeup_source_deactivate tracepoints
  ...
parents eb2689e0 8714c8d7
Loading
Loading
Loading
Loading
+28 −7
Original line number Diff line number Diff line
@@ -96,16 +96,26 @@ Description:
		is read-only.  If the device is not enabled to wake up the
		system from sleep states, this attribute is not present.

What:		/sys/devices/.../power/wakeup_hit_count
Date:		September 2010
What:		/sys/devices/.../power/wakeup_abort_count
Date:		February 2012
Contact:	Rafael J. Wysocki <rjw@sisk.pl>
Description:
		The /sys/devices/.../wakeup_hit_count attribute contains the
		The /sys/devices/.../wakeup_abort_count attribute contains the
		number of times the processing of a wakeup event associated with
		the device might prevent the system from entering a sleep state.
		This attribute is read-only.  If the device is not enabled to
		wake up the system from sleep states, this attribute is not
		present.
		the device might have aborted system transition into a sleep
		state in progress.  This attribute is read-only.  If the device
		is not enabled to wake up the system from sleep states, this
		attribute is not present.

What:		/sys/devices/.../power/wakeup_expire_count
Date:		February 2012
Contact:	Rafael J. Wysocki <rjw@sisk.pl>
Description:
		The /sys/devices/.../wakeup_expire_count attribute contains the
		number of times a wakeup event associated with the device has
		been reported with a timeout that expired.  This attribute is
		read-only.  If the device is not enabled to wake up the system
		from sleep states, this attribute is not present.

What:		/sys/devices/.../power/wakeup_active
Date:		September 2010
@@ -148,6 +158,17 @@ Description:
		not enabled to wake up the system from sleep states, this
		attribute is not present.

What:		/sys/devices/.../power/wakeup_prevent_sleep_time_ms
Date:		February 2012
Contact:	Rafael J. Wysocki <rjw@sisk.pl>
Description:
		The /sys/devices/.../wakeup_prevent_sleep_time_ms attribute
		contains the total time the device has been preventing
		opportunistic transitions to sleep states from occuring.
		This attribute is read-only.  If the device is not enabled to
		wake up the system from sleep states, this attribute is not
		present.

What:		/sys/devices/.../power/autosuspend_delay_ms
Date:		September 2010
Contact:	Alan Stern <stern@rowland.harvard.edu>
+59 −0
Original line number Diff line number Diff line
@@ -172,3 +172,62 @@ Description:

		Reading from this file will display the current value, which is
		set to 1 MB by default.

What:		/sys/power/autosleep
Date:		April 2012
Contact:	Rafael J. Wysocki <rjw@sisk.pl>
Description:
		The /sys/power/autosleep file can be written one of the strings
		returned by reads from /sys/power/state.  If that happens, a
		work item attempting to trigger a transition of the system to
		the sleep state represented by that string is queued up.  This
		attempt will only succeed if there are no active wakeup sources
		in the system at that time.  After every execution, regardless
		of whether or not the attempt to put the system to sleep has
		succeeded, the work item requeues itself until user space
		writes "off" to /sys/power/autosleep.

		Reading from this file causes the last string successfully
		written to it to be returned.

What:		/sys/power/wake_lock
Date:		February 2012
Contact:	Rafael J. Wysocki <rjw@sisk.pl>
Description:
		The /sys/power/wake_lock file allows user space to create
		wakeup source objects and activate them on demand (if one of
		those wakeup sources is active, reads from the
		/sys/power/wakeup_count file block or return false).  When a
		string without white space is written to /sys/power/wake_lock,
		it will be assumed to represent a wakeup source name.  If there
		is a wakeup source object with that name, it will be activated
		(unless active already).  Otherwise, a new wakeup source object
		will be registered, assigned the given name and activated.
		If a string written to /sys/power/wake_lock contains white
		space, the part of the string preceding the white space will be
		regarded as a wakeup source name and handled as descrived above.
		The other part of the string will be regarded as a timeout (in
		nanoseconds) such that the wakeup source will be automatically
		deactivated after it has expired.  The timeout, if present, is
		set regardless of the current state of the wakeup source object
		in question.

		Reads from this file return a string consisting of the names of
		wakeup sources created with the help of it that are active at
		the moment, separated with spaces.


What:		/sys/power/wake_unlock
Date:		February 2012
Contact:	Rafael J. Wysocki <rjw@sisk.pl>
Description:
		The /sys/power/wake_unlock file allows user space to deactivate
		wakeup sources created with the help of /sys/power/wake_lock.
		When a string is written to /sys/power/wake_unlock, it will be
		assumed to represent the name of a wakeup source to deactivate.
		If a wakeup source object of that name exists and is active at
		the moment, it will be deactivated.

		Reads from this file return a string consisting of the names of
		wakeup sources created with the help of /sys/power/wake_lock
		that are inactive at the moment, separated with spaces.
+2 −0
Original line number Diff line number Diff line
@@ -2463,6 +2463,8 @@ bytes respectively. Such letter suffixes can also be entirely omitted.

	resume=		[SWSUSP]
			Specify the partition device for software suspend
			Format:
			{/dev/<dev> | PARTUUID=<uuid> | <int>:<int> | <hex>}

	resume_offset=	[SWSUSP]
			Specify the offset from the beginning of the partition
+1 −1
Original line number Diff line number Diff line
@@ -29,7 +29,7 @@ More details follow:

                                  Write 'mem' to
                                /sys/power/state
                                    syfs file
                                    sysfs file
                                        |
                                        v
                               Acquire pm_mutex lock
+131 −45
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@
#include <linux/io.h>
#include <linux/pm_runtime.h>
#include <linux/pm_domain.h>
#include <linux/pm_qos.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/sched.h>
@@ -38,11 +39,13 @@
	ktime_t __start = ktime_get();						\
	type __retval = GENPD_DEV_CALLBACK(genpd, type, callback, dev);		\
	s64 __elapsed = ktime_to_ns(ktime_sub(ktime_get(), __start));		\
	struct generic_pm_domain_data *__gpd_data = dev_gpd_data(dev);		\
	if (__elapsed > __gpd_data->td.field) {					\
		__gpd_data->td.field = __elapsed;				\
	struct gpd_timing_data *__td = &dev_gpd_data(dev)->td;			\
	if (!__retval && __elapsed > __td->field) {				\
		__td->field = __elapsed;					\
		dev_warn(dev, name " latency exceeded, new value %lld ns\n",	\
			__elapsed);						\
		genpd->max_off_time_changed = true;				\
		__td->constraint_changed = true;				\
	}									\
	__retval;								\
})
@@ -211,6 +214,7 @@ int __pm_genpd_poweron(struct generic_pm_domain *genpd)
		elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
		if (elapsed_ns > genpd->power_on_latency_ns) {
			genpd->power_on_latency_ns = elapsed_ns;
			genpd->max_off_time_changed = true;
			if (genpd->name)
				pr_warning("%s: Power-on latency exceeded, "
					"new value %lld ns\n", genpd->name,
@@ -247,6 +251,53 @@ int pm_genpd_poweron(struct generic_pm_domain *genpd)

#ifdef CONFIG_PM_RUNTIME

static int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
				     unsigned long val, void *ptr)
{
	struct generic_pm_domain_data *gpd_data;
	struct device *dev;

	gpd_data = container_of(nb, struct generic_pm_domain_data, nb);

	mutex_lock(&gpd_data->lock);
	dev = gpd_data->base.dev;
	if (!dev) {
		mutex_unlock(&gpd_data->lock);
		return NOTIFY_DONE;
	}
	mutex_unlock(&gpd_data->lock);

	for (;;) {
		struct generic_pm_domain *genpd;
		struct pm_domain_data *pdd;

		spin_lock_irq(&dev->power.lock);

		pdd = dev->power.subsys_data ?
				dev->power.subsys_data->domain_data : NULL;
		if (pdd) {
			to_gpd_data(pdd)->td.constraint_changed = true;
			genpd = dev_to_genpd(dev);
		} else {
			genpd = ERR_PTR(-ENODATA);
		}

		spin_unlock_irq(&dev->power.lock);

		if (!IS_ERR(genpd)) {
			mutex_lock(&genpd->lock);
			genpd->max_off_time_changed = true;
			mutex_unlock(&genpd->lock);
		}

		dev = dev->parent;
		if (!dev || dev->power.ignore_children)
			break;
	}

	return NOTIFY_DONE;
}

/**
 * __pm_genpd_save_device - Save the pre-suspend state of a device.
 * @pdd: Domain data of the device to save the state of.
@@ -435,6 +486,7 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
		elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
		if (elapsed_ns > genpd->power_off_latency_ns) {
			genpd->power_off_latency_ns = elapsed_ns;
			genpd->max_off_time_changed = true;
			if (genpd->name)
				pr_warning("%s: Power-off latency exceeded, "
					"new value %lld ns\n", genpd->name,
@@ -443,17 +495,6 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
	}

	genpd->status = GPD_STATE_POWER_OFF;
	genpd->power_off_time = ktime_get();

	/* Update PM QoS information for devices in the domain. */
	list_for_each_entry_reverse(pdd, &genpd->dev_list, list_node) {
		struct gpd_timing_data *td = &to_gpd_data(pdd)->td;

		pm_runtime_update_max_time_suspended(pdd->dev,
					td->start_latency_ns +
					td->restore_state_latency_ns +
					genpd->power_on_latency_ns);
	}

	list_for_each_entry(link, &genpd->slave_links, slave_node) {
		genpd_sd_counter_dec(link->master);
@@ -514,9 +555,6 @@ static int pm_genpd_runtime_suspend(struct device *dev)
	if (ret)
		return ret;

	pm_runtime_update_max_time_suspended(dev,
				dev_gpd_data(dev)->td.start_latency_ns);

	/*
	 * If power.irq_safe is set, this routine will be run with interrupts
	 * off, so it can't use mutexes.
@@ -613,6 +651,12 @@ void pm_genpd_poweroff_unused(void)

#else

static inline int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
					    unsigned long val, void *ptr)
{
	return NOTIFY_DONE;
}

static inline void genpd_power_off_work_fn(struct work_struct *work) {}

#define pm_genpd_runtime_suspend	NULL
@@ -1209,12 +1253,15 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
		return -EINVAL;

	genpd_acquire_lock(genpd);
	gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL);
	if (!gpd_data)
		return -ENOMEM;

	if (genpd->status == GPD_STATE_POWER_OFF) {
		ret = -EINVAL;
		goto out;
	}
	mutex_init(&gpd_data->lock);
	gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier;
	dev_pm_qos_add_notifier(dev, &gpd_data->nb);

	genpd_acquire_lock(genpd);

	if (genpd->prepared_count > 0) {
		ret = -EAGAIN;
@@ -1227,26 +1274,35 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
			goto out;
		}

	gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL);
	if (!gpd_data) {
		ret = -ENOMEM;
		goto out;
	}

	genpd->device_count++;
	genpd->max_off_time_changed = true;

	dev->pm_domain = &genpd->domain;
	dev_pm_get_subsys_data(dev);

	mutex_lock(&gpd_data->lock);
	spin_lock_irq(&dev->power.lock);
	dev->pm_domain = &genpd->domain;
	dev->power.subsys_data->domain_data = &gpd_data->base;
	gpd_data->base.dev = dev;
	gpd_data->need_restore = false;
	list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
	gpd_data->need_restore = genpd->status == GPD_STATE_POWER_OFF;
	if (td)
		gpd_data->td = *td;

	gpd_data->td.constraint_changed = true;
	gpd_data->td.effective_constraint_ns = -1;
	spin_unlock_irq(&dev->power.lock);
	mutex_unlock(&gpd_data->lock);

	genpd_release_lock(genpd);

	return 0;

 out:
	genpd_release_lock(genpd);

	dev_pm_qos_remove_notifier(dev, &gpd_data->nb);
	kfree(gpd_data);
	return ret;
}

@@ -1290,12 +1346,15 @@ int __pm_genpd_of_add_device(struct device_node *genpd_node, struct device *dev,
int pm_genpd_remove_device(struct generic_pm_domain *genpd,
			   struct device *dev)
{
	struct generic_pm_domain_data *gpd_data;
	struct pm_domain_data *pdd;
	int ret = -EINVAL;
	int ret = 0;

	dev_dbg(dev, "%s()\n", __func__);

	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)
	    ||  IS_ERR_OR_NULL(dev->pm_domain)
	    ||  pd_to_genpd(dev->pm_domain) != genpd)
		return -EINVAL;

	genpd_acquire_lock(genpd);
@@ -1305,21 +1364,27 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd,
		goto out;
	}

	list_for_each_entry(pdd, &genpd->dev_list, list_node) {
		if (pdd->dev != dev)
			continue;
	genpd->device_count--;
	genpd->max_off_time_changed = true;

	spin_lock_irq(&dev->power.lock);
	dev->pm_domain = NULL;
	pdd = dev->power.subsys_data->domain_data;
	list_del_init(&pdd->list_node);
	dev->power.subsys_data->domain_data = NULL;
	spin_unlock_irq(&dev->power.lock);

	gpd_data = to_gpd_data(pdd);
	mutex_lock(&gpd_data->lock);
	pdd->dev = NULL;
		dev_pm_put_subsys_data(dev);
		dev->pm_domain = NULL;
		kfree(to_gpd_data(pdd));
	mutex_unlock(&gpd_data->lock);

		genpd->device_count--;
	genpd_release_lock(genpd);

		ret = 0;
		break;
	}
	dev_pm_qos_remove_notifier(dev, &gpd_data->nb);
	kfree(gpd_data);
	dev_pm_put_subsys_data(dev);
	return 0;

 out:
	genpd_release_lock(genpd);
@@ -1347,6 +1412,26 @@ void pm_genpd_dev_always_on(struct device *dev, bool val)
}
EXPORT_SYMBOL_GPL(pm_genpd_dev_always_on);

/**
 * pm_genpd_dev_need_restore - Set/unset the device's "need restore" flag.
 * @dev: Device to set/unset the flag for.
 * @val: The new value of the device's "need restore" flag.
 */
void pm_genpd_dev_need_restore(struct device *dev, bool val)
{
	struct pm_subsys_data *psd;
	unsigned long flags;

	spin_lock_irqsave(&dev->power.lock, flags);

	psd = dev_to_psd(dev);
	if (psd && psd->domain_data)
		to_gpd_data(psd->domain_data)->need_restore = val;

	spin_unlock_irqrestore(&dev->power.lock, flags);
}
EXPORT_SYMBOL_GPL(pm_genpd_dev_need_restore);

/**
 * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
 * @genpd: Master PM domain to add the subdomain to.
@@ -1378,7 +1463,7 @@ int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
		goto out;
	}

	list_for_each_entry(link, &genpd->slave_links, slave_node) {
	list_for_each_entry(link, &genpd->master_links, master_node) {
		if (link->slave == subdomain && link->master == genpd) {
			ret = -EINVAL;
			goto out;
@@ -1690,6 +1775,7 @@ void pm_genpd_init(struct generic_pm_domain *genpd,
	genpd->resume_count = 0;
	genpd->device_count = 0;
	genpd->max_off_time_ns = -1;
	genpd->max_off_time_changed = true;
	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
Loading