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

PM / Wakeup: Introduce wakeup source objects and event statistics (v3)



Introduce struct wakeup_source for representing system wakeup sources
within the kernel and for collecting statistics related to them.
Make the recently introduced helper functions pm_wakeup_event(),
pm_stay_awake() and pm_relax() use struct wakeup_source objects
internally, so that wakeup statistics associated with wakeup devices
can be collected and reported in a consistent way (the definition of
pm_relax() is changed, which is harmless, because this function is
not called directly by anyone yet).  Introduce new wakeup-related
sysfs device attributes in /sys/devices/.../power for reporting the
device wakeup statistics.

Change the global wakeup events counters event_count and
events_in_progress into atomic variables, so that it is not necessary
to acquire a global spinlock in pm_wakeup_event(), pm_stay_awake()
and pm_relax(), which should allow us to avoid lock contention in
these functions on SMP systems with many wakeup devices.

Signed-off-by: default avatarRafael J. Wysocki <rjw@sisk.pl>
Acked-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 0702d9ee
Loading
Loading
Loading
Loading
+70 −0
Original line number Original line Diff line number Diff line
@@ -77,3 +77,73 @@ Description:
		devices this attribute is set to "enabled" by bus type code or
		devices this attribute is set to "enabled" by bus type code or
		device drivers and in that cases it should be safe to leave the
		device drivers and in that cases it should be safe to leave the
		default value.
		default value.

What:		/sys/devices/.../power/wakeup_count
Date:		September 2010
Contact:	Rafael J. Wysocki <rjw@sisk.pl>
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.

What:		/sys/devices/.../power/wakeup_active_count
Date:		September 2010
Contact:	Rafael J. Wysocki <rjw@sisk.pl>
Description:
		The /sys/devices/.../wakeup_active_count attribute contains the
		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.

What:		/sys/devices/.../power/wakeup_hit_count
Date:		September 2010
Contact:	Rafael J. Wysocki <rjw@sisk.pl>
Description:
		The /sys/devices/.../wakeup_hit_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 empty.

What:		/sys/devices/.../power/wakeup_active
Date:		September 2010
Contact:	Rafael J. Wysocki <rjw@sisk.pl>
Description:
		The /sys/devices/.../wakeup_active attribute contains either 1,
		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.

What:		/sys/devices/.../power/wakeup_total_time_ms
Date:		September 2010
Contact:	Rafael J. Wysocki <rjw@sisk.pl>
Description:
		The /sys/devices/.../wakeup_total_time_ms attribute contains
		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.

What:		/sys/devices/.../power/wakeup_max_time_ms
Date:		September 2010
Contact:	Rafael J. Wysocki <rjw@sisk.pl>
Description:
		The /sys/devices/.../wakeup_max_time_ms attribute contains
		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.

What:		/sys/devices/.../power/wakeup_last_time_ms
Date:		September 2010
Contact:	Rafael J. Wysocki <rjw@sisk.pl>
Description:
		The /sys/devices/.../wakeup_last_time_ms attribute contains
		the value of the monotonic clock corresponding to the time of
		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.
+3 −1
Original line number Original line Diff line number Diff line
@@ -60,7 +60,8 @@ void device_pm_init(struct device *dev)
	dev->power.status = DPM_ON;
	dev->power.status = DPM_ON;
	init_completion(&dev->power.completion);
	init_completion(&dev->power.completion);
	complete_all(&dev->power.completion);
	complete_all(&dev->power.completion);
	dev->power.wakeup_count = 0;
	dev->power.wakeup = NULL;
	spin_lock_init(&dev->power.lock);
	pm_runtime_init(dev);
	pm_runtime_init(dev);
}
}


@@ -120,6 +121,7 @@ void device_pm_remove(struct device *dev)
	mutex_lock(&dpm_list_mtx);
	mutex_lock(&dpm_list_mtx);
	list_del_init(&dev->power.entry);
	list_del_init(&dev->power.entry);
	mutex_unlock(&dpm_list_mtx);
	mutex_unlock(&dpm_list_mtx);
	device_wakeup_disable(dev);
	pm_runtime_remove(dev);
	pm_runtime_remove(dev);
}
}


+1 −0
Original line number Original line Diff line number Diff line
@@ -34,6 +34,7 @@ extern void device_pm_move_last(struct device *);


static inline void device_pm_init(struct device *dev)
static inline void device_pm_init(struct device *dev)
{
{
	spin_lock_init(&dev->power.lock);
	pm_runtime_init(dev);
	pm_runtime_init(dev);
}
}


+0 −2
Original line number Original line Diff line number Diff line
@@ -1099,8 +1099,6 @@ EXPORT_SYMBOL_GPL(pm_runtime_allow);
 */
 */
