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

Commit cb8f51bd authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

PM: Do not create wakeup sysfs files for devices that cannot wake up



Currently, wakeup sysfs attributes are created for all devices,
regardless of whether or not they are wakeup-capable.  This is
excessive and complicates wakeup device identification from user
space (i.e. to identify wakeup-capable devices user space has to read
/sys/devices/.../power/wakeup for all devices and see if they are not
empty).

Fix this issue by avoiding to create wakeup sysfs files for devices
that cannot wake up the system from sleep states (i.e. whose
power.can_wakeup flags are unset during registration) and modify
device_set_wakeup_capable() so that it adds (or removes) the relevant
sysfs attributes if a device's wakeup capability status is changed.

Signed-off-by: default avatarRafael J. Wysocki <rjw@sisk.pl>
parent 4681b171
Loading
Loading
Loading
Loading
+10 −10
Original line number Diff line number Diff line
@@ -29,9 +29,8 @@ Description:
		"disabled" to it.

		For the devices that are not capable of generating system wakeup
		events this file contains "\n".  In that cases the user space
		cannot modify the contents of this file and the device cannot be
		enabled to wake up the system.
		events this file is not present.  In that case the device cannot
		be enabled to wake up the system from sleep states.

What:		/sys/devices/.../power/control
Date:		January 2009
@@ -85,7 +84,7 @@ Description:
		The /sys/devices/.../wakeup_count attribute contains the number
		of signaled wakeup events associated with the device.  This
		attribute is read-only.  If the device is not enabled to wake up
		the system from sleep states, this attribute is empty.
		the system from sleep states, this attribute is not present.

What:		/sys/devices/.../power/wakeup_active_count
Date:		September 2010
@@ -95,7 +94,7 @@ Description:
		number of times the processing of wakeup events associated with
		the device was completed (at the kernel level).  This attribute
		is read-only.  If the device is not enabled to wake up the
		system from sleep states, this attribute is empty.
		system from sleep states, this attribute is not present.

What:		/sys/devices/.../power/wakeup_hit_count
Date:		September 2010
@@ -105,7 +104,8 @@ Description:
		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 empty.
		wake up the system from sleep states, this attribute is not
		present.

What:		/sys/devices/.../power/wakeup_active
Date:		September 2010
@@ -115,7 +115,7 @@ Description:
		or 0, depending on whether or not a wakeup event associated with
		the device is being processed (1).  This attribute is read-only.
		If the device is not enabled to wake up the system from sleep
		states, this attribute is empty.
		states, this attribute is not present.

What:		/sys/devices/.../power/wakeup_total_time_ms
Date:		September 2010
@@ -125,7 +125,7 @@ Description:
		the total time of processing wakeup events associated with the
		device, in milliseconds.  This attribute is read-only.  If the
		device is not enabled to wake up the system from sleep states,
		this attribute is empty.
		this attribute is not present.

What:		/sys/devices/.../power/wakeup_max_time_ms
Date:		September 2010
@@ -135,7 +135,7 @@ Description:
		the maximum time of processing a single wakeup event associated
		with the device, in milliseconds.  This attribute is read-only.
		If the device is not enabled to wake up the system from sleep
		states, this attribute is empty.
		states, this attribute is not present.

What:		/sys/devices/.../power/wakeup_last_time_ms
Date:		September 2010
@@ -146,7 +146,7 @@ Description:
		signaling the last wakeup event associated with the device, in
		milliseconds.  This attribute is read-only.  If the device is
		not enabled to wake up the system from sleep states, this
		attribute is empty.
		attribute is not present.

What:		/sys/devices/.../power/autosuspend_delay_ms
Date:		September 2010
+10 −10
Original line number Diff line number Diff line
@@ -159,18 +159,18 @@ matter, and the kernel is responsible for keeping track of it. By contrast,
whether or not a wakeup-capable device should issue wakeup events is a policy
decision, and it is managed by user space through a sysfs attribute: the
power/wakeup file.  User space can write the strings "enabled" or "disabled" to
set or clear the should_wakeup flag, respectively.  Reads from the file will
return the corresponding string if can_wakeup is true, but if can_wakeup is
false then reads will return an empty string, to indicate that the device
doesn't support wakeup events.  (But even though the file appears empty, writes
will still affect the should_wakeup flag.)
set or clear the "should_wakeup" flag, respectively.  This file is only present
for wakeup-capable devices (i.e. devices whose "can_wakeup" flags are set)
and is created (or removed) by device_set_wakeup_capable().  Reads from the
file will return the corresponding string.

The device_may_wakeup() routine returns true only if both flags are set.
Drivers should check this routine when putting devices in a low-power state
during a system sleep transition, to see whether or not to enable the devices'
wakeup mechanisms.  However for runtime power management, wakeup events should
be enabled whenever the device and driver both support them, regardless of the
should_wakeup flag.
This information is used by subsystems, like the PCI bus type code, to see
whether or not to enable the devices' wakeup mechanisms.  If device wakeup
mechanisms are enabled or disabled directly by drivers, they also should use
device_may_wakeup() to decide what to do during a system sleep transition.
However for runtime power management, wakeup events should be enabled whenever
the device and driver both support them, regardless of the should_wakeup flag.


/sys/devices/.../power/control files
+10 −11
Original line number Diff line number Diff line
@@ -58,19 +58,18 @@ static inline void device_pm_move_last(struct device *dev) {}
 * sysfs.c
 */

extern int dpm_sysfs_add(struct device *);
extern void dpm_sysfs_remove(struct device *);
extern void rpm_sysfs_remove(struct device *);
extern int dpm_sysfs_add(struct device *dev);
extern void dpm_sysfs_remove(struct device *dev);
extern void rpm_sysfs_remove(struct device *dev);
extern int wakeup_sysfs_add(struct device *dev);
extern void wakeup_sysfs_remove(struct device *dev);

#else /* CONFIG_PM */

static inline int dpm_sysfs_add(struct device *dev)
{
	return 0;
}

static inline void dpm_sysfs_remove(struct device *dev)
{
}
static inline int dpm_sysfs_add(struct device *dev) { return 0; }
static inline void dpm_sysfs_remove(struct device *dev) {}
static inline void rpm_sysfs_remove(struct device *dev) {}
static inline int wakeup_sysfs_add(struct device *dev) { return 0; }
static inline void wakeup_sysfs_remove(struct device *dev) {}

#endif
+51 −27
Original line number Diff line number Diff line
@@ -432,25 +432,17 @@ static DEVICE_ATTR(async, 0644, async_show, async_store);
#endif /* CONFIG_PM_ADVANCED_DEBUG */

static struct attribute *power_attrs[] = {
	&dev_attr_wakeup.attr,
#ifdef CONFIG_PM_SLEEP
	&dev_attr_wakeup_count.attr,
	&dev_attr_wakeup_active_count.attr,
	&dev_attr_wakeup_hit_count.attr,
	&dev_attr_wakeup_active.attr,
	&dev_attr_wakeup_total_time_ms.attr,
	&dev_attr_wakeup_max_time_ms.attr,
	&dev_attr_wakeup_last_time_ms.attr,
#endif
#ifdef CONFIG_PM_ADVANCED_DEBUG
#ifdef CONFIG_PM_SLEEP
	&dev_attr_async.attr,
#endif
#ifdef CONFIG_PM_RUNTIME
	&dev_attr_runtime_status.attr,
	&dev_attr_runtime_usage.attr,
	&dev_attr_runtime_active_kids.attr,
	&dev_attr_runtime_enabled.attr,
#endif
#endif
#endif /* CONFIG_PM_ADVANCED_DEBUG */
	NULL,
};
static struct attribute_group pm_attr_group = {
@@ -458,9 +450,26 @@ static struct attribute_group pm_attr_group = {
	.attrs	= power_attrs,
};

#ifdef CONFIG_PM_RUNTIME
static struct attribute *wakeup_attrs[] = {
#ifdef CONFIG_PM_SLEEP
	&dev_attr_wakeup.attr,
	&dev_attr_wakeup_count.attr,
	&dev_attr_wakeup_active_count.attr,
	&dev_attr_wakeup_hit_count.attr,
	&dev_attr_wakeup_active.attr,
	&dev_attr_wakeup_total_time_ms.attr,
	&dev_attr_wakeup_max_time_ms.attr,
	&dev_attr_wakeup_last_time_ms.attr,
#endif
	NULL,
};
static struct attribute_group pm_wakeup_attr_group = {
	.name	= power_group_name,
	.attrs	= wakeup_attrs,
};

static struct attribute *runtime_attrs[] = {
#ifdef CONFIG_PM_RUNTIME
#ifndef CONFIG_PM_ADVANCED_DEBUG
	&dev_attr_runtime_status.attr,
#endif
@@ -468,6 +477,7 @@ static struct attribute *runtime_attrs[] = {
	&dev_attr_runtime_suspended_time.attr,
	&dev_attr_runtime_active_time.attr,
	&dev_attr_autosuspend_delay_ms.attr,
#endif /* CONFIG_PM_RUNTIME */
	NULL,
};
static struct attribute_group pm_runtime_attr_group = {
@@ -480,35 +490,49 @@ int dpm_sysfs_add(struct device *dev)
	int rc;