void pm_runtime_init(struct device *dev)
void pm_runtime_init(struct device *dev)
{
{
	spin_lock_init(&dev->power.lock);

	dev->power.runtime_status = RPM_SUSPENDED;
	dev->power.runtime_status = RPM_SUSPENDED;
	dev->power.idle_notification = false;
	dev->power.idle_notification = false;


+119 −2
Original line number Original line Diff line number Diff line
@@ -210,11 +210,122 @@ static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store);
static ssize_t wakeup_count_show(struct device *dev,
static ssize_t wakeup_count_show(struct device *dev,
				struct device_attribute *attr, char *buf)
				struct device_attribute *attr, char *buf)
{
{
	return sprintf(buf, "%lu\n", dev->power.wakeup_count);
	unsigned long count = 0;
	bool enabled = false;

	spin_lock_irq(&dev->power.lock);
	if (dev->power.wakeup) {
		count = dev->power.wakeup->event_count;
		enabled = true;
	}
	spin_unlock_irq(&dev->power.lock);
	return enabled ? sprintf(buf, "%lu\n", count) : sprintf(buf, "\n");
}
}


static DEVICE_ATTR(wakeup_count, 0444, wakeup_count_show, NULL);
static DEVICE_ATTR(wakeup_count, 0444, wakeup_count_show, NULL);
#endif

static ssize_t wakeup_active_count_show(struct device *dev,
				struct device_attribute *attr, char *buf)
{
	unsigned long count = 0;
	bool enabled = false;

	spin_lock_irq(&dev->power.lock);
	if (dev->power.wakeup) {
		count = dev->power.wakeup->active_count;
		enabled = true;
	}
	spin_unlock_irq(&dev->power.lock);
	return enabled ? sprintf(buf, "%lu\n", count) : sprintf(buf, "\n");
}

static DEVICE_ATTR(wakeup_active_count, 0444, wakeup_active_count_show, NULL);

static ssize_t wakeup_hit_count_show(struct device *dev,
				struct device_attribute *attr, char *buf)
{
	unsigned long count = 0;
	bool enabled = false;

	spin_lock_irq(&dev->power.lock);
	if (dev->power.wakeup) {
		count = dev->power.wakeup->hit_count;
		enabled = true;
	}
	spin_unlock_irq(&dev->power.lock);
	return enabled ? sprintf(buf, "%lu\n", count) : sprintf(buf, "\n");
}

static DEVICE_ATTR(wakeup_hit_count, 0444, wakeup_hit_count_show, NULL);

static ssize_t wakeup_active_show(struct device *dev,
				struct device_attribute *attr, char *buf)
{
	unsigned int active = 0;
	bool enabled = false;

	spin_lock_irq(&dev->power.lock);
	if (dev->power.wakeup) {
		active = dev->power.wakeup->active;
		enabled = true;
	}
	spin_unlock_irq(&dev->power.lock);
	return enabled ? sprintf(buf, "%u\n", active) : sprintf(buf, "\n");
}

static DEVICE_ATTR(wakeup_active, 0444, wakeup_active_show, NULL);

static ssize_t wakeup_total_time_show(struct device *dev,
				struct device_attribute *attr, char *buf)
{
	s64 msec = 0;
	bool enabled = false;

	spin_lock_irq(&dev->power.lock);
	if (dev->power.wakeup) {
		msec = ktime_to_ms(dev->power.wakeup->total_time);
		enabled = true;
	}
	spin_unlock_irq(&dev->power.lock);
	return enabled ? sprintf(buf, "%lld\n", msec) : sprintf(buf, "\n");
}

static DEVICE_ATTR(wakeup_total_time_ms, 0444, wakeup_total_time_show, NULL);

static ssize_t wakeup_max_time_show(struct device *dev,
				struct device_attribute *attr, char *buf)
{
	s64 msec = 0;
	bool enabled = false;

	spin_lock_irq(&dev->power.lock);
	if (dev->power.wakeup) {
		msec = ktime_to_ms(dev->power.wakeup->max_time);
		enabled = true;
	}
	spin_unlock_irq(&dev->power.lock);
	return enabled ? sprintf(buf, "%lld\n", msec) : sprintf(buf, "\n");
}

static DEVICE_ATTR(wakeup_max_time_ms, 0444, wakeup_max_time_show, NULL);

static ssize_t wakeup_last_time_show(struct device *dev,
				struct device_attribute *attr, char *buf)
{
	s64 msec = 0;
	bool enabled = false;

	spin_lock_irq(&dev->power.lock);
	if (dev->power.wakeup) {
		msec = ktime_to_ms(dev->power.wakeup->last_time);
		enabled = true;
	}
	spin_unlock_irq(&dev->power.lock);
	return enabled ? sprintf(buf, "%lld\n", msec) : sprintf(buf, "\n");
}

static DEVICE_ATTR(wakeup_last_time_ms, 0444, wakeup_last_time_show, NULL);
#endif /* CONFIG_PM_SLEEP */


#ifdef CONFIG_PM_ADVANCED_DEBUG
#ifdef CONFIG_PM_ADVANCED_DEBUG
#ifdef CONFIG_PM_RUNTIME
#ifdef CONFIG_PM_RUNTIME
@@ -288,6 +399,12 @@ static struct attribute * power_attrs[] = {
	&dev_attr_wakeup.attr,
	&dev_attr_wakeup.attr,
#ifdef CONFIG_PM_SLEEP
#ifdef CONFIG_PM_SLEEP
	&dev_attr_wakeup_count.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
#endif
#ifdef CONFIG_PM_ADVANCED_DEBUG
#ifdef CONFIG_PM_ADVANCED_DEBUG
	&dev_attr_async.attr,
	&dev_attr_async.attr,
Loading