	rc = sysfs_create_group(&dev->kobj, &pm_attr_group);
	if (rc == 0 && !dev->power.no_callbacks) {
	if (rc)
		return rc;

	if (pm_runtime_callbacks_present(dev)) {
		rc = sysfs_merge_group(&dev->kobj, &pm_runtime_attr_group);
		if (rc)
			sysfs_remove_group(&dev->kobj, &pm_attr_group);
			goto err_out;
	}

	if (device_can_wakeup(dev)) {
		rc = sysfs_merge_group(&dev->kobj, &pm_wakeup_attr_group);
		if (rc) {
			if (pm_runtime_callbacks_present(dev))
				sysfs_unmerge_group(&dev->kobj,
						    &pm_runtime_attr_group);
			goto err_out;
		}
	}
	return 0;

 err_out:
	sysfs_remove_group(&dev->kobj, &pm_attr_group);
	return rc;
}

void rpm_sysfs_remove(struct device *dev)
int wakeup_sysfs_add(struct device *dev)
{
	sysfs_unmerge_group(&dev->kobj, &pm_runtime_attr_group);
	return sysfs_merge_group(&dev->kobj, &pm_wakeup_attr_group);
}

void dpm_sysfs_remove(struct device *dev)
void wakeup_sysfs_remove(struct device *dev)
{
	rpm_sysfs_remove(dev);
	sysfs_remove_group(&dev->kobj, &pm_attr_group);
	sysfs_unmerge_group(&dev->kobj, &pm_wakeup_attr_group);
}

#else /* CONFIG_PM_RUNTIME */

int dpm_sysfs_add(struct device * dev)
void rpm_sysfs_remove(struct device *dev)
{
	return sysfs_create_group(&dev->kobj, &pm_attr_group);
	sysfs_unmerge_group(&dev->kobj, &pm_runtime_attr_group);
}

void dpm_sysfs_remove(struct device *dev)
{
	rpm_sysfs_remove(dev);
	sysfs_unmerge_group(&dev->kobj, &pm_wakeup_attr_group);
	sysfs_remove_group(&dev->kobj, &pm_attr_group);
}

#endif
+29 −0
Original line number Diff line number Diff line
@@ -241,6 +241,35 @@ int device_wakeup_disable(struct device *dev)
}
EXPORT_SYMBOL_GPL(device_wakeup_disable);

/**
 * device_set_wakeup_capable - Set/reset device wakeup capability flag.
 * @dev: Device to handle.
 * @capable: Whether or not @dev is capable of waking up the system from sleep.
 *
 * If @capable is set, set the @dev's power.can_wakeup flag and add its
 * wakeup-related attributes to sysfs.  Otherwise, unset the @dev's
 * power.can_wakeup flag and remove its wakeup-related attributes from sysfs.
 *
 * This function may sleep and it can't be called from any context where
 * sleeping is not allowed.
 */
void device_set_wakeup_capable(struct device *dev, bool capable)
{
	if (!!dev->power.can_wakeup == !!capable)
		return;

	if (device_is_registered(dev)) {
		if (capable) {
			if (wakeup_sysfs_add(dev))
				return;
		} else {
			wakeup_sysfs_remove(dev);
		}
	}
	dev->power.can_wakeup = capable;
}
EXPORT_SYMBOL_GPL(device_set_wakeup_capable);

/**
 * device_init_wakeup - Device wakeup initialization.
 * @dev: Device to handle.
Loading